Skip to content

Commit 2ebbd3a

Browse files
authored
Add .js file extension to all in-project imports (#10994)
* add `.js` file extension to all in-project imports * bundler adjustments * remove postprocessing step * fixup, script to detect build differences * more fixes * changeset * adjust jest file resolution * fix up wrong import in `batchHttpLink` * add AreTheTypesWrong check * minor action changes * fixup * use ascii format for attw * update AreTheTypesWrong, use new `--pack` option * add build comparison action * omit minified comparison output * trigger ci * eslint configuration changes, temp custom eslint version * trigger ci * give workflows distincive names? * revert to latest working CI build * revert changes to workflows * clean up newly merged imports * fix command name * adjust fetch-depth * use runner temp dir * dir tweaks * temp file dir? * fir argument * logging * more detail logging * fix binary logic * remove a bunch of logging again * disable `set` * partially reintroduce * now, to find that last command... * this seems like a good compromise * bump the TS eslint parser to deal with TS 5.1 * apply feedback from review
1 parent 3a62d82 commit 2ebbd3a

File tree

178 files changed

+2536
-983
lines changed

Some content is hidden

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

178 files changed

+2536
-983
lines changed

.attw.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"ignoreRules": [
3+
"false-esm",
4+
"cjs-resolves-to-esm"
5+
]
6+
}

.changeset/lemon-fans-shake.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@apollo/client': minor
3+
---
4+
5+
Add .js file extensions to imports in src and dist/**/*.d.ts

.eslintrc

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"parser": "@typescript-eslint/parser",
3-
"plugins": ["@typescript-eslint"],
3+
"plugins": ["@typescript-eslint", "import"],
44
"env": {
55
"browser": true,
66
"node": true,
@@ -9,17 +9,35 @@
99
"parserOptions": {
1010
"ecmaVersion": "latest"
1111
},
12+
"settings": {
13+
"import/parsers": {
14+
"@typescript-eslint/parser": [".ts", ".tsx"]
15+
},
16+
"import/resolver": {
17+
"typescript": {
18+
"alwaysTryTypes": true
19+
}
20+
}
21+
},
1222
"overrides": [
1323
{
1424
"files": ["**/*.ts", "**/*.tsx"],
1525
"excludedFiles": ["**/__tests__/**/*.*"],
1626
"rules": {
17-
"@typescript-eslint/consistent-type-imports": ["error", {
27+
"@typescript-eslint/consistent-type-imports": ["error", {
1828
"prefer": "type-imports",
1929
"disallowTypeAnnotations": false,
2030
"fixStyle": "separate-type-imports"
2131
}],
22-
"@typescript-eslint/no-import-type-side-effects": "error"
32+
"@typescript-eslint/no-import-type-side-effects": "error",
33+
"import/extensions": [
34+
"error",
35+
"always",
36+
{
37+
"ignorePackages": true,
38+
"checkTypeImports": true
39+
}
40+
]
2341
}
2442
},
2543
{
@@ -31,4 +49,7 @@
3149
}
3250
}
3351
],
52+
"rules": {
53+
"import/no-unresolved": "error"
54+
}
3455
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: AreTheTypesWrong
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
- release-*
7+
8+
concurrency: ${{ github.workflow }}-${{ github.ref }}
9+
10+
jobs:
11+
arethetypeswrong:
12+
name: Are the types wrong
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout repo
16+
uses: actions/checkout@v3
17+
- name: Setup Node.js 18.x
18+
uses: actions/setup-node@v3
19+
with:
20+
node-version: 18.x
21+
- name: Install dependencies (with cache)
22+
uses: bahmutov/npm-install@v1
23+
24+
- name: Run build
25+
run: npm run build
26+
- name: Run AreTheTypesWrong
27+
id: attw
28+
run: ./node_modules/.bin/attw --format ascii --pack dist > $GITHUB_STEP_SUMMARY

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,6 @@ junit.xml
7070
reports
7171

7272
esbuild-why-*.html
73+
74+
.yalc
75+
yalc.lock

config/entryPoints.js

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ exports.check = function (id, parentId) {
8888
function partsAfterDist(id) {
8989
const parts = id.split(path.sep);
9090
const distIndex = parts.lastIndexOf("dist");
91+
if (/^index.jsx?$/.test(parts[parts.length - 1])) {
92+
parts.pop();
93+
}
9194
if (distIndex >= 0) {
9295
return parts.slice(distIndex + 1);
9396
}

config/jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const defaults = {
2323
},
2424
],
2525
},
26+
resolver: "ts-jest-resolver",
2627
};
2728

2829
const ignoreTSFiles = '.ts$';

config/postprocessDist.ts

+4-117
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import * as fs from "fs";
2-
import * as path from "path";
3-
import resolve from "resolve";
4-
import { distDir, eachFile, reparse, reprint } from './helpers';
1+
import { distDir } from './helpers.ts';
2+
import fs from 'node:fs';
3+
import path from 'node:path';
54

65
const globalTypesFile = path.resolve(distDir, "utilities/globals/global.d.ts");
76
fs.writeFileSync(globalTypesFile,
@@ -10,116 +9,4 @@ fs.writeFileSync(globalTypesFile,
109
.filter(line => line.trim() !== 'const __DEV__: boolean;')
1110
.join("\n"),
1211
"utf8"
13-
);
14-
15-
// The primary goal of the 'npm run resolve' script is to make ECMAScript
16-
// modules exposed by Apollo Client easier to consume natively in web browsers,
17-
// without bundling, and without help from package.json files. It accomplishes
18-
// this goal by rewriting internal ./ and ../ (relative) imports to refer to a
19-
// specific ESM module (not a directory), including its file extension. Because
20-
// of this limited goal, this script only touches ESM modules that have .js file
21-
// extensions, not .cjs CommonJS bundles.
22-
23-
// A secondary goal of this script is to enforce that any module using the
24-
// __DEV__ global constant imports the @apollo/client/utilities/globals polyfill
25-
// module first.
26-
27-
eachFile(distDir, (file, relPath) => new Promise((resolve, reject) => {
28-
fs.readFile(file, "utf8", (error, source) => {
29-
if (error) return reject(error);
30-
31-
const tr = new Transformer;
32-
const output = tr.transform(source, file);
33-
34-
if (source === output) {
35-
resolve(file);
36-
} else {
37-
fs.writeFile(file, output, "utf8", error => {
38-
error ? reject(error) : resolve(file);
39-
});
40-
}
41-
});
42-
}));
43-
44-
import * as recast from "recast";
45-
const n = recast.types.namedTypes;
46-
type Node = recast.types.namedTypes.Node;
47-
48-
class Transformer {
49-
absolutePaths = new Set<string>();
50-
51-
transform(code: string, file: string) {
52-
const ast = reparse(code);
53-
const transformer = this;
54-
55-
recast.visit(ast, {
56-
visitImportDeclaration(path) {
57-
this.traverse(path);
58-
transformer.normalizeSourceString(file, path.node.source);
59-
},
60-
61-
visitImportExpression(path) {
62-
this.traverse(path);
63-
transformer.normalizeSourceString(file, path.node.source);
64-
},
65-
66-
visitExportAllDeclaration(path) {
67-
this.traverse(path);
68-
transformer.normalizeSourceString(file, path.node.source);
69-
},
70-
71-
visitExportNamedDeclaration(path) {
72-
this.traverse(path);
73-
transformer.normalizeSourceString(file, path.node.source);
74-
},
75-
});
76-
77-
return reprint(ast);
78-
}
79-
80-
isRelative(id: string) {
81-
return id.startsWith("./") || id.startsWith("../");
82-
}
83-
84-
normalizeSourceString(file: string, source?: Node | null) {
85-
if (source && n.StringLiteral.check(source)) {
86-
// We mostly only worry about normalizing _relative_ module identifiers,
87-
// which start with a ./ or ../ and refer to other modules within the
88-
// @apollo/client package, but we also manually normalize one non-relative
89-
// identifier, ts-invariant/process, to prevent webpack 5 errors
90-
// containing the phrase "failed to resolve only because it was resolved
91-
// as fully specified," referring to webpack's resolve.fullySpecified
92-
// option, which is apparently now true by default when the enclosing
93-
// package's package.json file has "type": "module" (which became true for
94-
// Apollo Client in v3.5).
95-
if (source.value.split("/", 2).join("/") === "ts-invariant/process") {
96-
source.value = "ts-invariant/process/index.js";
97-
} else if (this.isRelative(source.value)) {
98-
try {
99-
source.value = this.normalizeId(source.value, file);
100-
} catch (error) {
101-
console.error(`Failed to resolve ${source.value} in ${file} with error ${error}`);
102-
process.exit(1);
103-
}
104-
}
105-
}
106-
}
107-
108-
normalizeId(id: string, file: string) {
109-
const basedir = path.dirname(file);
110-
const absPath = resolve.sync(id, {
111-
basedir,
112-
extensions: [".mjs", ".js"],
113-
packageFilter(pkg) {
114-
return pkg.module ? {
115-
...pkg,
116-
main: pkg.module,
117-
} : pkg;
118-
},
119-
});
120-
this.absolutePaths.add(absPath);
121-
const relPath = path.relative(basedir, absPath);
122-
const relId = relPath.split(path.sep).join('/');
123-
return this.isRelative(relId) ? relId : "./" + relId;
124-
}
125-
}
12+
);

config/processInvariants.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as fs from 'fs';
22
import { posix, join as osPathJoin } from 'path';
3-
import { distDir, eachFile, reparse, reprint } from './helpers';
3+
import { distDir, eachFile, reparse, reprint } from './helpers.ts';
44
import type { ExpressionKind } from 'ast-types/lib/gen/kinds';
55

66
eachFile(distDir, (file, relPath) => {
@@ -211,7 +211,6 @@ function transform(code: string, relativeFilePath: string) {
211211
b.literal(false)
212212
);
213213
}
214-
215214
return node;
216215
},
217216
});

config/rewriteSourceMaps.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as fs from "fs";
22
import * as path from "path";
3-
import { distDir } from './helpers';
3+
import { distDir } from './helpers.ts';
44
import glob = require("glob");
55

66
glob(`${distDir.replace(/\\/g, '/')}/**/*.js.map`, (error, files) => {

config/rollup.config.js

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import path from 'path';
1+
import path, { resolve, dirname } from 'path';
22
import { promises as fs } from "fs";
33

44
import nodeResolve from '@rollup/plugin-node-resolve';
@@ -109,9 +109,8 @@ function prepareBundle({
109109

110110
return {
111111
input: inputFile,
112-
external(id, parentId) {
113-
return isExternal(id, parentId, true);
114-
},
112+
// the external check is done by the `'externalize-dependency'` plugin
113+
// external(id, parentId) {}
115114
output: {
116115
file: outputFile,
117116
format: 'cjs',
@@ -120,6 +119,29 @@ function prepareBundle({
120119
externalLiveBindings: false,
121120
},
122121
plugins: [
122+
{
123+
name: 'externalize-dependency',
124+
resolveId(id, parentId) {
125+
if (!parentId) {
126+
return null;
127+
}
128+
function removeIndex(filename) {
129+
if (filename.endsWith(`${path.sep}index.js`)) {
130+
return filename.slice(0, -`${path.sep}index.js`.length)
131+
}
132+
return filename
133+
}
134+
135+
const external = isExternal(id, parentId, true)
136+
if (external) {
137+
if (id.startsWith(".")) {
138+
return { id: removeIndex(resolve(dirname(parentId), id)), external: true };
139+
}
140+
return { id: removeIndex(id), external: true };
141+
}
142+
return null;
143+
}
144+
},
123145
extensions ? nodeResolve({ extensions }) : nodeResolve(),
124146
{
125147
name: "copy *.cjs to *.cjs.native.js",

config/tsconfig.json

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"esModuleInterop": true,
1313
"sourceMap": true,
1414
"inlineSourceMap": false,
15+
"noEmit": true,
16+
"allowImportingTsExtensions": true,
1517
// This option should cause sourcesContent to be included in the
1618
// source map JSON, so we don't have to rely on the (nonexistent)
1719
// relative paths in the sources array, but it appears tsc does not

0 commit comments

Comments
 (0)