utils.ts 7.1 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1
import fs from 'fs'
fxy060608's avatar
fxy060608 已提交
2 3
import os from 'os'
import path from 'path'
fxy060608's avatar
fxy060608 已提交
4
import colors from 'picocolors'
fxy060608's avatar
fxy060608 已提交
5
import { camelize, capitalize } from '@vue/shared'
fxy060608's avatar
fxy060608 已提交
6
export { default as hash } from 'hash-sum'
fxy060608's avatar
fxy060608 已提交
7 8 9 10 11 12 13
import {
  EXTNAME_TS_RE,
  PAGE_EXTNAME,
  PAGE_EXTNAME_APP,
  X_PAGE_EXTNAME,
  X_PAGE_EXTNAME_APP,
} from './constants'
fxy060608's avatar
fxy060608 已提交
14

15
import {
fxy060608's avatar
fxy060608 已提交
16
  type ElementNode,
17
  NodeTypes,
fxy060608's avatar
fxy060608 已提交
18 19
  type RootNode,
  type TemplateChildNode,
20
} from '@vue/compiler-core'
fxy060608's avatar
fxy060608 已提交
21
import type { ParserPlugin } from '@babel/parser'
fxy060608's avatar
fxy060608 已提交
22
import { getPlatformDir } from './platform'
fxy060608's avatar
fxy060608 已提交
23
import { isInHBuilderX } from './hbx'
fxy060608's avatar
fxy060608 已提交
24
import { parseManifestJsonOnce } from './json'
fxy060608's avatar
fxy060608 已提交
25

fxy060608's avatar
fxy060608 已提交
26 27 28
// 专为 uts.ts 服务
export { camelize, capitalize, isArray } from '@vue/shared'

29 30 31 32
// export let isRunningWithYarnPnp: boolean
// try {
//   isRunningWithYarnPnp = Boolean(require('pnpapi'))
// } catch {}
fxy060608's avatar
fxy060608 已提交
33

34
export const isWindows = os.platform() === 'win32'
fxy060608's avatar
fxy060608 已提交
35
export function normalizePath(id: string): string {
36
  return isWindows ? id.replace(/\\/g, '/') : id
fxy060608's avatar
fxy060608 已提交
37
}
fxy060608's avatar
fxy060608 已提交
38

雪洛's avatar
雪洛 已提交
39
export function checkElementNodeTag(
40
  node: RootNode | TemplateChildNode | null | undefined,
雪洛's avatar
雪洛 已提交
41 42
  tag: string
): node is ElementNode {
43
  return !!node && node.type === NodeTypes.ELEMENT && node.tag === tag
雪洛's avatar
雪洛 已提交
44 45
}

46 47 48 49 50
/**
 * 根据 path 返回合法 js 变量
 * @param str pages.json.page.path
 * @returns
 */
fxy060608's avatar
fxy060608 已提交
51
export function normalizeIdentifier(str: string) {
52
  let _str = str.replace(/[^a-zA-Z0-9]+/g, '-')
53
  _str = capitalize(camelize(_str))
54 55 56 57
  // 不允许数字开头,补充 _
  if (/^\d/.test(_str)) {
    _str = '_' + _str
  }
58
  return _str
fxy060608's avatar
fxy060608 已提交
59
}
fxy060608's avatar
fxy060608 已提交
60 61

export function normalizePagePath(pagePath: string, platform: UniApp.PLATFORM) {
fxy060608's avatar
fxy060608 已提交
62
  const absolutePagePath = path.resolve(process.env.UNI_INPUT_DIR, pagePath)
fxy060608's avatar
fxy060608 已提交
63 64
  const isX = process.env.UNI_APP_X === 'true'
  let extensions = isX ? X_PAGE_EXTNAME : PAGE_EXTNAME
fxy060608's avatar
fxy060608 已提交
65
  if (platform === 'app') {
fxy060608's avatar
fxy060608 已提交
66
    extensions = isX ? X_PAGE_EXTNAME_APP : PAGE_EXTNAME_APP
fxy060608's avatar
fxy060608 已提交
67
  }
fxy060608's avatar
fxy060608 已提交
68 69 70
  for (let i = 0; i < extensions.length; i++) {
    const extname = extensions[i]
    if (fs.existsSync(absolutePagePath + extname)) {
fxy060608's avatar
fxy060608 已提交
71 72 73 74 75
      return pagePath + extname
    }
  }
  console.error(`${pagePath} not found`)
}
fxy060608's avatar
fxy060608 已提交
76

fxy060608's avatar
fxy060608 已提交
77 78
export function removeExt(str: string) {
  return str.split('?')[0].replace(/\.\w+$/g, '')
fxy060608's avatar
fxy060608 已提交
79
}
fxy060608's avatar
fxy060608 已提交
80 81 82 83 84 85

const NODE_MODULES_REGEX = /(\.\.\/)?node_modules/g

export function normalizeNodeModules(str: string) {
  str = normalizePath(str).replace(NODE_MODULES_REGEX, 'node-modules')
  // HBuilderX 内置模块路径转换
fxy060608's avatar
fxy060608 已提交
86 87 88 89
  str = str.replace(
    /.*\/plugins\/uniapp-cli-vite\/node[-_]modules/,
    'node-modules'
  )
fxy060608's avatar
fxy060608 已提交
90 91 92 93 94 95 96 97 98 99
  if (!isInHBuilderX()) {
    // 内部测试
    if (str.includes('uni-app-next/packages/')) {
      str = str.replace(
        /.*\/uni-app-next\/packages\//,
        'node-modules/@dcloudio/'
      )
    }
  }

fxy060608's avatar
fxy060608 已提交
100 101 102 103 104
  if (process.env.UNI_PLATFORM === 'mp-alipay') {
    str = str.replace('node-modules/@', 'node-modules/npm-scope-')
  }
  return str
}
fxy060608's avatar
fxy060608 已提交
105 106 107 108 109

export function normalizeMiniProgramFilename(
  filename: string,
  inputDir?: string
) {
fxy060608's avatar
fxy060608 已提交
110
  if (!inputDir || !path.isAbsolute(filename)) {
fxy060608's avatar
fxy060608 已提交
111 112 113 114
    return normalizeNodeModules(filename)
  }
  return normalizeNodeModules(path.relative(inputDir, filename))
}
fxy060608's avatar
fxy060608 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127 128

export function normalizeParsePlugins(
  importer: string,
  babelParserPlugins?: ParserPlugin[]
) {
  const isTS = EXTNAME_TS_RE.test(importer.split('?')[0])
  const plugins: ParserPlugin[] = []
  if (isTS) {
    plugins.push('jsx')
  }
  if (babelParserPlugins) plugins.push(...babelParserPlugins)
  if (isTS) plugins.push('typescript', 'decorators-legacy')
  return plugins
}
Q
qiang 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

export function pathToGlob(
  pathString: string,
  glob: string,
  options: { windows?: boolean; escape?: boolean } = {}
): string {
  const isWindows =
    'windows' in options ? options.windows : /^win/.test(process.platform)
  const useEscape = options.escape
  const str = isWindows ? pathString.replace(/\\/g, '/') : pathString
  let safeStr = str.replace(
    /[\\*?[\]{}()!]/g,
    isWindows || !useEscape ? '[$&]' : '\\$&'
  )
  return path.posix.join(safeStr, glob)
}
fxy060608's avatar
fxy060608 已提交
145

fxy060608's avatar
fxy060608 已提交
146 147 148 149
export function resolveSourceMapPath(
  outputDir?: string,
  platform?: UniApp.PLATFORM
) {
fxy060608's avatar
fxy060608 已提交
150
  return path.resolve(
fxy060608's avatar
fxy060608 已提交
151 152
    outputDir || process.env.UNI_OUTPUT_DIR,
    '../.sourcemap/' + (platform || getPlatformDir())
fxy060608's avatar
fxy060608 已提交
153
  )
fxy060608's avatar
fxy060608 已提交
154
}
fxy060608's avatar
fxy060608 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

function hasProjectYarn(cwd: string) {
  return fs.existsSync(path.join(cwd, 'yarn.lock'))
}

function hasProjectPnpm(cwd: string) {
  return fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))
}

function getInstallCommand(cwd: string) {
  return hasProjectYarn(cwd)
    ? 'yarn add'
    : hasProjectPnpm(cwd)
    ? 'pnpm i'
    : 'npm i'
}

export function installDepTips(
  type: 'dependencies' | 'devDependencies',
  module: string,
  version?: string
) {
  return `Cannot find module: ${module}
Please run \`${colors.cyan(
    `${getInstallCommand(process.cwd())} ${
      module + (version ? '@' + version : '')
    }${type === 'devDependencies' ? ' -D' : ''}`
  )}\` and try again.`
}
fxy060608's avatar
fxy060608 已提交
184

185 186 187 188 189
/**
 * 根据路径判断是否为 App.(u?)vue
 * @param {string} filename 相对、绝对路径
 * @returns
 */
fxy060608's avatar
fxy060608 已提交
190
export function isAppVue(filename: string) {
191 192
  const _filePath = normalizePath(filename)
  return /(\/|\\)app\.(u?)vue$/.test(_filePath.toLowerCase())
fxy060608's avatar
fxy060608 已提交
193 194 195
}

export function resolveAppVue(inputDir: string) {
196 197 198 199 200
  if (process.env.UNI_APP_X === 'true') {
    const appUVue = path.resolve(inputDir, 'App.uvue')
    if (fs.existsSync(appUVue)) {
      return normalizePath(appUVue)
    }
fxy060608's avatar
fxy060608 已提交
201 202 203
  }
  return normalizePath(path.resolve(inputDir, 'App.vue'))
}
204 205 206 207 208 209 210 211 212 213 214 215

export function parseImporter(importer: string) {
  importer = importer.split('?')[0]
  if (path.isAbsolute(importer)) {
    return normalizePath(path.relative(process.env.UNI_INPUT_DIR, importer))
  }
  return importer
}

export function createResolveErrorMsg(source: string, importer: string) {
  return `Could not resolve "${source}" from "${parseImporter(importer)}"`
}
fxy060608's avatar
fxy060608 已提交
216 217 218 219 220 221 222

export function enableSourceMap() {
  return (
    process.env.NODE_ENV === 'development' &&
    process.env.UNI_COMPILE_TARGET !== 'uni_modules'
  )
}
fxy060608's avatar
fxy060608 已提交
223 224 225 226 227 228 229 230

export function requireUniHelpers() {
  require(path.resolve(
    process.env.UNI_HBUILDERX_PLUGINS,
    'uni_helpers/lib/bytenode'
  ))
  return require(path.join(process.env.UNI_HBUILDERX_PLUGINS, 'uni_helpers'))
}
fxy060608's avatar
fxy060608 已提交
231 232

export function normalizeEmitAssetFileName(fileName: string) {
fxy060608's avatar
fxy060608 已提交
233 234
  const extname = path.extname(fileName)

fxy060608's avatar
fxy060608 已提交
235
  if (process.env.UNI_APP_X_TSC === 'true') {
fxy060608's avatar
fxy060608 已提交
236
    if (extname !== '.ts') {
fxy060608's avatar
fxy060608 已提交
237 238
      return fileName + '.ts'
    }
fxy060608's avatar
fxy060608 已提交
239 240 241 242 243
  } else {
    // logo.png、pages.json 等
    if (!['.ts', '.uts', '.uvue', '.vue'].includes(extname)) {
      fileName = fileName + '.uts'
    }
fxy060608's avatar
fxy060608 已提交
244 245 246
  }
  return fileName
}
fxy060608's avatar
fxy060608 已提交
247

fxy060608's avatar
fxy060608 已提交
248
function createIdent() {
fxy060608's avatar
fxy060608 已提交
249 250 251 252 253 254 255 256 257
  if (process.env.UNI_INPUT_DIR) {
    const manifestJson = parseManifestJsonOnce(process.env.UNI_INPUT_DIR)
    const id = (manifestJson.appid || '').replace('__UNI__', '')
    if (id) {
      return Buffer.from(Buffer.from(id).toString('base64')).toString('hex')
    }
  }
  return ''
}
fxy060608's avatar
fxy060608 已提交
258 259

export function createShadowImageUrl(cdn: number, type: string = 'grey') {
fxy060608's avatar
fxy060608 已提交
260 261 262 263 264
  let identStr = ''
  if (process.env.UNI_PLATFORM !== 'h5' && process.env.UNI_PLATFORM !== 'web') {
    const ident = createIdent()
    identStr = ident ? `${ident}/` : ''
  }
fxy060608's avatar
fxy060608 已提交
265 266 267 268
  return `https://cdn${
    (cdn || 0) + (process.env.UNI_APP_X === 'true' ? 1000 : 0) || ''
  }.dcloud.net.cn/${identStr}img/shadow-${type}.png`
}