From 287e95395e46bc5d1aa6b711c3767c6562b13a69 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 27 Apr 2026 23:45:49 +0100 Subject: [PATCH 1/7] wip --- codemods/invariant/index.js | 32 +++--------- codemods/shared-ast-grep.js | 84 +++++++++++++++++++++++++++++-- codemods/strip-ansi/bun/index.js | 64 +++++++---------------- codemods/strip-ansi/node/index.js | 66 +++++++----------------- 4 files changed, 123 insertions(+), 123 deletions(-) diff --git a/codemods/invariant/index.js b/codemods/invariant/index.js index 28fe3bd..8284fdd 100644 --- a/codemods/invariant/index.js +++ b/codemods/invariant/index.js @@ -1,5 +1,5 @@ import { ts } from '@ast-grep/napi'; -import { findNamedDefaultImport } from '../shared-ast-grep.js'; +import { replaceDefaultImport } from '../shared-ast-grep.js'; /** * @typedef {import('../../types.js').Codemod} Codemod @@ -17,32 +17,12 @@ export default function (options) { transform: ({ file }) => { const ast = ts.parse(file.source); const root = ast.root(); - const edits = []; - const imports = findNamedDefaultImport(root, 'invariant'); - - for (const imp of imports) { - const nameMatch = imp.getMatch('NAME'); - if (nameMatch) { - const name = nameMatch.text(); - const source = imp.text(); - const quoteType = source.includes('"') ? '"' : "'"; - - if (imp.text().startsWith('import')) { - edits.push( - imp.replace( - `import ${name} from ${quoteType}tiny-invariant${quoteType};`, - ), - ); - } else { - edits.push( - imp.replace( - `const ${name} = require(${quoteType}tiny-invariant${quoteType});`, - ), - ); - } - } - } + const { edits } = replaceDefaultImport( + root, + 'invariant', + 'tiny-invariant', + ); return root.commitEdits(edits); }, diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index d83a149..c734e2c 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -225,11 +225,12 @@ export function computePolyfillMethodCallReplacementEdits( /** * Replace a default import/require of one package with another. + * If toIdentifier is not provided, the original local name is preserved. * * @param {SgNode} root - The root of the AST. * @param {string} fromPackage - The package being replaced * @param {string} toPackage - The new package specifier - * @param {string} toIdentifier - The local name to use in the replacement + * @param {string} [toIdentifier] - The local name to use in the replacement (defaults to original name) * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} */ export function replaceDefaultImport( @@ -269,6 +270,83 @@ export function replaceDefaultImport( const localNames = []; let quoteType = "'"; + for (const imp of imports) { + const nameMatch = imp.getMatch('NAME'); + if (!nameMatch) continue; + + const localName = nameMatch.text(); + localNames.push(localName); + + const impText = imp.text(); + if (impText.includes('"')) quoteType = '"'; + + const identifier = toIdentifier ?? localName; + const isCommonJS = imp.find('require($SOURCE)') !== null; + + if (isCommonJS) { + edits.push( + imp.replace( + `const ${identifier} = require(${quoteType}${toPackage}${quoteType});`, + ), + ); + } else { + edits.push( + imp.replace( + `import ${identifier} from ${quoteType}${toPackage}${quoteType};`, + ), + ); + } + } + + return { edits, localNames, quoteType }; +} + +/** + * Replace a default import/require of one package with a named import from another package. + * + * @param {SgNode} root - The root of the AST. + * @param {string} fromPackage - The package being replaced + * @param {string} toPackage - The new package specifier + * @param {string} namedImport - The named import to use (e.g., 'stripVTControlCharacters') + * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} + */ +export function replaceDefaultWithNamedImport( + root, + fromPackage, + toPackage, + namedImport, +) { + const imports = root.findAll({ + rule: { + any: [ + { + pattern: { + context: `import $NAME from '${fromPackage}'`, + strictness: 'relaxed', + }, + }, + { + pattern: { + context: `const $NAME = require('${fromPackage}')`, + strictness: 'relaxed', + }, + }, + { + pattern: { + context: `var $NAME = require('${fromPackage}')`, + strictness: 'relaxed', + }, + }, + ], + }, + }); + + /** @type {Edit[]} */ + const edits = []; + /** @type {string[]} */ + const localNames = []; + let quoteType = "'"; + for (const imp of imports) { const nameMatch = imp.getMatch('NAME'); if (!nameMatch) continue; @@ -283,13 +361,13 @@ export function replaceDefaultImport( if (isCommonJS) { edits.push( imp.replace( - `const ${toIdentifier} = require(${quoteType}${toPackage}${quoteType});`, + `const { ${namedImport} } = require(${quoteType}${toPackage}${quoteType});`, ), ); } else { edits.push( imp.replace( - `import ${toIdentifier} from ${quoteType}${toPackage}${quoteType};`, + `import { ${namedImport} } from ${quoteType}${toPackage}${quoteType};`, ), ); } diff --git a/codemods/strip-ansi/bun/index.js b/codemods/strip-ansi/bun/index.js index 4d95df0..b884662 100644 --- a/codemods/strip-ansi/bun/index.js +++ b/codemods/strip-ansi/bun/index.js @@ -1,5 +1,8 @@ import { ts } from '@ast-grep/napi'; -import { findNamedDefaultImport } from '../../shared-ast-grep.js'; +import { + computeSimpleCallReplacementEdits, + replaceDefaultWithNamedImport, +} from '../../shared-ast-grep.js'; /** * @typedef {import('../../../types.js').Codemod} Codemod @@ -17,52 +20,21 @@ export default function (options) { transform: ({ file }) => { const ast = ts.parse(file.source); const root = ast.root(); - const edits = []; - const importNames = new Set(); - const imports = findNamedDefaultImport(root, 'strip-ansi'); - - for (const imp of imports) { - const nameMatch = imp.getMatch('NAME'); - if (nameMatch) { - const name = nameMatch.text(); - const source = imp.text(); - const quoteType = source.includes('"') ? '"' : "'"; - - importNames.add(name); - - if (imp.text().startsWith('import')) { - edits.push( - imp.replace( - `import { stripANSI } from ${quoteType}bun${quoteType};`, - ), - ); - } else { - edits.push( - imp.replace( - `const { stripANSI } = require(${quoteType}bun${quoteType});`, - ), - ); - } - } - } - - for (const importName of importNames) { - const functionCalls = root.findAll({ - rule: { - pattern: { - context: `${importName}($VALUE)`, - strictness: 'relaxed', - }, - }, - }); - - for (const call of functionCalls) { - const valueMatch = call.getMatch('VALUE'); - if (valueMatch) { - edits.push(call.replace(`stripANSI(${valueMatch.text()})`)); - } - } + const { edits, localNames } = replaceDefaultWithNamedImport( + root, + 'strip-ansi', + 'bun', + 'stripANSI', + ); + + for (const importName of localNames) { + const callEdits = computeSimpleCallReplacementEdits( + root, + importName, + 'stripANSI', + ); + edits.push(...callEdits); } return root.commitEdits(edits); diff --git a/codemods/strip-ansi/node/index.js b/codemods/strip-ansi/node/index.js index e7c1d10..4c6a640 100644 --- a/codemods/strip-ansi/node/index.js +++ b/codemods/strip-ansi/node/index.js @@ -1,5 +1,8 @@ import { ts } from '@ast-grep/napi'; -import { findNamedDefaultImport } from '../../shared-ast-grep.js'; +import { + computeSimpleCallReplacementEdits, + replaceDefaultWithNamedImport, +} from '../../shared-ast-grep.js'; /** * @typedef {import('../../../types.js').Codemod} Codemod @@ -17,54 +20,21 @@ export default function (options) { transform: ({ file }) => { const ast = ts.parse(file.source); const root = ast.root(); - const edits = []; - const importNames = new Set(); - const imports = findNamedDefaultImport(root, 'strip-ansi'); - - for (const imp of imports) { - const nameMatch = imp.getMatch('NAME'); - if (nameMatch) { - const name = nameMatch.text(); - const source = imp.text(); - const quoteType = source.includes('"') ? '"' : "'"; - - importNames.add(name); - - if (imp.text().startsWith('import')) { - edits.push( - imp.replace( - `import { stripVTControlCharacters } from ${quoteType}node:util${quoteType};`, - ), - ); - } else { - edits.push( - imp.replace( - `const { stripVTControlCharacters } = require(${quoteType}node:util${quoteType});`, - ), - ); - } - } - } - - for (const importName of importNames) { - const functionCalls = root.findAll({ - rule: { - pattern: { - context: `${importName}($VALUE)`, - strictness: 'relaxed', - }, - }, - }); - - for (const call of functionCalls) { - const valueMatch = call.getMatch('VALUE'); - if (valueMatch) { - edits.push( - call.replace(`stripVTControlCharacters(${valueMatch.text()})`), - ); - } - } + const { edits, localNames } = replaceDefaultWithNamedImport( + root, + 'strip-ansi', + 'node:util', + 'stripVTControlCharacters', + ); + + for (const importName of localNames) { + const callEdits = computeSimpleCallReplacementEdits( + root, + importName, + 'stripVTControlCharacters', + ); + edits.push(...callEdits); } return root.commitEdits(edits); From 741f74c46767cee8d9e2386df1784412ff55ce00 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 27 Apr 2026 23:57:13 +0100 Subject: [PATCH 2/7] Update shared-ast-grep.js --- codemods/shared-ast-grep.js | 109 +++++++++++++++++------------------- 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index c734e2c..3f4a11b 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -224,21 +224,13 @@ export function computePolyfillMethodCallReplacementEdits( } /** - * Replace a default import/require of one package with another. - * If toIdentifier is not provided, the original local name is preserved. + * Find all default imports/requires for a package and extract common metadata. * * @param {SgNode} root - The root of the AST. - * @param {string} fromPackage - The package being replaced - * @param {string} toPackage - The new package specifier - * @param {string} [toIdentifier] - The local name to use in the replacement (defaults to original name) - * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} + * @param {string} fromPackage - The package to find imports for. + * @returns {{ imports: SgNode[], localNames: string[], quoteType: string }} */ -export function replaceDefaultImport( - root, - fromPackage, - toPackage, - toIdentifier, -) { +function findDefaultImports(root, fromPackage) { const imports = root.findAll({ rule: { any: [ @@ -264,8 +256,6 @@ export function replaceDefaultImport( }, }); - /** @type {Edit[]} */ - const edits = []; /** @type {string[]} */ const localNames = []; let quoteType = "'"; @@ -273,26 +263,53 @@ export function replaceDefaultImport( for (const imp of imports) { const nameMatch = imp.getMatch('NAME'); if (!nameMatch) continue; - - const localName = nameMatch.text(); - localNames.push(localName); - + localNames.push(nameMatch.text()); const impText = imp.text(); if (impText.includes('"')) quoteType = '"'; + } + + return { imports, localNames, quoteType }; +} + +/** + * Replace a default import/require of one package with a named import from another package. + * + * @param {SgNode} root - The root of the AST. + * @param {string} fromPackage - The package being replaced + * @param {string} toPackage - The new package specifier + * @param {string} namedImport - The named import to use (e.g., 'stripVTControlCharacters') + * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} + */ +export function replaceDefaultWithNamedImport( + root, + fromPackage, + toPackage, + namedImport, +) { + const { imports, localNames, quoteType } = findDefaultImports( + root, + fromPackage, + ); + + /** @type {Edit[]} */ + const edits = []; + + for (const imp of imports) { + const nameMatch = imp.getMatch('NAME'); + if (!nameMatch) continue; - const identifier = toIdentifier ?? localName; const isCommonJS = imp.find('require($SOURCE)') !== null; if (isCommonJS) { edits.push( imp.replace( - `const ${identifier} = require(${quoteType}${toPackage}${quoteType});`, + `const { ${namedImport} } = require(${quoteType}${toPackage}${quoteType});`, ), ); } else { edits.push( imp.replace( - `import ${identifier} from ${quoteType}${toPackage}${quoteType};`, + `import { ${namedImport} } from ${quoteType}${toPackage}${quoteType};`, ), ); } @@ -302,72 +319,46 @@ export function replaceDefaultImport( } /** - * Replace a default import/require of one package with a named import from another package. + * Replace a default import/require of one package with another. + * If toIdentifier is not provided, the original local name is preserved. * * @param {SgNode} root - The root of the AST. * @param {string} fromPackage - The package being replaced * @param {string} toPackage - The new package specifier - * @param {string} namedImport - The named import to use (e.g., 'stripVTControlCharacters') + * @param {string} [toIdentifier] - The local name to use in the replacement (defaults to original name) * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} */ -export function replaceDefaultWithNamedImport( +export function replaceDefaultImport( root, fromPackage, toPackage, - namedImport, + toIdentifier, ) { - const imports = root.findAll({ - rule: { - any: [ - { - pattern: { - context: `import $NAME from '${fromPackage}'`, - strictness: 'relaxed', - }, - }, - { - pattern: { - context: `const $NAME = require('${fromPackage}')`, - strictness: 'relaxed', - }, - }, - { - pattern: { - context: `var $NAME = require('${fromPackage}')`, - strictness: 'relaxed', - }, - }, - ], - }, - }); + const { imports, localNames, quoteType } = findDefaultImports( + root, + fromPackage, + ); /** @type {Edit[]} */ const edits = []; - /** @type {string[]} */ - const localNames = []; - let quoteType = "'"; for (const imp of imports) { const nameMatch = imp.getMatch('NAME'); if (!nameMatch) continue; - localNames.push(nameMatch.text()); - - const impText = imp.text(); - if (impText.includes('"')) quoteType = '"'; - + const identifier = toIdentifier || nameMatch.text(); const isCommonJS = imp.find('require($SOURCE)') !== null; if (isCommonJS) { edits.push( imp.replace( - `const { ${namedImport} } = require(${quoteType}${toPackage}${quoteType});`, + `const ${identifier} = require(${quoteType}${toPackage}${quoteType});`, ), ); } else { edits.push( imp.replace( - `import { ${namedImport} } from ${quoteType}${toPackage}${quoteType};`, + `import ${identifier} from ${quoteType}${toPackage}${quoteType};`, ), ); } From 557b2225dc7908cc96b363143691d29d6555df56 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 27 Apr 2026 23:58:31 +0100 Subject: [PATCH 3/7] Update shared-ast-grep.js --- codemods/shared-ast-grep.js | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index 3f4a11b..f4e9a98 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -98,31 +98,7 @@ export function removeImport(root, moduleName) { * @returns {SgNode[]} - An array of matched import nodes. */ export function findNamedDefaultImport(root, moduleName) { - const imports = root.findAll({ - rule: { - any: [ - { - pattern: { - context: `import $NAME from '${moduleName}'`, - strictness: 'relaxed', - }, - }, - { - pattern: { - context: `const $NAME = require('${moduleName}')`, - strictness: 'relaxed', - }, - }, - { - pattern: { - context: `var $NAME = require('${moduleName}')`, - strictness: 'relaxed', - }, - }, - ], - }, - }); - + const { imports } = findDefaultImports(root, moduleName); return imports; } From 463435bc763e9a2fa9823e96ee3aad61f90800a7 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 28 Apr 2026 00:14:48 +0100 Subject: [PATCH 4/7] simplify --- codemods/iterate-iterator/index.js | 16 +++------------- codemods/shared-ast-grep.js | 29 +++++------------------------ types/codemods/shared-ast-grep.d.ts | 8 -------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/codemods/iterate-iterator/index.js b/codemods/iterate-iterator/index.js index a3d476d..9810ef2 100644 --- a/codemods/iterate-iterator/index.js +++ b/codemods/iterate-iterator/index.js @@ -1,5 +1,5 @@ import { ts } from '@ast-grep/napi'; -import { findNamedDefaultImport } from '../shared-ast-grep.js'; +import { removeImport } from '../shared-ast-grep.js'; /** * @typedef {import('../../types.js').Codemod} Codemod @@ -17,20 +17,10 @@ export default function (options) { transform: ({ file }) => { const ast = ts.parse(file.source); const root = ast.root(); - const edits = []; - const importNames = new Set(); - const imports = findNamedDefaultImport(root, 'iterate-iterator'); + const { edits, localNames } = removeImport(root, 'iterate-iterator'); - for (const imp of imports) { - const nameMatch = imp.getMatch('NAME'); - if (nameMatch) { - importNames.add(nameMatch.text()); - } - edits.push(imp.replace('')); - } - - for (const importName of importNames) { + for (const importName of localNames) { const singleArgCalls = root.findAll({ rule: { pattern: { diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index f4e9a98..eba91fa 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -90,18 +90,6 @@ export function removeImport(root, moduleName) { return { edits, localNames }; } -/** - * Find all named imports from a specific module in the AST. - * - * @param {SgNode} root - The root of the AST. - * @param {string} moduleName - The name of the module to find imports from. - * @returns {SgNode[]} - An array of matched import nodes. - */ -export function findNamedDefaultImport(root, moduleName) { - const { imports } = findDefaultImports(root, moduleName); - return imports; -} - /** * Find default imports of a module and resolve the local identifier name. * @@ -110,15 +98,8 @@ export function findNamedDefaultImport(root, moduleName) { * @returns {{ imports: SgNode[], identifierName: string | null }} */ export function findDefaultImportIdentifier(root, moduleName) { - const imports = findNamedDefaultImport(root, moduleName); - let identifierName = null; - for (const imp of imports) { - const nameMatch = imp.getMatch('NAME'); - if (nameMatch) { - identifierName = nameMatch.text(); - break; - } - } + const { imports } = findNamedDefaultImports(root, moduleName); + const identifierName = imports[0]?.getMatch('NAME')?.text() ?? null; return { imports, identifierName }; } @@ -206,7 +187,7 @@ export function computePolyfillMethodCallReplacementEdits( * @param {string} fromPackage - The package to find imports for. * @returns {{ imports: SgNode[], localNames: string[], quoteType: string }} */ -function findDefaultImports(root, fromPackage) { +function findNamedDefaultImports(root, fromPackage) { const imports = root.findAll({ rule: { any: [ @@ -262,7 +243,7 @@ export function replaceDefaultWithNamedImport( toPackage, namedImport, ) { - const { imports, localNames, quoteType } = findDefaultImports( + const { imports, localNames, quoteType } = findNamedDefaultImports( root, fromPackage, ); @@ -310,7 +291,7 @@ export function replaceDefaultImport( toPackage, toIdentifier, ) { - const { imports, localNames, quoteType } = findDefaultImports( + const { imports, localNames, quoteType } = findNamedDefaultImports( root, fromPackage, ); diff --git a/types/codemods/shared-ast-grep.d.ts b/types/codemods/shared-ast-grep.d.ts index 9fb65c0..c9fdab9 100644 --- a/types/codemods/shared-ast-grep.d.ts +++ b/types/codemods/shared-ast-grep.d.ts @@ -19,13 +19,5 @@ export function removeImport(root: SgNode, moduleName: string): { edits: Edit[]; localNames: string[]; }; -/** - * Find all named imports from a specific module in the AST. - * - * @param {SgNode} root - The root of the AST. - * @param {string} moduleName - The name of the module to find imports from. - * @returns {SgNode[]} - An array of matched import nodes. - */ -export function findNamedDefaultImport(root: SgNode, moduleName: string): SgNode[]; export type SgNode = import("@ast-grep/napi").SgNode; export type Edit = import("@ast-grep/napi").Edit; From 119044e437408168090012509819d4949aa7c324 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 28 Apr 2026 00:16:33 +0100 Subject: [PATCH 5/7] Update shared-ast-grep.js --- codemods/shared-ast-grep.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index eba91fa..6280642 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -229,19 +229,20 @@ function findNamedDefaultImports(root, fromPackage) { } /** - * Replace a default import/require of one package with a named import from another package. + * Replace a default import/require of one package with another. + * If toIdentifier is not provided, the original local name is preserved. * * @param {SgNode} root - The root of the AST. * @param {string} fromPackage - The package being replaced * @param {string} toPackage - The new package specifier - * @param {string} namedImport - The named import to use (e.g., 'stripVTControlCharacters') + * @param {string} [toIdentifier] - The local name to use in the replacement (defaults to original name) * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} */ -export function replaceDefaultWithNamedImport( +export function replaceDefaultImport( root, fromPackage, toPackage, - namedImport, + toIdentifier, ) { const { imports, localNames, quoteType } = findNamedDefaultImports( root, @@ -255,18 +256,19 @@ export function replaceDefaultWithNamedImport( const nameMatch = imp.getMatch('NAME'); if (!nameMatch) continue; + const identifier = toIdentifier || nameMatch.text(); const isCommonJS = imp.find('require($SOURCE)') !== null; if (isCommonJS) { edits.push( imp.replace( - `const { ${namedImport} } = require(${quoteType}${toPackage}${quoteType});`, + `const ${identifier} = require(${quoteType}${toPackage}${quoteType});`, ), ); } else { edits.push( imp.replace( - `import { ${namedImport} } from ${quoteType}${toPackage}${quoteType};`, + `import ${identifier} from ${quoteType}${toPackage}${quoteType};`, ), ); } @@ -276,20 +278,19 @@ export function replaceDefaultWithNamedImport( } /** - * Replace a default import/require of one package with another. - * If toIdentifier is not provided, the original local name is preserved. + * Replace a default import/require of one package with a named import from another package. * * @param {SgNode} root - The root of the AST. * @param {string} fromPackage - The package being replaced * @param {string} toPackage - The new package specifier - * @param {string} [toIdentifier] - The local name to use in the replacement (defaults to original name) + * @param {string} namedImport - The named import to use (e.g., 'stripVTControlCharacters') * @returns {{ edits: Edit[], localNames: string[], quoteType: string }} */ -export function replaceDefaultImport( +export function replaceDefaultWithNamedImport( root, fromPackage, toPackage, - toIdentifier, + namedImport, ) { const { imports, localNames, quoteType } = findNamedDefaultImports( root, @@ -303,19 +304,18 @@ export function replaceDefaultImport( const nameMatch = imp.getMatch('NAME'); if (!nameMatch) continue; - const identifier = toIdentifier || nameMatch.text(); const isCommonJS = imp.find('require($SOURCE)') !== null; if (isCommonJS) { edits.push( imp.replace( - `const ${identifier} = require(${quoteType}${toPackage}${quoteType});`, + `const { ${namedImport} } = require(${quoteType}${toPackage}${quoteType});`, ), ); } else { edits.push( imp.replace( - `import ${identifier} from ${quoteType}${toPackage}${quoteType};`, + `import { ${namedImport} } from ${quoteType}${toPackage}${quoteType};`, ), ); } From b955085e0f9eece1f9879f74cee233bebddb96b7 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 28 Apr 2026 00:17:22 +0100 Subject: [PATCH 6/7] Update shared-ast-grep.js --- codemods/shared-ast-grep.js | 96 ++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index 6280642..1496f2f 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -90,6 +90,54 @@ export function removeImport(root, moduleName) { return { edits, localNames }; } +/** + * Find all default imports/requires for a package and extract common metadata. + * + * @param {SgNode} root - The root of the AST. + * @param {string} fromPackage - The package to find imports for. + * @returns {{ imports: SgNode[], localNames: string[], quoteType: string }} + */ +function findNamedDefaultImports(root, fromPackage) { + const imports = root.findAll({ + rule: { + any: [ + { + pattern: { + context: `import $NAME from '${fromPackage}'`, + strictness: 'relaxed', + }, + }, + { + pattern: { + context: `const $NAME = require('${fromPackage}')`, + strictness: 'relaxed', + }, + }, + { + pattern: { + context: `var $NAME = require('${fromPackage}')`, + strictness: 'relaxed', + }, + }, + ], + }, + }); + + /** @type {string[]} */ + const localNames = []; + let quoteType = "'"; + + for (const imp of imports) { + const nameMatch = imp.getMatch('NAME'); + if (!nameMatch) continue; + localNames.push(nameMatch.text()); + const impText = imp.text(); + if (impText.includes('"')) quoteType = '"'; + } + + return { imports, localNames, quoteType }; +} + /** * Find default imports of a module and resolve the local identifier name. * @@ -180,54 +228,6 @@ export function computePolyfillMethodCallReplacementEdits( return edits; } -/** - * Find all default imports/requires for a package and extract common metadata. - * - * @param {SgNode} root - The root of the AST. - * @param {string} fromPackage - The package to find imports for. - * @returns {{ imports: SgNode[], localNames: string[], quoteType: string }} - */ -function findNamedDefaultImports(root, fromPackage) { - const imports = root.findAll({ - rule: { - any: [ - { - pattern: { - context: `import $NAME from '${fromPackage}'`, - strictness: 'relaxed', - }, - }, - { - pattern: { - context: `const $NAME = require('${fromPackage}')`, - strictness: 'relaxed', - }, - }, - { - pattern: { - context: `var $NAME = require('${fromPackage}')`, - strictness: 'relaxed', - }, - }, - ], - }, - }); - - /** @type {string[]} */ - const localNames = []; - let quoteType = "'"; - - for (const imp of imports) { - const nameMatch = imp.getMatch('NAME'); - if (!nameMatch) continue; - localNames.push(nameMatch.text()); - const impText = imp.text(); - if (impText.includes('"')) quoteType = '"'; - } - - return { imports, localNames, quoteType }; -} - /** * Replace a default import/require of one package with another. * If toIdentifier is not provided, the original local name is preserved. From a2e2e7453cdce8ecc901afda5fed2c98d840eec3 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 28 Apr 2026 00:19:47 +0100 Subject: [PATCH 7/7] Update shared-ast-grep.js --- codemods/shared-ast-grep.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/codemods/shared-ast-grep.js b/codemods/shared-ast-grep.js index 1496f2f..bc2c20e 100644 --- a/codemods/shared-ast-grep.js +++ b/codemods/shared-ast-grep.js @@ -94,28 +94,28 @@ export function removeImport(root, moduleName) { * Find all default imports/requires for a package and extract common metadata. * * @param {SgNode} root - The root of the AST. - * @param {string} fromPackage - The package to find imports for. + * @param {string} moduleName - The package to find imports for. * @returns {{ imports: SgNode[], localNames: string[], quoteType: string }} */ -function findNamedDefaultImports(root, fromPackage) { +function findNamedDefaultImports(root, moduleName) { const imports = root.findAll({ rule: { any: [ { pattern: { - context: `import $NAME from '${fromPackage}'`, + context: `import $NAME from '${moduleName}'`, strictness: 'relaxed', }, }, { pattern: { - context: `const $NAME = require('${fromPackage}')`, + context: `const $NAME = require('${moduleName}')`, strictness: 'relaxed', }, }, { pattern: { - context: `var $NAME = require('${fromPackage}')`, + context: `var $NAME = require('${moduleName}')`, strictness: 'relaxed', }, },