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:
commit
a10c91790f
13
.babelrc.js
13
.babelrc.js
@ -46,7 +46,9 @@ module.exports = function(api) {
|
||||
"@babel/proposal-export-namespace-from",
|
||||
"@babel/proposal-numeric-separator",
|
||||
["@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),
|
||||
overrides: [
|
||||
{
|
||||
@ -56,6 +58,15 @@ module.exports = function(api) {
|
||||
["@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),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ const assertTest = function(stdout, stderr, opts) {
|
||||
chai.expect(stderr).to.equal(expectStderr, "stderr didn't match");
|
||||
}
|
||||
} else if (stderr) {
|
||||
throw new Error("stderr:\n" + stderr);
|
||||
throw new Error("stderr:\n" + stderr + "\n\nstdout:\n" + stdout);
|
||||
}
|
||||
|
||||
const expectStdout = opts.stdout.trim();
|
||||
|
||||
@ -100,3 +100,34 @@ Disable the cache.
|
||||
```sh
|
||||
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.
|
||||
|
||||
@ -55,37 +55,47 @@ function compile(code, filename) {
|
||||
|
||||
if (env) cacheKey += `:${env}`;
|
||||
|
||||
if (cache) {
|
||||
const cached = cache[cacheKey];
|
||||
if (cached && cached.mtime === mtime(filename)) {
|
||||
return cached.code;
|
||||
let cached = cache && cache[cacheKey];
|
||||
|
||||
if (!cached || cached.mtime !== mtime(filename)) {
|
||||
cached = babel.transform(code, {
|
||||
...opts,
|
||||
sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps,
|
||||
ast: false,
|
||||
});
|
||||
|
||||
if (cache) {
|
||||
cache[cacheKey] = cached;
|
||||
cached.mtime = mtime(filename);
|
||||
}
|
||||
}
|
||||
|
||||
const result = babel.transform(code, {
|
||||
...opts,
|
||||
sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps,
|
||||
ast: false,
|
||||
});
|
||||
|
||||
if (cache) {
|
||||
cache[cacheKey] = result;
|
||||
result.mtime = mtime(filename);
|
||||
}
|
||||
|
||||
if (result.map) {
|
||||
if (cached.map) {
|
||||
if (Object.keys(maps).length === 0) {
|
||||
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) {
|
||||
if (piratesRevert) piratesRevert();
|
||||
piratesRevert = addHook(compile, { exts, ignoreNodeModules: false });
|
||||
piratesRevert = addHook(compileHook, { exts, ignoreNodeModules: false });
|
||||
}
|
||||
|
||||
export function revert() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user