Skip to content

Commit 0aaf9fd

Browse files
committed
Merge branch 'main' into axios-to-fetch
2 parents 5a58a85 + df9accb commit 0aaf9fd

File tree

27 files changed

+703
-59
lines changed

27 files changed

+703
-59
lines changed

apps/cli/src/test/run.ts

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import type { PrinterBlueprint } from "@codemod-com/printer";
2+
import type { TelemetrySender } from "@codemod-com/telemetry";
3+
import { type fs, vol } from "memfs";
4+
import { afterEach, describe, expect, it, vi } from "vitest";
5+
6+
import { randomBytes } from "node:crypto";
7+
import type { GlobalArgvOptions, RunArgvOptions } from "../buildOptions";
8+
import { handleRunCliCommand } from "../commands/run";
9+
10+
const mockedArgs = {
11+
_: ["recipe1"],
12+
target: "./target",
13+
telemetry: true,
14+
cache: true,
15+
format: true,
16+
threads: 1,
17+
dry: false,
18+
install: true,
19+
"disable-tree-version-check": true,
20+
} as unknown as GlobalArgvOptions & RunArgvOptions;
21+
22+
const mockedSendDangerousEvent = vi.fn();
23+
24+
const mockedTelemetry = {
25+
sendDangerousEvent: mockedSendDangerousEvent,
26+
sendEvent: vi.fn(),
27+
sendError: vi.fn(),
28+
} as unknown as TelemetrySender<any>;
29+
30+
const mockedPrinter = {
31+
__jsonOutput: true,
32+
printMessage: vi.fn(),
33+
printOperationMessage: vi.fn(),
34+
printConsoleMessage: vi.fn(),
35+
withLoaderMessage: vi.fn(),
36+
} as unknown as PrinterBlueprint;
37+
38+
const mocks = vi.hoisted(() => {
39+
return {
40+
CodemodDownloader: vi.fn(),
41+
};
42+
});
43+
44+
const mockedRecipe = {
45+
bundleType: "package",
46+
source: "registry",
47+
name: "recipe1",
48+
version: "1.0.0",
49+
engine: "recipe",
50+
codemods: [
51+
{
52+
bundleType: "package",
53+
source: "registry",
54+
name: "codemod1",
55+
version: "1.0.0",
56+
engine: "jscodeshift",
57+
indexPath: "./codemods/codemod1/dist/index.cjs",
58+
directoryPath: "./codemods/codemod1",
59+
arguments: [],
60+
},
61+
{
62+
bundleType: "package",
63+
source: "registry",
64+
name: "codemod2",
65+
version: "1.0.0",
66+
engine: "jscodeshift",
67+
indexPath: "./codemods/codemod2/dist/index.cjs",
68+
directoryPath: "./codemods/codemod2",
69+
arguments: [],
70+
},
71+
],
72+
directoryPath: "./codemods/recipe1",
73+
arguments: [],
74+
};
75+
76+
const mockedCodemod = {
77+
bundleType: "package",
78+
source: "registry",
79+
name: "codemod1",
80+
version: "1.0.0",
81+
engine: "jscodeshift",
82+
indexPath: "./codemods/codemod1/dist/index.cjs",
83+
directoryPath: "./codemods/codemod1",
84+
arguments: [],
85+
};
86+
87+
vi.mock("../downloadCodemod.ts", async () => {
88+
return {
89+
CodemodDownloader: mocks.CodemodDownloader,
90+
};
91+
});
92+
93+
vi.mock("../fileDownloadService.ts");
94+
vi.mock("node:fs/promises", async () => {
95+
const memfs: { fs: typeof fs } = await vi.importActual("memfs");
96+
97+
return memfs.fs.promises;
98+
});
99+
100+
vi.mock("../../../../packages/runner/dist/buildGlobGenerator.js", () => {
101+
return {
102+
buildPathGlobGenerator: async function* () {
103+
yield "target/src/index.tsx";
104+
},
105+
};
106+
});
107+
108+
vi.mock("../../../../packages/runner/dist/workerThreadManager.js", () => {
109+
return {
110+
WorkerThreadManager: function WorkerThreadManager(...args: any[]) {
111+
const onMessage = args[2];
112+
const onCommand = args[3];
113+
114+
onCommand({
115+
kind: "updateFile",
116+
oldPath: `./target/src/file_${randomBytes(8).toString("hex")}.tsx`,
117+
oldData: "",
118+
newData: "updated",
119+
formatWithPrettier: false,
120+
});
121+
122+
onMessage({
123+
kind: "finish",
124+
});
125+
},
126+
};
127+
});
128+
129+
vi.mock(
130+
"../../../../packages/runner/dist/fileCommands.js",
131+
(requireOriginal) => {
132+
return {
133+
...requireOriginal,
134+
modifyFileSystemUponCommand: vi.fn(),
135+
};
136+
},
137+
);
138+
139+
const memfsVolumeJSON = {
140+
"codemods/codemod1/dist/index.cjs": `module.exports = function transform(file, api, options) {
141+
return "transformed by codemod1"
142+
`,
143+
"codemods/codemod2/dist/index.cjs": `module.exports = function transform(file, api, options) {
144+
return "transformed by codemod2"
145+
`,
146+
"codemods/codemod1/.codemodrc.json": `{
147+
"version": "1.0.0",
148+
"private": false,
149+
"name": "codemod1",
150+
"engine": "jscodeshift"
151+
}`,
152+
"codemods/codemod2/.codemodrc.json": `{
153+
"version": "1.0.0",
154+
"private": false,
155+
"name": "codemod2",
156+
"engine": "jscodeshift"
157+
}`,
158+
"codemods/recipe1/.codemodrc.json": `{
159+
"name": "recipe1",
160+
"version": "1.0.0",
161+
"private": false,
162+
"engine": "recipe",
163+
"names": ["codemod1", "codemod2"]
164+
}`,
165+
};
166+
167+
afterEach(() => {
168+
mockedSendDangerousEvent.mockReset();
169+
});
170+
171+
describe("Run command", () => {
172+
// tracks both subcodemod runs and recipe run
173+
it("Should properly track recipe and sub-codemods of the recipe", async () => {
174+
vol.fromJSON(memfsVolumeJSON);
175+
176+
mocks.CodemodDownloader.mockReturnValue({
177+
async download() {
178+
return mockedRecipe;
179+
},
180+
});
181+
182+
await handleRunCliCommand({
183+
printer: mockedPrinter,
184+
args: mockedArgs,
185+
telemetry: mockedTelemetry,
186+
});
187+
188+
expect(
189+
mockedSendDangerousEvent.mock.calls.map(
190+
([{ executionId, ...rest }]) => rest,
191+
),
192+
).toStrictEqual([
193+
{
194+
kind: "codemodExecuted",
195+
codemodName: "codemod1",
196+
fileCount: 1,
197+
recipeName: "recipe1",
198+
},
199+
{
200+
kind: "codemodExecuted",
201+
codemodName: "codemod2",
202+
fileCount: 1,
203+
recipeName: "recipe1",
204+
},
205+
{
206+
kind: "codemodExecuted",
207+
codemodName: "recipe1",
208+
fileCount: 2,
209+
recipeName: "recipe1",
210+
},
211+
]);
212+
});
213+
214+
it("Should properly track single codemod", async () => {
215+
vol.fromJSON(memfsVolumeJSON);
216+
217+
mocks.CodemodDownloader.mockReturnValue({
218+
download() {
219+
return mockedCodemod;
220+
},
221+
});
222+
223+
await handleRunCliCommand({
224+
printer: mockedPrinter,
225+
args: {
226+
...mockedArgs,
227+
_: ["codemod1"],
228+
},
229+
telemetry: mockedTelemetry,
230+
});
231+
232+
const { executionId, ...event } = mockedSendDangerousEvent.mock.calls[0][0];
233+
234+
expect(event).toStrictEqual({
235+
kind: "codemodExecuted",
236+
codemodName: "codemod1",
237+
fileCount: 1,
238+
});
239+
});
240+
});

apps/frontend/app/(website)/about/page.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { loadAboutPage } from "@/data/sanity";
44
import { resolveSanityRouteMetadata } from "@/data/sanity/resolveSanityRouteMetadata";
55
import type { RouteProps } from "@/types";
66
import type { ResolvingMetadata } from "next";
7-
import dynamic from "next/dynamic";
87
import { draftMode } from "next/headers";
98
import { notFound } from "next/navigation";
109

apps/frontend/app/(website)/auth/sign-in/[[...param]]/page.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ function SignInPage() {
1111
return (
1212
<div className="flex h-screen w-screen items-center justify-center">
1313
<SignIn
14-
signUpUrl="/auth/sign-out"
1514
forceRedirectUrl={isStudio ? "/studio" : "/registry"}
16-
appearance={{ elements: { signUpLink: { display: "none" } } }}
15+
appearance={{ elements: { footer: { display: "none" } } }}
1716
/>
1817
</div>
1918
);

apps/frontend/app/(website)/layout.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,40 @@ import publicConfig from "@/config";
88
import { loadGlobalData } from "@/data/sanity";
99
import { GLOBAL_QUERY } from "@/data/sanity/queries";
1010
import { getOgImages } from "@/data/sanity/resolveSanityRouteMetadata";
11+
import { mergeDeepRight } from "ramda";
12+
import { metadata } from "./studio/studioMetadata";
1113

1214
const LiveVisualEditing = dynamic(
1315
() => import("@/components/LiveVisualEditing"),
1416
);
1517

16-
export async function generateMetadata(): Promise<Metadata> {
18+
/**
19+
* credits: https://github.com/vercel/next.js/discussions/50189#discussioncomment-9224262
20+
*
21+
* Get the pathname from the metadata state
22+
* This dives into async storage of promise state to get the pathname
23+
*
24+
* This is much more performant that using headers() from next as this doesn't opt out from the cache
25+
* @param state
26+
*/
27+
// @ts-ignore
28+
const getPathnameFromMetadataState = (state: any): string => {
29+
const res = Object.getOwnPropertySymbols(state || {})
30+
.map((p) => state[p])
31+
.filter(Boolean)
32+
.find((state) =>
33+
Object.prototype.hasOwnProperty.call(state, "urlPathname"),
34+
);
35+
36+
return res?.urlPathname.replace(/\?.+/, "") ?? "";
37+
};
38+
// @ts-ignore
39+
export async function generateMetadata(_: any, state: any): Promise<Metadata> {
40+
const pathname = getPathnameFromMetadataState(state);
41+
1742
const { data } = await loadGlobalData(GLOBAL_QUERY);
1843

19-
return {
44+
const globalOg = {
2045
title: publicConfig.siteName,
2146
openGraph: {
2247
title: publicConfig.siteName,
@@ -25,6 +50,10 @@ export async function generateMetadata(): Promise<Metadata> {
2550
: getOgImages(data.fallbackOGImage),
2651
},
2752
};
53+
54+
return pathname.includes("/studio")
55+
? mergeDeepRight(globalOg, metadata)
56+
: globalOg;
2857
}
2958

3059
export default async function Layout({

apps/frontend/app/(website)/studio/features/codemod-apply/useWebWorker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ export const useWebWorker = () => {
6161
};
6262

6363
worker.onerror = (ee) => {
64+
console.error(
65+
"-------!!!-----\n\n\n WebSocket error emitted: ",
66+
ee,
67+
"\n\n-------!!!-----",
68+
);
6469
const error =
6570
ee.error instanceof Error
6671
? ee.error

0 commit comments

Comments
 (0)