Skip to content

feat(@codemod-com/codemod-standalone): explore nodejs compat module #1609

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
WIP
  • Loading branch information
AugustinMauroy committed Jul 14, 2025
commit cb402cdac1342293e16ad8ff0a5915a19c7cba58
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"*.d.ts",
"*.js",
"dist",
"fixtures",
"cdmd_dist",
"build",
"pnpm-lock.yaml",
Expand Down
5 changes: 0 additions & 5 deletions packages/codemod-standalone/src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ export function registerCodemod(
for (const file of files) {
const filePath = path.resolve(file);

if (!fs.existsSync(filePath)) {
console.error(`File not found: ${filePath}`);
continue;
}

const content = fs.readFileSync(filePath, "utf-8");
const root = parse(astGrepLang, content);
const result = transform(root);
Expand Down
18 changes: 9 additions & 9 deletions packages/codemod-standalone/tests/fixtures/input.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
function example() {
logger.log("Hello world");
logger.log("Debug message");
logger.log(42);
logger.log(variable);
console.log("Hello world");
console.log("Debug message");
console.log(42);
console.log(variable);

// These should not be transformed
console.error("Error message");
console.warn("Warning");

// Nested cases
if (condition) {
logger.log("Nested log");
logger.log("Nested debug");
console.log("Nested log");
console.log("Nested debug");
}
}

const data = {
value: 123
};

logger.log(data);
console.log(data);
console.debug("Processing data:", data.value);
27 changes: 27 additions & 0 deletions packages/codemod-standalone/tests/fixtures/workflow-sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { SgRoot } from "@ast-grep/napi";
import { registerCodemod } from "../../src/mod.ts";

// Work with `npx codemod@next`
export default function transform(root: SgRoot): string {
const rootNode = root.root();

const nodes = rootNode.findAll({
rule: {
any: [
{ pattern: "console.log($ARG)" },
{ pattern: "console.debug($ARG)" },
],
},
});

const edits = nodes.map((node) => {
const arg = node.getMatch("ARG")?.text();
return node.replace(`logger.log(${arg})`);
});

const newSource = rootNode.commitEdits(edits);
return newSource;
}

// Register the codemod to use as `node src/transform.ts`
registerCodemod(transform, "TypeScript");
10 changes: 10 additions & 0 deletions packages/codemod-standalone/tests/fixtures/workflow-with-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { SgRoot } from "@ast-grep/napi";
import { registerCodemod } from "../../src/mod.ts";

// Work with `npx codemod@next`
export default async function transform(root: SgRoot): Promise<string> {
if (true) throw new Error("This is a test error in the workflow codemod");
}

// Register the codemod to use as `node src/transform.ts`
registerCodemod(transform, "TypeScript");
27 changes: 27 additions & 0 deletions packages/codemod-standalone/tests/fixtures/workflow-wrong-lang.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { SgRoot } from "@ast-grep/napi";
import { registerCodemod } from "../../src/mod.ts";

// Work with `npx codemod@next`
export default async function transform(root: SgRoot): Promise<string> {
const rootNode = root.root();

const nodes = rootNode.findAll({
rule: {
any: [
{ pattern: "console.log($ARG)" },
{ pattern: "console.debug($ARG)" },
],
},
});

const edits = nodes.map((node) => {
const arg = node.getMatch("ARG")?.text();
return node.replace(`logger.log(${arg})`);
});

const newSource = rootNode.commitEdits(edits);
return newSource;
}

// Register the codemod to use as `node src/transform.ts`
registerCodemod(transform, "invalid-language");
128 changes: 128 additions & 0 deletions packages/codemod-standalone/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,131 @@ test("should run the codemod successfully", () => {
"Output content does not match expected content",
);
});

test("should handle sync workflow", () => {
const outputFilePath = path.join(__dirname, "fixtures/input.js");
const expectedFilePath = path.join(__dirname, "fixtures/expected.js");
const inputTempFilePath = path.join(__dirname, "fixtures/input-temp.js");

assert.ok(fs.existsSync(outputFilePath), "Output file does not exist");
assert.ok(fs.existsSync(expectedFilePath), "Expected file does not exist");

// make input-temp.js
// then replace the content of input.js with the content of input-temp.js
before(() => {
const inputContent = fs.readFileSync(outputFilePath, "utf-8");
fs.writeFileSync(inputTempFilePath, inputContent, "utf-8");
fs.writeFileSync(outputFilePath, inputContent, "utf-8");
});

after(() => {
fs.writeFileSync(
outputFilePath,
fs.readFileSync(inputTempFilePath, "utf-8"),
);
fs.unlinkSync(inputTempFilePath);
});

const result = spawnSync(
execPath,
[
path.join(__dirname, "fixtures/workflow-sync.ts"),
"--input",
path.join(__dirname, "fixtures/**.js"),
"--exclude",
"**/expected.js",
],
{
encoding: "utf-8",
stdio: "inherit",
},
);

assert.strictEqual(result.status, 0, "Codemod did not run successfully");

const outputContent = fs.readFileSync(outputFilePath, "utf-8");
const expectedContent = fs.readFileSync(expectedFilePath, "utf-8");

assert.strictEqual(
outputContent,
expectedContent,
"Output content does not match expected content",
);
});

test("should handle workflow with error", () => {
const result = spawnSync(
execPath,
[
path.join(__dirname, "fixtures/workflow-with-error.ts"),
"--input",
path.join(__dirname, "fixtures/**.js"),
"--exclude",
"**/expected.js",
],
{
encoding: "utf-8",
stdio: "pipe",
},
);

assert.ok(
result.stderr.includes("This is a test error in the workflow codemod"),
);
});

test("should handle invalid language gracefully", () => {
const result = spawnSync(
execPath,
[
path.join(__dirname, "fixtures/workflow-wrong-lang.ts"),
"--input",
path.join(__dirname, "fixtures/**.js"),
"--exclude",
"**/expected.js",
"--language",
"invalid-language",
],
{
encoding: "utf-8",
stdio: "pipe",
},
);

assert.notStrictEqual(result.signal, 1, "Codemod should have failed");
assert.ok(result.stderr.includes("Unsupported language: invalid-language"));
});

test("should error on missing input", () => {
const result = spawnSync(
execPath,
[path.join(__dirname, "fixtures/workflow.ts")],
{
encoding: "utf-8",
stdio: "pipe",
},
);

assert.notStrictEqual(result.status, 0, "Codemod should have failed");
assert.ok(
result.stderr.includes("Input file or directory path is required."),
);
});

test("should handle if any files are found to process", () => {
const result = spawnSync(
execPath,
[
path.join(__dirname, "fixtures/workflow.ts"),
"--input",
path.join(__dirname, "fixtures/nonexistent/**.js"),
],
{
encoding: "utf-8",
stdio: "pipe",
},
);

assert.strictEqual(result.status, 0, "Codemod should run successfully");
assert.ok(result.stderr.includes("No files found to process."));
});
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy