Merge pull request #7588 from loganfsmyth/register-cycle-fixes

Have @babel/core lazy-load all dependencies and make @babel/register not explode because of that
This commit is contained in:
Logan Smyth 2018-03-25 15:24:00 -07:00 committed by GitHub
commit a10c91790f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 21 deletions

View File

@ -46,7 +46,9 @@ module.exports = function(api) {
"@babel/proposal-export-namespace-from", "@babel/proposal-export-namespace-from",
"@babel/proposal-numeric-separator", "@babel/proposal-numeric-separator",
["@babel/proposal-object-rest-spread", { useBuiltIns: true }], ["@babel/proposal-object-rest-spread", { useBuiltIns: true }],
convertESM ? "@babel/transform-modules-commonjs" : null,
// Explicitly use the lazy version of CommonJS modules.
convertESM ? ["@babel/transform-modules-commonjs", { lazy: true }] : null,
].filter(Boolean), ].filter(Boolean),
overrides: [ overrides: [
{ {
@ -56,6 +58,15 @@ module.exports = function(api) {
["@babel/transform-for-of", { assumeArray: true }], ["@babel/transform-for-of", { assumeArray: true }],
], ],
}, },
{
test: "./packages/babel-register",
plugins: [
// Override the root options to disable lazy imports for babel-register
// because otherwise the require hook will try to lazy-import things
// leading to dependency cycles.
convertESM ? "@babel/transform-modules-commonjs" : null,
].filter(Boolean),
},
], ],
}; };

View File

@ -64,7 +64,7 @@ const assertTest = function(stdout, stderr, opts) {
chai.expect(stderr).to.equal(expectStderr, "stderr didn't match"); chai.expect(stderr).to.equal(expectStderr, "stderr didn't match");
} }
} else if (stderr) { } else if (stderr) {
throw new Error("stderr:\n" + stderr); throw new Error("stderr:\n" + stderr + "\n\nstdout:\n" + stdout);
} }
const expectStdout = opts.stdout.trim(); const expectStdout = opts.stdout.trim();

View File

@ -100,3 +100,34 @@ Disable the cache.
```sh ```sh
BABEL_DISABLE_CACHE=1 babel-node script.js BABEL_DISABLE_CACHE=1 babel-node script.js
``` ```
## Compiling plugins and presets on the fly
`@babel/register` uses Node's `require()` hook system to compile files
on the fly when they are loaded. While this is quite helpful overall, it means
that there can be confusing cases where code within a `require()` hook causes
_more_ calls to `require`, causing a dependency cycle. In Babel's case for
instance, this could mean that in the process of Babel trying to compile a
user's file, Babel could end up trying to compile itself _as it is loading_.
To avoid this problem, this module explicitly disallows re-entrant compilation,
e.g. Babel's own compilation logic explicitly cannot trigger further compilation
of any other files on the fly. The downside of this is that if you want to
define a plugin or preset that is itself live-compiled, the process is
complicated.
The crux of it is that your own code needs to load the plugin/preset first.
Assuming the plugin/preset loads all of its dependencies up front, what you'll
want to do is:
```
require("@babel/register")({
// ...
});
require("./my-plugin");
```
Because it is your own code that triggered the load, and not the logic within
`@babel/register` itself, this should successfully compile any plugin/preset
that that loads synchronously.

View File

@ -55,37 +55,47 @@ function compile(code, filename) {
if (env) cacheKey += `:${env}`; if (env) cacheKey += `:${env}`;
if (cache) { let cached = cache && cache[cacheKey];
const cached = cache[cacheKey];
if (cached && cached.mtime === mtime(filename)) {
return cached.code;
}
}
const result = babel.transform(code, { if (!cached || cached.mtime !== mtime(filename)) {
cached = babel.transform(code, {
...opts, ...opts,
sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps, sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps,
ast: false, ast: false,
}); });
if (cache) { if (cache) {
cache[cacheKey] = result; cache[cacheKey] = cached;
result.mtime = mtime(filename); cached.mtime = mtime(filename);
}
} }
if (result.map) { if (cached.map) {
if (Object.keys(maps).length === 0) { if (Object.keys(maps).length === 0) {
installSourceMapSupport(); installSourceMapSupport();
} }
maps[filename] = result.map; maps[filename] = cached.map;
} }
return result.code; return cached.code;
}
let compiling = false;
function compileHook(code, filename) {
if (compiling) return code;
try {
compiling = true;
return compile(code, filename);
} finally {
compiling = false;
}
} }
function hookExtensions(exts) { function hookExtensions(exts) {
if (piratesRevert) piratesRevert(); if (piratesRevert) piratesRevert();
piratesRevert = addHook(compile, { exts, ignoreNodeModules: false }); piratesRevert = addHook(compileHook, { exts, ignoreNodeModules: false });
} }
export function revert() { export function revert() {