Skip to content

Commit 707c460

Browse files
feat(create-require-from-path): introduce (#111)
Co-authored-by: Aviv Keller <[email protected]>
1 parent c205bb5 commit 707c460

File tree

17 files changed

+248
-1
lines changed

17 files changed

+248
-1
lines changed

package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# `createRequireFromPath` DEP0130
2+
3+
This recipe transforms the usage of `createRequireFromPath` to use the `createRequire` function from the `node:module` module.
4+
5+
See [DEP0130](https://nodejs.org/api/deprecations.html#DEP0130).
6+
7+
## Example
8+
9+
**Before:**
10+
```js
11+
const { createRequireFromPath } = require('node:module');
12+
13+
// Using createRequireFromPath
14+
const requireFromPath = createRequireFromPath('/path/to/module');
15+
const myModule = requireFromPath('./myModule.cjs');
16+
```
17+
18+
**After:**
19+
```js
20+
const { createRequire } = require('node:module');
21+
22+
// Using createRequire with a specific path
23+
const require = createRequire('/path/to/module');
24+
const myModule = require('./myModule.cjs');
25+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
schema_version: "1.0"
2+
name: nodejs/create-require-from-path
3+
version: 0.0.1
4+
description: Handle DEP0130 via transforming `createRequireFromPath` to `createRequire`.
5+
author: Augustin Mauroy
6+
license: MIT
7+
workflow: workflow.yaml
8+
category: migration
9+
10+
targets:
11+
languages:
12+
- javascript
13+
- typescript
14+
15+
keywords:
16+
- transformation
17+
- migration
18+
19+
registry:
20+
access: public
21+
visibility: public
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "@nodejs/create-require-from-path",
3+
"version": "0.0.1",
4+
"description": "Handle DEP0130 via transforming `createRequireFromPath` to `createRequire`.",
5+
"type": "module",
6+
"scripts": {
7+
"test": "npx codemod@next jssg test -l typescript ./src/workflow.ts ./"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/nodejs/userland-migrations.git",
12+
"directory": "recipes/create-require-from-path",
13+
"bugs": "https://github.com/nodejs/userland-migrations/issues"
14+
},
15+
"author": "Augustin Mauroy",
16+
"license": "MIT",
17+
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/create-require-from-path/README.md",
18+
"devDependencies": {
19+
"@types/node": "^24.0.3",
20+
"@codemod.com/jssg-types": "^1.0.3"
21+
},
22+
"dependencies": {
23+
"@nodejs/codemod-utils": "0.0.0"
24+
}
25+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { getNodeImportStatements } from "@nodejs/codemod-utils/ast-grep/import-statement";
2+
import { getNodeRequireCalls } from "@nodejs/codemod-utils/ast-grep/require-call";
3+
import type { SgRoot, Edit } from "@codemod.com/jssg-types/main";
4+
5+
/**
6+
* Transform function that updates code to replace deprecated `createRequireFromPath` usage
7+
* with the modern `createRequire` API from the `module` or `node:module` package.
8+
*
9+
* Handles:
10+
* 1. Updates import/require statements that import `createRequireFromPath`:
11+
* - `const { createRequireFromPath } = require('module')` -> `const { createRequire } = require('module')`
12+
* - `const { createRequireFromPath } = require('node:module')` -> `const { createRequire } = require('node:module')`
13+
* - `import { createRequireFromPath } from 'module'` -> `import { createRequire } from 'module'`
14+
* - `import { createRequireFromPath } from 'node:module'` -> `import { createRequire } from 'node:module'`
15+
*
16+
* 2. Updates variable declarations that use `createRequireFromPath`:
17+
* - `const myRequire = createRequireFromPath(arg)` -> `const myRequire = createRequire(arg)`
18+
* - `let myRequire = createRequireFromPath(arg)` -> `let myRequire = createRequire(arg)`
19+
* - `var myRequire = createRequireFromPath(arg)` -> `var myRequire = createRequire(arg)`
20+
*
21+
* 3. Preserves original variable names and declaration types.
22+
*/
23+
export default function transform(root: SgRoot): string | null {
24+
const rootNode = root.root();
25+
const edits: Edit[] = [];
26+
let hasChanges = false;
27+
28+
// Step 1: Find and update destructuring assignments from require('module') or require('node:module')
29+
// @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+
}
50+
51+
// @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);
93+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* we use createRequireFromPath beause ...*/ const { createRequire } /* we use createRequireFromPath beause ...*/ = require('module'); // we use createRequireFromPath beause ...
2+
3+
var r1 = createRequire('/path/to/module');
4+
let r2 = createRequire('/path/to/module');
5+
const r3 = createRequire('/path/to/module');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const { createRequire } = require('module');
2+
3+
// no semicolon is needed for test
4+
const require = createRequire('/path/to/module')
5+
const myModule = require('./myModule.cjs');
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createRequire } from 'module';
2+
3+
const require = createRequire(import.meta.url);
4+
const data = require('./data.json');
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const { createRequire, otherFunction } = require('node:module');
2+
3+
const require = createRequire(__filename);
4+
const pkg = require('./package.json');
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createRequire, resolve } from 'node:module';
2+
3+
const require = createRequire('/some/path');
4+
const lib = require('some-library');

0 commit comments

Comments
 (0)