UNPKG

@launchmenu/build-tools

Version:

This package contains some build tools with a CLI interface that can be used to work on launchmenu itself, or applets of launchmenu.

307 lines (292 loc) 9.44 kB
const spawn = require("cross-spawn-promise"); const cpx = require("cpx"); const Path = require("path"); const FS = require("fs"); const rimraf = require("rimraf"); const chalk = require("chalk"); const exportTools = require("./exportTools"); const error = chalk.rgb(200, 0, 0); const info = chalk.rgb(100, 100, 255); // Obtain a path to ts const resolvePackageDir = packageName => Path.dirname(require.resolve(`${packageName}/package.json`)); const tsPath = Path.join(resolvePackageDir("typescript"), "bin/tsc"); const electronPath = Path.join(resolvePackageDir("electron"), process.platform == "win32" ? "dist/electron.exe" : "/dist/Electron.app/Contents/MacOS/Electron"); /** * Default values for variables */ const defaults = { srcDir: "src", buildDir: "build", cleanup: false, build: false, watch: false, reexport: false, launch: false, launchParams: {}, launchElectron: true, production: false, srcEntry: "index.ts", entry: "index.js", copyExtensions: ["html", "css", "jpg", "png", "ttf", "js", "vbs", "exe"], tsConfig: Path.join(process.cwd(), "tsconfig.json"), verbose: true, srcMaps: true, emitDeclarations: true, apiDir: "api", typesDir: "types", exportToFileName: ".exportTo", noExportText: "noExport", indexPath: "index", }; /** * Compiles TS code according to the given configuration * @param {object} config The configuration * @returns {Promise<void>} A promise that resolves once compilation finishes */ function compileTS({ srcDir = defaults.srcDir, buildDir = defaults.buildDir, srcMaps = defaults.srcMaps, emitDeclarations = defaults.emitDeclarations, tsConfig = defaults.tsConfig, verbose = defaults.verbose, watchMode = defaults.watch, srcEntry = defaults.srcEntry, } = {}) { let params = []; if (FS.existsSync(tsConfig)) { params = [ ...(watchMode ? ["--watch"] : []), ...(verbose && !watchMode ? ["--diagnostics"] : []), "--project", tsConfig, ]; } else { params = [ ...(watchMode ? ["--watch"] : []), ...(srcMaps ? ["--sourceMap"] : []), ...(emitDeclarations ? ["--declaration"] : []), ...(emitDeclarations && srcMaps ? ["--declarationMap"] : []), ...(verbose && !watchMode ? ["--diagnostics"] : []), "--esModuleInterop", "--tsBuildInfoFile", ".tsbuildinfo", "--incremental", "--outDir", buildDir, "--rootDir", srcDir, `${srcDir}/${srcEntry}`, ]; } return spawn("node", [tsPath, ...params], {stdio: "inherit"}); } /** * Moves files of the specified type from the specified source to build directory * @param {object} config The configuration * @returns {Promise<void>} A promise that resolves once moving finishes */ function moveFiles({ srcDir = defaults.srcDir, buildDir = defaults.buildDir, extensions = defaults.copyExtensions, verbose = defaults.verbose, watchMode = defaults.watch, } = {}) { const srcBlob = `${srcDir}/**/*.{${extensions.join(",")}}`; if (watchMode) { return new Promise(() => { const watcher = cpx.watch(srcBlob, buildDir, { initialCopy: false, }); if (verbose) watcher .on("copy", e => { console.log(`Copied ${e.srcPath} -> ${e.dstPath}`); }) .on("remove", e => { console.log(`Removed ${e.path}`); }); }); } else { return new Promise((res, rej) => { cpx.copy(srcBlob, buildDir, err => { if (err) rej(err); else res(); }); }); } } /** * Resets the contents of the build dir * @param {object} config The configuration * @returns {Promise<void>} A promise that resolves once deletion finishes */ function resetBuildDir({buildDir = defaults.buildDir} = {}) { return new Promise((res, rej) => { rimraf(buildDir, {glob: false}, err => { if (err) rej(err); else res(); }); }); } /** * Launches the application that the build is for * @param {object} config The configuration * @returns {Promise<void>} A promise that resolves once the application closes */ function launchApp({ buildDir = defaults.buildDir, entry = defaults.entry, launchParams = defaults.launchParams, launchElectron = defaults.launchElectron, production = defaults.production, } = {}) { if (!production) launchParams = {...launchParams, NODE_ENV: "dev"}; const params = [`${buildDir}/${entry}`]; Object.keys(launchParams).forEach(key => { params.push("--" + key, launchParams[key]); }); if (launchElectron) return spawn(electronPath, params, {stdio: "inherit", env: launchParams}); else return spawn("node", params, {stdio: "inherit"}); } /** * Runs the build tools * @param {object} config The configuration * @returns {Promise<void>} A promise that resolves once build finishes */ async function run({ srcDir = defaults.srcDir, buildDir = defaults.buildDir, cleanup = defaults.cleanup, build = defaults.build, watch = defaults.watch, reexport = defaults.reexport, launch = defaults.launch, launchParams = defaults.launchParams, launchElectron = defaults.launchElectron, entry = defaults.entry, srcEntry = defaults.srcEntry, copyExtensions = defaults.copyExtensions, tsConfig = defaults.tsConfig, verbose = defaults.verbose, srcMaps = defaults.srcMaps, production = defaults.production, emitDeclarations = defaults.emitDeclarations, apiDir = defaults.apiDir, typesDir = defaults.typesDir, exportToFileName = defaults.exportToFileName, noExportText = defaults.noExportText, indexPath = defaults.indexPath, } = {}) { if (typeof copyExtensions == "string") copyExtensions = copyExtensions.split(/,\s*/); if (typeof launchParams == "string") launchParams = JSON.parse(launchParams); if (cleanup) { if (verbose) console.log(info("[Cleanup]: started")); await resetBuildDir({buildDir}); if (verbose) console.log(info("[Cleanup]: finished")); } let exportOutputs; if (build) { if (verbose) console.log(info("[build]: started copying files")); await moveFiles({ srcDir, buildDir, extensions: copyExtensions, verbose, watchMode: false, }); if (verbose) console.log(info("[build]: finished copying files")); if (verbose) console.log(info("[build]: started transpiling typescript")); await compileTS({ srcDir, buildDir, srcMaps, emitDeclarations, tsConfig, verbose, srcEntry, watchMode: false, }); if (verbose) console.log(info("[build]: finished transpiling typescript")); if (reexport) { if (verbose) console.log(info("[build]: started adding exports structure")); exportOutputs = await exportTools.buildExports({ srcDir, buildDir, apiDir, typesDir, exportToFileName, noExportText, indexPath, watchMode: false, }); if (verbose) console.log(info("[build]: finished adding exports structure")); } } let launchPromise; if (launch) { if (verbose) console.log(info("[launch]: launching application")); launchPromise = launchApp({ buildDir, entry, launchParams, launchElectron, production, }).then(() => { if (verbose) console.log(info("[launch]: quit application")); }); } let watchPromise; if (watch) { if (verbose) console.log(info("[watch]: watching for file changes")); watchPromise = Promise.all([ moveFiles({ srcDir, buildDir, extensions: copyExtensions, verbose, watchMode: true, }), compileTS({ srcDir, buildDir, srcMaps, emitDeclarations, tsConfig, verbose, srcEntry, watchMode: true, }), reexport ? exportTools.buildExports({ srcDir, buildDir, apiDir, typesDir, exportToFileName, noExportText, indexPath, watchMode: true, outputs: exportOutputs, }) : [], ]).then(() => { if (verbose) console.log(info("[watch]: stopped watching for file changes")); }); } await launchPromise; await watchPromise; } module.exports = { compileTS, moveFiles, resetBuildDir, launchApp, run, defaults, exportTools, };