Skip to content

Commit 18b0628

Browse files
authored
fix(create-require-from-path): handle require/import with alias (#165)
1 parent 6aa0940 commit 18b0628

File tree

9 files changed

+144
-69
lines changed

9 files changed

+144
-69
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

recipes/create-require-from-path/codemod.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
schema_version: "1.0"
22
name: "@nodejs/create-require-from-path"
3-
version: 1.0.0
3+
version: 1.1.0
44
description: Handle DEP0130 via transforming `createRequireFromPath` to `createRequire`.
55
author: Augustin Mauroy
66
license: MIT

recipes/create-require-from-path/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nodejs/create-require-from-path",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Handle DEP0130 via transforming `createRequireFromPath` to `createRequire`.",
55
"type": "module",
66
"scripts": {

recipes/create-require-from-path/src/workflow.ts

Lines changed: 128 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -21,73 +21,136 @@ import type { SgRoot, Edit } from "@codemod.com/jssg-types/main";
2121
* 3. Preserves original variable names and declaration types.
2222
*/
2323
export default function transform(root: SgRoot): string | null {
24-
const rootNode = root.root();
25-
const edits: Edit[] = [];
26-
let hasChanges = false;
24+
const rootNode = root.root();
25+
const edits: Edit[] = [];
26+
let hasChanges = false;
2727

28-
// Step 1: Find and update destructuring assignments from require('module') or require('node:module')
28+
// Step 1: Find and update destructuring assignments from require('module') or require('node:module')
2929
// @ts-ignore - ast-grep types are not fully compatible with JSSG types
30-
const requireStatements = getNodeRequireCalls(root, "module")
31-
32-
for (const statement of requireStatements) {
33-
// Find the object pattern (destructuring)
34-
const objectPattern = statement.find({
35-
rule: {
36-
kind: "object_pattern"
37-
}
38-
});
39-
40-
if (objectPattern) {
41-
const originalText = objectPattern.text();
42-
43-
if (originalText.includes("createRequireFromPath")) {
44-
const newText = originalText.replace(/\bcreateRequireFromPath\b/g, "createRequire");
45-
edits.push(objectPattern.replace(newText));
46-
hasChanges = true;
47-
}
48-
}
49-
}
30+
const requireStatements = getNodeRequireCalls(root, "module");
31+
32+
for (const statement of requireStatements) {
33+
// Find the object pattern (destructuring)
34+
const objectPattern = statement.find({
35+
rule: {
36+
kind: "object_pattern",
37+
},
38+
});
39+
40+
if (objectPattern) {
41+
const originalText = objectPattern.text();
42+
43+
if (originalText.includes("createRequireFromPath")) {
44+
const newText = originalText.replace(/\bcreateRequireFromPath\b/g, "createRequire");
45+
edits.push(objectPattern.replace(newText));
46+
hasChanges = true;
47+
}
48+
}
49+
}
5050

5151
// @ts-ignore - ast-grep types are not fully compatible with JSSG types
52-
const importStatements = getNodeImportStatements(root, "module");
53-
54-
for (const statement of importStatements) {
55-
// Find the named imports
56-
const namedImports = statement.find({
57-
rule: {
58-
kind: "named_imports"
59-
}
60-
});
61-
62-
if (namedImports) {
63-
const originalText = namedImports.text();
64-
65-
if (originalText.includes("createRequireFromPath")) {
66-
const newText = originalText.replace(/\bcreateRequireFromPath\b/g, "createRequire");
67-
edits.push(namedImports.replace(newText));
68-
hasChanges = true;
69-
}
70-
}
71-
}
72-
73-
// Step 2: Find and replace createRequireFromPath function calls
74-
const functionCalls = rootNode.findAll({
75-
rule: {
76-
pattern: "createRequireFromPath($ARG)"
77-
}
78-
});
79-
80-
for (const call of functionCalls) {
81-
const argMatch = call.getMatch("ARG");
82-
if (argMatch) {
83-
const arg = argMatch.text();
84-
const replacement = `createRequire(${arg})`;
85-
edits.push(call.replace(replacement));
86-
hasChanges = true;
87-
}
88-
}
89-
90-
if (!hasChanges) return null;
91-
92-
return rootNode.commitEdits(edits);
52+
const importStatements = getNodeImportStatements(root, "module");
53+
54+
for (const statement of importStatements) {
55+
// Find the named imports
56+
const namedImports = statement.find({
57+
rule: {
58+
kind: "named_imports",
59+
},
60+
});
61+
62+
if (namedImports) {
63+
const originalText = namedImports.text();
64+
65+
if (originalText.includes("createRequireFromPath")) {
66+
const newText = originalText.replace(/\bcreateRequireFromPath\b/g, "createRequire");
67+
edits.push(namedImports.replace(newText));
68+
hasChanges = true;
69+
}
70+
}
71+
}
72+
73+
const renamedImports = rootNode.findAll({
74+
rule: {
75+
any: [
76+
{
77+
kind: "pair_pattern",
78+
all: [
79+
{
80+
has: {
81+
field: "key",
82+
kind: "property_identifier",
83+
},
84+
},
85+
{
86+
has: {
87+
field: "value",
88+
kind: "identifier",
89+
},
90+
},
91+
],
92+
},
93+
{
94+
kind: "import_specifier",
95+
all: [
96+
{
97+
has: {
98+
field: "alias",
99+
kind: "identifier",
100+
},
101+
},
102+
{
103+
has: {
104+
field: "name",
105+
kind: "identifier",
106+
},
107+
},
108+
],
109+
},
110+
],
111+
},
112+
});
113+
114+
for (const rename of renamedImports) {
115+
if (rename?.text().includes("createRequireFromPath")) {
116+
const key = rename.find({
117+
rule: {
118+
has:
119+
rename.kind() === "import_specifier"
120+
? {
121+
field: "name",
122+
kind: "identifier",
123+
}
124+
: {
125+
field: "key",
126+
kind: "property_identifier",
127+
},
128+
},
129+
});
130+
131+
edits.push(key.replace("createRequire"));
132+
hasChanges = true;
133+
}
134+
}
135+
136+
// Step 2: Find and replace createRequireFromPath function calls
137+
const functionCalls = rootNode.findAll({
138+
rule: {
139+
pattern: "createRequireFromPath($ARG)",
140+
},
141+
});
142+
143+
for (const call of functionCalls) {
144+
const argMatch = call.getMatch("ARG");
145+
if (argMatch) {
146+
const arg = argMatch.text();
147+
const replacement = `createRequire(${arg})`;
148+
edits.push(call.replace(replacement));
149+
hasChanges = true;
150+
}
151+
}
152+
153+
if (!hasChanges) return null;
154+
155+
return rootNode.commitEdits(edits);
93156
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { other as bar, createRequire as foo } from "../index.mjs";
2+
3+
const r3 = foo("/path/to/module");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { createRequire: foo } = require("../index.cjs");
2+
3+
const r3 = foo("/path/to/module");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { other as bar, createRequireFromPath as foo } from "../index.mjs";
2+
3+
const r3 = foo("/path/to/module");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { createRequireFromPath: foo } = require("../index.cjs");
2+
3+
const r3 = foo("/path/to/module");

utils/src/ast-grep/resolve-binding-path.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const supportedKinds = [...requireKinds, ...importKinds];
3737
* ```
3838
*/
3939
export function resolveBindingPath(node: SgNode<TypesMap, Kinds<TypesMap>>, path: string) {
40-
let activeNode = node;
40+
const activeNode = node;
4141
const rootKind = activeNode.kind().toString();
4242

4343
if (!supportedKinds.includes(rootKind.toString())) {

0 commit comments

Comments
 (0)