Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)

Build system updates (#839)

* Add 'remove' and 'splice' actions

* Destructure

* Clone values applied to the schema

* Move manifest modifications

* Use clone

* Fix destructure

* Add support for inheritance

* Create build function

* Add createVariantManifest

* Add support for command line arguments

* Enable argument passing when using the build scripts

authored by

toasted-nutbread and committed by
GitHub
e9d6c4cc 19e1f33b

+152 -31
+1 -1
build.bat
··· 1 - @npm run-script build 1 + @npm run-script build -- %*
+1 -1
build.sh
··· 1 1 #!/bin/bash 2 - npm run-script build 2 + npm run-script build -- "$@"
+150 -29
dev/build.js
··· 23 23 const {getAllFiles, getDefaultManifestAndVariants, createManifestString} = util; 24 24 25 25 26 + function clone(value) { 27 + return JSON.parse(JSON.stringify(value)); 28 + } 29 + 26 30 async function createZip(directory, outputFileName, sevenZipExes=[], onUpdate=null) { 27 31 for (const exe of sevenZipExes) { 28 32 try { ··· 71 75 fs.writeFileSync(outputFileName, data, {encoding: null, flag: 'w'}); 72 76 } 73 77 74 - function createModifiedManifest(manifest, modifications) { 75 - manifest = JSON.parse(JSON.stringify(manifest)); 76 - 78 + function applyModifications(manifest, modifications) { 77 79 if (Array.isArray(modifications)) { 78 80 for (const modification of modifications) { 79 81 const {action, path: path2} = modification; 80 82 switch (action) { 81 83 case 'set': 82 84 { 85 + const {value: newValue} = modification; 83 86 const value = getObjectProperties(manifest, path2, path2.length - 1); 84 87 const last = path2[path2.length - 1]; 85 - value[last] = modification.value; 88 + value[last] = clone(newValue); 86 89 } 87 90 break; 88 91 case 'replace': 89 92 { 93 + const {pattern, patternFlags, replacement} = modification; 90 94 const value = getObjectProperties(manifest, path2, path2.length - 1); 91 - const regex = new RegExp(modification.pattern, modification.patternFlags); 95 + const regex = new RegExp(pattern, patternFlags); 92 96 const last = path2[path2.length - 1]; 93 97 let value2 = value[last]; 94 - value2 = `${value2}`.replace(regex, modification.replacement); 98 + value2 = `${value2}`.replace(regex, replacement); 95 99 value[last] = value2; 96 100 } 97 101 break; ··· 102 106 delete value[last]; 103 107 } 104 108 break; 109 + case 'remove': 110 + { 111 + const {item} = modification; 112 + const value = getObjectProperties(manifest, path2, path2.length); 113 + const index = value.indexOf(item); 114 + if (index >= 0) { value.splice(index, 1); } 115 + } 116 + break; 117 + case 'splice': 118 + { 119 + const {start, deleteCount, items} = modification; 120 + const value = getObjectProperties(manifest, path2, path2.length); 121 + const itemsNew = items.map((v) => clone(v)); 122 + value.splice(start, deleteCount, ...itemsNew); 123 + } 124 + break; 105 125 } 106 126 } 107 127 } ··· 116 136 return object; 117 137 } 118 138 139 + function getInheritanceChain(variant, variantMap) { 140 + const visited = new Set(); 141 + const inheritance = []; 142 + while (true) { 143 + const {name, inherit} = variant; 144 + if (visited.has(name)) { break; } 119 145 120 - async function main() { 121 - const {manifest, variants} = getDefaultManifestAndVariants(); 146 + visited.add(name); 147 + inheritance.unshift(variant); 122 148 123 - const rootDir = path.join(__dirname, '..'); 124 - const extDir = path.join(rootDir, 'ext'); 125 - const buildDir = path.join(rootDir, 'builds'); 126 - const manifestPath = path.join(extDir, 'manifest.json'); 149 + if (typeof inherit !== 'string') { break; } 150 + 151 + const nextVariant = variantMap.get(inherit); 152 + if (typeof nextVariant === 'undefined') { break; } 153 + 154 + variant = nextVariant; 155 + } 156 + return inheritance; 157 + } 158 + 159 + function createVariantManifest(manifest, variant, variantMap) { 160 + let modifiedManifest = clone(manifest); 161 + for (const {modifications} of getInheritanceChain(variant, variantMap)) { 162 + modifiedManifest = applyModifications(modifiedManifest, modifications); 163 + } 164 + return modifiedManifest; 165 + } 166 + 167 + async function build(manifest, buildDir, extDir, manifestPath, variantMap, variantNames) { 127 168 const sevenZipExes = ['7za', '7z']; 128 169 129 170 // Create build directory 130 171 if (!fs.existsSync(buildDir)) { 131 172 fs.mkdirSync(buildDir, {recursive: true}); 132 173 } 133 - 134 174 135 175 const onUpdate = (metadata) => { 136 176 let message = `Progress: ${metadata.percent.toFixed(2)}%`; ··· 143 183 process.stdout.write(message); 144 184 }; 145 185 146 - try { 147 - for (const variant of variants) { 148 - const {name, fileName, fileCopies, modifications} = variant; 149 - process.stdout.write(`Building ${name}...\n`); 186 + for (const variantName of variantNames) { 187 + const variant = variantMap.get(variantName); 188 + if (typeof variant === 'undefined') { continue; } 189 + 190 + const {name, fileName, fileCopies} = variant; 191 + process.stdout.write(`Building ${name}...\n`); 192 + 193 + const modifiedManifest = createVariantManifest(manifest, variant, variantMap); 194 + 195 + const fileNameSafe = path.basename(fileName); 196 + const fullFileName = path.join(buildDir, fileNameSafe); 197 + fs.writeFileSync(manifestPath, createManifestString(modifiedManifest)); 198 + await createZip(extDir, fullFileName, sevenZipExes, onUpdate); 199 + 200 + if (Array.isArray(fileCopies)) { 201 + for (const fileName2 of fileCopies) { 202 + const fileName2Safe = path.basename(fileName2); 203 + fs.copyFileSync(fullFileName, path.join(buildDir, fileName2Safe)); 204 + } 205 + } 206 + 207 + process.stdout.write('\n'); 208 + } 209 + } 150 210 151 - const fileNameSafe = path.basename(fileName); 152 - const modifiedManifest = createModifiedManifest(manifest, modifications); 153 - const fullFileName = path.join(buildDir, fileNameSafe); 154 - fs.writeFileSync(manifestPath, createManifestString(modifiedManifest)); 155 - await createZip(extDir, fullFileName, sevenZipExes, onUpdate); 211 + function getArs(args, argMap) { 212 + let key = null; 213 + let canKey = true; 214 + let onKey = false; 215 + for (const arg of args) { 216 + onKey = false; 156 217 157 - if (Array.isArray(fileCopies)) { 158 - for (const fileName2 of fileCopies) { 159 - const fileName2Safe = path.basename(fileName2); 160 - fs.copyFileSync(fullFileName, path.join(buildDir, fileName2Safe)); 161 - } 218 + if (canKey && arg.startsWith('--')) { 219 + if (arg.length === 2) { 220 + canKey = false; 221 + key = null; 222 + onKey = false; 223 + } else { 224 + key = arg.substring(2); 225 + onKey = true; 162 226 } 227 + } 163 228 164 - process.stdout.write('\n'); 229 + const target = argMap.get(key); 230 + if (typeof target === 'boolean') { 231 + argMap.set(key, true); 232 + key = null; 233 + } else if (typeof target === 'number') { 234 + argMap.set(key, target + 1); 235 + key = null; 236 + } else if (target === null || typeof target === 'string') { 237 + if (!onKey) { 238 + argMap.set(key, arg); 239 + key = null; 240 + } 241 + } else if (Array.isArray(target)) { 242 + if (!onKey) { 243 + target.push(arg); 244 + key = null; 245 + } 246 + } else { 247 + console.error(`Unknown argument: ${arg}`); 248 + key = null; 165 249 } 250 + } 251 + 252 + return argMap; 253 + } 254 + 255 + 256 + async function main() { 257 + const argv = process.argv.slice(2); 258 + const args = getArs(argv, new Map([ 259 + ['all', false], 260 + ['default', false], 261 + ['manifest', null], 262 + [null, []] 263 + ])); 264 + 265 + const {manifest, variants} = getDefaultManifestAndVariants(); 266 + 267 + const rootDir = path.join(__dirname, '..'); 268 + const extDir = path.join(rootDir, 'ext'); 269 + const buildDir = path.join(rootDir, 'builds'); 270 + const manifestPath = path.join(extDir, 'manifest.json'); 271 + 272 + const variantMap = new Map(); 273 + for (const variant of variants) { 274 + variantMap.set(variant.name, variant); 275 + } 276 + 277 + try { 278 + const variantNames = (argv.length === 0 || args.get('all') ? variants.map(({name}) => name) : args.get(null)); 279 + await build(manifest, buildDir, extDir, manifestPath, variantMap, variantNames); 166 280 } finally { 167 281 // Restore manifest 282 + let restoreManifest = manifest; 283 + if (!args.get('default') && args.get('manifest') !== null) { 284 + const variant = variantMap.get(args.get('manifest')); 285 + if (typeof variant !== 'undefined') { 286 + restoreManifest = createVariantManifest(manifest, variant, variantMap); 287 + } 288 + } 168 289 process.stdout.write('Restoring manifest...\n'); 169 - fs.writeFileSync(manifestPath, createManifestString(manifest)); 290 + fs.writeFileSync(manifestPath, createManifestString(restoreManifest)); 170 291 } 171 292 } 172 293