diff --git a/package-lock.json b/package-lock.json index b778d696..9fbf19ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1480,6 +1480,10 @@ "resolved": "recipes/chalk-to-util-styletext", "link": true }, + "node_modules/@nodejs/cjs-to-esm": { + "resolved": "recipes/cjs-to-esm", + "link": true + }, "node_modules/@nodejs/codemod-utils": { "resolved": "utils", "link": true @@ -4303,6 +4307,17 @@ "@codemod.com/jssg-types": "^1.3.1" } }, + "recipes/cjs-to-esm": { + "name": "@nodejs/cjs-to-esm", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@nodejs/codemod-utils": "*" + }, + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.1" + } + }, "recipes/correct-ts-specifiers": { "name": "@nodejs/correct-ts-specifiers", "version": "1.0.0", @@ -4374,6 +4389,18 @@ "@codemod.com/jssg-types": "^1.3.1" } }, + "recipes/esm-migration": { + "name": "@nodejs/cjs-to-esm", + "version": "1.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@nodejs/codemod-utils": "*" + }, + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.1" + } + }, "recipes/fs-access-mode-constants": { "name": "@nodejs/fs-access-mode-constants", "version": "1.0.1", diff --git a/recipes/cjs-to-esm/.gitkeep b/recipes/cjs-to-esm/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/recipes/cjs-to-esm/README.md b/recipes/cjs-to-esm/README.md new file mode 100644 index 00000000..516c98dc --- /dev/null +++ b/recipes/cjs-to-esm/README.md @@ -0,0 +1,6 @@ + +# ESM Migration Codemod + + diff --git a/recipes/cjs-to-esm/codemod.yml b/recipes/cjs-to-esm/codemod.yml new file mode 100644 index 00000000..9fdbb05f --- /dev/null +++ b/recipes/cjs-to-esm/codemod.yml @@ -0,0 +1,25 @@ +schema_version: "1.0" +name: "@nodejs/cjs-to-esm" +version: "1.0.0" +description: "Codemod to assist CommonJS -> ESM migrations (imports, exports, package.json guidance)" +author: "Augustin Mauroy" +license: "MIT" +workflow: workflow.yaml +category: migration + +targets: + languages: + - javascript + - typescript + +keywords: + - migration + - esm + - commonjs + +registry: + access: public + visibility: public + +capabilities: + - fs diff --git a/recipes/cjs-to-esm/package.json b/recipes/cjs-to-esm/package.json new file mode 100644 index 00000000..a08b90d8 --- /dev/null +++ b/recipes/cjs-to-esm/package.json @@ -0,0 +1,28 @@ +{ + "name": "@nodejs/cjs-to-esm", + "version": "1.0.0", + "description": "Codemod to assist CommonJS -> ESM migrations (imports, exports, package.json guidance)", + "type": "module", + "scripts": { + "test": "echo \"The test will be runned when implementation is done.\"", + "test:import": "npx codemod jssg test -l typescript ./src/import-process.ts ./tests/import", + "test:export": "npx codemod jssg test -l typescript ./src/export-process.ts ./tests/export", + "test:package-json": "npx codemod jssg test -l json ./src/package-json-process.ts ./tests/package-json", + "test:context-local-variable": "npx codemod jssg test -l typescript ./src/context-local-variable-process.ts ./tests/context-local-variable" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/nodejs/userland-migrations.git", + "directory": "recipes/cjs-to-esm", + "bugs": "https://github.com/nodejs/userland-migrations/issues" + }, + "author": "Augustin Mauroy", + "license": "MIT", + "homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/cjs-to-esm/README.md", + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.1" + }, + "dependencies": { + "@nodejs/codemod-utils": "*" + } +} diff --git a/recipes/cjs-to-esm/src/context-local-variable-process.ts b/recipes/cjs-to-esm/src/context-local-variable-process.ts new file mode 100644 index 00000000..6b6706ae --- /dev/null +++ b/recipes/cjs-to-esm/src/context-local-variable-process.ts @@ -0,0 +1,16 @@ +import type { SgRoot, Edit } from '@codemod.com/jssg-types/main'; +import type JS from '@codemod.com/jssg-types/langs/javascript'; + +/** + * @see https://github.com/nodejs/package-examples/blob/main/guide/05-cjs-esm-migration/migrating-context-local-variables/README.md + */ +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const edits: Edit[] = []; + + // do some stuff + + if (!edits.length) return null; + + return rootNode.commitEdits(edits); +} diff --git a/recipes/cjs-to-esm/src/export-process.ts b/recipes/cjs-to-esm/src/export-process.ts new file mode 100644 index 00000000..8227462a --- /dev/null +++ b/recipes/cjs-to-esm/src/export-process.ts @@ -0,0 +1,15 @@ +import type { SgRoot, Edit } from '@codemod.com/jssg-types/main'; +import type JS from '@codemod.com/jssg-types/langs/javascript'; + +/** + * @see https://github.com/nodejs/package-examples/tree/main/guide/05-cjs-esm-migration/migrating-exports + */ +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const edits: Edit[] = []; + + // do some stuff + + if (!edits.length) return null; + return rootNode.commitEdits(edits); +} diff --git a/recipes/cjs-to-esm/src/extension-change.ts b/recipes/cjs-to-esm/src/extension-change.ts new file mode 100644 index 00000000..79c0bd4e --- /dev/null +++ b/recipes/cjs-to-esm/src/extension-change.ts @@ -0,0 +1,26 @@ +/* +To interact with step output we need to use this functions. +import { + getOrSetStepOutput, + setStepOutput, +} from '@codemod.com/jssg-types/workflow'; +*/ +import type { SgRoot } from '@codemod.com/jssg-types/main'; +import type JS from '@codemod.com/jssg-types/langs/javascript'; + +//const STEP_ID = 'change-extensions'; +//const OUTPUT_NAME = 'extension_changes'; + +export default async function transform( + root: SgRoot, +): Promise { + const sourcePath = root.filename(); + + // do some stuff + + if (sourcePath.endsWith('.cjs')) { + // ... + } + + return null; +} diff --git a/recipes/cjs-to-esm/src/import-process.ts b/recipes/cjs-to-esm/src/import-process.ts new file mode 100644 index 00000000..a66bd8a2 --- /dev/null +++ b/recipes/cjs-to-esm/src/import-process.ts @@ -0,0 +1,16 @@ +import type { SgRoot, Edit } from '@codemod.com/jssg-types/main'; +import type JS from '@codemod.com/jssg-types/langs/javascript'; + +/** + * @see https://github.com/nodejs/package-examples/tree/main/guide/05-cjs-esm-migration/migrating-imports + */ +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const edits: Edit[] = []; + + // do some stuff + + if (!edits.length) return null; + + return rootNode.commitEdits(edits); +} diff --git a/recipes/cjs-to-esm/src/package-json-process.ts b/recipes/cjs-to-esm/src/package-json-process.ts new file mode 100644 index 00000000..9d8288d8 --- /dev/null +++ b/recipes/cjs-to-esm/src/package-json-process.ts @@ -0,0 +1,16 @@ +import type { Edit, SgRoot } from '@codemod.com/jssg-types/main'; +import type Json from '@codemod.com/jssg-types/langs/json'; + +/** + * @see https://github.com/nodejs/package-examples/tree/main/guide/05-cjs-esm-migration/migrating-package-json + */ +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const edits: Edit[] = []; + + // do some stuff + + if (!edits.length) return null; + + return rootNode.commitEdits(edits); +} diff --git a/recipes/cjs-to-esm/tests/context-local-variable/.gitkeep b/recipes/cjs-to-esm/tests/context-local-variable/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/recipes/cjs-to-esm/tests/export/.gitkeep b/recipes/cjs-to-esm/tests/export/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/recipes/cjs-to-esm/tests/import/.gitkeep b/recipes/cjs-to-esm/tests/import/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/recipes/cjs-to-esm/workflow.yaml b/recipes/cjs-to-esm/workflow.yaml new file mode 100644 index 00000000..19fd95f2 --- /dev/null +++ b/recipes/cjs-to-esm/workflow.yaml @@ -0,0 +1,90 @@ +version: "1" + +state: + schema: + extension_changes: + description: Tracks files renamed from .cjs/.mjs to .js for downstream specifier fixes + type: array + items: + type: object + properties: + from: + type: string + to: + type: string + +nodes: + - id: normalize-extensions + name: Normalize file extensions + type: automatic + runtime: + type: direct + steps: + - id: change-extensions + name: Rename .cjs/.mjs files to .js and record mapping + js-ast-grep: + js_file: src/extension-change.ts + base_path: . + include: + - "**/*.cjs" + - "**/*.mjs" + exclude: + - "**/node_modules/**" + language: typescript + + - id: apply-transforms + name: Apply AST transformations + depends_on: + - normalize-extensions + type: automatic + runtime: + type: direct + steps: + - name: Convert requires/imports to ESM imports + js-ast-grep: + js_file: src/import-process.ts + base_path: . + include: + - "**/*.cjs" + - "**/*.js" + - "**/*.mjs" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript + - name: Convert CommonJS exports to ESM exports + js-ast-grep: + js_file: src/export-process.ts + base_path: . + include: + - "**/*.cjs" + - "**/*.js" + - "**/*.mjs" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript + - name: Migrate context-local variables and built-ins + js-ast-grep: + js_file: src/context-local-variable-process.ts + base_path: . + include: + - "**/*.cjs" + - "**/*.js" + - "**/*.mjs" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript + - name: "Suggest package.json updates (type: module)" + js-ast-grep: + js_file: src/package-json-process.ts + base_path: . + include: + - "package.json" + exclude: + - "**/node_modules/**" + language: json