mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 01:49:41 -05:00
Use explicit extensions for imports within src (#58421)
This commit is contained in:
70
scripts/eslint/rules/js-extensions.cjs
Normal file
70
scripts/eslint/rules/js-extensions.cjs
Normal file
@@ -0,0 +1,70 @@
|
||||
const { createRule } = require("./utils.cjs");
|
||||
|
||||
module.exports = createRule({
|
||||
name: "js-extensions",
|
||||
meta: {
|
||||
docs: {
|
||||
description: ``,
|
||||
},
|
||||
messages: {
|
||||
missingJsExtension: `This relative module reference is missing a '.js' extension`,
|
||||
},
|
||||
schema: [],
|
||||
type: "suggestion",
|
||||
fixable: "code",
|
||||
},
|
||||
defaultOptions: [],
|
||||
|
||||
create(context) {
|
||||
/** @type {(
|
||||
* node:
|
||||
* | import("@typescript-eslint/utils").TSESTree.ImportDeclaration
|
||||
* | import("@typescript-eslint/utils").TSESTree.ExportAllDeclaration
|
||||
* | import("@typescript-eslint/utils").TSESTree.ExportNamedDeclaration
|
||||
* | import("@typescript-eslint/utils").TSESTree.TSImportEqualsDeclaration
|
||||
* | import("@typescript-eslint/utils").TSESTree.TSModuleDeclaration
|
||||
* ) => void}
|
||||
*/
|
||||
const check = node => {
|
||||
let source;
|
||||
if (node.type === "TSImportEqualsDeclaration") {
|
||||
const moduleReference = node.moduleReference;
|
||||
if (
|
||||
moduleReference.type === "TSExternalModuleReference"
|
||||
&& moduleReference.expression.type === "Literal"
|
||||
&& typeof moduleReference.expression.value === "string"
|
||||
) {
|
||||
source = moduleReference.expression;
|
||||
}
|
||||
}
|
||||
else if (node.type === "TSModuleDeclaration") {
|
||||
if (node.kind === "module" && node.id.type === "Literal") {
|
||||
source = node.id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
source = node.source;
|
||||
}
|
||||
|
||||
// This is not 100% accurate; this could point to a nested package, or to a directory
|
||||
// containing an index.js file. But we don't have anything like that in our repo,
|
||||
// so this check is good enough. Replicate this logic at your own risk.
|
||||
if (source?.value.startsWith(".") && !/\.[cm]?js$/.test(source.value)) {
|
||||
const quote = source.raw[0];
|
||||
context.report({
|
||||
messageId: "missingJsExtension",
|
||||
node: source,
|
||||
fix: fixer => fixer.replaceText(source, `${quote}${source.value}.js${quote}`),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
ImportDeclaration: check,
|
||||
ExportAllDeclaration: check,
|
||||
ExportNamedDeclaration: check,
|
||||
TSImportEqualsDeclaration: check,
|
||||
TSModuleDeclaration: check,
|
||||
};
|
||||
},
|
||||
});
|
||||
149
scripts/eslint/tests/js-extensions.cjs
Normal file
149
scripts/eslint/tests/js-extensions.cjs
Normal file
@@ -0,0 +1,149 @@
|
||||
const { RuleTester } = require("./support/RuleTester.cjs");
|
||||
const rule = require("../rules/js-extensions.cjs");
|
||||
|
||||
const ruleTester = new RuleTester({
|
||||
parserOptions: {
|
||||
warnOnUnsupportedTypeScriptVersion: false,
|
||||
},
|
||||
parser: require.resolve("@typescript-eslint/parser"),
|
||||
});
|
||||
|
||||
ruleTester.run("js-extensions", rule, {
|
||||
valid: [
|
||||
{
|
||||
code: `
|
||||
import "some-library";
|
||||
import "./a.js";
|
||||
import "./a.mjs";
|
||||
import "./a.cjs";
|
||||
import "../foo/a.js";
|
||||
import "../foo/a.mjs";
|
||||
import "../foo/a.cjs";
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import * as blah from "some-library";
|
||||
import * as blah from "./a.js";
|
||||
import * as blah from "./a.mjs";
|
||||
import * as blah from "./a.cjs";
|
||||
import * as blah from "../foo/a.js";
|
||||
import * as blah from "../foo/a.mjs";
|
||||
import * as blah from "../foo/a.cjs";
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
export * from "some-library";
|
||||
export * from "./a.js";
|
||||
export * from "./a.mjs";
|
||||
export * from "./a.cjs";
|
||||
export * from "../foo/a.js";
|
||||
export * from "../foo/a.mjs";
|
||||
export * from "../foo/a.cjs";
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import blah = require("some-library");
|
||||
import blah = require("./a.js");
|
||||
import blah = require("./a.mjs");
|
||||
import blah = require("./a.cjs");
|
||||
import blah = require("../foo/a.js");
|
||||
import blah = require("../foo/a.mjs");
|
||||
import blah = require("../foo/a.cjs");
|
||||
`,
|
||||
},
|
||||
],
|
||||
|
||||
invalid: [
|
||||
{
|
||||
code: `
|
||||
import "./a";
|
||||
import "../foo/a";
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 2,
|
||||
column: 8,
|
||||
},
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 3,
|
||||
column: 8,
|
||||
},
|
||||
],
|
||||
output: `
|
||||
import "./a.js";
|
||||
import "../foo/a.js";
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import * as blah from "./a";
|
||||
import * as blah from "../foo/a";
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 2,
|
||||
column: 23,
|
||||
},
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 3,
|
||||
column: 23,
|
||||
},
|
||||
],
|
||||
output: `
|
||||
import * as blah from "./a.js";
|
||||
import * as blah from "../foo/a.js";
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
export * from "./a";
|
||||
export * from "../foo/a";
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 2,
|
||||
column: 15,
|
||||
},
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 3,
|
||||
column: 15,
|
||||
},
|
||||
],
|
||||
output: `
|
||||
export * from "./a.js";
|
||||
export * from "../foo/a.js";
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import blah = require("./a");
|
||||
import blah = require("../foo/a");
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 2,
|
||||
column: 23,
|
||||
},
|
||||
{
|
||||
messageId: "missingJsExtension",
|
||||
line: 3,
|
||||
column: 23,
|
||||
},
|
||||
],
|
||||
output: `
|
||||
import blah = require("./a.js");
|
||||
import blah = require("../foo/a.js");
|
||||
`,
|
||||
},
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user