ソースを参照

修复gulp,支持edge\firefox.json

zxlie 4 ヶ月 前
コミット
baa82f17c3
4 ファイル変更257 行追加284 行削除
  1. 99 0
      apps/edge.json
  2. 158 284
      gulpfile.js
  3. BIN
      output-edge/fehelper.zip
  4. BIN
      output-firefox/fehelper.xpi

+ 99 - 0
apps/edge.json

@@ -0,0 +1,99 @@
+{
+  "name": "FeHelper(前端助手)-Dev",
+  "short_name": "FeHelper",
+  "version": "2025.05.2014",
+  "manifest_version": 3,
+  "description": "JSON自动格式化、手动格式化,支持排序、解码、下载等,更多功能可在配置页按需安装!",
+  "icons": {
+    "16": "static/img/fe-16.png",
+    "48": "static/img/fe-48.png",
+    "128": "static/img/fe-128.png"
+  },
+  "action": {
+    "default_icon": "static/img/fe-16.png",
+    "default_title": "FeHelper(前端助手)",
+    "default_popup": "popup/index.html"
+  },
+  "background": {
+    "service_worker": "background/background.js",
+    "type": "module"
+  },
+  "options_ui": {
+    "page": "options/index.html",
+    "open_in_tab": true
+  },
+  "permissions": [
+    "tabs",
+    "scripting",
+    "contextMenus",
+    "activeTab",
+    "storage",
+    "notifications",
+    "unlimitedStorage"
+  ],
+  "host_permissions": [
+    "http://*/*",
+    "https://*/*",
+    "file://*/*"
+    ],
+  "optional_permissions": [
+    "downloads"
+  ],
+  "commands": {
+    "_execute_action": {
+      "suggested_key": {
+        "default": "Alt+Shift+J"
+      }
+    }
+  },
+  "web_accessible_resources": [
+      {
+          "resources":[
+            "static/img/fe-16.png",
+            "static/img/fe-48.png",
+            "static/img/loading.gif",
+            "json-format/format-lib.js",
+            "json-format/json-abc.js",
+            "json-format/json-bigint.js",
+            "json-format/json-decode.js",
+            "json-format/json-worker.js",
+            "static/vendor/jquery/jquery-3.3.1.min.js",
+            "static/vendor/evalCore.min.js",
+
+            "background/awesome.js",
+            "background/tools.js",
+
+            "code-beautify/beautify.js",
+            "code-beautify/beautify-css.js",
+
+            "page-timing/timing.js"
+        ],
+        "matches": ["<all_urls>"]
+      }
+  ],
+  "content_scripts": [
+    {
+      "matches": [
+        "http://*/*",
+        "https://*/*",
+        "file://*/*"
+      ],
+      "exclude_globs": [
+        "https://chrome.google.com/*"
+      ],
+      "js": [
+        "static/vendor/jquery/jquery-3.3.1.min.js",
+        "static/vendor/evalCore.min.js"
+      ],
+      "run_at": "document_start",
+      "all_frames": true
+    }
+  ],
+  "content_security_policy": {
+    "extension_pages": "script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'self'"
+  },
+  "homepage_url": "https://www.fehelper.com"
+}
+
+
+

+ 158 - 284
gulpfile.js

@@ -26,86 +26,56 @@ const imageminSvgo = require('imagemin-svgo');
 
 let isSilentDetect = false; // <-- 添加全局标志位
 
-// 在Gulp 4.x中,runSequence已被移除,使用gulp.series和gulp.parallel代替
-// let runSequence = require('run-sequence');
+const FIREFOX_REMOVE_TOOLS = [
+    'color-picker', 'postman', 'devtools', 'websocket', 'page-timing',
+    'grid-ruler', 'naotu', 'screenshot', 'page-monkey', 'excel2json'
+];
 
 // 清理输出目录
-function cleanOutput() {
-    return gulp.src('output-chrome', {read: false, allowEmpty: true}).pipe(clean({force: true}));
+function cleanOutput(outputDir = 'output-chrome') {
+    return gulp.src(outputDir, {read: false, allowEmpty: true}).pipe(clean({force: true}));
 }
 
 // 复制静态资源
-function copyAssets() {
-    return gulp.src(['apps/**/*.{gif,png,jpg,jpeg,cur,ico,ttf,woff2,svg,md,txt}', '!apps/static/screenshot/**/*']).pipe(copy('output-chrome'));
+function copyAssets(outputDir = 'output-chrome/apps') {
+    return gulp.src(['apps/**/*.{gif,png,jpg,jpeg,cur,ico,ttf,woff2,svg,md,txt,json}']).pipe(gulp.dest(outputDir));
 }
 
 // 处理JSON文件
-function processJson() {
-    return gulp.src('apps/**/*.json').pipe(jsonmin()).pipe(gulp.dest('output-chrome/apps'));
+function processJson(outputDir = 'output-chrome/apps') {
+    return gulp.src('apps/**/*.json').pipe(jsonmin()).pipe(gulp.dest(outputDir));
 }
 
-// 处理Chrome的manifest文件
-function processChromeManifest(cb) {
-    
-    // 确保输出目录存在
-    if (!fs.existsSync('output-chrome/apps')) {
-        fs.mkdirSync('output-chrome/apps', { recursive: true });
-    }
-    
-    // 复制chrome.json为manifest.json
-    fs.copyFileSync('apps/chrome.json', 'output-chrome/apps/manifest.json');
-    
-    // 删除firefox.json (如果存在)
-    const firefoxJsonPath = 'output-chrome/apps/firefox.json';
-    if (fs.existsSync(firefoxJsonPath)) {
-        fs.unlinkSync(firefoxJsonPath);
-    }
-    
-    // 删除chrome.json (如果存在)
-    const chromeJsonPath = 'output-chrome/apps/chrome.json';
-    if (fs.existsSync(chromeJsonPath)) {
-        fs.unlinkSync(chromeJsonPath);
-    }
-    
-    cb();
-}
 
 // 处理HTML文件
-function processHtml() {
-    return gulp.src('apps/**/*.html').pipe(htmlmin({collapseWhitespace: true})).pipe(gulp.dest('output-chrome/apps'));
+function processHtml(outputDir = 'output-chrome/apps') {
+    return gulp.src('apps/**/*.html').pipe(htmlmin({collapseWhitespace: true})).pipe(gulp.dest(outputDir));
 }
 
 // 合并 & 压缩 js
-function processJs() {
+function processJs(outputDir = 'output-chrome/apps') {
     let jsMerge = () => {
         return through.obj(function (file, enc, cb) {
             let contents = file.contents.toString('utf-8');
-
             let merge = (fp, fc) => {
-                // 合并 __importScript
                 return fc.replace(/__importScript\(\s*(['"])([^'"]*)\1\s*\)/gm, function (frag, $1, mod) {
                     let mp = path.resolve(fp, '../' + mod + (/\.js$/.test(mod) ? '' : '.js'));
                     let mc = fs.readFileSync(mp).toString('utf-8');
                     return merge(mp, mc + ';');
                 });
             };
-
             contents = merge(file.path, contents);
             file.contents = Buffer.from(contents);
             this.push(file);
             return cb();
         })
     };
-
-    // 定义哪些文件不需要 Babel 和 Uglify 处理
     const shouldSkipProcessing = (file) => {
         const relativePath = path.relative(path.join(process.cwd(), 'apps'), file.path);
-        // 跳过这三个文件
         return relativePath === 'chart-maker/lib/xlsx.full.min.js' 
             || relativePath === 'static/vendor/evalCore.min.js' 
             || relativePath === 'code-compress/htmlminifier.min.js';
     };
-
     return gulp.src('apps/**/*.js')
         .pipe(jsMerge())
         .pipe(gulpIf(file => !shouldSkipProcessing(file), babel({
@@ -118,15 +88,14 @@ function processJs() {
                 ecma: 2015
             }
         })))
-        .pipe(gulp.dest('output-chrome/apps'));
+        .pipe(gulp.dest(outputDir));
 }
 
 // 合并 & 压缩 css
-function processCss() {
+function processCss(outputDir = 'output-chrome/apps') {
     let cssMerge = () => {
         return through.obj(function (file, enc, cb) {
             let contents = file.contents.toString('utf-8');
-
             let merge = (fp, fc) => {
                 return fc.replace(/\@import\s+(url\()?\s*(['"])(.*)\2\s*(\))?\s*;?/gm, function (frag, $1, $2, mod) {
                     let mp = path.resolve(fp, '../' + mod + (/\.css$/.test(mod) ? '' : '.css'));
@@ -134,20 +103,18 @@ function processCss() {
                     return merge(mp, mc);
                 });
             };
-
             contents = merge(file.path, contents);
             file.contents = Buffer.from(contents);
             this.push(file);
             return cb();
         })
     };
-
-    return gulp.src('apps/**/*.css').pipe(cssMerge()).pipe(uglifycss()).pipe(gulp.dest('output-chrome/apps'));
+    return gulp.src('apps/**/*.css').pipe(cssMerge()).pipe(uglifycss()).pipe(gulp.dest(outputDir));
 }
 
 // 添加图片压缩任务
-function compressImages() {
-    return gulp.src('output-chrome/apps/**/*.{png,jpg,jpeg,gif,svg}') // 源目录应为 output
+function compressImages(outputDir = 'output-chrome/apps') {
+    return gulp.src(path.join(outputDir, '**/*.{png,jpg,jpeg,gif,svg}'))
         .pipe(imagemin([
             imageminGifsicle({interlaced: true}),
             imageminMozjpeg({quality: 75, progressive: true}),
@@ -158,122 +125,33 @@ function compressImages() {
                 ]
             })
         ]))
-        .pipe(gulp.dest('output-chrome/apps')); // 覆盖回 output
+        .pipe(gulp.dest(outputDir));
 }
 
 // 清理冗余文件,并且打包成zip,发布到chrome webstore
-function zipPackage(cb) {
-    // 读取manifest文件
-    let pathOfMF = './output-chrome/apps/manifest.json';
-    let manifest = require(pathOfMF);
-
+function zipPackage(outputRoot = 'output-chrome', cb) {
+    let pathOfMF = path.join(outputRoot, 'apps/manifest.json');
+    let manifest = require(path.resolve(pathOfMF));
     manifest.name = manifest.name.replace('-Dev', '');
     fs.writeFileSync(pathOfMF, JSON.stringify(manifest));
-
-    // ============压缩打包================================================
-    shell.exec('cd output-chrome/ && rm -rf fehelper.zip && zip -r fehelper.zip apps/ > /dev/null && cd ../');
-    let size = fs.statSync('output-chrome/fehelper.zip').size;
-    size = pretty(size);
-
-
-    console.log('\n\n================================================================================');
-    console.log('    当前版本:', manifest.version, '\t文件大小:', size);
-    console.log('    去Chrome商店发布吧:https://chrome.google.com/webstore/devconsole');
-    console.log('================================================================================\n\n');
-    
-    cb();
-}
-
-// 打包ms-edge安装包
-function edgePackage(cb) {
-    shell.exec('rm -rf output-edge && cp -r output-chrome output-edge && rm -rf output-edge/fehelper.zip');
-
-    // 更新edge所需的配置文件
-    let pathOfMF = './output-edge/apps/manifest.json';
-    let manifest = require(pathOfMF);
-    manifest.description = 'FE助手:JSON工具、代码美化、代码压缩、二维码工具、网页定制工具、便签笔记,等等';
-    delete manifest.update_url;
-    manifest.version = manifest.version.split('.').map(v => parseInt(v)).join('.');
-    delete manifest.update_url;
-    fs.writeFileSync(pathOfMF, JSON.stringify(manifest));
-
-    shell.exec('cd output-edge/apps && zip -r ../fehelper.zip ./ > /dev/null && cd ../../');
-    let size = fs.statSync('output-edge/fehelper.zip').size;
-    size = pretty(size);
-
-    console.log('\n\nfehelper.zip 已打包完成!');
-
-    console.log('\n\n================================================================================');
-    console.log('    当前版本:', manifest.version, '\t文件大小:', size);
-    console.log('    去Edge商店发布吧:https://partner.microsoft.com/zh-cn/dashboard/microsoftedge/overview');
-    console.log('================================================================================\n\n');
-    
-    cb();
-}
-
-// 清理Firefox输出目录
-function cleanFirefoxOutput() {
-    return gulp.src('output-firefox', {read: false, allowEmpty: true}).pipe(clean({force: true}));
-}
-
-// 处理Firefox的manifest文件
-function processFirefoxManifest(cb) {
-    
-    // 确保输出目录存在
-    if (!fs.existsSync('output-firefox/apps')) {
-        fs.mkdirSync('output-firefox/apps', { recursive: true });
+    let pkgName = 'fehelper.zip';
+    if (outputRoot === 'output-firefox') {
+        pkgName = 'fehelper.xpi';
     }
-    
-    // 复制firefox.json为manifest.json
-    fs.copyFileSync('apps/firefox.json', 'output-firefox/apps/manifest.json');
-    
-    // 删除firefox.json (如果存在)
-    const firefoxJsonPath = 'output-firefox/apps/firefox.json';
-    if (fs.existsSync(firefoxJsonPath)) {
-        fs.unlinkSync(firefoxJsonPath);
-    }
-    
-    cb();
-}
-
-// 复制资源到Firefox输出目录
-function copyAssetsToFirefox() {
-    return gulp.src(['apps/**/*.{gif,png,jpg,jpeg,cur,ico,ttf,woff2,svg,md,txt,html,js,css,json}', '!apps/static/screenshot/**/*', '!apps/chrome.json', '!apps/manifest.json'])
-        .pipe(copy('output-firefox'));
-}
-
-// 打包Firefox安装包
-function firefoxPackage(cb) {
-    // 清理掉firefox里不支持的tools
-    let rmTools = ['page-capture', 'color-picker', 'ajax-debugger', 'wpo', 'code-standards', 'ruler', 'remove-bg'];
-    shell.cd('output-firefox/apps');
-    shell.find('./').forEach(f => {
-        if (rmTools.includes(f)) {
-            shell.rm('-rf', f);
-            console.log('已删除不支持的工具:', f);
-        }
-    });
-    shell.cd('../../');
-
-    shell.exec('cd output-firefox/apps && zip -r ../fehelper.xpi ./ > /dev/null && cd ../../');
-    let size = fs.statSync('output-firefox/fehelper.xpi').size;
+    shell.exec(`cd ${outputRoot}/ && rm -rf ${pkgName} && zip -r ${pkgName} apps/ > /dev/null && cd ../`);
+    let size = fs.statSync(`${outputRoot}/${pkgName}`).size;
     size = pretty(size);
-
-    // 读取manifest文件
-    let manifest = JSON.parse(fs.readFileSync('./output-firefox/apps/manifest.json', 'utf8'));
-
-    console.log('\n\nfehelper.xpi 已打包完成!');
-
     console.log('\n\n================================================================================');
     console.log('    当前版本:', manifest.version, '\t文件大小:', size);
-    console.log('    去Firefox商店发布吧:https://addons.mozilla.org/zh-CN/developers/addon/web%E5%89%8D%E7%AB%AF%E5%8A%A9%E6%89%8B-fehelper/versions');
+    if (outputRoot === 'output-chrome') {
+        console.log('    去Chrome商店发布吧:https://chrome.google.com/webstore/devconsole');
+    } else if (outputRoot === 'output-edge') {
+        console.log('    去Edge商店发布吧:https://partner.microsoft.com/zh-cn/dashboard/microsoftedge/overview');
+    } else if (outputRoot === 'output-firefox') {
+        console.log('    去Firefox商店发布吧:https://addons.mozilla.org/zh-CN/developers/addon/web前端助手-fehelper/versions');
+    }
     console.log('================================================================================\n\n');
-    
-    cb();
-}
-
-function syncFiles() {
-    return gulp.src('apps/**/*').pipe(gulp.dest('output-chrome/apps'));
+    if (cb) cb();
 }
 
 // 设置静默标志
@@ -281,118 +159,74 @@ function setSilentDetect(cb) {
     isSilentDetect = true;
     cb();
 }
-
-// 取消静默标志
 function unsetSilentDetect(cb) {
     isSilentDetect = false;
     cb();
 }
 
-// 检测未使用的静态文件
-function detectUnusedFiles(cb) {
+// 检测未使用的静态文件(参数化outputDir)
+function detectUnusedFiles(outputDir = 'output-chrome/apps', cb) {
     const allFiles = new Set();
     const referencedFiles = new Set();
-    
-    // 检查文件是否应该被排除
     function shouldExcludeFile(filePath) {
-        // 排除 content-script 文件
-        if (filePath.includes('content-script.js') || filePath.includes('content-script.css')) {
-            return true;
-        }
-        // 排除 node_modules 目录
-        if (filePath.includes('node_modules')) {
-            return true;
-        }
-        // 排除 fh-config.js
-        if (filePath.endsWith('fh-config.js')) {
-            return true;
-        }
+        if (filePath.includes('content-script.js') || filePath.includes('content-script.css')) return true;
+        if (filePath.includes('node_modules')) return true;
+        if (filePath.endsWith('fh-config.js')) return true;
         return false;
     }
-    
-    // 递归获取所有文件
     function getAllFiles(dir) {
         const files = fs.readdirSync(dir);
         files.forEach(file => {
             const fullPath = path.join(dir, file);
             if (fs.statSync(fullPath).isDirectory()) {
-                if (file !== 'node_modules') {  // 排除 node_modules 目录
+                if (file !== 'node_modules') {
                     getAllFiles(fullPath);
                 }
             } else {
-                // 只关注静态资源文件,并排除特殊文件
                 if (/\.(js|css|png|jpg|jpeg|gif|svg)$/i.test(file) && !shouldExcludeFile(fullPath)) {
-                    const relativePath = path.relative('output-chrome/apps', fullPath);
+                    const relativePath = path.relative(outputDir, fullPath);
                     allFiles.add(relativePath);
                 }
             }
         });
     }
-    
-    // 从文件内容中查找引用
     function findReferences(content, filePath) {
         const fileDir = path.dirname(filePath);
         const patterns = [
-            // Capture content inside quotes (potential paths)
-            /['"`][^`'"]*?([./\w-]+\.(?:js|css|png|jpg|jpeg|gif|svg))['"`]/g, 
-            // Capture content inside url()
-            /url\(['"]?([./\w-]+(?:\.(?:png|jpg|jpeg|gif|svg))?)['"]?\)/gi, 
-            // Capture @import paths
-            /@import\s+['"]([^'"]+\.css)['"];?/gi, 
-            // Capture src/href attributes, including chrome-extension
-            /(?:src|href)=['"](chrome-extension:\/\/[^/]+\/)?([^'"?#]+(?:\.(?:js|css|png|jpg|jpeg|gif|svg)))['"]/gi 
+            /['"`][^`'\"]*?([./\w-]+\.(?:js|css|png|jpg|jpeg|gif|svg))['"`]/g,
+            /url\(['"]?([./\w-]+(?:\.(?:png|jpg|jpeg|gif|svg))?)['"]?\)/gi,
+            /@import\s+['"]([^'\"]+\.css)['"];?/gi,
+            /(?:src|href)=['"](chrome-extension:\/\/[^/]+\/)?([^'"?#]+(?:\.(?:js|css|png|jpg|jpeg|gif|svg)))['"]/gi
         ];
-
         patterns.forEach((pattern, index) => {
             let match;
             while ((match = pattern.exec(content)) !== null) {
-                
                 let extractedPath = '';
-                // Special handling for src/href pattern which captures optional chrome-extension part
-                if (index === 3) { 
-                    extractedPath = match[2]; // The path part after potential chrome-extension prefix
+                if (index === 3) {
+                    extractedPath = match[2];
                 } else {
                     extractedPath = match[1];
                 }
-
-                // Skip empty or invalid matches
                 if (!extractedPath || typeof extractedPath !== 'string') continue;
-
-                // Skip special files early
-                if (shouldExcludeFile(extractedPath)) {
-                    continue;
-                }
-
+                if (shouldExcludeFile(extractedPath)) continue;
                 let finalPathToAdd = '';
-
-                // Check if it was originally a chrome-extension url (by checking match[1] from the specific regex)
-                const isChromeExt = index === 3 && match[1]; 
-                
+                const isChromeExt = index === 3 && match[1];
                 if (isChromeExt || extractedPath.startsWith('/')) {
-                    // chrome-extension paths are relative to root, absolute paths are relative to root
                     finalPathToAdd = extractedPath.startsWith('/') ? extractedPath.slice(1) : extractedPath;
                 } else {
-                    // Resolve relative paths (./, ../, or direct filename)
                     const absolutePath = path.resolve(fileDir, extractedPath);
-                    finalPathToAdd = path.relative('output-chrome/apps', absolutePath);
+                    finalPathToAdd = path.relative(outputDir, absolutePath);
                 }
-                
-                // Final check before adding
                 if (finalPathToAdd && !shouldExcludeFile(finalPathToAdd)) {
-                    // Ensure consistent path separators (use /) and add to set
                     referencedFiles.add(finalPathToAdd.replace(/\\/g, '/'));
                 }
             }
         });
     }
-    
-    // 读取manifest.json中的引用
     function processManifest() {
-        const manifestPath = 'output-chrome/apps/manifest.json';
+        const manifestPath = path.join(outputDir, 'manifest.json');
         if (fs.existsSync(manifestPath)) {
             const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
-            
-            // 检查manifest中的各种可能包含资源的字段
             const checkManifestField = (obj) => {
                 if (typeof obj === 'string' && /\.(js|css|png|jpg|jpeg|gif|svg)$/i.test(obj)) {
                     const normalizedPath = obj.startsWith('/') ? obj.slice(1) : obj;
@@ -405,8 +239,6 @@ function detectUnusedFiles(cb) {
                     Object.values(obj).forEach(value => checkManifestField(value));
                 }
             };
-            
-            // 特殊处理content_scripts
             if (manifest.content_scripts) {
                 manifest.content_scripts.forEach(script => {
                     if (script.js) {
@@ -423,22 +255,15 @@ function detectUnusedFiles(cb) {
                     }
                 });
             }
-            
             checkManifestField(manifest);
         }
     }
-    
-    // 单元测试
     function runTests() {
         if (!isSilentDetect) console.log('\n运行单元测试...');
-        
-        // 测试文件排除逻辑
         assert.strictEqual(shouldExcludeFile('path/to/content-script.js'), true, 'Should exclude content-script.js');
         assert.strictEqual(shouldExcludeFile('path/to/content-script.css'), true, 'Should exclude content-script.css');
         assert.strictEqual(shouldExcludeFile('path/to/node_modules/file.js'), true, 'Should exclude node_modules files');
         assert.strictEqual(shouldExcludeFile('path/to/normal.js'), false, 'Should not exclude normal files');
-        
-        // 测试路径解析
         const testContent = `
             <link rel="stylesheet" href="./style.css">
             <script src="../js/script.js"></script>
@@ -447,12 +272,9 @@ function detectUnusedFiles(cb) {
             @import '../common.css';
             <img src="chrome-extension://abcdefgh/static/icon.png">
         `;
-        
-        const testFilePath = 'output-chrome/apps/test/index.html';
-        referencedFiles.clear();  // 清理之前的测试数据
+        const testFilePath = path.join(outputDir, 'test/index.html');
+        referencedFiles.clear();
         findReferences(testContent, testFilePath);
-        
-        // 验证引用是否被正确解析
         const refs = Array.from(referencedFiles);
         assert(refs.includes('test/style.css'), 'Should handle relative path with ./');
         assert(refs.includes('js/script.js'), 'Should handle relative path with ../');
@@ -460,36 +282,23 @@ function detectUnusedFiles(cb) {
         assert(refs.includes('test/bg.jpg'), 'Should handle url() in CSS');
         assert(refs.includes('common.css'), 'Should handle @import in CSS');
         assert(refs.includes('static/icon.png'), 'Should handle chrome-extension urls');
-        
-        // 清理测试数据
         referencedFiles.clear();
-        
         if (!isSilentDetect) console.log('单元测试通过!');
     }
-    
     try {
-        // 运行单元测试
         runTests();
-        
-        // 执行实际检测
-        getAllFiles('output-chrome/apps');
+        getAllFiles(outputDir);
         processManifest();
-        
-        // 扫描所有文件内容中的引用
-        const filesToScan = fs.readdirSync('output-chrome/apps', { recursive: true })
+        const filesToScan = fs.readdirSync(outputDir, { recursive: true })
             .filter(file => !shouldExcludeFile(file));
-            
         filesToScan.forEach(file => {
-            const fullPath = path.join('output-chrome/apps', file);
+            const fullPath = path.join(outputDir, file);
             if (fs.statSync(fullPath).isFile() && /\.(html|js|css|json)$/i.test(file)) {
                 const content = fs.readFileSync(fullPath, 'utf8');
                 findReferences(content, fullPath);
             }
         });
-        
-        // 找出未被引用的文件
         const unusedFiles = Array.from(allFiles).filter(file => !referencedFiles.has(file));
-        
         if (unusedFiles.length > 0) {
             if (!isSilentDetect) console.log('\n发现以下未被引用的文件:');
             if (!isSilentDetect) console.log('=====================================');
@@ -497,10 +306,9 @@ function detectUnusedFiles(cb) {
             unusedFiles.forEach(file => {
                 if (!isSilentDetect) console.log(file);
                 try {
-                    const fullPath = path.join('output-chrome/apps', file);
+                    const fullPath = path.join(outputDir, file);
                     if (fs.existsSync(fullPath)) {
                        totalUnusedSize += fs.statSync(fullPath).size; 
-                       // 删除文件
                        fs.unlinkSync(fullPath);
                        if (!isSilentDetect) console.log(`  -> 已删除: ${file}`);
                     } else {
@@ -509,12 +317,9 @@ function detectUnusedFiles(cb) {
                 } catch (err) {
                     if (!isSilentDetect) console.warn(`无法删除或获取文件大小: ${file}`, err);
                 }
-                
             });
             if (!isSilentDetect) console.log('=====================================');
             if (!isSilentDetect) console.log(`共清理 ${unusedFiles.length} 个未使用的文件,释放空间: ${pretty(totalUnusedSize)}`);
-            
-            // 输出详细信息(仅在DEBUG模式下)
             if (process.env.DEBUG && !isSilentDetect) {
                 console.log('\n调试信息:');
                 console.log('----------------------------------------');
@@ -530,48 +335,117 @@ function detectUnusedFiles(cb) {
     } catch (error) {
         if (!isSilentDetect) console.error('检测过程中发生错误:', error);
     }
-    
+    cb && cb();
+}
+
+// Firefox前置处理
+function firefoxPreprocess(cb) {
+    const srcDir = 'apps';
+    const destDir = 'output-firefox/apps';
+    shell.exec(`mv ${destDir}/firefox.json ${destDir}/manifest.json`);
+    shell.exec(`rm -rf ${destDir}/chrome.json`);
+    shell.exec(`rm -rf ${destDir}/edge.json`);
+
+    FIREFOX_REMOVE_TOOLS.forEach(tool => {
+        const toolDir = path.join(destDir, tool);
+        if (fs.existsSync(toolDir)) {
+            shell.exec(`rm -rf ${toolDir}`);
+            console.log(`已删除不支持的工具: ${tool}`);
+        }
+    });
+    cb();
+}
+
+
+// chrome前置处理
+function chromePreprocess(cb) {
+    const destDir = 'output-chrome/apps';
+    shell.exec(`mv ${destDir}/chrome.json ${destDir}/manifest.json`);
+    shell.exec(`rm -rf ${destDir}/firefox.json`);
+    shell.exec(`rm -rf ${destDir}/edge.json`);
+
+    cb();
+}
+
+// edge前置处理
+function edgePreprocess(cb) {
+    const destDir = 'output-edge/apps';
+    shell.exec(`mv ${destDir}/edge.json ${destDir}/manifest.json`);
+    shell.exec(`rm -rf ${destDir}/chrome.json`);
+    shell.exec(`rm -rf ${destDir}/firefox.json`);
+
     cb();
 }
 
 // 注册任务
-gulp.task('clean', cleanOutput);
-gulp.task('copy', copyAssets);
-gulp.task('json', processJson);
-gulp.task('html', processHtml);
-gulp.task('js', processJs);
-gulp.task('css', processCss);
-gulp.task('chromeManifest', processChromeManifest);
-gulp.task('compressImages', compressImages);
-gulp.task('zip', zipPackage);
-gulp.task('edge', edgePackage);
-gulp.task('firefoxManifest', processFirefoxManifest);
-gulp.task('cleanFirefox', cleanFirefoxOutput);
-gulp.task('copyToFirefox', copyAssetsToFirefox);
-gulp.task('firefox', 
+// Chrome默认打包
+function cleanChrome() { return cleanOutput('output-chrome'); }
+function copyChrome() { return copyAssets('output-chrome/apps'); }
+function cssChrome() { return processCss('output-chrome/apps'); }
+function jsChrome() { return processJs('output-chrome/apps'); }
+function htmlChrome() { return processHtml('output-chrome/apps'); }
+function jsonChrome() { return processJson('output-chrome/apps'); }
+function detectChrome(cb) { detectUnusedFiles('output-chrome/apps', cb); }
+function zipChrome(cb) { zipPackage('output-chrome', cb); }
+
+gulp.task('default', 
     gulp.series(
-        cleanFirefoxOutput,
-        copyAssetsToFirefox,
-        processFirefoxManifest,
-        firefoxPackage
+        cleanChrome,
+        copyChrome,
+        gulp.parallel(cssChrome, jsChrome, htmlChrome, jsonChrome),
+        chromePreprocess,
+        setSilentDetect,
+        detectChrome,
+        unsetSilentDetect,
+        zipChrome
     )
 );
-gulp.task('sync', syncFiles);
-gulp.task('detect', detectUnusedFiles);
-gulp.task('setSilent', setSilentDetect);
-gulp.task('unsetSilent', unsetSilentDetect);
 
-// 定义默认任务 - 在Gulp 4.x中,使用series和parallel代替runSequence
-gulp.task('default', 
+// ------------------------------------------------------------
+
+// edge打包
+function cleanEdge() { return cleanOutput('output-edge'); }
+function copyEdge() { return copyAssets('output-edge/apps'); }
+function cssEdge() { return processCss('output-edge/apps'); }
+function jsEdge() { return processJs('output-edge/apps'); }
+function htmlEdge() { return processHtml('output-edge/apps'); }
+function jsonEdge() { return processJson('output-edge/apps'); }
+function detectEdge(cb) { detectUnusedFiles('output-edge/apps', cb); }
+function zipEdge(cb) { zipPackage('output-edge', cb); }
+
+gulp.task('edge', 
+    gulp.series(
+        cleanEdge,
+        copyEdge,
+        gulp.parallel(cssEdge, jsEdge, htmlEdge, jsonEdge),
+        edgePreprocess,
+        setSilentDetect,
+        detectEdge,
+        unsetSilentDetect,
+        zipEdge
+    )
+);
+
+// Firefox打包主任务
+function cleanFirefox() { return cleanOutput('output-firefox'); }
+function copyFirefox() { return copyAssets('output-firefox/apps'); }
+function cssFirefox() { return processCss('output-firefox/apps'); }
+function jsFirefox() { return processJs('output-firefox/apps'); }
+function htmlFirefox() { return processHtml('output-firefox/apps'); }
+function jsonFirefox() { return processJson('output-firefox/apps'); }
+function detectFirefox(cb) { detectUnusedFiles('output-firefox/apps', cb); }
+function zipFirefox(cb) { zipPackage('output-firefox', cb); }
+
+gulp.task('firefox',
     gulp.series(
-        cleanOutput, 
-        gulp.parallel(copyAssets, processCss, processJs, processHtml, processJson),
-        processChromeManifest, // 新增处理Chrome manifest的步骤
-        // compressImages,  // 已关闭图片压缩功能
+        cleanFirefox,
+        copyFirefox,
+        gulp.parallel(cssFirefox, jsFirefox, htmlFirefox, jsonFirefox),
+        firefoxPreprocess,
         setSilentDetect,
-        detectUnusedFiles,
+        detectFirefox,
         unsetSilentDetect,
-        zipPackage
+        zipFirefox
     )
 );
 

BIN
output-edge/fehelper.zip


BIN
output-firefox/fehelper.xpi