From 453088937fe0a177628bf27eb11ee9d3dc540579 Mon Sep 17 00:00:00 2001 From: Augustin Mauroy <97875033+AugustinMauroy@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:53:52 +0100 Subject: [PATCH] doc(`utils`): update --- utils/README.md | 105 +++++++++++++++++++++++++++++++++++++-------- utils/package.json | 4 +- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/utils/README.md b/utils/README.md index e041cf58..e39dc2af 100644 --- a/utils/README.md +++ b/utils/README.md @@ -13,6 +13,37 @@ Building Node.js codemods with [ast-grep](https://ast-grep.github.io/) requires These utilities provide battle-tested solutions for common codemod operations, reducing boilerplate and ensuring consistent behavior across all migration recipes. +## Package Utilities + +### `removeDependencies(dependenciesToRemove, options?)` + +Removes dependencies from `dependencies` and `devDependencies` in `package.json`, then optionally runs the detected package manager install command. + +```typescript +import removeDependencies from '@nodejs/codemod-utils/remove-dependencies'; + +await removeDependencies(['chalk', 'lodash'], { + runInstall: false, +}); +``` + +#### Parameters + +- `dependenciesToRemove`: `string | string[]` — package name(s) to remove. +- `options.packageJsonPath`: optional path to a target `package.json` (default: `package.json`). +- `options.runInstall`: set to `false` to skip install after changes (default: `true`). +- `options.persistFileWrite`: set to `false` to preview returned JSON without writing to disk (default: `true`). + +#### Return value + +- Returns updated `package.json` content as a string when changes are applied. +- Returns `null` when there is nothing to remove, no `package.json`, or on parsing/runtime failure. + +#### Notes + +- Package manager detection order: `packageJson.packageManager` → lockfiles (`pnpm-lock.yaml`, `yarn.lock`) → `npm`. +- When `runInstall` is enabled, runs ` install` in the package directory. + ## AST-grep Utilities ### Import and Require Detection @@ -24,7 +55,7 @@ Under the hood, calls `getNodeRequireCalls`, `getNodeImportStatements`, `getNode combines the return values. ```typescript -import { getNodeImportStatements } from '@nodejs/codemod-utils'; +import { getModuleDependencies } from '@nodejs/codemod-utils/ast-grep/module-dependencies'; // Finds: import fs from 'fs'; import { readFile } from 'node:fs'; // Finds: const fs = require('fs') @@ -37,7 +68,7 @@ const fsImports = getModuleDependencies(ast, 'fs'); Finds all ES module import statements for a specific Node.js module. ```typescript -import { getNodeImportStatements } from '@nodejs/codemod-utils'; +import { getNodeImportStatements } from '@nodejs/codemod-utils/ast-grep/import-statement'; // Finds: import fs from 'fs'; import { readFile } from 'node:fs'; const fsImports = getNodeImportStatements(ast, 'fs'); @@ -48,7 +79,7 @@ const fsImports = getNodeImportStatements(ast, 'fs'); Finds dynamic import calls assigned to variables (excludes unassigned imports). ```typescript -import { getNodeImportCalls } from '@nodejs/codemod-utils'; +import { getNodeImportCalls } from '@nodejs/codemod-utils/ast-grep/import-statement'; // Finds: const fs = await import('node:fs'); // Ignores: import('node:fs'); // unassigned @@ -60,7 +91,7 @@ const fsImportCalls = getNodeImportCalls(ast, 'fs'); Finds CommonJS require calls assigned to variables. ```typescript -import { getNodeRequireCalls } from '@nodejs/codemod-utils'; +import { getNodeRequireCalls } from '@nodejs/codemod-utils/ast-grep/require-call'; // Finds: const fs = require('fs'); const { readFile } = require('node:fs'); const fsRequires = getNodeRequireCalls(ast, 'fs'); @@ -73,7 +104,7 @@ const fsRequires = getNodeRequireCalls(ast, 'fs'); Resolves how a global API path should be accessed based on the import pattern. ```typescript -import { resolveBindingPath } from '@nodejs/codemod-utils'; +import { resolveBindingPath } from '@nodejs/codemod-utils/ast-grep/resolve-binding-path'; // Given: const { types } = require('node:util'); // resolveBindingPath(node, '$.types.isNativeError') → 'types.isNativeError' @@ -90,7 +121,7 @@ import { resolveBindingPath } from '@nodejs/codemod-utils'; Removes a specific binding from imports/requires, or removes the entire statement if it's the only binding. ```typescript -import { removeBinding } from '@nodejs/codemod-utils'; +import { removeBinding } from '@nodejs/codemod-utils/ast-grep/remove-binding'; // Given: const { types, isNativeError } = require('node:util'); // removeBinding(node, 'isNativeError') → Edit to: const { types } = require('node:util'); @@ -107,7 +138,7 @@ import { removeBinding } from '@nodejs/codemod-utils'; Updates a specific binding from imports/requires. It can be used to replace, add, or remove bindings. ```typescript -import { updateBinding } from '@nodejs/codemod-utils'; +import { updateBinding } from '@nodejs/codemod-utils/ast-grep/update-binding'; // Given: const { isNativeError } = require('node:util'); // updateBinding(node, {old: 'isNativeError', new: 'types'}) → Edit to: const { types } = require('node:util'); @@ -126,7 +157,7 @@ import { updateBinding } from '@nodejs/codemod-utils'; Safely removes multiple line ranges from source code, handling overlaps and duplicates. ```typescript -import { removeLines } from '@nodejs/codemod-utils'; +import { removeLines } from '@nodejs/codemod-utils/ast-grep/remove-lines'; const ranges = [ { start: { line: 5, column: 0 }, end: { line: 5, column: 50 } }, @@ -143,7 +174,7 @@ const cleanedCode = removeLines(sourceCode, ranges); Finds the "scripts" section in a package.json AST. ```typescript -import { getScriptsNode } from '@nodejs/codemod-utils'; +import { getScriptsNode } from '@nodejs/codemod-utils/ast-grep/package-json'; const scriptsNodes = getScriptsNode(packageJsonAst); ``` @@ -153,20 +184,50 @@ const scriptsNodes = getScriptsNode(packageJsonAst); Finds all references to `node` or `node.exe` in package.json scripts. ```typescript -import { getNodeJsUsage } from '@nodejs/codemod-utils'; +import { getNodeJsUsage } from '@nodejs/codemod-utils/ast-grep/package-json'; // Finds scripts like: "start": "node server.js", "build": "node.exe build.js" const nodeUsages = getNodeJsUsage(packageJsonAst); ``` -### Additional AST-grep Helpers (missing in earlier list) +### Additional AST-grep Helpers + +#### `detectIndentUnit(source)` + +Detects the indentation unit used in source code (`\t` or spaces). + +```typescript +import { detectIndentUnit } from '@nodejs/codemod-utils/ast-grep/indent'; + +const indentUnit = detectIndentUnit(sourceCode); +``` + +#### `getLineIndent(source, index)` + +Returns the indentation prefix for the line containing the given character index. + +```typescript +import { getLineIndent } from '@nodejs/codemod-utils/ast-grep/indent'; + +const indent = getLineIndent(sourceCode, node.range().start.index); +``` + +#### `getShebang(rootNode)` + +Returns the Node.js shebang line node when present (for example `#!/usr/bin/env node`). + +```typescript +import { getShebang } from '@nodejs/codemod-utils/ast-grep/shebang'; + +const shebang = getShebang(ast); +``` #### `getScope(node, customParent?)` Finds the enclosing scope for a node (e.g., `statement_block` or `program`). ```typescript -import { getScope } from '@nodejs/codemod-utils'; +import { getScope } from '@nodejs/codemod-utils/ast-grep/get-scope'; const scope = getScope(node); ``` @@ -176,7 +237,7 @@ const scope = getScope(node); Returns the identifier node for a default `import` (e.g., `import fs from 'fs'`). ```typescript -import { getDefaultImportIdentifier } from '@nodejs/codemod-utils'; +import { getDefaultImportIdentifier } from '@nodejs/codemod-utils/ast-grep/import-statement'; const id = getDefaultImportIdentifier(importNode); ``` @@ -186,20 +247,30 @@ const id = getDefaultImportIdentifier(importNode); Returns the identifier node for namespace-style `require` (e.g., `const util = require('util')`). ```typescript -import { getRequireNamespaceIdentifier } from '@nodejs/codemod-utils'; +import { getRequireNamespaceIdentifier } from '@nodejs/codemod-utils/ast-grep/require-call'; const id = getRequireNamespaceIdentifier(varDeclaratorNode); ``` +#### `replaceNodeJsArgs(rootNode, argsToValues)` (shebang) + +Replaces Node.js arguments in shebang lines. + +```typescript +import { replaceNodeJsArgs as replaceShebangNodeJsArgs } from '@nodejs/codemod-utils/ast-grep/shebang'; + +const edits = replaceShebangNodeJsArgs(ast, { '--inspect': '' }); +``` + #### `replaceNodeJsArgs(packageJsonRootNode, argsToValues)` Replaces Node.js script arguments in `package.json` scripts. Useful for normalizing or rewriting CLI arguments used with `node`. ```typescript -import { replaceNodeJsArgs } from '@nodejs/codemod-utils'; +import { replaceNodeJsArgs as replacePackageJsonNodeJsArgs } from '@nodejs/codemod-utils/ast-grep/package-json'; -const edits = replaceNodeJsArgs(packageJsonAst, { '--inspect': '' }); +const edits = replacePackageJsonNodeJsArgs(packageJsonAst, { '--inspect': '' }); ``` #### `removeNodeJsArgs(packageJsonRootNode, argsToRemove)` @@ -207,7 +278,7 @@ const edits = replaceNodeJsArgs(packageJsonAst, { '--inspect': '' }); Removes specified arguments from Node.js script usages in `package.json`. ```typescript -import { removeNodeJsArgs } from '@nodejs/codemod-utils'; +import { removeNodeJsArgs } from '@nodejs/codemod-utils/ast-grep/package-json'; const edits = removeNodeJsArgs(packageJsonAst, ['--inspect']); ``` diff --git a/utils/package.json b/utils/package.json index cb8fe3b5..63a6c9c8 100644 --- a/utils/package.json +++ b/utils/package.json @@ -3,9 +3,7 @@ "version": "0.0.0", "description": "Utilities for Node.js userland migrations.", "exports": { - "./*": [ - "./src/*.ts" - ] + "./*":"./src/*.ts" }, "scripts": { "test": "node --no-warnings --import=./src/codemod-jssg-context.ts --experimental-test-snapshots --experimental-strip-types --import='./src/snapshots.ts' --test --experimental-test-coverage --test-coverage-include='src/**/*' --test-coverage-exclude='**/*.test.ts' './**/*.test.ts'"