Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a transform for createPromiseClient -> createClient #1236

Merged
merged 4 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
19 changes: 13 additions & 6 deletions packages/connect-migrate/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { parseCommandLineArgs } from "./arguments";
import { scan } from "./lib/scan";
import { Logger } from "./lib/logger";
import { v0_13_1 } from "./migrations/v0.13.1";
import { v1_16_0 } from "./migrations/v1.16.0";
import type { Migration } from "./migration";

const usage = `USAGE: connect-migrate [flags]
Updates references to connect-es packages in your project to use @connectrpc.
Expand All @@ -32,7 +34,7 @@ Flags:
`;

const logger = new Logger();

const migrations = [v0_13_1, v1_16_0];
void main();

async function main() {
Expand All @@ -51,12 +53,17 @@ async function main() {
if (!scanned.ok) {
return exitErr(scanned.errorMessage, false);
}
if (v0_13_1.applicable(scanned)) {
const result = v0_13_1.migrate({ scanned, args, print, logger });
if (!result.ok) {
return exitErr(result.errorMessage, result.dumpLogfile ?? false);
const applied: Migration[] = [];
for (const migration of migrations) {
if (migration.applicable(scanned)) {
const result = migration.migrate({ scanned, args, print, logger });
if (!result.ok) {
return exitErr(result.errorMessage, result.dumpLogfile ?? false);
}
applied.push(migration);
}
} else {
}
if (applied.length == 0) {
exitOk("It looks like you are already up to date 🎉");
}
} catch (e) {
Expand Down
17 changes: 6 additions & 11 deletions packages/connect-migrate/src/lib/replace-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,19 @@ export type DependencyReplacement = {
* Replace all dependencies matching "from" with "to".
*/
export function replaceDependencies(
pkg: Readonly<PackageJson>,
pkg: PackageJson,
replacements: DependencyReplacement[],
): PackageJson | null {
const modifiedPackageNames = new Set<string>();
const replacedPackageNames = new Map<string, string>();
const copy = clonePackageJson(pkg);
// replace dependencies
for (const replacement of replacements) {
for (const p of [
"dependencies",
"devDependencies",
"peerDependencies",
] as const) {
const deps = copy[p] ?? {};
const deps = pkg[p] ?? {};
for (const [packageName, versionRange] of Object.entries(deps)) {
if (packageName !== replacement.from.name) {
continue;
Expand Down Expand Up @@ -72,14 +71,14 @@ export function replaceDependencies(
}
// replace bundled dependencies, but only for package names we replaced
for (const p of ["bundleDependencies", "bundledDependencies"] as const) {
const bundled = copy[p];
const bundled = pkg[p];
if (!Array.isArray(bundled)) {
continue;
}
copy[p] = bundled.map((n) => replacedPackageNames.get(n) ?? n);
pkg[p] = bundled.map((n) => replacedPackageNames.get(n) ?? n);
}
// replace peer dependency meta, but only for package names we replaced
const meta = copy.peerDependenciesMeta;
const meta = pkg.peerDependenciesMeta;
if (meta !== undefined) {
for (const [n, value] of Object.entries(meta)) {
const newName = replacedPackageNames.get(n);
Expand All @@ -90,9 +89,5 @@ export function replaceDependencies(
meta[newName] = value;
}
}
return modifiedPackageNames.size > 0 ? copy : null;
}

function clonePackageJson(pkg: PackageJson): PackageJson {
return JSON.parse(JSON.stringify(pkg)) as PackageJson;
return modifiedPackageNames.size > 0 ? pkg : null;
}
12 changes: 9 additions & 3 deletions packages/connect-migrate/src/migrations/v0.13.1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ export const dependencyReplacements: DependencyReplacement[] = [
export const v0_13_1: Migration = {
applicable(scanned: Scanned) {
return scanned.packageFiles.some(
({ pkg }) => replaceDependencies(pkg, dependencyReplacements) !== null,
({ pkg }) =>
replaceDependencies(structuredClone(pkg), dependencyReplacements) !==
null,
);
},
migrate({
Expand All @@ -149,7 +151,9 @@ export const v0_13_1: Migration = {
const oldPluginUsed = scanned.packageFiles
.filter(
({ pkg }) =>
replaceDependencies(pkg, [oldPluginReplacement]) !== null,
replaceDependencies(structuredClone(pkg), [
oldPluginReplacement,
]) !== null,
)
.map(({ path }) => path);
if (oldPluginUsed.length > 0) {
Expand All @@ -163,7 +167,9 @@ export const v0_13_1: Migration = {
const removedWebExportsUsed = scanned.packageFiles
.filter(
({ pkg }) =>
replaceDependencies(pkg, [removedWebExportReplacement]) !== null,
replaceDependencies(structuredClone(pkg), [
removedWebExportReplacement,
]) !== null,
)
.map(({ path }) => path);
if (removedWebExportsUsed.length > 0) {
Expand Down
207 changes: 207 additions & 0 deletions packages/connect-migrate/src/migrations/v1.16.0-transform.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Copyright 2021-2024 The Connect Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import jscodeshift from "jscodeshift";
import transform from "./v1.16.0-transform";

function t(
source: string,
parser: "tsx" | "babel" | "ts" | "babylon" | "flow" = "tsx",
) {
const shift = jscodeshift.withParser(parser);
return transform(
{ path: "test-file", source },
{
jscodeshift: shift,
j: shift,
stats: () => {},
report: () => {},
},
{},
);
}

describe("rename symbols using", () => {
describe("'import' with", () => {
describe("for type", () => {
describe("local identifier", () => {
it("plain", () => {
const got = `
import { PromiseClient as client } from "@connectrpc/connect";
type PromiseClient = client;
`;
const want = `
import { Client as client } from "@connectrpc/connect";
type PromiseClient = client;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("type qualified", () => {
const got = `
import { type PromiseClient as client } from "@connectrpc/connect";
type PromiseClient = client;
`;
const want = `
import { type Client as client } from "@connectrpc/connect";
type PromiseClient = client;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("type only", () => {
const got = `
import type { PromiseClient as client } from "@connectrpc/connect";
type PromiseClient = client;
`;
const want = `
import type { Client as client } from "@connectrpc/connect";
type PromiseClient = client;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
});
describe("identifier", () => {
it("plain", () => {
const got = `
import { PromiseClient } from "@connectrpc/connect";
type a = PromiseClient;
type R = Readonly<PromiseClient>;
`;
const want = `
import { Client } from "@connectrpc/connect";
type a = Client;
type R = Readonly<Client>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
});
});
it("local identifier", () => {
const got = `
import { createPromiseClient as create } from "@connectrpc/connect";
const createPromiseClient = create;
`;
const want = `
import { createClient as create } from "@connectrpc/connect";
const createPromiseClient = create;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("identifier", () => {
const got = `
import { createPromiseClient } from "@connectrpc/connect";
const a = createPromiseClient;
console.log(createPromiseClient);
const c = createPromiseClient();
type R = ReturnType<typeof createPromiseClient>;
`;
const want = `
import { createClient } from "@connectrpc/connect";
const a = createClient;
console.log(createClient);
const c = createClient();
type R = ReturnType<typeof createClient>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("namespace", () => {
const got = `
import * as connect from "@connectrpc/connect";
const a = connect.createPromiseClient;
console.log(connect.createPromiseClient);
const c = connect.createPromiseClient();
type R = ReturnType<typeof connect.createPromiseClient>;
`;
const want = `
import * as connect from "@connectrpc/connect";
const a = connect.createClient;
console.log(connect.createClient);
const c = connect.createClient();
type R = ReturnType<typeof connect.createClient>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("default", () => {
const got = `
import connect from "@connectrpc/connect";
const a = connect.createPromiseClient;
console.log(connect.createPromiseClient);
const c = connect.createPromiseClient();
type R = ReturnType<typeof connect.createPromiseClient>;
`;
const want = `
import connect from "@connectrpc/connect";
const a = connect.createClient;
console.log(connect.createClient);
const c = connect.createClient();
type R = ReturnType<typeof connect.createClient>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
});
describe("'require' with", () => {
it("const", () => {
const got = `
const connect = require("@connectrpc/connect");
const a = connect.createPromiseClient;
console.log(connect.createPromiseClient);
const c = connect.createPromiseClient();
type R = ReturnType<typeof connect.createPromiseClient>;
`;
const want = `
const connect = require("@connectrpc/connect");
const a = connect.createClient;
console.log(connect.createClient);
const c = connect.createClient();
type R = ReturnType<typeof connect.createClient>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("spread", () => {
const got = `
const { createPromiseClient } = require("@connectrpc/connect");
const a = createPromiseClient;
console.log(createPromiseClient);
const c = createPromiseClient();
type R = ReturnType<typeof createPromiseClient>;
`;
const want = `
const { createClient } = require("@connectrpc/connect");
const a = createClient;
console.log(createClient);
const c = createClient();
type R = ReturnType<typeof createClient>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
it("let", () => {
const got = `
let connect;
connect = require("@connectrpc/connect");
const a = connect.createPromiseClient;
console.log(connect.createPromiseClient);
const c = connect.createPromiseClient();
type R = ReturnType<typeof connect.createPromiseClient>;
`;
const want = `
let connect;
connect = require("@connectrpc/connect");
const a = connect.createClient;
console.log(connect.createClient);
const c = connect.createClient();
type R = ReturnType<typeof connect.createClient>;
`;
expect(t(got)?.trim()).toBe(want.trim());
});
});
});
Loading