Skip to content

Commit

Permalink
changes to logic
Browse files Browse the repository at this point in the history
  • Loading branch information
r4zendev committed Aug 27, 2024
1 parent d4204ea commit 9ddeba5
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 87 deletions.
8 changes: 6 additions & 2 deletions apps/backend/src/publishHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import type { UserDataPopulatedRequest } from "@codemod-com/auth";
import { prisma } from "@codemod-com/database";
// Direct import because tree-shaking helps this to not throw.
import { getCodemodExecutable } from "@codemod-com/runner/dist/source-code.js";
import { BUILT_SOURCE_PATH } from "@codemod-com/runner/dist/source-code.js";
import {
buildCodemodSlug,
codemodNameRegex,
Expand All @@ -34,6 +34,7 @@ import {
unzip,
} from "@codemod-com/utilities";

import { readFile } from "node:fs/promises";
import { buildRevalidateHelper } from "./revalidate.js";
import { environment } from "./util.js";

Expand Down Expand Up @@ -147,7 +148,10 @@ export const publishHandler: RouteHandler<{
throwOnNotFound: false,
});

const built = await getCodemodExecutable(unpackPath).catch(() => null);
const built = await readFile(
join(unpackPath, BUILT_SOURCE_PATH),
"utf8",
).catch(() => null);

if (path === null || built === null) {
return reply.code(400).send({
Expand Down
36 changes: 28 additions & 8 deletions apps/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import inquirer from "inquirer";
import terminalLink from "terminal-link";

import { type Printer, chalk } from "@codemod-com/printer";
import {
BUILT_SOURCE_PATH,
bundleJS,
getCodemodExecutable,
} from "@codemod-com/runner";
import {
ALL_ENGINES,
type KnownEngines,
Expand All @@ -31,21 +36,28 @@ import { oraCheckmark } from "#utils/constants.js";
import { detectCodemodEngine } from "#utils/detectCodemodEngine.js";
import { isFile } from "#utils/general.js";

// @TODO: decompose
export const handleInitCliCommand = async (options: {
printer: Printer;
target: string;
source?: string;
engine?: string;
esm?: boolean;
build?: boolean;
noLogs?: boolean;
noFixtures?: boolean;
useDefaultName?: boolean;
}) => {
const {
printer,
target,
source,
engine,
useDefaultName = false,
esm,
build = true,
noLogs = false,
noFixtures = false,
useDefaultName = false,
} = options;

if (source) {
Expand Down Expand Up @@ -135,6 +147,9 @@ export const handleInitCliCommand = async (options: {
downloadInput.codemodBody = await readFile(source, "utf-8");
}

if (noFixtures) {
downloadInput.cases = [];
}
const files = getCodemodProjectFiles(downloadInput);

const codemodBaseDir = join(target ?? process.cwd(), downloadInput.name);
Expand All @@ -154,11 +169,7 @@ export const handleInitCliCommand = async (options: {
});
} catch (err) {
for (const createdPath of created) {
try {
await unlink(join(codemodBaseDir, createdPath));
} catch (err) {
//
}
await unlink(join(codemodBaseDir, createdPath)).catch(() => null);
}

throw new Error(
Expand All @@ -172,10 +183,19 @@ export const handleInitCliCommand = async (options: {
}
}

if (noLogs) {
return codemodBaseDir;
if (build) {
const outputPath = join(codemodBaseDir, BUILT_SOURCE_PATH);
const bundledCode =
source && isSourceAFile
? await bundleJS({ entry: source, output: outputPath, esm })
: await getCodemodExecutable(codemodBaseDir, esm);

await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, bundledCode);
}

if (noLogs) return codemodBaseDir;

printer.printConsoleMessage(
"info",
chalk.cyan("Codemod package created at", `${chalk.bold(codemodBaseDir)}.`),
Expand Down
15 changes: 9 additions & 6 deletions apps/cli/src/commands/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import inquirer from "inquirer";
import * as semver from "semver";
import * as v from "valibot";

import { tmpdir } from "node:os";
import { type Printer, chalk } from "@codemod-com/printer";
import { BUILT_SOURCE_PATH, getCodemodExecutable } from "@codemod-com/runner";
import type { TelemetrySender } from "@codemod-com/telemetry";
Expand All @@ -23,23 +24,22 @@ import { extractPrintableApiError, getCodemod, publish } from "#api.js";
import { getCurrentUserOrLogin } from "#auth-utils.js";
import { handleInitCliCommand } from "#commands/init.js";
import type { TelemetryEvent } from "#telemetry.js";
import { codemodDirectoryPath } from "#utils/constants.js";
import { isFile } from "#utils/general.js";

export const handlePublishCliCommand = async (options: {
printer: Printer;
source: string;
telemetry: TelemetrySender<TelemetryEvent>;
esm?: boolean;
}) => {
let { source, printer, telemetry } = options;
let { source, printer, telemetry, esm } = options;

const { token, allowedNamespaces, organizations } =
await getCurrentUserOrLogin({
message: "Authentication is required to publish codemods. Proceed?",
printer,
});

const tempDirectory = join(codemodDirectoryPath, "temp");
const formData = new FormData();
const excludedPaths = [
"node_modules/**",
Expand All @@ -53,8 +53,11 @@ export const handlePublishCliCommand = async (options: {
source = await handleInitCliCommand({
printer,
source,
target: tempDirectory,
target: tmpdir(),
noLogs: true,
noFixtures: true,
build: true,
esm,
});

const { choice } = await inquirer.prompt<{ choice: string }>({
Expand Down Expand Up @@ -260,7 +263,7 @@ export const handlePublishCliCommand = async (options: {
);

if (codemodRc.engine !== "recipe") {
const builtExecutable = await getCodemodExecutable(source).catch(
const builtExecutable = await getCodemodExecutable(source, esm).catch(
() => null,
);

Expand Down Expand Up @@ -315,7 +318,7 @@ export const handlePublishCliCommand = async (options: {
message: errorMessage,
});
} finally {
if (source.includes(tempDirectory)) {
if (source.includes(tmpdir())) {
await fs.promises.rm(source, { recursive: true, force: true });
}
}
Expand Down
36 changes: 8 additions & 28 deletions apps/cli/src/fetch-codemod.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { createHash, randomBytes } from "node:crypto";
import { lstat, mkdir, writeFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import { lstat, mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, parse as pathParse, resolve } from "node:path";
import type { AxiosError } from "axios";
import inquirer from "inquirer";
import semver from "semver";
import { flatten } from "valibot";

import { type Printer, chalk } from "@codemod-com/printer";
import { bundleJS } from "@codemod-com/runner";
import {
type Codemod,
type CodemodValidationInput,
buildCodemodSlug,
doubleQuotify,
getCodemodRc,
isJavaScriptName,
isRecipeCodemod,
parseCodemod,
parseEngineOptions,
Expand Down Expand Up @@ -123,34 +122,15 @@ export const fetchCodemod = async (options: FetchOptions): Promise<Codemod> => {
}

// Standalone codemod
// For standalone codemods, before creating a compatible package, we attempt to
// build the binary because it might have other dependencies in the folder
const tempFolderPath = join(codemodDirectoryPath, "temp");
await mkdir(tempFolderPath, { recursive: true });

let codemodPath = nameOrPath;
if (isJavaScriptName(nameOrPath)) {
codemodPath = join(
tempFolderPath,
`${randomBytes(8).toString("hex")}.js`,
);

try {
const executable = await bundleJS({ entry: nameOrPath });
await writeFile(codemodPath, executable);
} catch (err) {
throw new Error(
`Error bundling codemod: ${(err as Error).message ?? "Unknown error"}`,
);
}
}

const codemodPackagePath = await handleInitCliCommand({
printer,
source: codemodPath,
target: tempFolderPath,
source: nameOrPath,
target: tmpdir(),
useDefaultName: true,
noLogs: true,
noFixtures: true,
build: true,
esm: argv.esm,
});

const { config } = await getCodemodRc({
Expand Down
6 changes: 6 additions & 0 deletions apps/cli/src/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export const buildGlobalOptions = <T>(y: Argv<T>) =>
alias: "v",
description: "Show version number",
})
.option("esm", {
type: "boolean",
description:
"Use to specify that you intend to use ESM-specific features in your codemods.",
default: false,
})
.option("json", {
alias: "j",
type: "boolean",
Expand Down
2 changes: 2 additions & 0 deletions apps/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export const main = async () => {
printer,
source: args.source ?? process.cwd(),
telemetry: telemetryService,
esm: args.esm,
});
});
},
Expand Down Expand Up @@ -326,6 +327,7 @@ export const main = async () => {
printer,
target: args.target ?? process.cwd(),
engine: args.engine,
esm: args.esm,
}),
);
},
Expand Down
6 changes: 5 additions & 1 deletion apps/cli/src/safe-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export const buildSafeArgumentRecord = async (
if (codemod.type === "standalone") {
// no checks performed for local codemods
// b/c no source of truth for the arguments
return argvRecord as ArgumentRecord;
const { ...safeRecord } = argvRecord;
delete safeRecord._;
delete safeRecord.include;
delete safeRecord.exclude;
return safeRecord as ArgumentRecord;
}

const validateStringArg = (options: {
Expand Down
5 changes: 3 additions & 2 deletions apps/cli/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ const messageHandler = async (m: unknown) => {
try {
message = decodeMainThreadMessage(m);
} catch (err) {
throw new Error(`Failed to decode message: ${String(err)}`);
throw new Error(
`Failed to decode message: ${String(err)} - ${JSON.stringify(m)}`,
);
}

if (message.kind === "initialization") {
Expand Down Expand Up @@ -118,7 +120,6 @@ const messageHandler = async (m: unknown) => {
kind: "error",
message: error instanceof Error ? error.message : String(error),
path: error instanceof PathAwareError ? error.path : undefined,
stack: error instanceof Error ? error.stack : undefined,
} satisfies WorkerThreadMessage);
}
};
Expand Down
11 changes: 11 additions & 0 deletions apps/docs/deploying-codemods/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,17 @@ codemod [codemod-name] --format
</ResponseField>
<ResponseField name="--esm" type="boolean">
If you want to use ESM-specific features like top-level `await` or `import.meta` in your codemods, you can use this flag.
<Tip>You can also rename your codemod entry-point file to use `.mjs` or `.mts` extension to omit using this compatibility flag.</Tip>
```bash
codemod [codemod-name] --json
```
</ResponseField>
<ResponseField name="--no-cache" type="boolean">
Can be used to disable caching downloaded codemod files.
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/guides/sharing/publishing-codemods.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Creating and publishing a [Codemod-compatible package](/guides/building-codemods
To generate a [Codemod-compatible package](/guides/building-codemods/package-requirements), Codemod CLI will ask you a few questions about your codemod to generate the [`codemodrc.json` configuration file](/guides/building-codemods/package-requirements#codemodrc-json-reference) automatically for you.
</Note>

<Tip>If you want to use ESM-specific features like top-level `await` or `import.meta`, you can rename it to use `.mjs` or `.mts` extension or specify `--esm` flag to treat the source file as ESM package explicitly.</Tip>

```bash
codemod publish [path] --source
```
Expand Down
25 changes: 14 additions & 11 deletions apps/frontend/app/(website)/studio/main/RunOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ export const RunOptions = () => {
const modStore = useModStore();
const { engine, getAllSnippets } = useSnippetsStore();

const allSnippets = getAllSnippets();
const cases = allSnippets.before.reduce(
(acc, before, i) => {
const after = allSnippets.after[i];
if (!after) {
return acc;
}

return acc.concat({ before, after });
},
[] as { before: string; after: string }[],
);

const { session } = useSession();
const { getToken } = useAuth();

Expand Down Expand Up @@ -77,17 +90,7 @@ export const RunOptions = () => {
// TODO: temporary fix, most likely we need to upgrade monaco editor or babel or whatever is responsible
// for taking the code from the web-editor and converting it to string
codemodBody: modStore.content.replace(/\n *as\n *const/g, " as const"),
cases: allSnippets.before.reduce(
(acc, before, i) => {
const after = allSnippets.after[i];
if (!after) {
return acc;
}

return acc.concat({ before, after });
},
[] as { before: string; after: string }[],
),
cases: cases.length ? cases : undefined,
engine,
username: session.user.username ?? session.user.fullName,
});
Expand Down
1 change: 0 additions & 1 deletion packages/printer/src/schemata/worker-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const workerThreadMessageSchema = v.union([
kind: v.literal("error"),
message: v.string(),
path: v.optional(v.string()),
stack: v.optional(v.string()),
}),
v.object({
kind: v.literal("console"),
Expand Down
Loading

0 comments on commit 9ddeba5

Please sign in to comment.