From 8ff675ad6982c83c182e9ec79f5b682702d97648 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 1 May 2018 22:32:01 -0700 Subject: [PATCH] Refactor CLI to use promises/async fns. --- packages/babel-cli/src/babel/dir.js | 196 ++++++++---------- packages/babel-cli/src/babel/file.js | 176 ++++++++-------- packages/babel-cli/src/babel/index.js | 5 +- packages/babel-cli/src/babel/util.js | 18 +- .../babel/empty dir --out-dir/stdout.txt | 1 + packages/babel-core/package.json | 2 +- 6 files changed, 194 insertions(+), 204 deletions(-) diff --git a/packages/babel-cli/src/babel/dir.js b/packages/babel-cli/src/babel/dir.js index 1f6311b1fd..5ae00b440b 100644 --- a/packages/babel-cli/src/babel/dir.js +++ b/packages/babel-cli/src/babel/dir.js @@ -6,15 +6,14 @@ import fs from "fs"; import * as util from "./util"; -let compiledFiles = 0; - -export default function({ cliOptions, babelOptions }) { +export default async function({ cliOptions, babelOptions }) { const filenames = cliOptions.filenames; - function write(src, base, callback) { + async function write(src, base) { let relative = path.relative(base, src); + if (!util.isCompilableExtension(relative, cliOptions.extensions)) { - return process.nextTick(callback); + return false; } // remove extension and then append back on .js @@ -22,45 +21,47 @@ export default function({ cliOptions, babelOptions }) { const dest = getDest(relative, base); - util.compile( - src, - defaults( - { - sourceFileName: slash(path.relative(dest + "/..", src)), - }, - babelOptions, - ), - function(err, res) { - if (err && cliOptions.watch) { - console.error(err); - err = null; - } - if (err) return callback(err); - if (!res) return callback(); + try { + const res = await util.compile( + src, + defaults( + { + sourceFileName: slash(path.relative(dest + "/..", src)), + }, + babelOptions, + ), + ); - // we've requested explicit sourcemaps to be written to disk - if ( - res.map && - babelOptions.sourceMaps && - babelOptions.sourceMaps !== "inline" - ) { - const mapLoc = dest + ".map"; - res.code = util.addSourceMappingUrl(res.code, mapLoc); - res.map.file = path.basename(relative); - outputFileSync(mapLoc, JSON.stringify(res.map)); - } + if (!res) return false; - outputFileSync(dest, res.code); - util.chmod(src, dest); + // we've requested explicit sourcemaps to be written to disk + if ( + res.map && + babelOptions.sourceMaps && + babelOptions.sourceMaps !== "inline" + ) { + const mapLoc = dest + ".map"; + res.code = util.addSourceMappingUrl(res.code, mapLoc); + res.map.file = path.basename(relative); + outputFileSync(mapLoc, JSON.stringify(res.map)); + } - compiledFiles += 1; + outputFileSync(dest, res.code); + util.chmod(src, dest); - if (cliOptions.verbose) { - console.log(src + " -> " + dest); - } - return callback(null, true); - }, - ); + if (cliOptions.verbose) { + console.log(src + " -> " + dest); + } + + return true; + } catch (err) { + if (cliOptions.watch) { + console.error(err); + return false; + } + + throw err; + } } function getDest(filename, base) { @@ -77,84 +78,62 @@ export default function({ cliOptions, babelOptions }) { } } - function handleFile(src, base, callback) { - write(src, base, function(err, res) { - if (err) return callback(err); - if (!res && cliOptions.copyFiles) { - const filename = path.relative(base, src); - const dest = getDest(filename, base); - outputFileSync(dest, fs.readFileSync(src)); - util.chmod(src, dest); - } + async function handleFile(src, base) { + const written = await write(src, base); - return callback(); - }); + if (!written && cliOptions.copyFiles) { + const filename = path.relative(base, src); + const dest = getDest(filename, base); + outputFileSync(dest, fs.readFileSync(src)); + util.chmod(src, dest); + } + return written; } - function sequentialHandleFile(files, dirname, index, callback) { - if (files.length === 0) { - outputDestFolder(cliOptions.outDir); - return; - } + async function handle(filenameOrDir) { + if (!fs.existsSync(filenameOrDir)) return 0; - if (typeof index === "function") { - callback = index; - index = 0; - } + const stat = fs.statSync(filenameOrDir); - const filename = files[index]; - const src = path.join(dirname, filename); + if (stat.isDirectory(filenameOrDir)) { + const dirname = filenameOrDir; - handleFile(src, dirname, function(err) { - if (err) return callback(err); - index++; - if (index !== files.length) { - sequentialHandleFile(files, dirname, index, callback); - } else { - callback(); - } - }); - } - - function handle(filename, callback) { - if (!fs.existsSync(filename)) return; - - const stat = fs.statSync(filename); - - if (stat.isDirectory(filename)) { - const dirname = filename; - - if (cliOptions.deleteDirOnStart) { - util.deleteDir(cliOptions.outDir); - } + let count = 0; const files = util.readdir(dirname, cliOptions.includeDotfiles); - sequentialHandleFile(files, dirname, callback); - } else { - write(filename, path.dirname(filename), callback); - } - } + for (const filename of files) { + const src = path.join(dirname, filename); - function sequentialHandle(filenames, index = 0) { - const filename = filenames[index]; - - handle(filename, function(err) { - if (err) throw new Error(err); - index++; - if (index !== filenames.length) { - sequentialHandle(filenames, index); - } else { - console.log( - `🎉 Successfully compiled ${compiledFiles} ${ - compiledFiles > 1 ? "files" : "file" - } with Babel.`, - ); + const written = await handleFile(src, dirname); + if (written) count += 1; } - }); + + return count; + } else { + const filename = filenameOrDir; + const written = await handleFile(filename, path.dirname(filename)); + + return written ? 1 : 0; + } } if (!cliOptions.skipInitialBuild) { - sequentialHandle(filenames); + if (cliOptions.deleteDirOnStart) { + util.deleteDir(cliOptions.outDir); + } + + outputDestFolder(cliOptions.outDir); + + let compiledFiles = 0; + for (const filename of cliOptions.filenames) { + compiledFiles += await handle(filename); + } + + console.log( + `🎉 Successfully compiled ${compiledFiles} ${ + compiledFiles !== 1 ? "files" : "file" + } with Babel.`, + ); } if (cliOptions.watch) { @@ -177,10 +156,9 @@ export default function({ cliOptions, babelOptions }) { filename === filenameOrDir ? path.dirname(filenameOrDir) : filenameOrDir, - function(err) { - if (err) console.error(err.stack); - }, - ); + ).catch(err => { + console.error(err); + }); }); }); }); diff --git a/packages/babel-cli/src/babel/file.js b/packages/babel-cli/src/babel/file.js index 2d043914d8..31433b33da 100644 --- a/packages/babel-cli/src/babel/file.js +++ b/packages/babel-cli/src/babel/file.js @@ -7,12 +7,8 @@ import fs from "fs"; import * as util from "./util"; -export default function({ cliOptions, babelOptions }) { - const filenames = cliOptions.filenames; - - let results = []; - - const buildResult = function() { +export default async function({ cliOptions, babelOptions }) { + function buildResult(fileResults) { const map = new sourceMap.SourceMapGenerator({ file: cliOptions.sourceMapTarget || @@ -24,7 +20,9 @@ export default function({ cliOptions, babelOptions }) { let code = ""; let offset = 0; - results.forEach(function(result) { + for (const result of fileResults) { + if (!result) continue; + code += result.code + "\n"; if (result.map) { @@ -59,7 +57,7 @@ export default function({ cliOptions, babelOptions }) { offset = code.split("\n").length - 1; } - }); + } // add the inline sourcemap comment if we've either explicitly asked for inline source // maps, or we've requested them without any output file @@ -74,10 +72,10 @@ export default function({ cliOptions, babelOptions }) { map: map, code: code, }; - }; + } - const output = function() { - const result = buildResult(); + function output(fileResults) { + const result = buildResult(fileResults); if (cliOptions.outFile) { // we've requested for a sourcemap to be written to disk @@ -91,40 +89,45 @@ export default function({ cliOptions, babelOptions }) { } else { process.stdout.write(result.code + "\n"); } - }; + } - const stdin = function() { - let code = ""; + function readStdin() { + return new Promise((resolve, reject) => { + let code = ""; - process.stdin.setEncoding("utf8"); + process.stdin.setEncoding("utf8"); - process.stdin.on("readable", function() { - const chunk = process.stdin.read(); - if (chunk !== null) code += chunk; + process.stdin.on("readable", function() { + const chunk = process.stdin.read(); + if (chunk !== null) code += chunk; + }); + + process.stdin.on("end", function() { + resolve(code); + }); + process.stdin.on("error", reject); }); + } - process.stdin.on("end", function() { - util.transform( - cliOptions.filename, - code, - defaults( - { - sourceFileName: "stdin", - }, - babelOptions, - ), - function(err, res) { - if (err) throw err; - results.push(res); - output(); + async function stdin() { + const code = await readStdin(); + + const res = await util.transform( + cliOptions.filename, + code, + defaults( + { + sourceFileName: "stdin", }, - ); - }); - }; + babelOptions, + ), + ); - const walk = function() { + output([res]); + } + + async function walk(filenames) { const _filenames = []; - results = []; filenames.forEach(function(filename) { if (!fs.existsSync(filename)) return; @@ -143,55 +146,51 @@ export default function({ cliOptions, babelOptions }) { } }); - let filesProcessed = 0; + const results = await Promise.all( + _filenames.map(async function(filename) { + let sourceFilename = filename; + if (cliOptions.outFile) { + sourceFilename = path.relative( + path.dirname(cliOptions.outFile), + sourceFilename, + ); + } + sourceFilename = slash(sourceFilename); - _filenames.forEach(function(filename, index) { - let sourceFilename = filename; - if (cliOptions.outFile) { - sourceFilename = path.relative( - path.dirname(cliOptions.outFile), - sourceFilename, - ); - } - sourceFilename = slash(sourceFilename); - - util.compile( - filename, - defaults( - { - sourceFileName: sourceFilename, - // Since we're compiling everything to be merged together, - // "inline" applies to the final output file, but to the individual - // files being concatenated. - sourceMaps: - babelOptions.sourceMaps === "inline" - ? true - : babelOptions.sourceMaps, - }, - babelOptions, - ), - function(err, res) { - if (err && cliOptions.watch) { - console.error(err); - err = null; + try { + return await util.compile( + filename, + defaults( + { + sourceFileName: sourceFilename, + // Since we're compiling everything to be merged together, + // "inline" applies to the final output file, but to the individual + // files being concatenated. + sourceMaps: + babelOptions.sourceMaps === "inline" + ? true + : babelOptions.sourceMaps, + }, + babelOptions, + ), + ); + } catch (err) { + if (!cliOptions.watch) { + throw err; } - if (err) throw err; + console.error(err); + return null; + } + }), + ); - filesProcessed++; - if (res) results[index] = res; + output(results); + } - if (filesProcessed === _filenames.length) { - output(); - } - }, - ); - }); - }; - - const files = function() { + async function files(filenames) { if (!cliOptions.skipInitialBuild) { - walk(); + await walk(filenames); } if (cliOptions.watch) { @@ -214,19 +213,18 @@ export default function({ cliOptions, babelOptions }) { if (cliOptions.verbose) { console.log(type + " " + filename); } - try { - walk(); - } catch (err) { - console.error(err.stack); - } + + walk(filenames).catch(err => { + console.error(err); + }); } }); } - }; + } - if (filenames.length) { - files(); + if (cliOptions.filenames.length) { + await files(cliOptions.filenames); } else { - stdin(); + await stdin(); } } diff --git a/packages/babel-cli/src/babel/index.js b/packages/babel-cli/src/babel/index.js index da482b029a..f319457607 100755 --- a/packages/babel-cli/src/babel/index.js +++ b/packages/babel-cli/src/babel/index.js @@ -7,4 +7,7 @@ import fileCommand from "./file"; const opts = parseArgv(process.argv); const fn = opts.cliOptions.outDir ? dirCommand : fileCommand; -fn(opts); +fn(opts).catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/packages/babel-cli/src/babel/util.js b/packages/babel-cli/src/babel/util.js index f5319b3668..5d0cdc479e 100644 --- a/packages/babel-cli/src/babel/util.js +++ b/packages/babel-cli/src/babel/util.js @@ -45,17 +45,27 @@ export function addSourceMappingUrl(code, loc) { return code + "\n//# sourceMappingURL=" + path.basename(loc); } -export function transform(filename, code, opts, callback) { +export function transform(filename, code, opts) { opts = { ...opts, filename, }; - babel.transform(code, opts, callback); + return new Promise((resolve, reject) => { + babel.transform(code, opts, (err, result) => { + if (err) reject(err); + else resolve(result); + }); + }); } -export function compile(filename, opts, callback) { - babel.transformFile(filename, opts, callback); +export function compile(filename, opts) { + return new Promise((resolve, reject) => { + babel.transformFile(filename, opts, (err, result) => { + if (err) reject(err); + else resolve(result); + }); + }); } export function deleteDir(path) { diff --git a/packages/babel-cli/test/fixtures/babel/empty dir --out-dir/stdout.txt b/packages/babel-cli/test/fixtures/babel/empty dir --out-dir/stdout.txt index e69de29bb2..32542635ae 100644 --- a/packages/babel-cli/test/fixtures/babel/empty dir --out-dir/stdout.txt +++ b/packages/babel-cli/test/fixtures/babel/empty dir --out-dir/stdout.txt @@ -0,0 +1 @@ +🎉 Successfully compiled 0 files with Babel. diff --git a/packages/babel-core/package.json b/packages/babel-core/package.json index f4b7e5a7d1..7401358575 100644 --- a/packages/babel-core/package.json +++ b/packages/babel-core/package.json @@ -23,7 +23,7 @@ "compiler" ], "engines": { - "node": ">=6.9.0 < 11.0.0-0" + "node": ">=6.9.0" }, "browser": { "./lib/config/files/index.js": "./lib/config/files/index-browser.js",