const path = require('path') const fs = require('fs') const hash = require('hash-sum') const crypto = require('crypto') const escapeStringRegexp = require('escape-string-regexp') const escapeGlob = require('glob-escape') const webpack = require('webpack') const uniI18n = require('@dcloudio/uni-cli-i18n') const isWin = /^win/.test(process.platform) const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path) let aboutPkg try { aboutPkg = require(path.resolve(__dirname, '../../../../../about/package.json')) } catch (e) {} const isInHBuilderX = !!aboutPkg const isInHBuilderXAlpha = !!(isInHBuilderX && aboutPkg.alpha) function getCLIContext () { var context = path.resolve(__dirname, '../../../../') // const isInHBuilderX = fs.existsSync(path.resolve(context, 'bin/uniapp-cli.js')) if (isInHBuilderX) { return context } const pnpmFind = __dirname.match(/.+?[/\\].pnpm[/\\]/) if (pnpmFind) { const pnpm = pnpmFind[0] context = path.resolve(pnpm, '../../') } const isInCLI = fs.existsSync(path.resolve(context, './src')) if (isInCLI) { return context } return process.cwd() } function removeExt (str, ext) { if (ext) { const reg = new RegExp(ext.replace(/\./, '\\.') + '$') return normalizePath(str.replace(reg, '')) } return normalizePath(str.replace(/\.\w+$/g, '')) } function hashify (filepath) { const relativePath = removeExt(path.relative(process.env.UNI_INPUT_DIR, filepath)) return hash(relativePath) } function md5 (str) { const hash = crypto.createHash('md5') hash.update(str) return hash.digest('hex') } function cached (fn) { const cache = Object.create(null) return function cachedFn (str) { const hit = cache[str] return hit || (cache[str] = fn(str)) } } const camelizeRE = /-(\w)/g const camelize = cached((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') }) function capitalize (str) { return str.charAt(0).toUpperCase() + str.slice(1) } const hyphenateRE = /\B([A-Z])/g const hyphenate = cached((str) => { return str.replace(hyphenateRE, '-$1').toLowerCase() }) const REGEX_PX = /(:|\s|\(|\/)[+-]?\d+(\.\d+)?u?px/g const REGEX_UPX = /(:|\s|\(|\/)[+-]?\d+(\.\d+)?upx/g function convertStaticStyle (styleStr) { if (typeof styleStr === 'string') { let matches = styleStr.match(REGEX_UPX) if (matches && matches.length) { matches.forEach(function (match) { styleStr = styleStr.replace(match, match.substr(0, match.length - 3) + 'rpx') }) } // TODO 不应该再支持 px 转 rpx if (process.UNI_TRANSFORM_PX) { // 需要转换 px matches = styleStr.match(REGEX_PX) if (matches && matches.length) { matches.forEach(function (match) { styleStr = styleStr.replace(match, match.substr(0, match.length - 2) + 'rpx') }) } } } return styleStr } function hasModule (name) { try { return !!require.resolve(name) } catch (e) {} return false } const NODE_MODULES_REGEX = /(\.\.\/)?node_modules/g function normalizeNodeModules (str) { str = normalizePath(str).replace(NODE_MODULES_REGEX, 'node-modules') // HBuilderX 内置模块路径转换 str = str.replace(/.*\/plugins\/uniapp-cli\/node[-_]modules/, 'node-modules') if (process.env.UNI_PLATFORM === 'mp-alipay') { str = str.replace('node-modules/@', 'node-modules/npm-scope-') } return str } const _hasOwnProperty = Object.prototype.hasOwnProperty /** * pathToRegexp * @param {string} pathString * @param {{start:?boolean,end:?boolean,global:?boolean}?} options * @returns {RegExp} */ function pathToRegexp (pathString, options = {}) { return new RegExp((options.start ? '^' : '') + escapeStringRegexp(pathString) + (options.end ? '$' : ''), 'i' + (options.global ? 'g' : '')) } /** * pathToGlob * @param {string} pathString * @param {string} glob * @param {{windows:?boolean,escape:?boolean}?} options * @returns {string} */ function pathToGlob (pathString, glob, options = {}) { const isWindows = 'windows' in options ? options.windows : /^win/.test(process.platform) const useEscape = options.escape const str = isWindows ? pathString.replace(/\\/g, '/') : pathString let safeStr = escapeGlob(str) if (isWindows || !useEscape) { safeStr = safeStr.replace(/\\(.)/g, '[$1]') } return path.posix.join(safeStr, glob) } /** * 字节跳动小程序可以配置 ext:// 开头的插件页面模板,如 ext://microapp-trade-plugin/order-confirm * @param pagePath * @returns */ function isNormalPage (pagePath) { return !pagePath.startsWith('ext://') } function createSource (content) { return webpack.version[0] > 4 ? new webpack.sources.RawSource(content) : { size () { return Buffer.byteLength(content, 'utf8') }, source () { return content } } } function deleteAsset (compilation, name) { if ('deleteAsset' in compilation) { compilation.deleteAsset(name) } else { delete compilation.assets[name] } } /** * cli 项目增加运行方式提示 https://ask.dcloud.net.cn/question/165071 * App 端存在多次编译问题(vue 2 nvue 3),故做防抖处理 * @param delay * @returns */ let runPromptTimer = null function showRunPrompt (delay = 300) { if (runPromptTimer) { clearTimeout(runPromptTimer) } runPromptTimer = setTimeout(() => { if (!isInHBuilderX) { const chalk = require('chalk') const outputDir = path.relative( process.env.UNI_CLI_CONTEXT, process.env.UNI_OUTPUT_DIR ) const platform = process.env.UNI_PLATFORM.startsWith('quickapp-webview') && process.env.UNI_SUB_PLATFORM ? process.env.UNI_SUB_PLATFORM : process.env.UNI_PLATFORM console.log(uniI18n.__('prompt.run.message', { devtools: uniI18n.__(`prompt.run.devtools.${platform}`), outputDir: chalk.cyan(outputDir) })) } runPromptTimer = null }, delay) } module.exports = { isNormalPage, isInHBuilderX, isInHBuilderXAlpha, getCLIContext, normalizeNodeModules, md5, hasOwn (obj, key) { return _hasOwnProperty.call(obj, key) }, hasModule, parseStyle (style = {}) { Object.keys(style).forEach(name => { if (global.uniPlugin.platforms.includes(name)) { if (name === process.env.UNI_PLATFORM) { Object.assign(style, style[name] || {}) } delete style[name] } }) return style }, hashify, removeExt, camelize, capitalize, hyphenate, normalizePath, convertStaticStyle, pathToRegexp, pathToGlob, getComponentName: cached((str) => { if (str.indexOf('wx-') === 0) { return str.replace('wx-', 'weixin-') } return str }), getTemplatePath () { return path.join(__dirname, '../template') }, createSource, deleteAsset, showRunPrompt }