Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use native Node.js functions when available #12458

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
133 changes: 97 additions & 36 deletions babel.config.js
Expand Up @@ -21,7 +21,7 @@ module.exports = function (api) {
let convertESM = true;
let ignoreLib = true;
let includeRegeneratorRuntime = false;
let polyfillRequireResolve = false;
let needsPolyfillsForOldNode = false;

let transformRuntimeOptions;

Expand Down Expand Up @@ -54,15 +54,15 @@ module.exports = function (api) {
"packages/babel-compat-data"
);
if (env === "rollup") envOpts.targets = { node: nodeVersion };
polyfillRequireResolve = true;
needsPolyfillsForOldNode = true;
break;
case "test-legacy": // In test-legacy environment, we build babel on latest node but test on minimum supported legacy versions
case "production":
// Config during builds before publish.
envOpts.targets = {
node: nodeVersion,
};
polyfillRequireResolve = true;
needsPolyfillsForOldNode = true;
break;
case "development":
envOpts.debug = true;
Expand All @@ -79,7 +79,7 @@ module.exports = function (api) {

if (process.env.STRIP_BABEL_8_FLAG && bool(process.env.BABEL_8_BREAKING)) {
// Never apply polyfills when compiling for Babel 8
polyfillRequireResolve = false;
needsPolyfillsForOldNode = false;
}

if (includeRegeneratorRuntime) {
Expand Down Expand Up @@ -129,7 +129,7 @@ module.exports = function (api) {
pluginToggleBabel8Breaking,
{ breaking: bool(process.env.BABEL_8_BREAKING) },
],
polyfillRequireResolve && pluginPolyfillRequireResolve,
needsPolyfillsForOldNode && pluginPolyfillsOldNode,
].filter(Boolean),
overrides: [
{
Expand Down Expand Up @@ -180,41 +180,102 @@ function bool(value) {
return value && value !== "false" && value !== "0";
}

// TODO(Babel 8) This polyfill is only needed for Node.js 6 and 8
function pluginPolyfillRequireResolve({ template, types: t }) {
// TODO(Babel 8) This polyfills are only needed for Node.js 6 and 8
/** @param {import("@babel/core")} api */
function pluginPolyfillsOldNode({ template, types: t }) {
const polyfills = [
{
name: "require.resolve",
necessary({ node, parent }) {
return (
t.isCallExpression(parent, { callee: node }) &&
parent.arguments.length > 1
);
},
supported({ parent: { arguments: args } }) {
return (
t.isObjectExpression(args[1]) &&
args[1].properties.length === 1 &&
t.isIdentifier(args[1].properties[0].key, { name: "paths" }) &&
t.isArrayExpression(args[1].properties[0].value) &&
args[1].properties[0].value.elements.length === 1
);
},
// require.resolve's paths option has been introduced in Node.js 8.9
// https://nodejs.org/api/modules.html#modules_require_resolve_request_options
replacement: template({ syntacticPlaceholders: true })`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This polyfill was already there, I only moved it.

parseFloat(process.versions.node) >= 8.9
? require.resolve
: (/* request */ r, { paths: [/* base */ b] }, M = require("module")) => {
let /* filename */ f = M._findPath(r, M._nodeModulePaths(b).concat(b));
if (f) return f;
f = new Error(\`Cannot resolve module '\${r}'\`);
f.code = "MODULE_NOT_FOUND";
throw f;
}
`,
},
{
// NOTE: This polyfills depends on the "make-dir" library. Any package
// using fs.mkdirSync must have "make-dir" as a dependency.
name: "fs.mkdirSync",
necessary({ node, parent }) {
return (
t.isCallExpression(parent, { callee: node }) &&
parent.arguments.length > 1
);
},
supported({ parent: { arguments: args } }) {
return (
t.isObjectExpression(args[1]) &&
args[1].properties.length === 1 &&
t.isIdentifier(args[1].properties[0].key, { name: "recursive" }) &&
t.isBooleanLiteral(args[1].properties[0].value, { value: true })
);
},
// fs.mkdirSync's recursive option has been introduced in Node.js 10.12
// https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options
replacement: template`
parseFloat(process.versions.node) >= 10.12
? fs.mkdirSync
: require("make-dir").sync
`,
},
{
// NOTE: This polyfills depends on the "node-environment-flags"
// library. Any package using process.allowedNodeEnvironmentFlags
// must have "node-environment-flags" as a dependency.
name: "process.allowedNodeEnvironmentFlags",
necessary({ parent, node }) {
// To avoid infinite replacement loops
return !t.isConditionalExpression(parent, { consequent: node });
},
supported: () => true,
// process.allowedNodeEnvironmentFlags has been introduced in Node.js 10.10
// https://nodejs.org/api/process.html#process_process_allowednodeenvironmentflags
replacement: template`
process.allowedNodeEnvironmentFlags || require("node-environment-flags")
`,
},
];

return {
visitor: {
MemberExpression(path) {
if (!path.matchesPattern("require.resolve")) return;
if (!t.isCallExpression(path.parent, { callee: path.node })) return;

const args = path.parent.arguments;
if (args.length < 2) return;
if (
!t.isObjectExpression(args[1]) ||
args[1].properties.length !== 1 ||
!t.isIdentifier(args[1].properties[0].key, { name: "paths" }) ||
!t.isArrayExpression(args[1].properties[0].value) ||
args[1].properties[0].value.elements.length !== 1
) {
throw path.parentPath.buildCodeFrameError(
"This 'require.resolve' usage is not supported by the inline polyfill."
);
}
for (const polyfill of polyfills) {
if (!path.matchesPattern(polyfill.name)) continue;

if (!polyfill.necessary(path)) return;
if (!polyfill.supported(path)) {
throw path.buildCodeFrameError(
`This '${polyfill.name}' usage is not supported by the inline polyfill.`
);
}

// require.resolve's paths option has been introduced in Node.js 8.9
// https://nodejs.org/api/modules.html#modules_require_resolve_request_options
path.replaceWith(template.ast`
parseFloat(process.versions.node) >= 8.9
? require.resolve
: (/* request */ r, { paths: [/* base */ b] }, M = require("module")) => {
let /* filename */ f = M._findPath(r, M._nodeModulePaths(b).concat(b));
if (f) return f;
f = new Error(\`Cannot resolve module '\${r}'\`);
f.code = "MODULE_NOT_FOUND";
throw f;
}
`);
path.replaceWith(polyfill.replacement());

break;
}
},
},
};
Expand Down
5 changes: 2 additions & 3 deletions packages/babel-cli/src/babel/dir.js
@@ -1,7 +1,6 @@
// @flow

import debounce from "lodash/debounce";
import { sync as makeDirSync } from "make-dir";
import slash from "slash";
import path from "path";
import fs from "fs";
Expand All @@ -17,7 +16,7 @@ const FILE_TYPE = Object.freeze({
});

function outputFileSync(filePath: string, data: string | Buffer): void {
makeDirSync(path.dirname(filePath));
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, data);
}

Expand Down Expand Up @@ -163,7 +162,7 @@ export default async function ({
util.deleteDir(cliOptions.outDir);
}

makeDirSync(cliOptions.outDir);
fs.mkdirSync(cliOptions.outDir, { recursive: true });

startTime = process.hrtime();

Expand Down
3 changes: 1 addition & 2 deletions packages/babel-cli/src/babel/file.js
Expand Up @@ -3,7 +3,6 @@
import convertSourceMap from "convert-source-map";
import sourceMap from "source-map";
import slash from "slash";
import { sync as makeDirSync } from "make-dir";
import path from "path";
import fs from "fs";

Expand Down Expand Up @@ -89,7 +88,7 @@ export default async function ({
const result = buildResult(fileResults);

if (cliOptions.outFile) {
makeDirSync(path.dirname(cliOptions.outFile));
fs.mkdirSync(path.dirname(cliOptions.outFile), { recursive: true });

// we've requested for a sourcemap to be written to disk
if (babelOptions.sourceMaps && babelOptions.sourceMaps !== "inline") {
Expand Down
6 changes: 1 addition & 5 deletions packages/babel-node/src/babel-node.js
Expand Up @@ -6,10 +6,6 @@
import getV8Flags from "v8flags";
import path from "path";

// TODO: When support for node < 10.10 will be dropped, this package
// can be replaced with process.allowedNodeEnvironmentFlags
import allowedNodeEnvironmentFlags from "node-environment-flags";

let args = [path.join(__dirname, "_babel-node")];

let babelArgs = process.argv.slice(2);
Expand Down Expand Up @@ -59,7 +55,7 @@ getV8Flags(function (err, v8Flags) {
flag === "debug" || // node debug foo.js
flag === "inspect" ||
v8Flags.indexOf(getNormalizedV8Flag(flag)) >= 0 ||
allowedNodeEnvironmentFlags.has(flag)
process.allowedNodeEnvironmentFlags.has(flag)
) {
args.unshift(arg);
} else {
Expand Down
Expand Up @@ -2,7 +2,6 @@

const path = require("path");
const fs = require("fs");
const makeDirSync = require("make-dir").sync;
const helpers = require("@babel/helpers");
const babel = require("@babel/core");
const template = require("@babel/template");
Expand All @@ -15,7 +14,7 @@ const corejs2Definitions = require("../lib/runtime-corejs2-definitions").default
const corejs3Definitions = require("../lib/runtime-corejs3-definitions").default();

function outputFile(filePath, data) {
makeDirSync(path.dirname(filePath));
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, data);
}

Expand Down
7 changes: 3 additions & 4 deletions packages/babel-register/src/cache.js
@@ -1,7 +1,6 @@
import path from "path";
import fs from "fs";
import os from "os";
import { sync as makeDirSync } from "make-dir";
import * as babel from "@babel/core";
import findCacheDir from "find-cache-dir";

Expand Down Expand Up @@ -39,7 +38,7 @@ export function save() {
}

try {
makeDirSync(path.dirname(FILENAME));
fs.mkdirSync(path.dirname(FILENAME), { recursive: true });
fs.writeFileSync(FILENAME, serialised);
} catch (e) {
switch (e.code) {
Expand All @@ -49,14 +48,14 @@ export function save() {
case "EACCES":
case "EPERM":
console.warn(
`Babel could not write cache to file: ${FILENAME}
`Babel could not write cache to file: ${FILENAME}
due to a permission issue. Cache is disabled.`,
);
cacheDisabled = true;
break;
case "EROFS":
console.warn(
`Babel could not write cache to file: ${FILENAME}
`Babel could not write cache to file: ${FILENAME}
because it resides in a readonly filesystem. Cache is disabled.`,
);
cacheDisabled = true;
Expand Down
8 changes: 7 additions & 1 deletion packages/babel-register/test/index.js
Expand Up @@ -77,7 +77,13 @@ describe("@babel/register", function () {
cleanCache();
}

afterEach(() => {
afterEach(async () => {
// @babel/register saves the cache on process.nextTick.
// We need to wait for at least one tick so that when jest
// tears down the testing environment @babel/register has
// already finished.
await new Promise(setImmediate);

revertRegister();
currentHook = null;
currentOptions = null;
Expand Down