| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- /* eslint-disable no-await-in-loop */
- /*
- * @Author: cheaz
- * @Date: 2024-08-01 16:27:18
- * @LastEditors: cheaz
- * @LastEditTime: 2024-08-12 22:09:47
- * @Description: 工程发布至私有仓库
- */
- const params = process.argv.slice(2)[2]; // 获取打包参数
- const fs = require('fs');
- const path = require('path');
- const fsPromise = require('fs').promises;
- const { exec } = require('child_process');
- const { Parser } = require('acorn');
- const vueParser = require('vue-eslint-parser');
- const { resolveConfig } = require('vite');
- let packageVersion = require('./package.json').version;
- let packageName = require('./package.json').name;
- const entryFile = require('./package.json').main;
- const packageDependencies = require('./package.json').dependencies;
- // 自定义文件夹列表
- const customPathList = [`src/${packageName}`, `src/views/${packageName}`];
- // 添加支持多模块打包
- packageName = params || packageName;
- params && (packageVersion = require(`./src/views/${params}/package.json`).version);
- const viteConfig = require('./vite.config.js');
- let aliasPlugin;
- let resolvPlugin;
- /**
- * 加载vite配置,从vite中获取alias和resolve插件
- */
- const getResolvePlugin = async () => {
- const vitePlugins = (await resolveConfig(viteConfig, 'build', 'production', 'production')).worker.plugins;
- aliasPlugin = vitePlugins.find(({ name }) => name === 'alias');
- resolvPlugin = vitePlugins.find(({ name }) => name === 'vite:resolve');
- };
- /**
- * 分析依赖,先调用vite alias插件,再调用resolve插件,获取依赖的绝对路径
- * @param {*} importee 被引用内容
- * @param {*} importer 引用者
- * @returns 被依赖内容的绝对路径
- */
- const resolveModule = async (importee, importer) => {
- let aliasResult = await aliasPlugin.resolveId.call(Promise, importee, importer);
- aliasResult = aliasResult || importee;
- const rst = await resolvPlugin.resolveId.call({ getModuleInfo: () => ({}) }, aliasResult, importer, {});
- return rst?.id || rst;
- };
- /**
- * 获取package.json中的依赖项,排除宿主中已存在的依赖
- * @returns
- */
- const getPackageDependencies = () => {
- const existDependencies = [
- '@yufp/constant',
- '@yufp/icon',
- '@yufp/layout',
- '@yufp/layout-micro',
- '@yufp/layout-utils',
- '@yufp/locale',
- '@yufp/svgicon',
- '@yufp/toolbar',
- '@yufp/xy-utils',
- '@yuxp/compile',
- '@yuxp/integration',
- 'xy-utils',
- 'axios',
- 'element-ui',
- 'fingerprintjs2',
- 'nprogress',
- 'oca-web',
- 'qs',
- 'vue',
- 'vue-i18n',
- 'vue-router',
- 'vue-seamless-scroll',
- 'vuex',
- 'vuex-persist',
- 'yuwp-ui',
- ];
- return Object.fromEntries(
- Object.keys(packageDependencies)
- .filter((dep) => !existDependencies.includes(dep))
- .map((key) => [key, packageDependencies[key]]),
- );
- };
- /**
- * 判断文件是否需要拷贝到包中
- * 文件在 src/${packageName} 和 src/views/${packageName} 中,且仅支持 .js .vue 文件
- * @param {String} filePath 文件路径
- * @returns 是否为自定义的文件
- */
- const isCustomFile = (filePath) => {
- console.log('filePath===', filePath)
- if(!filePath) return false;
- if (!filePath.endsWith('.js') && !filePath.endsWith('.vue')) {
- return false;
- }
- if (!path.isAbsolute(filePath)) {
- filePath = path.join(__dirname, filePath);
- }
- const absolutPathList = customPathList.map((filePath) => path.join(__dirname, filePath));
- return !absolutPathList.every((absolutPath) => path.relative(absolutPath, filePath).startsWith('..'));
- };
- /**
- * 分析一个文件的依赖
- * 将文件中 @/views/xxx.js 转换为 ./views/xxx.js 的实际相对路径
- * 返回转换后的内容(字符串)以及此文件所依赖的文件
- * @param {*} entryFile 所分析的文件
- * @param {*} onlyCustomFile 是否限制自定义目录,true为限制,入口文件直接引用的文件不限制
- * @returns dependecyFileList:依赖的文件列表,replacedFile替换后的内容
- */
- const parseFileDependency = async (entryFile = './src/index.js', onlyCustomFile = true) => {
- const entryFileContent = String(fs.readFileSync(entryFile));
- let result;
- let replacedFile = entryFileContent;
- if (entryFile.endsWith('.vue')) {
- result = vueParser.parse(entryFileContent, { ecmaVersion: 2020, sourceType: 'module' });
- } else if (entryFile.endsWith('.js')) {
- result = Parser.parse(entryFileContent, { ecmaVersion: 2020, sourceType: 'module' });
- } else {
- return { dependecyFileList: [], replacedFile: '' };
- }
- const dependecyFileList = await Promise.all(
- result.body
- .filter(({ type }) => type === 'ImportDeclaration')
- .map(async ({ source }) => {
- const absolutPath = await resolveModule(source.value, entryFile);
- if (absolutPath === null) return null;
- if (!onlyCustomFile || isCustomFile(absolutPath)) {
- const toFile = path.relative(
- path.dirname(path.isAbsolute(entryFile) ? entryFile : path.join(__dirname, entryFile)),
- absolutPath,
- );
- replacedFile = replacedFile.replaceAll(source.value, `./${toFile.replaceAll(`\\`, '/')}`);
- return absolutPath;
- }
- return null;
- }),
- );
- return {
- dependecyFileList: dependecyFileList.filter((a) => a !== null),
- replacedFile,
- };
- };
- /**
- * 递归解析文件,调用vite resolve plugin,分析所依赖的文件。
- * 并将依赖转换为无alias的相对路径,并写道destDir中。
- * @param {*} entryFile 入口文件。
- */
- const parseAndeCopyCustomJs = async (entryFile) => {
- let parseFileStack = [entryFile];
- const parsedSet = new Set();
- const writeFileQueue = [];
- while (parseFileStack.length) {
- const parseFile = parseFileStack.pop();
- parsedSet.add(parseFile);
- // eslint-disable-next-line no-await-in-loop
- // entryFile支持引入非自定义路径下的路径,其他不支持
- const { dependecyFileList, replacedFile } = await parseFileDependency(parseFile, parseFile !== entryFile);
- const writeFilePath = `${packageName}/${path.relative(__dirname, parseFile)}`;
- // eslint-disable-next-line no-await-in-loop
- writeFileQueue.push(
- fsPromise
- .mkdir(path.dirname(path.join(__dirname, writeFilePath)), { recursive: true })
- .then(() => fsPromise.writeFile(writeFilePath, replacedFile)),
- );
- const filted = dependecyFileList.filter((f) => !parsedSet.has(f));
- // console.log(`${parseFile}依赖:${filted}${filted.length}`);
- parseFileStack = parseFileStack.concat(filted);
- }
- // 等待所有文件写入完成
- await Promise.all(writeFileQueue);
- console.log(`拷贝自定义文件列表:${[...parsedSet].join('\n')}
- 共${parsedSet.size}个文件`);
- };
- /**
- * 递归复制目录
- * @param {*} src 源目录
- * @param {*} dest 目标目录
- */
- async function copyDirectory(src, dest) {
- // 确保目标目录存在
- if (!fs.existsSync(dest)) {
- fs.mkdirSync(dest, { recursive: true });
- }
- // 获取源目录的内容
- const files = await fsPromise.readdir(src, { withFileTypes: true });
- for (const file of files) {
- const srcPath = path.join(src, file.name);
- const destPath = path.join(dest, file.name);
- if (file.isDirectory()) {
- // 如果是目录,则递归复制该目录
- // eslint-disable-next-line no-await-in-loop
- await copyDirectory(srcPath, destPath);
- } else {
- // 如果是文件,则直接复制
- // console.log(`Copying ${srcPath} to ${destPath}`);
- try {
- // eslint-disable-next-line no-await-in-loop
- await fsPromise.copyFile(srcPath, destPath);
- } catch (error) {
- console.error(`Error copying ${srcPath}:`, error);
- }
- }
- }
- }
- /**
- * 递归删除目录或文件
- * @param {*} fileUrl
- * @returns
- */
- function deleteTarget(fileUrl) {
- // 如果当前url不存在,则退出
- if (!fs.existsSync(fileUrl)) return;
- // 当前文件为文件夹时
- if (fs.statSync(fileUrl).isDirectory()) {
- const files = fs.readdirSync(fileUrl);
- const len = files.length;
- let removeNumber = 0;
- if (len > 0) {
- files.forEach((file) => {
- removeNumber++;
- const url = `${fileUrl}/${file}`;
- if (fs.statSync(url).isDirectory()) {
- deleteTarget(url);
- } else {
- fs.unlinkSync(url);
- }
- });
- if (removeNumber === len) {
- // 删除当前文件夹下的所有文件后,删除当前空文件夹(注:所有的都采用同步删除)
- fs.rmdirSync(fileUrl);
- console.log(`删除文件夹${fileUrl}成功`);
- }
- } else {
- fs.rmdirSync(fileUrl);
- }
- } else {
- // 当前文件为文件时
- fs.unlinkSync(fileUrl);
- console.log(`删除文件${fileUrl}成功`);
- }
- }
- (async function run(params) {
- await getResolvePlugin();
- await parseAndeCopyCustomJs(entryFile);
- await copyDirectory(path.join(__dirname, 'src/views/xpVue'), path.join(__dirname, packageName, 'xpVue'));
- console.log('xpCopy completed.');
- // 复制目录到指定位置
- await copyDirectory(path.join(__dirname, 'src/views', packageName), path.join(__dirname, packageName));
- console.log('Copy completed.');
- // 写入package.json 文件
- const packageContent = JSON.stringify(
- {
- name: packageName,
- version: packageVersion,
- license: 'MIT',
- description: 'Lightweight Rule Engine Frontend Page Code.',
- author: 'oca@yusys.com.cn',
- main: entryFile,
- dependencies: getPackageDependencies(),
- },
- '\n',
- 4,
- );
- fs.writeFileSync(`${packageName}/package.json`, packageContent);
- console.log('INFO=====准备将OCA推送至私有仓库');
- console.log(`INFO=====包名:${packageName}`);
- console.log(`INFO=====版本:${packageVersion}`);
- exec(`cd ${packageName} && yarn publish`, (error, stdout, stderr) => {
- if (error) {
- console.error(`ERROR=====执行出错: ${error}`);
- if (`${error}`.indexOf('not allow updating') > -1) {
- console.error(`ERROR=====仓库上已存在该版本,不允许覆盖推送,请调整发布版本号。`);
- }
- return;
- }
- // 发布完成删除文件夹
- setTimeout(() => {
- let deleteName = packageName;
- if (packageName.includes('/')) {
- deleteName = packageName.split('/')[0];
- }
- deleteTarget(path.join(__dirname, deleteName));
- }, 1000);
- console.log(`INFO=====${stdout}`);
- });
- })();
|