/* * Copyright (C) 2020 Yomichan Authors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ const fs = require('fs'); const path = require('path'); const readline = require('readline'); const childProcess = require('child_process'); const yomichanTest = require('../test/yomichan-test'); function getAllFiles(directory, relativeTo) { const results = []; const directories = [directory]; for (const dir of directories) { const fileNames = fs.readdirSync(dir); for (const fileName of fileNames) { const fullFileName = path.join(dir, fileName); const relativeFileName = path.relative(relativeTo, fullFileName); const stats = fs.lstatSync(fullFileName); if (stats.isFile()) { results.push(relativeFileName); } else if (stats.isDirectory()) { directories.push(fullFileName); } } } return results; } async function createZip(directory, outputFileName, sevenZipExes=[], onUpdate=null) { for (const exe of sevenZipExes) { try { childProcess.execFileSync( exe, [ 'a', outputFileName, '.' ], { cwd: directory } ); return; } catch (e) { // NOP } } return await createJSZip(directory, outputFileName, onUpdate); } async function createJSZip(directory, outputFileName, onUpdate) { const JSZip = yomichanTest.JSZip; const files = getAllFiles(directory, directory); const zip = new JSZip(); for (const fileName of files) { zip.file( fileName.replace(/\\/g, '/'), fs.readFileSync(path.join(directory, fileName), {encoding: null, flag: 'r'}), {} ); } if (typeof onUpdate !== 'function') { onUpdate = () => {}; // NOP } const data = await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE', compressionOptions: {level: 9} }, onUpdate); process.stdout.write('\n'); fs.writeFileSync(outputFileName, data, {encoding: null, flag: 'w'}); } function createModifiedManifest(manifest, modifications) { manifest = JSON.parse(JSON.stringify(manifest)); if (Array.isArray(modifications)) { for (const modification of modifications) { const {action, path: path2} = modification; switch (action) { case 'set': { const value = getObjectProperties(manifest, path2, path2.length - 1); const last = path2[path2.length - 1]; value[last] = modification.value; } break; case 'replace': { const value = getObjectProperties(manifest, path2, path2.length - 1); const regex = new RegExp(modification.pattern, modification.patternFlags); const last = path2[path2.length - 1]; let value2 = value[last]; value2 = `${value2}`.replace(regex, modification.replacement); value[last] = value2; } break; case 'delete': { const value = getObjectProperties(manifest, path2, path2.length - 1); const last = path2[path2.length - 1]; delete value[last]; } break; } } } return manifest; } function getObjectProperties(object, path2, count) { for (let i = 0; i < count; ++i) { object = object[path2[i]]; } return object; } function loadDefaultManifest() { const {manifest} = loadDefaultManifestAndVariants(); return manifest; } function loadDefaultManifestAndVariants() { const fileName = path.join(__dirname, 'data', 'manifest-variants.json'); const {manifest, variants} = JSON.parse(fs.readFileSync(fileName)); return {manifest, variants}; } function createManifestString(manifest) { return JSON.stringify(manifest, null, 4) + '\n'; } async function main() { const {manifest, variants} = loadDefaultManifestAndVariants(); const rootDir = path.join(__dirname, '..'); const extDir = path.join(rootDir, 'ext'); const buildDir = path.join(rootDir, 'builds'); const manifestPath = path.join(extDir, 'manifest.json'); const sevenZipExes = ['7za', '7z']; // Create build directory if (!fs.existsSync(buildDir)) { fs.mkdirSync(buildDir, {recursive: true}); } const onUpdate = (metadata) => { let message = `Progress: ${metadata.percent.toFixed(2)}%`; if (metadata.currentFile) { message += ` (${metadata.currentFile})`; } readline.clearLine(process.stdout); readline.cursorTo(process.stdout, 0); process.stdout.write(message); }; try { for (const variant of variants) { const {name, fileName, fileCopies, modifications} = variant; process.stdout.write(`Building ${name}...\n`); const fileNameSafe = path.basename(fileName); const modifiedManifest = createModifiedManifest(manifest, modifications); const fullFileName = path.join(buildDir, fileNameSafe); fs.writeFileSync(manifestPath, createManifestString(modifiedManifest)); await createZip(extDir, fullFileName, sevenZipExes, onUpdate); if (Array.isArray(fileCopies)) { for (const fileName2 of fileCopies) { const fileName2Safe = path.basename(fileName2); fs.copyFileSync(fullFileName, path.join(buildDir, fileName2Safe)); } } process.stdout.write('\n'); } } finally { // Restore manifest process.stdout.write('Restoring manifest...\n'); fs.writeFileSync(manifestPath, createManifestString(manifest)); } } module.exports = { loadDefaultManifest, loadDefaultManifestAndVariants, createManifestString }; if (require.main === module) { main(); }