diff options
Diffstat (limited to 'dev/build.js')
-rw-r--r-- | dev/build.js | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/dev/build.js b/dev/build.js new file mode 100644 index 00000000..6b62083e --- /dev/null +++ b/dev/build.js @@ -0,0 +1,214 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +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(); } |