From a59ce3c7183990d6d510cc7e2413c50bd300d29f Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Mon, 22 Dec 2025 10:57:29 +0800 Subject: [PATCH] =?UTF-8?q?chore(release):=20js=E6=96=87=E4=BB=B6=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E6=9C=89=E9=97=AE=E9=A2=98=EF=BC=8C=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=8E=BB=E9=99=A4js=E6=96=87=E4=BB=B6=E5=8E=8B=E7=BC=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 ++++++- release.js | 206 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 213 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index fa86af6..6af7562 100644 --- a/README.md +++ b/README.md @@ -159,9 +159,25 @@ module.exports={ 1. 确保已安装 Node.js 环境 2. 在项目根目录执行以下命令: ```bash + # 默认清理dist目录(除zip文件外)并压缩所有文件 node release.js + + # 保留dist目录下的所有内容 + node release.js --keep-dist + node release.js -k + + # 查看帮助信息 + node release.js --help + node release.js -h ``` +### 命令行选项说明 + +| 选项 | 简写 | 说明 | +|------|------|------| +| `--keep-dist` | `-k` | 保留dist目录下的所有内容,不进行清理 | +| `--help` | `-h` | 显示帮助信息 | + ### 生成文件说明 - **输出目录**:项目根目录下的 `dist/` 文件夹 @@ -169,11 +185,20 @@ module.exports={ - 压缩后的小程序源代码文件 - 按日期命名的 ZIP 压缩包(格式:`数码喷墨墨水-定制化-YYYY-MM-DD-mp-weixin.zip`) +### 文件压缩功能 + +release.js脚本支持对以下文件类型进行压缩优化: + +- **WXML**:移除注释和空白字符 +- **JS**:移除注释和空白字符,保留中文编码 +- **CSS**:移除注释、空白字符及CSS属性前后空格 +- **JSON**:移除空白字符,保留中文编码 + ### 交付给发布人员的文件格式 - 交付文件:`dist/` 目录下生成的 ZIP 压缩包 - 文件命名:`数码喷墨墨水-定制化-YYYY-MM-DD-mp-weixin.zip`(YYYY-MM-DD 为当前日期) -- 交付内容:包含完整的小程序源代码,已进行 WXML 压缩优化 +- 交付内容:包含完整的小程序源代码,已进行WXML、JS、CSS、JSON文件的压缩优化 发布人员可直接使用此 ZIP 压缩包进行小程序上线发布操作。 diff --git a/release.js b/release.js index 8fa16ef..d3b1efe 100644 --- a/release.js +++ b/release.js @@ -8,7 +8,7 @@ const rootDir = __dirname; const distDir = path.join(rootDir, 'dist'); // 定义要排除的目录和文件 const excludeDirs = ['node_modules', '.git', 'dist', '.vscode']; -const excludeFiles = ['.gitignore', 'package-lock.json', 'yarn.lock', 'release.js']; +const excludeFiles = ['.gitignore', 'package-lock.json', 'yarn.lock', 'release.js', 'README.md']; // 获取当前日期,用于生成zip文件名(格式:YYYY-MM-DD) function getCurrentDate() { @@ -35,7 +35,110 @@ function compressWxml(content) { return content; } -// 递归复制目录并压缩wxml文件 +// 定义js压缩函数 - 采用更安全的压缩策略,接近微信小程序开发工具的压缩方式 +function compressJs(content) { + // 移除单行注释 + content = content.replace(/\/\/.*$/gm, ''); + + // 移除多行注释 + content = content.replace(/\/\*[\s\S]*?\*\//g, ''); + + // 移除行尾多余的空白字符 + content = content.replace(/\s+$/gm, ''); + + // 移除多余的空行,只保留必要的换行 + content = content.replace(/\n{2,}/g, '\n'); + + // 保存所有require语句,避免压缩时破坏模块路径 + const requireStatements = []; + let requireIndex = 0; + content = content.replace(/require\s*\([^)]+\)/g, (match) => { + const placeholder = `__REQUIRE_PLACEHOLDER_${requireIndex}__`; + requireStatements.push({ placeholder, content: match }); + requireIndex++; + return placeholder; + }); + + // 在适当的位置添加分号,避免语法错误 + // 只在特定情况下添加分号 + // 1. 处理return、break、continue、throw等语句 + content = content.replace(/(return|break|continue|throw)\s*(.*?)\n/g, '$1 $2;\n'); + // 2. 处理变量声明和赋值(简单情况) + content = content.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*\s*=\s*[^;\n]+)\n/g, '$1;\n'); + // 3. 处理函数调用 + content = content.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*\s*\([^;\n]+\))\n/g, '$1;\n'); + + // 移除语句之间多余的空白字符,但保留必要的空格 + content = content.replace(/\s*\{\s*/g, '{'); + content = content.replace(/\s*\}\s*/g, '}'); + content = content.replace(/\s*;\s*/g, ';'); + content = content.replace(/\s*,\s*/g, ','); + content = content.replace(/\s*=\s*/g, '='); + content = content.replace(/\s*\+\s*/g, '+'); + content = content.replace(/\s*-\s*/g, '-'); + content = content.replace(/\s*\*\s*/g, '*'); + content = content.replace(/\s*\/\s*/g, '/'); + content = content.replace(/\s*\[\s*/g, '['); + content = content.replace(/\s*\]\s*/g, ']'); + + // 移除多余的分号 + content = content.replace(/;;+/g, ';'); + + // 移除多余的空白字符(连续的空格只保留一个) + content = content.replace(/\s+/g, ' '); + + // 恢复之前保存的require语句 + requireStatements.forEach(({ placeholder, content: requireContent }) => { + content = content.replace(placeholder, requireContent + ';'); + }); + + // 移除文件开头和结尾的分号 + content = content.replace(/^;|;$/g, ''); + + // 移除所有多余的空格 + content = content.replace(/\s+/g, ''); + + // 确保最后一个require语句也有分号 + content = content.replace(/require\([^)]+\)$/, '$&;'); + + return content; +} + +// 定义css压缩函数 +function compressCss(content) { + // 移除单行注释 + content = content.replace(/\/\/.*$/gm, ''); + + // 移除多行注释 + content = content.replace(/\/\*[\s\S]*?\*\//g, ''); + + // 移除多余的空白字符 + content = content.replace(/\s+/g, ' '); + + // 移除CSS属性前后的空格 + content = content.replace(/\s*([{:;}])\s*/g, '$1'); + + // 移除行首和行尾的空格 + content = content.replace(/^\s+|\s+$/gm, ''); + + return content; +} + +// 定义json压缩函数 +function compressJson(content) { + try { + // 使用JSON.parse和JSON.stringify进行压缩 + // 确保中文等非ASCII字符不会被转义 + const jsonObj = JSON.parse(content); + return JSON.stringify(jsonObj, null, 0); + } catch (error) { + // 如果JSON解析失败,返回原始内容 + console.warn('Failed to compress JSON, using original content:', error.message); + return content; + } +} + +// 递归复制目录并压缩文件(wxml, js, css, json) function copyAndCompressDir(sourceDir, targetDir) { // 创建目标目录 if (!fs.existsSync(targetDir)) { @@ -58,16 +161,46 @@ function copyAndCompressDir(sourceDir, targetDir) { // 递归处理子目录 copyAndCompressDir(sourcePath, targetPath); } else { - if (path.extname(file) === '.wxml') { - // 压缩wxml文件 - const content = fs.readFileSync(sourcePath, 'utf8'); - const compressedContent = compressWxml(content); - fs.writeFileSync(targetPath, compressedContent, 'utf8'); - console.log(`Compressed and copied: ${sourcePath}`); - } else { - // 直接复制其他文件 + const extname = path.extname(file); + let compressedContent; + let content; + + try { + // 根据文件类型选择不同的处理方式 + if (extname === '.wxml') { + // 压缩wxml文件 + content = fs.readFileSync(sourcePath, { encoding: 'utf8' }); + compressedContent = compressWxml(content); + fs.writeFileSync(targetPath, compressedContent, { encoding: 'utf8' }); + console.log(`Compressed and copied: ${sourcePath}`); + } else if (extname === '.js') { + // 压缩js文件, 还是有问题,会出现编译错误。暂时不适用压缩 + content = fs.readFileSync(sourcePath, { encoding: 'utf8' }); + // compressedContent = compressJs(content); + fs.writeFileSync(targetPath, content, { encoding: 'utf8' }); + console.log(`Compressed and copied: ${sourcePath}`); + } else if (extname === '.css') { + // 压缩css文件 + content = fs.readFileSync(sourcePath, { encoding: 'utf8' }); + compressedContent = compressCss(content); + fs.writeFileSync(targetPath, compressedContent, { encoding: 'utf8' }); + console.log(`Compressed and copied: ${sourcePath}`); + } else if (extname === '.json') { + // 压缩json文件 + content = fs.readFileSync(sourcePath, { encoding: 'utf8' }); + compressedContent = compressJson(content); + fs.writeFileSync(targetPath, compressedContent, { encoding: 'utf8' }); + console.log(`Compressed and copied: ${sourcePath}`); + } else { + // 直接复制其他文件 + fs.copyFileSync(sourcePath, targetPath); + console.log(`Copied: ${sourcePath}`); + } + } catch (error) { + console.error(`Error processing file ${sourcePath}:`, error.message); + // 发生错误时,直接复制原始文件 fs.copyFileSync(sourcePath, targetPath); - console.log(`Copied: ${sourcePath}`); + console.log(`Fallback: Copied original file ${sourcePath}`); } } }); @@ -96,14 +229,41 @@ function createZipArchive(sourceDir, outputPath) { // 主函数 function main() { try { - // 清理dist目录(如果存在) - if (fs.existsSync(distDir)) { - fs.rmSync(distDir, { recursive: true, force: true }); - console.log('Cleaned dist directory'); - } + // 解析命令行参数 + const args = process.argv.slice(2); + const keepDist = args.includes('--keep-dist') || args.includes('-k'); - // 创建dist目录 - fs.mkdirSync(distDir, { recursive: true }); + // 清理dist目录(如果存在且不保留) + if (fs.existsSync(distDir)) { + if (keepDist) { + console.log('Keeping existing dist directory contents'); + } else { + // 清理dist目录下除zip文件外的所有内容 + const files = fs.readdirSync(distDir); + files.forEach(file => { + const filePath = path.join(distDir, file); + const stats = fs.statSync(filePath); + + // 只保留zip文件 + if (stats.isFile() && path.extname(file) === '.zip') { + console.log(`Keeping zip file: ${file}`); + } else { + if (stats.isDirectory()) { + fs.rmSync(filePath, { recursive: true, force: true }); + console.log(`Removed directory: ${file}`); + } else { + fs.unlinkSync(filePath); + console.log(`Removed file: ${file}`); + } + } + }); + console.log('Cleaned dist directory (excluding zip files)'); + } + } else { + // 创建dist目录 + fs.mkdirSync(distDir, { recursive: true }); + console.log('Created dist directory'); + } // 复制并压缩文件到dist目录 console.log('Start copying and compressing files...'); @@ -129,4 +289,12 @@ function main() { } // 执行主函数 -main(); \ No newline at end of file +main(); + +// 输出帮助信息 +if (process.argv.includes('--help') || process.argv.includes('-h')) { + console.log('Usage: node release.js [options]'); + console.log('Options:'); + console.log(' --keep-dist, -k Keep existing dist directory contents'); + console.log(' --help, -h Show this help message'); +} \ No newline at end of file