Skip to content

Commit 17effc0

Browse files
brunocrohAugustinMauroyavivkellerJakobJingleheimer
authored
feat(process-main-module): introduce (#136)
Co-authored-by: Augustin Mauroy <[email protected]> Co-authored-by: Aviv Keller <[email protected]> Co-authored-by: Jacob Smith <[email protected]>
1 parent 8b6bcf6 commit 17effc0

18 files changed

+423
-0
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# `process.mainModule` DEP0138
2+
3+
This recipe transforms the usage of `process.mainModule` to use `require.main` in CommonJS modules.
4+
5+
See [DEP0138](https://nodejs.org/api/deprecations.html#DEP0138).
6+
7+
## Example
8+
9+
**Before:**
10+
11+
```js
12+
if (process.mainModule === "mod.js") {
13+
// cli thing
14+
} else {
15+
// module thing
16+
}
17+
```
18+
19+
**After:**
20+
21+
```js
22+
if (require.main === "mod.js") {
23+
// cli thing
24+
} else {
25+
// module thing
26+
}
27+
```
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/process-main-module
3+
version: 1.0.0
4+
description: Handle DEP0138 via transforming `process.mainModule` to `require.main`.
5+
author: Bruno Rodrigues
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/process-main-module",
3+
"version": "1.0.0",
4+
"description": "Handle DEP0138 via transforming `process.mainModule` to `require.main`.",
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/process-main-module",
13+
"bugs": "https://github.com/nodejs/userland-migrations/issues"
14+
},
15+
"author": "Bruno Rodrigues",
16+
"license": "MIT",
17+
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/process-main-module/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: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { Edit, SgRoot, Range } from "@codemod.com/jssg-types/main";
2+
import { getNodeRequireCalls } from "@nodejs/codemod-utils/ast-grep/require-call";
3+
import { removeLines } from "@nodejs/codemod-utils/ast-grep/remove-lines";
4+
5+
/**
6+
* Transforms `process.mainModule` usage to `require.main`. Handles direct global access
7+
* with `process.mainModule` or `require("node:process")` imports, as `mainModule` is CJS-only.
8+
*
9+
*
10+
* Handles:
11+
* 1. Find all destructuring require statements from 'node:process' module that import 'mainModule'
12+
*
13+
* 2. Handle the destructuring import:
14+
* - If 'mainModule' is the only imported property → Remove the entire require statement
15+
* - If other properties are also imported → Remove only 'mainModule' from the destructuring
16+
*
17+
* 3. Replace all code references:
18+
* - Change 'mainModule' → 'require.main'
19+
* - Change 'process.mainModule' → 'require.main'
20+
*/
21+
export default function transform(root: SgRoot): string | null {
22+
const rootNode = root.root();
23+
const edits: Edit[] = [];
24+
const linesToRemove: Range[] = [];
25+
const patternsToReplace = [
26+
{
27+
pattern: "process.mainModule",
28+
},
29+
];
30+
31+
// @ts-ignore - ast-grep types are not fully compatible with JSSG types
32+
const requireDeclarations = getNodeRequireCalls(root, "process");
33+
34+
const destructureDeclarations = rootNode.findAll({
35+
rule: {
36+
kind: "lexical_declaration",
37+
has: {
38+
kind: "variable_declarator",
39+
has: {
40+
kind: "identifier",
41+
regex: "process",
42+
},
43+
},
44+
},
45+
});
46+
47+
for (const declarationNode of [...requireDeclarations, ...destructureDeclarations]) {
48+
// Step 1: Get all requires from module nodule:process that is destructuring mainModule:
49+
if (declarationNode.text().includes("mainModule")) {
50+
const objectPattern = declarationNode.find({
51+
rule: {
52+
kind: "object_pattern",
53+
},
54+
});
55+
56+
if (!objectPattern) continue;
57+
58+
// Step2: Handle the destructuring import:
59+
const declarations = declarationNode.findAll({
60+
rule: {
61+
kind: "shorthand_property_identifier_pattern",
62+
},
63+
});
64+
65+
if (declarations.length !== 0) {
66+
patternsToReplace.push({
67+
pattern: "mainModule",
68+
});
69+
}
70+
71+
// When 'mainModule' is the only item imported, remove to whole thing to avoid an empty import
72+
if (declarations.length === 1) {
73+
linesToRemove.push(declarationNode.range());
74+
}
75+
76+
if (declarations.length > 1) {
77+
const restDeclarations = declarations
78+
.map((d) => d.text())
79+
.filter((d) => d !== "mainModule");
80+
81+
edits.push(objectPattern.replace(`{ ${restDeclarations.join(", ")} }`));
82+
}
83+
}
84+
}
85+
86+
// Step 3: Replace all code references:
87+
const nodes = rootNode.findAll({
88+
rule: {
89+
any: patternsToReplace,
90+
},
91+
});
92+
93+
for (const node of nodes) {
94+
edits.push(node.replace("require.main"));
95+
}
96+
97+
let sourceCode = rootNode.commitEdits(edits);
98+
sourceCode = removeLines(sourceCode, linesToRemove);
99+
return sourceCode;
100+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
if (require.main === module) {
3+
console.log(require.main.filename);
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
if (require.main === module) {
2+
// cli thing
3+
} else {
4+
// module thing
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const { env, cwd } = require("node:process"); // mainModule as first
2+
const { env, cwd } = require("node:process"); // mainModule as last
3+
const { env, cwd } = require("node:process"); // mainModule at middle
4+
5+
if (require.main === module) {
6+
console.log(env, cwd);
7+
} else {
8+
//code
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { env } = require("node:process");
2+
3+
if (require.main === module) {
4+
//code
5+
} else {
6+
//code
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
if (require.main === module) {
3+
//code
4+
} else {
5+
//code
6+
}

0 commit comments

Comments
 (0)