Skip to content

Commit 71ccfb5

Browse files
Add a new legacyEntryPoints codemod transformation step. (#12846)
* Add a new `legacyEntryPoints` codemod transformation step. * Update scripts/codemods/ac3-to-ac4/src/util/handleModuleRename.ts Co-authored-by: Jerel Miller <jerelmiller@gmail.com> --------- Co-authored-by: Jerel Miller <jerelmiller@gmail.com>
1 parent 777349b commit 71ccfb5

File tree

9 files changed

+321
-12
lines changed

9 files changed

+321
-12
lines changed

.changeset/brave-pandas-battle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client-codemod-migrate-3-to-4": patch
3+
---
4+
5+
Add a new `legacyEntryPoints` transformation step that moves imports from old legacy entry points like `@apollo/client/main.cjs` or `@apollo/client/react/react.cjs` to the new entry points like `@apollo/client` or `@apollo/client/react`.

scripts/codemods/ac3-to-ac4/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ npx @apollo/client-codemod-migrate-3-to-4 --parser ts file1.ts file2.ts --codem
1414

1515
### Available codemods:
1616

17+
In the order they will be applied per default if you don't manually specify a codemod:
18+
19+
- `legacyEntryPoints`: Moves imports from old legacy entry points like `@apollo/client/main.cjs` or `@apollo/client/react/react.cjs` to the new entry points like `@apollo/client` or `@apollo/client/react`.
1720
- `imports`: Moves imports that have been moved or renamed in Apollo Client 4. Also moves types into namespace imports where applicable.
1821
- `links`: Moves `split`, `from`, `concat` and `empty` onto the `ApolloLink` namespace, changes funtion link invocations like `createHttpLink(...)` to their class equivalents like (`new HttpLink(...)`).
1922
Does not change `setContext((operation, prevContext) => {})` to `new ContextLink((prevContext, operation) => {})` - this requires manual intervention, as the order of callback arguments is flipped and this is not reliable codemoddable.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { applyTransform } from "jscodeshift/dist/testUtils";
2+
import { describe, expect, test } from "vitest";
3+
4+
import imports from "../imports.js";
5+
6+
const transform = ([source]: TemplateStringsArray) =>
7+
applyTransform(imports, {}, { source }, { parser: "ts" });
8+
9+
describe("aliases if `legacyEntryPoints` transform was not applied", () => {
10+
test('moves into existing compatible "legacy" entry point', () => {
11+
expect(
12+
transform`
13+
import { useQuery } from "@apollo/client/index.js";
14+
import { somethingElse } from "@apollo/client/react/react.cjs";
15+
`
16+
).toMatchInlineSnapshot(
17+
`"import { somethingElse, useQuery } from "@apollo/client/react/react.cjs";"`
18+
);
19+
});
20+
test("if nonexistant, creates new modern entry points", () => {
21+
expect(
22+
transform`
23+
import { useQuery } from "@apollo/client/index.js";
24+
`
25+
).toMatchInlineSnapshot(
26+
`"import { useQuery } from "@apollo/client/react";"`
27+
);
28+
});
29+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { applyTransform } from "jscodeshift/dist/testUtils";
2+
import { expect, test } from "vitest";
3+
4+
import legacyEntrypointTransform from "../legacyEntrypoints.js";
5+
6+
const transform = ([source]: TemplateStringsArray) =>
7+
applyTransform(legacyEntrypointTransform, {}, { source }, { parser: "ts" });
8+
9+
test("renames", () => {
10+
expect(
11+
transform`import { something } from "@apollo/client/apollo-client.cjs";`
12+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);
13+
14+
expect(
15+
transform`import { something } from "@apollo/client/apollo-client.min.cjs";`
16+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);
17+
18+
expect(
19+
transform`import { something } from "@apollo/client/index.js";`
20+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);
21+
22+
expect(
23+
transform`import { something } from "@apollo/client/main.cjs";`
24+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);
25+
26+
expect(
27+
transform`import { something } from "@apollo/client/main.cjs.native.js";`
28+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);
29+
30+
expect(
31+
transform`import { something } from "@apollo/client/core/index.js";`
32+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);
33+
34+
expect(
35+
transform`import { something } from "@apollo/client/link/core/index.js";`
36+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client/link";"`);
37+
38+
expect(
39+
transform`import { something } from "@apollo/client/link/core/core.cjs";`
40+
).toMatchInlineSnapshot(`"import { something } from "@apollo/client/link";"`);
41+
});
42+
43+
test("merge into existing import", () => {
44+
expect(
45+
transform`
46+
import { something } from "@apollo/client/core/index.js";
47+
import { somethingElse } from "@apollo/client";
48+
`
49+
).toMatchInlineSnapshot(
50+
`"import { somethingElse, something } from "@apollo/client";"`
51+
);
52+
});
53+
54+
test("avoids merging type/value", () => {
55+
expect(
56+
transform`
57+
import type { something } from "@apollo/client/core/index.js";
58+
import { somethingElse } from "@apollo/client";
59+
`
60+
).toMatchInlineSnapshot(
61+
`
62+
"import type { something } from "@apollo/client";
63+
import { somethingElse } from "@apollo/client";"
64+
`
65+
);
66+
67+
expect(
68+
transform`
69+
import { something } from "@apollo/client/core/index.js";
70+
import type { somethingElse } from "@apollo/client";
71+
`
72+
).toMatchInlineSnapshot(
73+
`
74+
"import { something } from "@apollo/client";
75+
import type { somethingElse } from "@apollo/client";"
76+
`
77+
);
78+
});

scripts/codemods/ac3-to-ac4/src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import type { API, FileInfo, Options, Transform } from "jscodeshift";
22

33
import imports from "./imports.js";
4+
import legacyEntrypoints from "./legacyEntrypoints.js";
45
import links from "./links.js";
56
import removals from "./removals.js";
67
import { monkeyPatchAstTypes } from "./util/monkeyPatchAstTypes.js";
78

8-
export const codemods = { imports, links, removals } satisfies Record<
9-
string,
10-
Transform
11-
>;
9+
export const codemods = {
10+
legacyEntrypoints,
11+
imports,
12+
links,
13+
removals,
14+
} satisfies Record<string, Transform>;
1215

1316
export default async function transform(
1417
file: FileInfo,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { Transform } from "jscodeshift";
2+
3+
import { entryPointAliases } from "./util/entryPointAliases.js";
4+
import { handleModuleRename } from "./util/handleModuleRename.js";
5+
6+
const legacyEntrypointTransform: Transform = function transform(file, api) {
7+
const j = api.jscodeshift;
8+
const source = j(file.source);
9+
const context = { j, source };
10+
11+
let modified = false;
12+
for (const [to, legacy] of Object.entries(entryPointAliases)) {
13+
for (const from of legacy) {
14+
handleModuleRename({
15+
rename: { to: { module: to }, from: { module: from } },
16+
context,
17+
onModify() {
18+
modified = true;
19+
},
20+
});
21+
}
22+
}
23+
return modified ? source.toSource() : undefined;
24+
};
25+
export default legacyEntrypointTransform;
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
```sh
3+
jq --slurpfile packages <(find . -name package.json | xargs cat) '$packages|map({key:.name, value: [.name+"/"+.main,.name+"/"+.module]})|from_entries' <(echo '{}')
4+
```
5+
*/
6+
const fromPackageJson = {
7+
"@apollo/client/core": [
8+
"@apollo/client/core/core.cjs",
9+
"@apollo/client/core/index.js",
10+
],
11+
"@apollo/client/cache": [
12+
"@apollo/client/cache/cache.cjs",
13+
"@apollo/client/cache/index.js",
14+
],
15+
"@apollo/client/utilities/globals": [
16+
"@apollo/client/utilities/globals/globals.cjs",
17+
"@apollo/client/utilities/globals/index.js",
18+
],
19+
"@apollo/client/utilities/subscriptions/urql": [
20+
"@apollo/client/utilities/subscriptions/urql/urql.cjs",
21+
"@apollo/client/utilities/subscriptions/urql/index.js",
22+
],
23+
"@apollo/client/utilities/subscriptions/relay": [
24+
"@apollo/client/utilities/subscriptions/relay/relay.cjs",
25+
"@apollo/client/utilities/subscriptions/relay/index.js",
26+
],
27+
"@apollo/client/utilities": [
28+
"@apollo/client/utilities/utilities.cjs",
29+
"@apollo/client/utilities/index.js",
30+
],
31+
"@apollo/client/testing/experimental": [
32+
"@apollo/client/testing/experimental/experimental.cjs",
33+
"@apollo/client/testing/experimental/index.js",
34+
],
35+
"@apollo/client/testing/core": [
36+
"@apollo/client/testing/core/core.cjs",
37+
"@apollo/client/testing/core/index.js",
38+
],
39+
"@apollo/client/testing": [
40+
"@apollo/client/testing/testing.cjs",
41+
"@apollo/client/testing/index.js",
42+
],
43+
"@apollo/client/link/remove-typename": [
44+
"@apollo/client/link/remove-typename/remove-typename.cjs",
45+
"@apollo/client/link/remove-typename/index.js",
46+
],
47+
"@apollo/client/link/context": [
48+
"@apollo/client/link/context/context.cjs",
49+
"@apollo/client/link/context/index.js",
50+
],
51+
"@apollo/client/link/core": [
52+
"@apollo/client/link/core/core.cjs",
53+
"@apollo/client/link/core/index.js",
54+
],
55+
"@apollo/client/link/retry": [
56+
"@apollo/client/link/retry/retry.cjs",
57+
"@apollo/client/link/retry/index.js",
58+
],
59+
"@apollo/client/link/utils": [
60+
"@apollo/client/link/utils/utils.cjs",
61+
"@apollo/client/link/utils/index.js",
62+
],
63+
"@apollo/client/link/subscriptions": [
64+
"@apollo/client/link/subscriptions/subscriptions.cjs",
65+
"@apollo/client/link/subscriptions/index.js",
66+
],
67+
"@apollo/client/link/schema": [
68+
"@apollo/client/link/schema/schema.cjs",
69+
"@apollo/client/link/schema/index.js",
70+
],
71+
"@apollo/client/link/http": [
72+
"@apollo/client/link/http/http.cjs",
73+
"@apollo/client/link/http/index.js",
74+
],
75+
"@apollo/client/link/batch": [
76+
"@apollo/client/link/batch/batch.cjs",
77+
"@apollo/client/link/batch/index.js",
78+
],
79+
"@apollo/client/link/batch-http": [
80+
"@apollo/client/link/batch-http/batch-http.cjs",
81+
"@apollo/client/link/batch-http/index.js",
82+
],
83+
"@apollo/client/link/error": [
84+
"@apollo/client/link/error/error.cjs",
85+
"@apollo/client/link/error/index.js",
86+
],
87+
"@apollo/client/link/persisted-queries": [
88+
"@apollo/client/link/persisted-queries/persisted-queries.cjs",
89+
"@apollo/client/link/persisted-queries/index.js",
90+
],
91+
"@apollo/client/link/ws": [
92+
"@apollo/client/link/ws/ws.cjs",
93+
"@apollo/client/link/ws/index.js",
94+
],
95+
"@apollo/client": ["@apollo/client/./main.cjs", "@apollo/client/./index.js"],
96+
"@apollo/client/dev": [
97+
"@apollo/client/dev/dev.cjs",
98+
"@apollo/client/dev/index.js",
99+
],
100+
"@apollo/client/errors": [
101+
"@apollo/client/errors/errors.cjs",
102+
"@apollo/client/errors/index.js",
103+
],
104+
"@apollo/client/react/context": [
105+
"@apollo/client/react/context/context.cjs",
106+
"@apollo/client/react/context/index.js",
107+
],
108+
"@apollo/client/react/hoc": [
109+
"@apollo/client/react/hoc/hoc.cjs",
110+
"@apollo/client/react/hoc/index.js",
111+
],
112+
"@apollo/client/react/internal": [
113+
"@apollo/client/react/internal/internal.cjs",
114+
"@apollo/client/react/internal/index.js",
115+
],
116+
"@apollo/client/react/parser": [
117+
"@apollo/client/react/parser/parser.cjs",
118+
"@apollo/client/react/parser/index.js",
119+
],
120+
"@apollo/client/react/ssr": [
121+
"@apollo/client/react/ssr/ssr.cjs",
122+
"@apollo/client/react/ssr/index.js",
123+
],
124+
"@apollo/client/react/components": [
125+
"@apollo/client/react/components/components.cjs",
126+
"@apollo/client/react/components/index.js",
127+
],
128+
"@apollo/client/react": [
129+
"@apollo/client/react/react.cjs",
130+
"@apollo/client/react/index.js",
131+
],
132+
"@apollo/client/react/hooks": [
133+
"@apollo/client/react/hooks/hooks.cjs",
134+
"@apollo/client/react/hooks/index.js",
135+
],
136+
"@apollo/client/masking": [
137+
"@apollo/client/masking/masking.cjs",
138+
"@apollo/client/masking/index.js",
139+
],
140+
} satisfies Record<string, string[]>;
141+
142+
export const entryPointAliases = {
143+
...fromPackageJson,
144+
"@apollo/client/core": [] as string[],
145+
"@apollo/client": [
146+
"@apollo/client/apollo-client.cjs",
147+
"@apollo/client/apollo-client.min.cjs",
148+
"@apollo/client/index.js",
149+
"@apollo/client/main.cjs",
150+
"@apollo/client/main.cjs.native.js",
151+
...fromPackageJson["@apollo/client/core"],
152+
],
153+
"@apollo/client/link/core": [] as string[],
154+
"@apollo/client/link": fromPackageJson["@apollo/client/link/core"],
155+
} satisfies Record<string, string[]>;

scripts/codemods/ac3-to-ac4/src/util/findImportDeclarationFor.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type * as j from "jscodeshift/src/core.js";
44
import type { IdentifierRename } from "../renames.js";
55
import type { ImportKind, UtilContext } from "../types.js";
66

7+
import { entryPointAliases } from "./entryPointAliases.js";
8+
79
const typeImportsFirst = (
810
a: j.ASTPath<namedTypes.ImportDeclaration>,
911
b: j.ASTPath<namedTypes.ImportDeclaration>
@@ -16,13 +18,15 @@ export function findImportDeclarationFor({
1618
description,
1719
compatibleWith = "type",
1820
context: { j, source },
21+
exact = false,
1922
}: {
2023
description: Pick<
2124
IdentifierRename["from"] & IdentifierRename["to"],
2225
"module" | "alternativeModules"
2326
>;
2427
compatibleWith?: ImportKind;
2528
context: UtilContext;
29+
exact?: boolean;
2630
}): j.Collection<namedTypes.ImportDeclaration> {
2731
const test = (node: namedTypes.ImportDeclaration) => {
2832
const isValidImportKind =
@@ -48,12 +52,18 @@ export function findImportDeclarationFor({
4852
)
4953
.paths()
5054
.sort(typeImportsFirst);
51-
return j(perfectMatch.concat(...alternativeMatches));
52-
/**
53-
* {
54-
* const moduleMatches =
55-
* description.module == "" + node.source.value ||
56-
* description.alternativeModules?.includes("" + node.source.value) ||
57-
* false;
58-
*/
55+
const fallback =
56+
entryPointAliases[description.module as keyof typeof entryPointAliases];
57+
const fallbackMatches =
58+
exact ?
59+
[]
60+
: source
61+
.find(
62+
j.ImportDeclaration,
63+
(node) =>
64+
(test(node) && fallback?.includes("" + node.source.value)) || false
65+
)
66+
.paths()
67+
.sort(typeImportsFirst);
68+
return j(perfectMatch.concat(...alternativeMatches, ...fallbackMatches));
5969
}

scripts/codemods/ac3-to-ac4/src/util/handleModuleRename.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function handleModuleRename({
4747
let targetImport = findImportDeclarationFor({
4848
description: rename.to,
4949
context,
50+
exact: true,
5051
})
5152
.filter(
5253
(declaration) =>

0 commit comments

Comments
 (0)