Skip to content

Commit 16beff1

Browse files
authored
Use explicit extensions for imports within src (microsoft#58421)
1 parent 9598d35 commit 16beff1

File tree

582 files changed

+2583
-2360
lines changed

Some content is hidden

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

582 files changed

+2583
-2360
lines changed

Diff for: .eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@
144144
"local/no-in-operator": "error",
145145
"local/debug-assert": "error",
146146
"local/no-keywords": "error",
147-
"local/jsdoc-format": "error"
147+
"local/jsdoc-format": "error",
148+
"local/js-extensions": "error"
148149
},
149150
"overrides": [
150151
// By default, the ESLint CLI only looks at .js files. But, it will also look at

Diff for: .vscode/settings.template.json

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
".git-blame-ignore-revs"
1919
],
2020

21+
"javascript.preferences.importModuleSpecifierEnding": "js",
22+
"typescript.preferences.importModuleSpecifierEnding": "js",
23+
2124
// Match dprint in organize/auto-imports.
2225
"typescript.unstable": {
2326
"organizeImportsCollation": "unicode",

Diff for: scripts/eslint/rules/js-extensions.cjs

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const { createRule } = require("./utils.cjs");
2+
3+
module.exports = createRule({
4+
name: "js-extensions",
5+
meta: {
6+
docs: {
7+
description: ``,
8+
},
9+
messages: {
10+
missingJsExtension: `This relative module reference is missing a '.js' extension`,
11+
},
12+
schema: [],
13+
type: "suggestion",
14+
fixable: "code",
15+
},
16+
defaultOptions: [],
17+
18+
create(context) {
19+
/** @type {(
20+
* node:
21+
* | import("@typescript-eslint/utils").TSESTree.ImportDeclaration
22+
* | import("@typescript-eslint/utils").TSESTree.ExportAllDeclaration
23+
* | import("@typescript-eslint/utils").TSESTree.ExportNamedDeclaration
24+
* | import("@typescript-eslint/utils").TSESTree.TSImportEqualsDeclaration
25+
* | import("@typescript-eslint/utils").TSESTree.TSModuleDeclaration
26+
* ) => void}
27+
*/
28+
const check = node => {
29+
let source;
30+
if (node.type === "TSImportEqualsDeclaration") {
31+
const moduleReference = node.moduleReference;
32+
if (
33+
moduleReference.type === "TSExternalModuleReference"
34+
&& moduleReference.expression.type === "Literal"
35+
&& typeof moduleReference.expression.value === "string"
36+
) {
37+
source = moduleReference.expression;
38+
}
39+
}
40+
else if (node.type === "TSModuleDeclaration") {
41+
if (node.kind === "module" && node.id.type === "Literal") {
42+
source = node.id;
43+
}
44+
}
45+
else {
46+
source = node.source;
47+
}
48+
49+
// This is not 100% accurate; this could point to a nested package, or to a directory
50+
// containing an index.js file. But we don't have anything like that in our repo,
51+
// so this check is good enough. Replicate this logic at your own risk.
52+
if (source?.value.startsWith(".") && !/\.[cm]?js$/.test(source.value)) {
53+
const quote = source.raw[0];
54+
context.report({
55+
messageId: "missingJsExtension",
56+
node: source,
57+
fix: fixer => fixer.replaceText(source, `${quote}${source.value}.js${quote}`),
58+
});
59+
}
60+
};
61+
62+
return {
63+
ImportDeclaration: check,
64+
ExportAllDeclaration: check,
65+
ExportNamedDeclaration: check,
66+
TSImportEqualsDeclaration: check,
67+
TSModuleDeclaration: check,
68+
};
69+
},
70+
});

Diff for: scripts/eslint/tests/js-extensions.cjs

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
const { RuleTester } = require("./support/RuleTester.cjs");
2+
const rule = require("../rules/js-extensions.cjs");
3+
4+
const ruleTester = new RuleTester({
5+
parserOptions: {
6+
warnOnUnsupportedTypeScriptVersion: false,
7+
},
8+
parser: require.resolve("@typescript-eslint/parser"),
9+
});
10+
11+
ruleTester.run("js-extensions", rule, {
12+
valid: [
13+
{
14+
code: `
15+
import "some-library";
16+
import "./a.js";
17+
import "./a.mjs";
18+
import "./a.cjs";
19+
import "../foo/a.js";
20+
import "../foo/a.mjs";
21+
import "../foo/a.cjs";
22+
`,
23+
},
24+
{
25+
code: `
26+
import * as blah from "some-library";
27+
import * as blah from "./a.js";
28+
import * as blah from "./a.mjs";
29+
import * as blah from "./a.cjs";
30+
import * as blah from "../foo/a.js";
31+
import * as blah from "../foo/a.mjs";
32+
import * as blah from "../foo/a.cjs";
33+
`,
34+
},
35+
{
36+
code: `
37+
export * from "some-library";
38+
export * from "./a.js";
39+
export * from "./a.mjs";
40+
export * from "./a.cjs";
41+
export * from "../foo/a.js";
42+
export * from "../foo/a.mjs";
43+
export * from "../foo/a.cjs";
44+
`,
45+
},
46+
{
47+
code: `
48+
import blah = require("some-library");
49+
import blah = require("./a.js");
50+
import blah = require("./a.mjs");
51+
import blah = require("./a.cjs");
52+
import blah = require("../foo/a.js");
53+
import blah = require("../foo/a.mjs");
54+
import blah = require("../foo/a.cjs");
55+
`,
56+
},
57+
],
58+
59+
invalid: [
60+
{
61+
code: `
62+
import "./a";
63+
import "../foo/a";
64+
`,
65+
errors: [
66+
{
67+
messageId: "missingJsExtension",
68+
line: 2,
69+
column: 8,
70+
},
71+
{
72+
messageId: "missingJsExtension",
73+
line: 3,
74+
column: 8,
75+
},
76+
],
77+
output: `
78+
import "./a.js";
79+
import "../foo/a.js";
80+
`,
81+
},
82+
{
83+
code: `
84+
import * as blah from "./a";
85+
import * as blah from "../foo/a";
86+
`,
87+
errors: [
88+
{
89+
messageId: "missingJsExtension",
90+
line: 2,
91+
column: 23,
92+
},
93+
{
94+
messageId: "missingJsExtension",
95+
line: 3,
96+
column: 23,
97+
},
98+
],
99+
output: `
100+
import * as blah from "./a.js";
101+
import * as blah from "../foo/a.js";
102+
`,
103+
},
104+
{
105+
code: `
106+
export * from "./a";
107+
export * from "../foo/a";
108+
`,
109+
errors: [
110+
{
111+
messageId: "missingJsExtension",
112+
line: 2,
113+
column: 15,
114+
},
115+
{
116+
messageId: "missingJsExtension",
117+
line: 3,
118+
column: 15,
119+
},
120+
],
121+
output: `
122+
export * from "./a.js";
123+
export * from "../foo/a.js";
124+
`,
125+
},
126+
{
127+
code: `
128+
import blah = require("./a");
129+
import blah = require("../foo/a");
130+
`,
131+
errors: [
132+
{
133+
messageId: "missingJsExtension",
134+
line: 2,
135+
column: 23,
136+
},
137+
{
138+
messageId: "missingJsExtension",
139+
line: 3,
140+
column: 23,
141+
},
142+
],
143+
output: `
144+
import blah = require("./a.js");
145+
import blah = require("../foo/a.js");
146+
`,
147+
},
148+
],
149+
});

Diff for: scripts/processDiagnosticMessages.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ function buildInfoFileOutput(messageTable, inputFilePathRel) {
8686
"// <auto-generated />",
8787
`// generated from '${inputFilePathRel}'`,
8888
"",
89-
'import { DiagnosticCategory, DiagnosticMessage } from "./types";',
89+
'import { DiagnosticCategory, DiagnosticMessage } from "./types.js";',
9090
"",
9191
"function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {",
9292
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };",

Diff for: src/compiler/_namespaces/ts.moduleSpecifiers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/* Generated file to emulate the ts.moduleSpecifiers namespace. */
22

3-
export * from "../moduleSpecifiers";
3+
export * from "../moduleSpecifiers.js";

Diff for: src/compiler/_namespaces/ts.performance.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/* Generated file to emulate the ts.performance namespace. */
22

3-
export * from "../performance";
3+
export * from "../performance.js";

0 commit comments

Comments
 (0)