Skip to content

Commit

Permalink
Add a transform for createPromiseClient -> createClient (#1236)
Browse files Browse the repository at this point in the history
Signed-off-by: Sri Krishna Paritala <[email protected]>
  • Loading branch information
srikrsna-buf authored Sep 18, 2024
1 parent 8bed930 commit f074ab1
Show file tree
Hide file tree
Showing 7 changed files with 597 additions and 20 deletions.
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

0 comments on commit f074ab1

Please sign in to comment.