Skip to content

Commit d04842a

Browse files
Avoid using CJS globals in internal source files (#12963)
* Lint against CJS globals in modules * Use `import.meta.url` instead of `__filename` in `src` files * Prepare fixtures runner for `import.meta.url` * Use `import.meta.url` instead of `__filename` in `test/index` files * Remove `__dirname` from remaining test files dirname * Avoid using `module` in `src` files * Avoid using `require` in `src` files * Avoid using `require` in `test` files * Update `@types/node` * Compile dynamic import in `@babel/node` * Fix windows * Use `@babel/plugin-proposal-dynamic-import` from npm
1 parent ea620e8 commit d04842a

147 files changed

Lines changed: 653 additions & 348 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.cjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const path = require("path");
44

5+
const cjsGlobals = ["__dirname", "__filename", "require", "module", "exports"];
6+
57
module.exports = {
68
root: true,
79
plugins: [
@@ -68,6 +70,25 @@ module.exports = {
6870
"import/extensions": ["error", { json: "always", cjs: "always" }],
6971
},
7072
},
73+
{
74+
files: [
75+
"packages/*/src/**/*.{js,ts}",
76+
"codemods/*/src/**/*.{js,ts}",
77+
"eslint/*/src/**/*.{js,ts}",
78+
"packages/*/test/**/*.js",
79+
"codemods/*/test/**/*.js",
80+
"eslint/*/test/**/*.js",
81+
"packages/babel-helper-transform-fixture-test-runner/src/helpers.{ts,js}",
82+
"test/**/*.js",
83+
],
84+
excludedFiles: [
85+
// @babel/register is the require() hook, so it will always be CJS-based
86+
"packages/babel-register/**/*.js",
87+
],
88+
rules: {
89+
"no-restricted-globals": ["error", ...cjsGlobals],
90+
},
91+
},
7192
{
7293
files: ["packages/babel-plugin-*/src/index.{js,ts}"],
7394
excludedFiles: ["packages/babel-plugin-transform-regenerator/**/*.js"],

babel.config.js

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ module.exports = function (api) {
159159
convertESM ? "@babel/proposal-export-namespace-from" : null,
160160
convertESM ? "@babel/transform-modules-commonjs" : null,
161161
convertESM ? pluginNodeImportInterop : null,
162+
convertESM ? pluginImportMetaUrl : null,
162163

163164
pluginPackageJsonMacro,
164165

@@ -177,14 +178,17 @@ module.exports = function (api) {
177178
plugins: ["babel-plugin-transform-charcodes"],
178179
assumptions: parserAssumptions,
179180
},
180-
{
181+
convertESM && {
181182
test: ["./packages/babel-cli", "./packages/babel-core"].map(normalize),
182183
plugins: [
183184
// Explicitly use the lazy version of CommonJS modules.
184-
convertESM
185-
? ["@babel/transform-modules-commonjs", { lazy: true }]
186-
: null,
187-
].filter(Boolean),
185+
["@babel/transform-modules-commonjs", { lazy: true }],
186+
],
187+
},
188+
convertESM && {
189+
test: ["./packages/babel-node/src"].map(normalize),
190+
// Used to conditionally import kexec
191+
plugins: ["@babel/plugin-proposal-dynamic-import"],
188192
},
189193
{
190194
test: sources.map(normalize),
@@ -465,3 +469,83 @@ function pluginNodeImportInterop({ template }) {
465469
},
466470
};
467471
}
472+
473+
function pluginImportMetaUrl({ types: t, template }) {
474+
const isImportMeta = node =>
475+
t.isMetaProperty(node) &&
476+
t.isIdentifier(node.meta, { name: "import" }) &&
477+
t.isIdentifier(node.property, { name: "meta" });
478+
479+
const isImportMetaUrl = node =>
480+
t.isMemberExpression(node, { computed: false }) &&
481+
t.isIdentifier(node.property, { name: "url" }) &&
482+
isImportMeta(node.object);
483+
484+
return {
485+
visitor: {
486+
Program(programPath) {
487+
// We must be sure to run this before the instanbul plugins, because its
488+
// instrumentation breaks our detection.
489+
programPath.traverse({
490+
// fileURLToPath(import.meta.url)
491+
CallExpression(path) {
492+
const { node } = path;
493+
494+
if (
495+
!t.isIdentifier(node.callee, { name: "fileURLToPath" }) ||
496+
node.arguments.length !== 1
497+
) {
498+
return;
499+
}
500+
501+
const arg = node.arguments[0];
502+
503+
if (
504+
!t.isMemberExpression(arg, { computed: false }) ||
505+
!t.isIdentifier(arg.property, { name: "url" }) ||
506+
!isImportMeta(arg.object)
507+
) {
508+
return;
509+
}
510+
511+
path.replaceWith(t.identifier("__filename"));
512+
},
513+
514+
// const require = createRequire(import.meta.url)
515+
VariableDeclarator(path) {
516+
const { node } = path;
517+
518+
if (
519+
!t.isIdentifier(node.id, { name: "require" }) ||
520+
!t.isCallExpression(node.init) ||
521+
!t.isIdentifier(node.init.callee, { name: "createRequire" }) ||
522+
node.init.arguments.length !== 1 ||
523+
!isImportMetaUrl(node.init.arguments[0])
524+
) {
525+
return;
526+
}
527+
528+
// Let's just remove this declaration to unshadow the "global" cjs require.
529+
path.remove();
530+
},
531+
532+
// import.meta.url
533+
MemberExpression(path) {
534+
if (!isImportMetaUrl(path.node)) return;
535+
536+
path.replaceWith(
537+
template.expression
538+
.ast`\`file://\${__filename.replace(/\\\\/g, "/")}\``
539+
);
540+
},
541+
542+
MetaProperty(path) {
543+
if (isImportMeta(path.node)) {
544+
throw path.buildCodeFrameError("Unsupported import.meta");
545+
}
546+
},
547+
});
548+
},
549+
},
550+
};
551+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import runner from "@babel/helper-plugin-test-runner";
22

3-
runner(__dirname);
3+
runner(import.meta.url);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import runner from "@babel/helper-plugin-test-runner";
22

3-
runner(__dirname);
3+
runner(import.meta.url);

eslint/babel-eslint-parser/test/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import path from "path";
22
import escope from "eslint-scope";
33
import unpad from "dedent";
4+
import { fileURLToPath } from "url";
5+
import { createRequire } from "module";
46
import { parseForESLint } from "../src";
57

68
const BABEL_OPTIONS = {
7-
configFile: require.resolve(
9+
configFile: path.resolve(
10+
path.dirname(fileURLToPath(import.meta.url)),
811
"../../babel-eslint-shared-fixtures/config/babel.config.js",
912
),
1013
};
@@ -73,6 +76,8 @@ describe("Babel and Espree", () => {
7376
}
7477

7578
beforeAll(async () => {
79+
const require = createRequire(import.meta.url);
80+
7681
// Use the version of Espree that is a dependency of
7782
// the version of ESLint we are testing against.
7883
const espreePath = require.resolve("espree", {
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dryErrorMessages from "./rules/dry-error-messages";
22

3-
module.exports = {
4-
rules: {
5-
"dry-error-messages": dryErrorMessages,
6-
},
3+
export const rules = {
4+
"dry-error-messages": dryErrorMessages,
75
};
6+
7+
export default { rules };

eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import path from "path";
22
import rule from "../../src/rules/dry-error-messages";
33
import RuleTester from "../../../babel-eslint-shared-fixtures/utils/RuleTester";
4+
import { fileURLToPath } from "url";
45

5-
const FILENAME = path.resolve(__dirname, "test/lib/index.js");
6+
const dirname = path.dirname(fileURLToPath(import.meta.url));
7+
8+
const FILENAME = path.resolve(dirname, "test/lib/index.js");
69
const ERRORS_MODULE = "errorsModule";
7-
const MODULE_SAME_DIR = path.resolve(__dirname, "test/lib/errorsModule.js");
8-
const MODULE_PARENT_DIR = path.resolve(__dirname, "test/errorsModule.js");
10+
const MODULE_SAME_DIR = path.resolve(dirname, "test/lib/errorsModule.js");
11+
const MODULE_PARENT_DIR = path.resolve(dirname, "test/errorsModule.js");
912

1013
const ruleTester = new RuleTester();
1114

eslint/babel-eslint-plugin-development/src/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import noDeprecatedClone from "./rules/no-deprecated-clone";
22
import noUndefinedIdentifier from "./rules/no-undefined-identifier";
33
import pluginName from "./rules/plugin-name";
44

5-
module.exports = {
6-
rules: {
7-
"no-deprecated-clone": noDeprecatedClone,
8-
"no-undefined-identifier": noUndefinedIdentifier,
9-
"plugin-name": pluginName,
10-
},
5+
export const rules = {
6+
"no-deprecated-clone": noDeprecatedClone,
7+
"no-undefined-identifier": noUndefinedIdentifier,
8+
"plugin-name": pluginName,
119
};
10+
11+
export default { rules };

eslint/babel-eslint-plugin/src/index.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ import noUnusedExpressions from "./rules/no-unused-expressions";
44
import objectCurlySpacing from "./rules/object-curly-spacing";
55
import semi from "./rules/semi";
66

7-
module.exports = {
8-
rules: {
9-
"new-cap": newCap,
10-
"no-invalid-this": noInvalidThis,
11-
"no-unused-expressions": noUnusedExpressions,
12-
"object-curly-spacing": objectCurlySpacing,
13-
semi,
14-
},
15-
rulesConfig: {
16-
"new-cap": "off",
17-
"no-invalid-this": "off",
18-
"no-unused-expressions": "off",
19-
"object-curly-spacing": "off",
20-
semi: "off",
21-
},
7+
export const rules = {
8+
"new-cap": newCap,
9+
"no-invalid-this": noInvalidThis,
10+
"no-unused-expressions": noUnusedExpressions,
11+
"object-curly-spacing": objectCurlySpacing,
12+
semi,
2213
};
14+
15+
export const rulesConfig = {
16+
"new-cap": "off",
17+
"no-invalid-this": "off",
18+
"no-unused-expressions": "off",
19+
"object-curly-spacing": "off",
20+
semi: "off",
21+
};
22+
23+
export default { rules, rulesConfig };

eslint/babel-eslint-tests/test/helpers/verifyAndAssertMessages.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import eslint from "eslint";
22
import unpad from "dedent";
3+
import path from "path";
4+
import { fileURLToPath } from "url";
35
import * as parser from "../../../babel-eslint-parser";
46

57
export default function verifyAndAssertMessages(
@@ -24,7 +26,8 @@ export default function verifyAndAssertMessages(
2426
sourceType,
2527
requireConfigFile: false,
2628
babelOptions: {
27-
configFile: require.resolve(
29+
configFile: path.resolve(
30+
path.dirname(fileURLToPath(import.meta.url)),
2831
"../../../babel-eslint-shared-fixtures/config/babel.config.js",
2932
),
3033
},

0 commit comments

Comments
 (0)