Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-pandas-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client-codemod-migrate-3-to-4": patch
---

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`.
3 changes: 3 additions & 0 deletions scripts/codemods/ac3-to-ac4/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ npx @apollo/client-codemod-migrate-3-to-4 --parser ts file1.ts file2.ts --codem

### Available codemods:

In the order they will be applied per default if you don't manually specify a codemod:

- `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`.
- `imports`: Moves imports that have been moved or renamed in Apollo Client 4. Also moves types into namespace imports where applicable.
- `links`: Moves `split`, `from`, `concat` and `empty` onto the `ApolloLink` namespace, changes funtion link invocations like `createHttpLink(...)` to their class equivalents like (`new HttpLink(...)`).
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.
Expand Down
29 changes: 29 additions & 0 deletions scripts/codemods/ac3-to-ac4/src/__tests__/imports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { applyTransform } from "jscodeshift/dist/testUtils";
import { describe, expect, test } from "vitest";

import imports from "../imports.js";

const transform = ([source]: TemplateStringsArray) =>
applyTransform(imports, {}, { source }, { parser: "ts" });

describe("aliases if `legacyEntryPoints` transform was not applied", () => {
test('moves into existing compatible "legacy" entry point', () => {
expect(
transform`
import { useQuery } from "@apollo/client/index.js";
import { somethingElse } from "@apollo/client/react/react.cjs";
`
).toMatchInlineSnapshot(
`"import { somethingElse, useQuery } from "@apollo/client/react/react.cjs";"`
);
});
test("if nonexistant, creates new modern entry points", () => {
expect(
transform`
import { useQuery } from "@apollo/client/index.js";
`
).toMatchInlineSnapshot(
`"import { useQuery } from "@apollo/client/react";"`
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { applyTransform } from "jscodeshift/dist/testUtils";
import { expect, test } from "vitest";

import legacyEntrypointTransform from "../legacyEntrypoints.js";

const transform = ([source]: TemplateStringsArray) =>
applyTransform(legacyEntrypointTransform, {}, { source }, { parser: "ts" });

test("renames", () => {
expect(
transform`import { something } from "@apollo/client/apollo-client.cjs";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);

expect(
transform`import { something } from "@apollo/client/apollo-client.min.cjs";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);

expect(
transform`import { something } from "@apollo/client/index.js";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);

expect(
transform`import { something } from "@apollo/client/main.cjs";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);

expect(
transform`import { something } from "@apollo/client/main.cjs.native.js";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);

expect(
transform`import { something } from "@apollo/client/core/index.js";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client";"`);

expect(
transform`import { something } from "@apollo/client/link/core/index.js";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client/link";"`);

expect(
transform`import { something } from "@apollo/client/link/core/core.cjs";`
).toMatchInlineSnapshot(`"import { something } from "@apollo/client/link";"`);
});

test("merge into existing import", () => {
expect(
transform`
import { something } from "@apollo/client/core/index.js";
import { somethingElse } from "@apollo/client";
`
).toMatchInlineSnapshot(
`"import { somethingElse, something } from "@apollo/client";"`
);
});

test("avoids merging type/value", () => {
expect(
transform`
import type { something } from "@apollo/client/core/index.js";
import { somethingElse } from "@apollo/client";
`
).toMatchInlineSnapshot(
`
"import type { something } from "@apollo/client";
import { somethingElse } from "@apollo/client";"
`
);

expect(
transform`
import { something } from "@apollo/client/core/index.js";
import type { somethingElse } from "@apollo/client";
`
).toMatchInlineSnapshot(
`
"import { something } from "@apollo/client";
import type { somethingElse } from "@apollo/client";"
`
);
});
11 changes: 7 additions & 4 deletions scripts/codemods/ac3-to-ac4/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { API, FileInfo, Options, Transform } from "jscodeshift";

import imports from "./imports.js";
import legacyEntrypoints from "./legacyEntrypoints.js";
import links from "./links.js";
import removals from "./removals.js";
import { monkeyPatchAstTypes } from "./util/monkeyPatchAstTypes.js";

export const codemods = { imports, links, removals } satisfies Record<
string,
Transform
>;
export const codemods = {
legacyEntrypoints,
imports,
links,
removals,
} satisfies Record<string, Transform>;

export default async function transform(
file: FileInfo,
Expand Down
25 changes: 25 additions & 0 deletions scripts/codemods/ac3-to-ac4/src/legacyEntrypoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Transform } from "jscodeshift";

import { entryPointAliases } from "./util/entryPointAliases.js";
import { handleModuleRename } from "./util/handleModuleRename.js";

const legacyEntrypointTransform: Transform = function transform(file, api) {
const j = api.jscodeshift;
const source = j(file.source);
const context = { j, source };

let modified = false;
for (const [to, legacy] of Object.entries(entryPointAliases)) {
for (const from of legacy) {
handleModuleRename({
rename: { to: { module: to }, from: { module: from } },
context,
onModify() {
modified = true;
},
});
}
}
return modified ? source.toSource() : undefined;
};
export default legacyEntrypointTransform;
155 changes: 155 additions & 0 deletions scripts/codemods/ac3-to-ac4/src/util/entryPointAliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
```sh
jq --slurpfile packages <(find . -name package.json | xargs cat) '$packages|map({key:.name, value: [.name+"/"+.main,.name+"/"+.module]})|from_entries' <(echo '{}')
```
*/
const fromPackageJson = {
"@apollo/client/core": [
"@apollo/client/core/core.cjs",
"@apollo/client/core/index.js",
],
"@apollo/client/cache": [
"@apollo/client/cache/cache.cjs",
"@apollo/client/cache/index.js",
],
"@apollo/client/utilities/globals": [
"@apollo/client/utilities/globals/globals.cjs",
"@apollo/client/utilities/globals/index.js",
],
"@apollo/client/utilities/subscriptions/urql": [
"@apollo/client/utilities/subscriptions/urql/urql.cjs",
"@apollo/client/utilities/subscriptions/urql/index.js",
],
"@apollo/client/utilities/subscriptions/relay": [
"@apollo/client/utilities/subscriptions/relay/relay.cjs",
"@apollo/client/utilities/subscriptions/relay/index.js",
],
"@apollo/client/utilities": [
"@apollo/client/utilities/utilities.cjs",
"@apollo/client/utilities/index.js",
],
"@apollo/client/testing/experimental": [
"@apollo/client/testing/experimental/experimental.cjs",
"@apollo/client/testing/experimental/index.js",
],
"@apollo/client/testing/core": [
"@apollo/client/testing/core/core.cjs",
"@apollo/client/testing/core/index.js",
],
"@apollo/client/testing": [
"@apollo/client/testing/testing.cjs",
"@apollo/client/testing/index.js",
],
"@apollo/client/link/remove-typename": [
"@apollo/client/link/remove-typename/remove-typename.cjs",
"@apollo/client/link/remove-typename/index.js",
],
"@apollo/client/link/context": [
"@apollo/client/link/context/context.cjs",
"@apollo/client/link/context/index.js",
],
"@apollo/client/link/core": [
"@apollo/client/link/core/core.cjs",
"@apollo/client/link/core/index.js",
],
"@apollo/client/link/retry": [
"@apollo/client/link/retry/retry.cjs",
"@apollo/client/link/retry/index.js",
],
"@apollo/client/link/utils": [
"@apollo/client/link/utils/utils.cjs",
"@apollo/client/link/utils/index.js",
],
"@apollo/client/link/subscriptions": [
"@apollo/client/link/subscriptions/subscriptions.cjs",
"@apollo/client/link/subscriptions/index.js",
],
"@apollo/client/link/schema": [
"@apollo/client/link/schema/schema.cjs",
"@apollo/client/link/schema/index.js",
],
"@apollo/client/link/http": [
"@apollo/client/link/http/http.cjs",
"@apollo/client/link/http/index.js",
],
"@apollo/client/link/batch": [
"@apollo/client/link/batch/batch.cjs",
"@apollo/client/link/batch/index.js",
],
"@apollo/client/link/batch-http": [
"@apollo/client/link/batch-http/batch-http.cjs",
"@apollo/client/link/batch-http/index.js",
],
"@apollo/client/link/error": [
"@apollo/client/link/error/error.cjs",
"@apollo/client/link/error/index.js",
],
"@apollo/client/link/persisted-queries": [
"@apollo/client/link/persisted-queries/persisted-queries.cjs",
"@apollo/client/link/persisted-queries/index.js",
],
"@apollo/client/link/ws": [
"@apollo/client/link/ws/ws.cjs",
"@apollo/client/link/ws/index.js",
],
"@apollo/client": ["@apollo/client/./main.cjs", "@apollo/client/./index.js"],
"@apollo/client/dev": [
"@apollo/client/dev/dev.cjs",
"@apollo/client/dev/index.js",
],
"@apollo/client/errors": [
"@apollo/client/errors/errors.cjs",
"@apollo/client/errors/index.js",
],
"@apollo/client/react/context": [
"@apollo/client/react/context/context.cjs",
"@apollo/client/react/context/index.js",
],
"@apollo/client/react/hoc": [
"@apollo/client/react/hoc/hoc.cjs",
"@apollo/client/react/hoc/index.js",
],
"@apollo/client/react/internal": [
"@apollo/client/react/internal/internal.cjs",
"@apollo/client/react/internal/index.js",
],
"@apollo/client/react/parser": [
"@apollo/client/react/parser/parser.cjs",
"@apollo/client/react/parser/index.js",
],
"@apollo/client/react/ssr": [
"@apollo/client/react/ssr/ssr.cjs",
"@apollo/client/react/ssr/index.js",
],
"@apollo/client/react/components": [
"@apollo/client/react/components/components.cjs",
"@apollo/client/react/components/index.js",
],
"@apollo/client/react": [
"@apollo/client/react/react.cjs",
"@apollo/client/react/index.js",
],
"@apollo/client/react/hooks": [
"@apollo/client/react/hooks/hooks.cjs",
"@apollo/client/react/hooks/index.js",
],
"@apollo/client/masking": [
"@apollo/client/masking/masking.cjs",
"@apollo/client/masking/index.js",
],
} satisfies Record<string, string[]>;

export const entryPointAliases = {
...fromPackageJson,
"@apollo/client/core": [] as string[],
"@apollo/client": [
"@apollo/client/apollo-client.cjs",
"@apollo/client/apollo-client.min.cjs",
"@apollo/client/index.js",
"@apollo/client/main.cjs",
"@apollo/client/main.cjs.native.js",
...fromPackageJson["@apollo/client/core"],
],
"@apollo/client/link/core": [] as string[],
"@apollo/client/link": fromPackageJson["@apollo/client/link/core"],
} satisfies Record<string, string[]>;
26 changes: 18 additions & 8 deletions scripts/codemods/ac3-to-ac4/src/util/findImportDeclarationFor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type * as j from "jscodeshift/src/core.js";
import type { IdentifierRename } from "../renames.js";
import type { ImportKind, UtilContext } from "../types.js";

import { entryPointAliases } from "./entryPointAliases.js";

const typeImportsFirst = (
a: j.ASTPath<namedTypes.ImportDeclaration>,
b: j.ASTPath<namedTypes.ImportDeclaration>
Expand All @@ -16,13 +18,15 @@ export function findImportDeclarationFor({
description,
compatibleWith = "type",
context: { j, source },
exact = false,
}: {
description: Pick<
IdentifierRename["from"] & IdentifierRename["to"],
"module" | "alternativeModules"
>;
compatibleWith?: ImportKind;
context: UtilContext;
exact?: boolean;
}): j.Collection<namedTypes.ImportDeclaration> {
const test = (node: namedTypes.ImportDeclaration) => {
const isValidImportKind =
Expand All @@ -48,12 +52,18 @@ export function findImportDeclarationFor({
)
.paths()
.sort(typeImportsFirst);
return j(perfectMatch.concat(...alternativeMatches));
/**
* {
* const moduleMatches =
* description.module == "" + node.source.value ||
* description.alternativeModules?.includes("" + node.source.value) ||
* false;
*/
const fallback =
entryPointAliases[description.module as keyof typeof entryPointAliases];
const fallbackMatches =
exact ?
[]
: source
.find(
j.ImportDeclaration,
(node) =>
(test(node) && fallback?.includes("" + node.source.value)) || false
)
.paths()
.sort(typeImportsFirst);
return j(perfectMatch.concat(...alternativeMatches, ...fallbackMatches));
}
1 change: 1 addition & 0 deletions scripts/codemods/ac3-to-ac4/src/util/handleModuleRename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function handleModuleRename({
let targetImport = findImportDeclarationFor({
description: rename.to,
context,
exact: true,
})
.filter(
(declaration) =>
Expand Down