123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- /* eslint-disable no-useless-escape */
- const os = require('os');
- const path = require('path');
- const crypto = require('crypto');
- const fs = require('fs-extra');
- const UNI_PLATFORM = process.env.UNI_PLATFORM || 'h5';
- const NODE_ENV = process.env.NODE_ENV || 'production';
- /**
- * 完整版本号,例如:3.9.0-16751-d1195b1d-20240605.0345
- * 通过环境变量传入
- * */
- const VUE_APP_LONG_VERSION_NAME = process.env.VUE_APP_LONG_VERSION_NAME || '';
- /**
- * 环境,编译生产包时值为 prod
- * 通过环境变量传入
- * */
- const VUE_APP_STAGE = process.env.VUE_APP_STAGE || '';
- const __PROD__ = NODE_ENV === 'production';
- const CDN_URL = process.env.CDN_URL;
- /**
- * yarn dev:mp-weixin 时,需要在本地启动 http server 来承载写在 css 中的静态资源,此变量为 http server 的端口。
- *
- * 通过环境变量传入,不传时默认值为 5002
- *
- * 如果提示端口被占用(很大可能是之前的dev server异常退出了)
- * 可以用 lsof -i:5000 查看是哪个进程占用,
- * 然后用 kill -9 [pid] 杀掉该进程
- */
- const IMAGES_SERVER_PORT = process.env.IMAGES_SERVER_PORT || '5002';
- /** yarn dev:mp-weixin 时用到的本地 http server 地址 */
- const localPath = `http://${getLocalIP()}:${IMAGES_SERVER_PORT}/`;
- /**
- * CDN 地址,build 时用到。
- * 如无需使用CDN(比如H5平台有时会不适用CDN),则打包时使用类似 CDN_URL= yarn build:h5 的命令
- * */
- const publicPath =
- CDN_URL !== undefined
- ? CDN_URL
- : VUE_APP_STAGE === 'prod'
- ? 'https://static.kerryprops.com.cn/kip/temporary-parking-frontend/'
- : 'https://static-le.kerryprops.com.cn/kip/temporary-parking-frontend/';
- const isWin = /^win/.test(process.platform);
- const normalizePath = (pathIn) => (isWin ? pathIn.replace(/\\/g, '/') : pathIn);
- /** 临时文件夹,编译生产包时,静态资源会被拷贝到该文件夹中,Jenkins 会将该文件中的文件上传到 CDN */
- const CDN_UPLOAD_FOLDER = 'temp';
- const REGEX_IMG = /\.(gif|jpe?g|png|svg)$/;
- const REGEX_FONT = /\.(woff2?|eot|ttf|otf)$/;
- const DIR_ASSET = 'static';
- const DIR_IMG = 'img';
- const DIR_FONTS = 'fonts';
- /** 获取本级 IP */
- function getLocalIP() {
- const ifaces = Object.values(os.networkInterfaces());
- for (const iface of ifaces) {
- for (const alias of iface) {
- if (alias.internal || alias.family !== 'IPv4') continue;
- return alias.address;
- }
- }
- }
- /**
- * 计算文件的 hash 值,返回包含 hash 值的文件路径
- * 注意:只能处理这几类文件:png|jpe?g|gif|svg|ttf
- * @param {string} resourcePath 文件在磁盘上的物理路径
- * @param {string} outputPath 待替换的输出路径,比如 static/img/abc.png
- * @returns 替换过的输出路径,比如 static/img/abc.65d956cc.png
- */
- function getFilePathWithHash(resourcePath, outputPath) {
- const buffer = fs.readFileSync(resourcePath);
- const hash = crypto
- .createHash('md5')
- .update(buffer)
- .digest('hex')
- .slice(0, 8);
- return outputPath.replace(/\.(png|jpe?g|gif|svg|ttf)$/, `.${hash}.$1`);
- }
- function formatImageUrl(url, filePath) {
- if (UNI_PLATFORM === 'app') {
- return url;
- }
- // console.log(filePath);
- const origin = __PROD__ ? publicPath : localPath;
- const absolutePath = url.startsWith('/')
- ? path.resolve('src', url.slice(1))
- : path.resolve(path.dirname(filePath), url);
- const href = origin + path.relative('src', absolutePath).replace(/\\/g, '/');
- // console.log(absolutePath);
- return __PROD__ ? getFilePathWithHash(absolutePath, href) : href;
- }
- /**
- * 返回当前git repo的header的commit id
- * @returns commit id,例如 fe40cff4c4a9b55eaf215ac79a0c37c079b067cd
- * @see https://stackoverflow.com/a/56975550/196519
- */
- function gitHash() {
- try {
- const rev = fs
- .readFileSync('.git/HEAD')
- .toString()
- .trim()
- .split(/.*[: ]/)
- .slice(-1)[0];
- if (rev.indexOf('/') === -1) {
- return rev;
- } else {
- return fs
- .readFileSync('.git/' + rev)
- .toString()
- .trim();
- }
- } catch (e) {
- // eslint-disable-next-line no-console
- console.log(e);
- return 'git_hash_error';
- }
- }
- /**
- * 根据配置替换文件内容
- * @param {{path: string, replace:[{reg: RegExp, replacement: string}]}} config
- */
- const fileContentReplace = function (config) {
- let content = fs.readFileSync(config.path).toString();
- config.replace.forEach((n) => {
- content = content.replace(n.reg, n.replacement);
- });
- // fs.writeFileSync(gradlePath + '2', gradle);
- fs.writeFileSync(config.path, content);
- };
- /**
- * 1. 在 ios app 的 AppInfo.xcconfig 中写入 versionName 和 versionCode
- * 2. 在 android app 的 build.gradle 中写入 versionName、versionCode 和 fileName
- */
- const updateVersionCode = function () {
- if (!VUE_APP_LONG_VERSION_NAME) {
- // eslint-disable-next-line no-console
- console.log('NO VUE_APP_LONG_VERSION_NAME, skip updateVersionCode()');
- return;
- }
- const arr = VUE_APP_LONG_VERSION_NAME.split('-');
- const versionName = arr[0];
- const versionCode = arr[1];
- if (!versionName || !versionCode) {
- // eslint-disable-next-line no-console
- console.log('missing versionName or versionCode, skip updateVersionCode()');
- return;
- }
- const env = VUE_APP_STAGE == 'prod' ? 'PROD' : 'DEV';
- const fileName = `KIPUI-${env}-${VUE_APP_LONG_VERSION_NAME}`;
- let config = {
- path: 'android/KIP-UI/simpleDemo/build.gradle',
- replace: [
- // { reg: /applicationId\s"(.*)"/, replacement: 'applicationId "' + buildConfigs[option.configuration].appId + '"' },
- {
- reg: /versionCode\s([\d\.]{1,})/,
- replacement: 'versionCode ' + versionCode,
- },
- {
- reg: /versionName\s"([\d\.]{1,})"/,
- replacement: 'versionName "' + versionName + '"',
- },
- {
- reg: /outputFileName\s=\s"(.*)"/,
- replacement: 'outputFileName = "' + fileName + '.apk"',
- },
- ],
- };
- fileContentReplace(config);
- // eslint-disable-next-line no-console
- console.log('updateVersionCode(): build.gradle DONE !');
- config = {
- path: 'ios/HBuilder-Hello/AppInfo.xcconfig',
- replace: [
- // { reg: /PRODUCT_NAME\s=\s(.*)/, replacement: 'PRODUCT_NAME = ' + buildConfigs[option.configuration].appName },
- // { reg: /PRODUCT_BUNDLE_IDENTIFIER\s=\s(.*)/, replacement: 'PRODUCT_BUNDLE_IDENTIFIER = ' + buildConfigs[option.configuration].appId },
- {
- reg: /MARKETING_VERSION\s=\s([\d\.]{1,})/,
- replacement: 'MARKETING_VERSION = ' + versionName,
- },
- {
- reg: /CURRENT_PROJECT_VERSION\s=\s([\d\.]{1,})/,
- replacement: 'CURRENT_PROJECT_VERSION = ' + versionCode,
- },
- ],
- };
- fileContentReplace(config);
- // eslint-disable-next-line no-console
- console.log('updateVersionCode(): AppInfo.xcconfig DONE !');
- };
- /**
- * 开发时,将【images文件夹下的图片】和【字体文件】的 url 转换为本地 http server 的地址
- *
- * build 时,将图片和字体的 url 转换为 CDN 地址,同时把文件 copy 到 temp 文件夹
- * @param {string} url url
- * @param {string} resourcePath 绝对路径
- * @returns 转换后的 url
- */
- const filePathHandler = (url, resourcePath) => {
- // 01. 处理 images 文件夹下的图片,包括 src/images/ 文件夹和 node_modules/@kip/ui-mobile 各组件中的 images 文件夹
- if (/images\//.test(resourcePath)) {
- if (!__PROD__) {
- // 开发时,用本地的 http server 来 host 静态资源
- // http://localhost:5000/src/images/abc/efg/hij.png
- return localPath + normalizePath(path.relative(__dirname, resourcePath));
- } else {
- // build 时,返回 CDN 地址,同时把文件 copy 到 temp 文件夹,
- // jenkins 会上传这些图片
- const tempPath = '/static/img/' + path.basename(resourcePath);
- const tempPathHashed = getFilePathWithHash(resourcePath, tempPath);
- fs.copy(resourcePath, './' + CDN_UPLOAD_FOLDER + tempPathHashed);
- // https://cdn.abc.com/static/img/hij.3677dgae.png
- return publicPath + tempPathHashed;
- }
- // 02. 处理字体文件
- } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(resourcePath)) {
- if (!__PROD__) {
- // 开发时,用本地的 http server 来 host 静态资源
- // http://localhost:5000/node_modules/@kip/ui-mobile/components/k-icon/kuiicons.ttf
- return localPath + normalizePath(path.relative(__dirname, resourcePath));
- } else {
- // build 时,返回 CDN 地址,同时把文件 copy 到 temp 文件夹,
- // jenkins 会上传这些字体
- const tempPath = '/static/fonts/' + path.basename(resourcePath);
- const tempPathHashed = getFilePathWithHash(resourcePath, tempPath);
- fs.copy(resourcePath, './' + CDN_UPLOAD_FOLDER + tempPathHashed);
- // https://cdn.abc.com/static/fonts/kuiicons.65d956cc.ttf
- return publicPath + tempPathHashed;
- }
- }
- // 03. 其他情况返回相对路径
- return (
- '/' + normalizePath(path.relative(process.env.UNI_INPUT_DIR, resourcePath))
- );
- };
- /**
- * 开发时,将 css 中的 url 转换为本地 http server 的地址
- *
- * build 时,返回 CDN 地址,同时把文件 copy 到 temp 文件夹
- * @param {string} resourcePath 绝对路径
- * @param {string} url css 中的原始 url
- * @returns 转换后的 url
- */
- const postcssUrlHandler = (resourcePath, url) => {
- // postcss-url 穿过来的 url 是 '@/images/home/css.png' 时,
- // resourcePath 会是 '/Users/joel.bh.zhang/Documents/Repo-template/uniapp-vue3-vite-clean/src/pages/index/@/images/home/css.png'
- // 需要处理一下
- if (url.startsWith('@')) {
- resourcePath = path.join(__dirname, './src/' + url.substring(1));
- console.log('after replace @ with ./src/ ==>', resourcePath);
- }
- // postcss-url 穿过来的 url: '/static/logo.png', 时,
- // resourcePath 会是 '/Users/joel.bh.zhang/Documents/Repo/ui-mobile/src/pages/index/static/logo.png',
- // 需要处理一下
- if (url.startsWith('/')) {
- resourcePath = path.join(__dirname, './src' + url);
- console.log('after replace start / with ./src/ ==>', resourcePath);
- }
- // 01. 处理 images 文件夹下的图片,包括 src/images/ 文件夹和 node_modules/@kip/ui-mobile 各组件中的 images 文件夹
- if (/\.(gif|jpe?g|png|svg)(\?.*)?$/.test(resourcePath)) {
- if (!__PROD__) {
- // 开发时,用本地的 http server 来 host 静态资源
- // http://localhost:5000/src/images/abc/efg/hij.png
- return localPath + normalizePath(path.relative(__dirname, resourcePath));
- } else {
- // build 时,返回 CDN 地址,同时把文件 copy 到 temp 文件夹,
- // jenkins 会上传这些图片
- const tempPath = '/static/img/' + path.basename(resourcePath);
- const tempPathHashed = getFilePathWithHash(resourcePath, tempPath);
- fs.copy(resourcePath, './' + CDN_UPLOAD_FOLDER + tempPathHashed);
- // https://cdn.abc.com/static/img/hij.3677dgae.png
- return publicPath + tempPathHashed;
- }
- // 02. 处理字体文件
- } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(resourcePath)) {
- if (!__PROD__) {
- // 开发时,用本地的 http server 来 host 静态资源
- // http://localhost:5000/node_modules/@kip/ui-mobile/components/k-icon/kuiicons.ttf
- return localPath + normalizePath(path.relative(__dirname, resourcePath));
- } else {
- // build 时,返回 CDN 地址,同时把文件 copy 到 temp 文件夹,
- // jenkins 会上传这些字体
- const tempPath = '/static/fonts/' + path.basename(resourcePath);
- const tempPathHashed = getFilePathWithHash(resourcePath, tempPath);
- fs.copy(resourcePath, './' + CDN_UPLOAD_FOLDER + tempPathHashed);
- // https://cdn.abc.com/static/fonts/kuiicons.65d956cc.ttf
- return publicPath + tempPathHashed;
- }
- }
- // 03. 其他情况返回相对路径
- return (
- '/' + normalizePath(path.relative(process.env.UNI_INPUT_DIR, resourcePath))
- );
- };
- /**
- * 处理 vite 在编译的时候出现 use-window-z-index.mjs 被
- * Could not resolve "../composables/use-window-z-index.mjs" from "node_modules/vant/es/config-provider/ConfigProvider.mjs"
- * file: /Users/sysadmin/code/kerry_project/temporary-parking-frontend/node_modules/vant/es/config-provider/ConfigProvider.mjs
- * 这个问题主要是在 vite.config.js 中配置了 define: {global: 'window'} 导致的
- * @param {string} params.scanDir 相对路径: node_modules/vant
- * @returns
- */
- function handleGlobalFiles({
- scanDir
- }) {
- return {
- name: 'handle-global-files',
- buildStart() {
- if(scanDir) {
- const rootDir = `${scanDir}`; // 指定你的源代码根目录
- // 递归函数,遍历所有子目录
- function traverseDirectory(dir) {
- const files = fs.readdirSync(dir);
- files.forEach(file => {
- const fullPath = path.join(dir, file);
- const stat = fs.statSync(fullPath);
- if (stat.isDirectory()) {
- // 如果是目录,递归处理
- traverseDirectory(fullPath);
- } else if (file.includes('global') && !fs.existsSync(file)) {
- // 如果文件名包含 'global' 并且还没有被创建的话
- const content = fs.readFileSync(fullPath, 'utf-8');
- const newContent = content.replace(/global/g, 'window');
- const newFileName = file.replace('global', 'window');
- const newFilePath = path.join(dir, newFileName);
- // 拷贝一份新的文件
- fs.writeFileSync(newFilePath, newContent, 'utf-8');
- }
- });
- }
- // 开始遍历根目录
- traverseDirectory(rootDir);
- }
- }
- };
- }
- module.exports = {
- IMAGES_SERVER_PORT,
- getLocalIP,
- getFilePathWithHash,
- formatImageUrl,
- gitHash,
- updateVersionCode,
- localPath,
- publicPath,
- filePathHandler,
- postcssUrlHandler,
- REGEX_IMG,
- REGEX_FONT,
- DIR_ASSET,
- DIR_IMG,
- DIR_FONTS,
- CDN_UPLOAD_FOLDER,
- handleGlobalFiles,
- };
|