UNPKG

sb-babel-cli

Version:
204 lines (203 loc) 9.27 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const os_1 = __importDefault(require("os")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const make_dir_1 = __importDefault(require("make-dir")); const chokidar_1 = __importDefault(require("chokidar")); const anymatch_1 = __importDefault(require("anymatch")); const debounce_1 = __importDefault(require("lodash/debounce")); const child_process_1 = __importDefault(require("child_process")); const sb_promise_queue_1 = require("sb-promise-queue"); const iterate_1 = __importDefault(require("./iterate")); const helpers_1 = require("./helpers"); async function main(cliConfig) { var _a; const config = await helpers_1.loadConfigFromRoot(cliConfig.rootDirectory, cliConfig); const resolvedSourceDirectory = path_1.default.resolve(config.rootDirectory, config.sourceDirectory); const resolvedOutputDirectory = path_1.default.resolve(config.rootDirectory, (_a = config.outputDirectory) !== null && _a !== void 0 ? _a : ''); if (config.printConfig) { console.log('CLI Config', JSON.stringify(config, null, 2)); console.log('Resolved Config (with config merged from Manifest)', JSON.stringify(config, null, 2)); console.log('resolvedSourceDirectory', resolvedSourceDirectory); console.log('resolvedOutputDirectory', resolvedOutputDirectory); process.exit(1); } if (!config.watch && config.execute) { console.error('ERROR: --execute is not supported without --watch'); process.exit(1); } if (!config.outputDirectory) { console.log('ERROR: You must specify output directory'); process.exit(1); } config.sourceDirectory = resolvedSourceDirectory; config.outputDirectory = resolvedOutputDirectory; // Sort the longest extensions to the shortest. This will help with replace config.extensions.sort((a, b) => b.length - a.length); let restartId = 0; let lastRestartId = 0; let spawnedProcess = null; const babelCore = helpers_1.getBabelCore(config.sourceDirectory); const contentHash = `${config.sourceDirectory}-${config.outputFileExtension}`; const contentHashCache = await helpers_1.getCacheDB(contentHash, !config.resetCache, config.cacheDirectory); const transformationQueue = new sb_promise_queue_1.PromiseQueue({ concurrency: os_1.default.cpus().length }); const getOutputFilePath = (filePath) => { const foundExt = config.extensions.find((ext) => filePath.endsWith(ext)); if (foundExt != null) { return `${filePath.slice(0, -1 * foundExt.length)}${config.outputFileExtension}`; } return filePath; }; const incrementRestartId = () => { restartId = (restartId + 1) % 1024; }; function log(...items) { if (config.silent) { return; } if (config.execute) { console.log(`${chalk_1.default.yellow('[sb-babel-cli]')}`, ...items); } else { console.log(...items); } } async function processFile(sourceFile, outputFile, sourceFileContents, stats) { if (!config.extensions.includes(path_1.default.extname(sourceFile))) return; const transformed = await babelCore.transformAsync(sourceFileContents, { root: config.rootDirectory, filename: sourceFile, sourceMaps: config.sourceMaps === 'inline' ? 'inline' : config.sourceMaps, }); if (transformed == null || transformed.code == null) { return; } await make_dir_1.default(path_1.default.dirname(outputFile)); const mapFile = `${outputFile}.map`; let outputContents = transformed.code; if (config.sourceMaps === true) { outputContents += `\n\n//# sourceMappingURL=${path_1.default.basename(mapFile)}`; } await Promise.all([ fs_1.default.promises.writeFile(outputFile, outputContents, { mode: stats.mode, }), // Write source maps if option is given. config.sourceMaps && config.sourceMaps !== 'inline' ? fs_1.default.promises.writeFile(mapFile, JSON.stringify(transformed.map, null, 2)) : null, ]); log(path_1.default.relative(config.rootDirectory, sourceFile), '->', path_1.default.relative(config.rootDirectory, outputFile)); contentHashCache.set(helpers_1.getSha1(sourceFile), helpers_1.getSha1(sourceFileContents)).write(); } const execute = debounce_1.default(function () { if (!config.execute) { return; } if (lastRestartId !== 0 && lastRestartId === restartId) { return; } lastRestartId = restartId; if (spawnedProcess == null) { log(chalk_1.default.yellow('to restart at any time, enter `rs`')); } log(chalk_1.default.green(`starting 'node ${config.execute}'`)); if (spawnedProcess != null) { spawnedProcess.kill('SIGINT'); spawnedProcess = null; } try { spawnedProcess = child_process_1.default.spawn(process.execPath, config.nodeArgs.concat([config.execute]).concat(config.programArgs), { stdio: 'inherit', }); } catch (err) { helpers_1.logError(err); } }, config.executeDelay); await iterate_1.default({ config, getOutputFilePath, async callback(sourceFile, outputFile, stats) { const cachedHash = await contentHashCache.get(helpers_1.getSha1(sourceFile)).value(); const sourceFileContents = await fs_1.default.promises.readFile(sourceFile, 'utf8'); let outputFileExists = false; try { await fs_1.default.promises.access(outputFile, fs_1.default.constants.R_OK); outputFileExists = true; } catch (_) { /* No Op */ } if (outputFileExists && cachedHash === helpers_1.getSha1(sourceFileContents)) { if (!config.execute) { log(path_1.default.relative(config.rootDirectory, sourceFile), 'is unchanged'); } return; } transformationQueue.add(() => processFile(sourceFile, outputFile, sourceFileContents, stats)).catch(helpers_1.logError); }, }); if (!config.watch) { await transformationQueue.waitTillIdle(); return; } if (config.execute && process.stdin) { if (typeof process.stdin.unref === 'function') { process.stdin.unref(); } process.stdin.on('data', function (chunk) { if (chunk.toString().trim() === 'rs') { incrementRestartId(); execute(); } }); } const watcher = chokidar_1.default.watch(config.sourceDirectory, { ignored: config.ignored ? config.ignored : null, alwaysStat: true, ignoreInitial: true, }); watcher.on('add', function (givenFileName, stats) { const fileName = path_1.default.relative(config.sourceDirectory, givenFileName); const sourceFile = path_1.default.join(config.sourceDirectory, fileName); const outputFile = path_1.default.join(config.outputDirectory, fileName); transformationQueue .add(async () => processFile(sourceFile, getOutputFilePath(outputFile), await fs_1.default.promises.readFile(sourceFile, 'utf8'), stats)) .catch(helpers_1.logError) .then(() => { if (!config.ignoredForRestart || !anymatch_1.default(config.ignoredForRestart, helpers_1.posixifyPath(sourceFile))) { incrementRestartId(); } }); }); watcher.on('change', function (givenFileName, stats) { const fileName = path_1.default.relative(config.sourceDirectory, givenFileName); const sourceFile = path_1.default.join(config.sourceDirectory, fileName); const outputFile = path_1.default.join(config.outputDirectory, fileName); transformationQueue .add(async () => processFile(sourceFile, getOutputFilePath(outputFile), await fs_1.default.promises.readFile(sourceFile, 'utf8'), stats)) .catch(helpers_1.logError) .then(() => { if (!config.ignoredForRestart || !anymatch_1.default(config.ignoredForRestart, helpers_1.posixifyPath(sourceFile))) { incrementRestartId(); } }); }); watcher.on('unlink', function (givenFileName) { const fileName = path_1.default.relative(config.sourceDirectory, givenFileName); const outputFile = path_1.default.join(config.outputDirectory, fileName); fs_1.default.promises.unlink(outputFile).catch(function () { /* No Op */ }); }); transformationQueue.onIdle(execute); execute(); } exports.default = main;