From 57c03321b7055ca784e28332a1f471749aaed646 Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Thu, 20 Mar 2025 16:51:28 +0100 Subject: [PATCH] Replace build script with convert script for module transformation --- package.json | 2 +- utils/build.js | 74 ------------------------- utils/convert.js | 140 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 75 deletions(-) delete mode 100644 utils/build.js create mode 100755 utils/convert.js diff --git a/package.json b/package.json index ec645c1e..805c23e7 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "scripts": { "lint": "eslint app core po/po2js.js po/xgettext-html.js tests utils", "test": "karma start karma.conf.cjs", - "prepare": "node ./utils/build.js --clean" + "prepare": "node ./utils/convert.js --clean" }, "repository": { "type": "git", diff --git a/utils/build.js b/utils/build.js deleted file mode 100644 index a7e0400c..00000000 --- a/utils/build.js +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env node -import fs from "fs"; -import path from "path"; -import fse from "fs-extra"; -import { program } from "commander"; -import { ensureDir } from "fs-extra"; -import { fileURLToPath } from "url"; - -program - .option("--clean", "clear the lib folder before building") - .parse(process.argv); - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -// the various important paths -const paths = { - main: path.resolve(__dirname, ".."), - core: path.resolve(__dirname, "..", "core"), - vendor: path.resolve(__dirname, "..", "vendor"), - libDirBase: path.resolve(__dirname, "..", "lib"), -}; - -// walkDir *recursively* walks directories trees, -// calling the callback for all normal files found. -const walkDir = async (basePath, cb, filter) => { - const files = await fs.promises.readdir(basePath); - const paths = files.map(filename => path.join(basePath, filename)); - return Promise.all( - paths.map(async (filepath) => { - const stats = await fs.promises.lstat(filepath); - if (filter !== undefined && !filter(filepath, stats)) return; - if (stats.isSymbolicLink()) return; - if (stats.isFile()) return cb(filepath); - if (stats.isDirectory()) return walkDir(filepath, cb, filter); - }) - ); -}; - -const makeLibFiles = async () => { - fse.ensureDirSync(paths.libDirBase); - const outFiles = []; - - const handleDir = async (vendorRewrite, inPathBase, filename) => { - const outPath = path.join( - paths.libDirBase, - path.relative(inPathBase, filename) - ); - if (path.extname(filename) !== ".js") { - return; // skip non-javascript files - } - await ensureDir(path.dirname(outPath)); - await fs.promises.copyFile(filename, outPath); - console.log(`Writing ${outPath}`); - outFiles.push(`${outPath}`); - }; - const handler = handleDir.bind(null, false, paths.main); - await walkDir(paths.vendor, handler); - const handler2 = handleDir.bind(null, true, paths.core); - await walkDir(paths.core, handler2); - return outFiles; -}; -const options = program.opts(); -if (options.clean) { - console.log(`Removing ${paths.libDirBase}`); - fse.removeSync(paths.libDirBase); -} -makeLibFiles() - .then((outFiles) => { - console.log(`Converted ${outFiles.length} files`); - }) - .catch((err) => { - console.error(`Failure converting modules: ${err}`); - process.exit(1); - }); diff --git a/utils/convert.js b/utils/convert.js new file mode 100755 index 00000000..31fa1ac7 --- /dev/null +++ b/utils/convert.js @@ -0,0 +1,140 @@ +#!/usr/bin/env node + +import path from 'path'; +import { program } from 'commander'; +import fs from 'fs'; +import fse from 'fs-extra'; +import babel from '@babel/core'; + +program + .option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ') + .option('--clean', 'clear the lib folder before building') + .parse(process.argv); + +// the various important paths +const paths = { + main: path.resolve(__dirname, '..'), + core: path.resolve(__dirname, '..', 'core'), + vendor: path.resolve(__dirname, '..', 'vendor'), + libDirBase: path.resolve(__dirname, '..', 'lib'), +}; + +// util.promisify requires Node.js 8.x, so we have our own +function promisify(original) { + return function promiseWrap() { + const args = Array.prototype.slice.call(arguments); + return new Promise((resolve, reject) => { + original.apply(this, args.concat((err, value) => { + if (err) return reject(err); + resolve(value); + })); + }); + }; +} + +const writeFile = promisify(fs.writeFile); + +const readdir = promisify(fs.readdir); +const lstat = promisify(fs.lstat); + +const ensureDir = promisify(fse.ensureDir); + +const babelTransformFile = promisify(babel.transformFile); + +// walkDir *recursively* walks directories trees, +// calling the callback for all normal files found. +function walkDir(basePath, cb, filter) { + return readdir(basePath) + .then((files) => { + const paths = files.map(filename => path.join(basePath, filename)); + return Promise.all(paths.map(filepath => lstat(filepath) + .then((stats) => { + if (filter !== undefined && !filter(filepath, stats)) return; + + if (stats.isSymbolicLink()) return; + if (stats.isFile()) return cb(filepath); + if (stats.isDirectory()) return walkDir(filepath, cb, filter); + }))); + }); +} + +function makeLibFiles(sourceMaps) { + // NB: we need to make a copy of babelOpts, since babel sets some defaults on it + const babelOpts = () => ({ + plugins: [], + presets: [ + [ '@babel/preset-env', + { modules: 'commonjs' } ] + ], + ast: false, + sourceMaps: sourceMaps, + }); + + fse.ensureDirSync(paths.libDirBase); + + const outFiles = []; + + const handleDir = (vendorRewrite, inPathBase, filename) => Promise.resolve() + .then(() => { + const outPath = path.join(paths.libDirBase, path.relative(inPathBase, filename)); + + if (path.extname(filename) !== '.js') { + return; // skip non-javascript files + } + return Promise.resolve() + .then(() => ensureDir(path.dirname(outPath))) + .then(() => { + const opts = babelOpts(); + // Adjust for the fact that we move the core files relative + // to the vendor directory + if (vendorRewrite) { + opts.plugins.push(["import-redirect", + {"root": paths.libDirBase, + "redirect": { "vendor/(.+)": "./vendor/$1"}}]); + } + + return babelTransformFile(filename, opts) + .then((res) => { + console.log(`Writing ${outPath}`); + const {map} = res; + let {code} = res; + if (sourceMaps === true) { + // append URL for external source map + code += `\n//# sourceMappingURL=${path.basename(outPath)}.map\n`; + } + outFiles.push(`${outPath}`); + return writeFile(outPath, code) + .then(() => { + if (sourceMaps === true || sourceMaps === 'both') { + console.log(` and ${outPath}.map`); + outFiles.push(`${outPath}.map`); + return writeFile(`${outPath}.map`, JSON.stringify(map)); + } + }); + }); + }); + }); + + Promise.resolve() + .then(() => { + const handler = handleDir.bind(null, false, paths.main); + return walkDir(paths.vendor, handler); + }) + .then(() => { + const handler = handleDir.bind(null, true, paths.core); + return walkDir(paths.core, handler); + }) + .catch((err) => { + console.error(`Failure converting modules: ${err}`); + process.exit(1); + }); +} + +let options = program.opts(); + +if (options.clean) { + console.log(`Removing ${paths.libDirBase}`); + fse.removeSync(paths.libDirBase); +} + +makeLibFiles(options.withSourceMaps);