diff --git a/package-lock.json b/package-lock.json index 7d682ce..b9f2a41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@publint/pack": "^0.1.2", "fdir": "^6.4.6", "gunshi": "^0.26.3", + "module-replacements": "^2.8.0", "module-replacements-codemods": "^1.1.0", "package-manager-detector": "^1.3.0", "picocolors": "^1.1.1", @@ -23,7 +24,7 @@ "tinyglobby": "^0.2.14" }, "bin": { - "e18e-report": "cli.js" + "e18e-cli": "cli.js" }, "devDependencies": { "@types/node": "^24.0.3", @@ -3356,6 +3357,20 @@ "node": ">=6.9.0" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -3966,6 +3981,11 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/module-replacements": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/module-replacements/-/module-replacements-2.8.0.tgz", + "integrity": "sha512-ecTdT19nf+jYxPhXNCUuopDwazNOfcEW/GVlRmVxFO/zWcFFedso+pV4GbJ0TKjI+su3htyghhCJJ99ZAkuUyg==" + }, "node_modules/module-replacements-codemods": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/module-replacements-codemods/-/module-replacements-codemods-1.1.0.tgz", @@ -4553,6 +4573,17 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "optional": true, + "peer": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -5118,6 +5149,27 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 0d4d0bc..be3aef2 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "lint": "eslint src", "format": "prettier --write src", "test": "FORCE_COLOR=1 vitest run --coverage", - "prepublishOnly": "npm run test" + "prepublishOnly": "npm run test", + "generate-fixable-replacements": "tsx src/scripts/generate-fixable-replacements.ts" }, "repository": { "type": "git", @@ -57,6 +58,7 @@ "@publint/pack": "^0.1.2", "fdir": "^6.4.6", "gunshi": "^0.26.3", + "module-replacements": "^2.8.0", "module-replacements-codemods": "^1.1.0", "package-manager-detector": "^1.3.0", "picocolors": "^1.1.1", diff --git a/src/cli.ts b/src/cli.ts index 66177e0..caf95ea 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,10 +4,21 @@ import * as prompts from '@clack/prompts'; import c from 'picocolors'; import {meta as analyzeMeta} from './commands/analyze.meta.js'; import {meta as migrateMeta} from './commands/migrate.meta.js'; +import {pino} from 'pino'; const version = createRequire(import.meta.url)('../package.json').version; // Create a logger instance with pretty printing for development +export const logger = pino({ + transport: { + target: 'pino-pretty', + options: { + colorize: true, + translateTime: 'SYS:standard', + ignore: 'pid,hostname' + } + } +}); const defaultCommand = define({ args: {}, diff --git a/src/commands/fixable-replacements.ts b/src/commands/fixable-replacements.ts new file mode 100644 index 0000000..42395d4 --- /dev/null +++ b/src/commands/fixable-replacements.ts @@ -0,0 +1,740 @@ +import type { Replacement } from '../types.js'; +import { codemods } from 'module-replacements-codemods'; + +export const fixableReplacements: Replacement[] = [ + { + from: "array-buffer-byte-length", + to: "TODO", + factory: codemods["array-buffer-byte-length"] + }, + { + from: "array-every", + to: "TODO", + factory: codemods["array-every"] + }, + { + from: "array-includes", + to: "TODO", + factory: codemods["array-includes"] + }, + { + from: "array-map", + to: "TODO", + factory: codemods["array-map"] + }, + { + from: "array.from", + to: "TODO", + factory: codemods["array.from"] + }, + { + from: "array.of", + to: "TODO", + factory: codemods["array.of"] + }, + { + from: "array.prototype.at", + to: "TODO", + factory: codemods["array.prototype.at"] + }, + { + from: "array.prototype.concat", + to: "TODO", + factory: codemods["array.prototype.concat"] + }, + { + from: "array.prototype.copywithin", + to: "TODO", + factory: codemods["array.prototype.copywithin"] + }, + { + from: "array.prototype.entries", + to: "TODO", + factory: codemods["array.prototype.entries"] + }, + { + from: "array.prototype.every", + to: "TODO", + factory: codemods["array.prototype.every"] + }, + { + from: "array.prototype.filter", + to: "TODO", + factory: codemods["array.prototype.filter"] + }, + { + from: "array.prototype.find", + to: "TODO", + factory: codemods["array.prototype.find"] + }, + { + from: "array.prototype.findindex", + to: "TODO", + factory: codemods["array.prototype.findindex"] + }, + { + from: "array.prototype.flat", + to: "TODO", + factory: codemods["array.prototype.flat"] + }, + { + from: "array.prototype.flatmap", + to: "TODO", + factory: codemods["array.prototype.flatmap"] + }, + { + from: "array.prototype.foreach", + to: "TODO", + factory: codemods["array.prototype.foreach"] + }, + { + from: "array.prototype.indexof", + to: "TODO", + factory: codemods["array.prototype.indexof"] + }, + { + from: "array.prototype.join", + to: "TODO", + factory: codemods["array.prototype.join"] + }, + { + from: "array.prototype.keys", + to: "TODO", + factory: codemods["array.prototype.keys"] + }, + { + from: "array.prototype.lastindexof", + to: "TODO", + factory: codemods["array.prototype.lastindexof"] + }, + { + from: "array.prototype.map", + to: "TODO", + factory: codemods["array.prototype.map"] + }, + { + from: "array.prototype.push", + to: "TODO", + factory: codemods["array.prototype.push"] + }, + { + from: "array.prototype.reduce", + to: "TODO", + factory: codemods["array.prototype.reduce"] + }, + { + from: "array.prototype.reduceright", + to: "TODO", + factory: codemods["array.prototype.reduceright"] + }, + { + from: "array.prototype.slice", + to: "TODO", + factory: codemods["array.prototype.slice"] + }, + { + from: "array.prototype.some", + to: "TODO", + factory: codemods["array.prototype.some"] + }, + { + from: "array.prototype.splice", + to: "TODO", + factory: codemods["array.prototype.splice"] + }, + { + from: "array.prototype.unshift", + to: "TODO", + factory: codemods["array.prototype.unshift"] + }, + { + from: "array.prototype.values", + to: "TODO", + factory: codemods["array.prototype.values"] + }, + { + from: "arraybuffer.prototype.slice", + to: "TODO", + factory: codemods["arraybuffer.prototype.slice"] + }, + { + from: "concat-map", + to: "TODO", + factory: codemods["concat-map"] + }, + { + from: "data-view-buffer", + to: "TODO", + factory: codemods["data-view-buffer"] + }, + { + from: "data-view-byte-length", + to: "TODO", + factory: codemods["data-view-byte-length"] + }, + { + from: "data-view-byte-offset", + to: "TODO", + factory: codemods["data-view-byte-offset"] + }, + { + from: "date", + to: "TODO", + factory: codemods["date"] + }, + { + from: "define-properties", + to: "TODO", + factory: codemods["define-properties"] + }, + { + from: "error-cause", + to: "TODO", + factory: codemods["error-cause"] + }, + { + from: "es-aggregate-error", + to: "TODO", + factory: codemods["es-aggregate-error"] + }, + { + from: "es-define-property", + to: "TODO", + factory: codemods["es-define-property"] + }, + { + from: "es-errors", + to: "TODO", + factory: codemods["es-errors"] + }, + { + from: "es-shim-unscopables", + to: "TODO", + factory: codemods["es-shim-unscopables"] + }, + { + from: "es-string-html-methods", + to: "TODO", + factory: codemods["es-string-html-methods"] + }, + { + from: "filter-array", + to: "TODO", + factory: codemods["filter-array"] + }, + { + from: "for-each", + to: "TODO", + factory: codemods["for-each"] + }, + { + from: "function-bind", + to: "TODO", + factory: codemods["function-bind"] + }, + { + from: "function.prototype.name", + to: "TODO", + factory: codemods["function.prototype.name"] + }, + { + from: "functions-have-names", + to: "TODO", + factory: codemods["functions-have-names"] + }, + { + from: "get-symbol-description", + to: "TODO", + factory: codemods["get-symbol-description"] + }, + { + from: "global", + to: "TODO", + factory: codemods["global"] + }, + { + from: "gopd", + to: "TODO", + factory: codemods["gopd"] + }, + { + from: "has", + to: "TODO", + factory: codemods["has"] + }, + { + from: "has-own-prop", + to: "TODO", + factory: codemods["has-own-prop"] + }, + { + from: "has-proto", + to: "TODO", + factory: codemods["has-proto"] + }, + { + from: "has-symbols", + to: "TODO", + factory: codemods["has-symbols"] + }, + { + from: "has-tostringtag", + to: "TODO", + factory: codemods["has-tostringtag"] + }, + { + from: "hasown", + to: "TODO", + factory: codemods["hasown"] + }, + { + from: "index-of", + to: "TODO", + factory: codemods["index-of"] + }, + { + from: "is-nan", + to: "TODO", + factory: codemods["is-nan"] + }, + { + from: "iterate-value", + to: "TODO", + factory: codemods["iterate-value"] + }, + { + from: "last-index-of", + to: "TODO", + factory: codemods["last-index-of"] + }, + { + from: "left-pad", + to: "TODO", + factory: codemods["left-pad"] + }, + { + from: "math.acosh", + to: "TODO", + factory: codemods["math.acosh"] + }, + { + from: "math.atanh", + to: "TODO", + factory: codemods["math.atanh"] + }, + { + from: "math.cbrt", + to: "TODO", + factory: codemods["math.cbrt"] + }, + { + from: "math.clz32", + to: "TODO", + factory: codemods["math.clz32"] + }, + { + from: "math.f16round", + to: "TODO", + factory: codemods["math.f16round"] + }, + { + from: "math.fround", + to: "TODO", + factory: codemods["math.fround"] + }, + { + from: "math.imul", + to: "TODO", + factory: codemods["math.imul"] + }, + { + from: "math.log10", + to: "TODO", + factory: codemods["math.log10"] + }, + { + from: "math.log1p", + to: "TODO", + factory: codemods["math.log1p"] + }, + { + from: "math.sign", + to: "TODO", + factory: codemods["math.sign"] + }, + { + from: "number.isfinite", + to: "TODO", + factory: codemods["number.isfinite"] + }, + { + from: "number.isinteger", + to: "TODO", + factory: codemods["number.isinteger"] + }, + { + from: "number.isnan", + to: "TODO", + factory: codemods["number.isnan"] + }, + { + from: "number.issafeinteger", + to: "TODO", + factory: codemods["number.issafeinteger"] + }, + { + from: "number.parsefloat", + to: "TODO", + factory: codemods["number.parsefloat"] + }, + { + from: "number.parseint", + to: "TODO", + factory: codemods["number.parseint"] + }, + { + from: "number.prototype.toexponential", + to: "TODO", + factory: codemods["number.prototype.toexponential"] + }, + { + from: "object-assign", + to: "TODO", + factory: codemods["object-assign"] + }, + { + from: "object-is", + to: "TODO", + factory: codemods["object-is"] + }, + { + from: "object-keys", + to: "TODO", + factory: codemods["object-keys"] + }, + { + from: "object.assign", + to: "TODO", + factory: codemods["object.assign"] + }, + { + from: "object.defineproperties", + to: "TODO", + factory: codemods["object.defineproperties"] + }, + { + from: "object.entries", + to: "TODO", + factory: codemods["object.entries"] + }, + { + from: "object.fromentries", + to: "TODO", + factory: codemods["object.fromentries"] + }, + { + from: "object.getprototypeof", + to: "TODO", + factory: codemods["object.getprototypeof"] + }, + { + from: "object.hasown", + to: "TODO", + factory: codemods["object.hasown"] + }, + { + from: "object.keys", + to: "TODO", + factory: codemods["object.keys"] + }, + { + from: "object.values", + to: "TODO", + factory: codemods["object.values"] + }, + { + from: "pad-left", + to: "TODO", + factory: codemods["pad-left"] + }, + { + from: "parseint", + to: "TODO", + factory: codemods["parseint"] + }, + { + from: "promise.allsettled", + to: "TODO", + factory: codemods["promise.allsettled"] + }, + { + from: "promise.any", + to: "TODO", + factory: codemods["promise.any"] + }, + { + from: "promise.prototype.finally", + to: "TODO", + factory: codemods["promise.prototype.finally"] + }, + { + from: "reflect.getprototypeof", + to: "TODO", + factory: codemods["reflect.getprototypeof"] + }, + { + from: "reflect.ownkeys", + to: "TODO", + factory: codemods["reflect.ownkeys"] + }, + { + from: "regexp.prototype.flags", + to: "TODO", + factory: codemods["regexp.prototype.flags"] + }, + { + from: "string.prototype.at", + to: "TODO", + factory: codemods["string.prototype.at"] + }, + { + from: "string.prototype.lastindexof", + to: "TODO", + factory: codemods["string.prototype.lastindexof"] + }, + { + from: "string.prototype.matchall", + to: "TODO", + factory: codemods["string.prototype.matchall"] + }, + { + from: "string.prototype.padend", + to: "TODO", + factory: codemods["string.prototype.padend"] + }, + { + from: "string.prototype.padleft", + to: "TODO", + factory: codemods["string.prototype.padleft"] + }, + { + from: "string.prototype.padright", + to: "TODO", + factory: codemods["string.prototype.padright"] + }, + { + from: "string.prototype.padstart", + to: "TODO", + factory: codemods["string.prototype.padstart"] + }, + { + from: "string.prototype.replaceall", + to: "TODO", + factory: codemods["string.prototype.replaceall"] + }, + { + from: "string.prototype.split", + to: "TODO", + factory: codemods["string.prototype.split"] + }, + { + from: "string.prototype.substr", + to: "TODO", + factory: codemods["string.prototype.substr"] + }, + { + from: "string.prototype.trim", + to: "TODO", + factory: codemods["string.prototype.trim"] + }, + { + from: "string.prototype.trimend", + to: "TODO", + factory: codemods["string.prototype.trimend"] + }, + { + from: "string.prototype.trimleft", + to: "TODO", + factory: codemods["string.prototype.trimleft"] + }, + { + from: "string.prototype.trimright", + to: "TODO", + factory: codemods["string.prototype.trimright"] + }, + { + from: "string.prototype.trimstart", + to: "TODO", + factory: codemods["string.prototype.trimstart"] + }, + { + from: "string.raw", + to: "TODO", + factory: codemods["string.raw"] + }, + { + from: "symbol.prototype.description", + to: "TODO", + factory: codemods["symbol.prototype.description"] + }, + { + from: "typed-array-buffer", + to: "TODO", + factory: codemods["typed-array-buffer"] + }, + { + from: "typed-array-byte-length", + to: "TODO", + factory: codemods["typed-array-byte-length"] + }, + { + from: "typed-array-byte-offset", + to: "TODO", + factory: codemods["typed-array-byte-offset"] + }, + { + from: "typed-array-length", + to: "TODO", + factory: codemods["typed-array-length"] + }, + { + from: "typedarray.prototype.slice", + to: "TODO", + factory: codemods["typedarray.prototype.slice"] + }, + { + from: "xtend", + to: "TODO", + factory: codemods["xtend"] + }, + { + from: "clone-regexp", + to: "TODO", + factory: codemods["clone-regexp"] + }, + { + from: "es-get-iterator", + to: "TODO", + factory: codemods["es-get-iterator"] + }, + { + from: "es-set-tostringtag", + to: "TODO", + factory: codemods["es-set-tostringtag"] + }, + { + from: "is-array-buffer", + to: "TODO", + factory: codemods["is-array-buffer"] + }, + { + from: "is-boolean-object", + to: "TODO", + factory: codemods["is-boolean-object"] + }, + { + from: "is-date-object", + to: "TODO", + factory: codemods["is-date-object"] + }, + { + from: "is-even", + to: "TODO", + factory: codemods["is-even"] + }, + { + from: "is-negative-zero", + to: "TODO", + factory: codemods["is-negative-zero"] + }, + { + from: "is-npm", + to: "TODO", + factory: codemods["is-npm"] + }, + { + from: "is-number", + to: "TODO", + factory: codemods["is-number"] + }, + { + from: "is-number-object", + to: "TODO", + factory: codemods["is-number-object"] + }, + { + from: "is-odd", + to: "TODO", + factory: codemods["is-odd"] + }, + { + from: "is-plain-object", + to: "TODO", + factory: codemods["is-plain-object"] + }, + { + from: "is-primitive", + to: "TODO", + factory: codemods["is-primitive"] + }, + { + from: "is-regexp", + to: "TODO", + factory: codemods["is-regexp"] + }, + { + from: "is-string", + to: "TODO", + factory: codemods["is-string"] + }, + { + from: "is-travis", + to: "TODO", + factory: codemods["is-travis"] + }, + { + from: "is-whitespace", + to: "TODO", + factory: codemods["is-whitespace"] + }, + { + from: "is-windows", + to: "TODO", + factory: codemods["is-windows"] + }, + { + from: "split-lines", + to: "TODO", + factory: codemods["split-lines"] + }, + { + from: "chalk", + to: "picocolors", + factory: codemods["chalk"] + }, + { + from: "deep-equal", + to: "TODO", + factory: codemods["deep-equal"] + }, + { + from: "is-builtin-module", + to: "TODO", + factory: codemods["is-builtin-module"] + }, + { + from: "md5", + to: "TODO", + factory: codemods["md5"] + }, + { + from: "qs", + to: "TODO", + factory: codemods["qs"] + }, + { + from: "traverse", + to: "TODO", + factory: codemods["traverse"] + }, +]; diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index 961921c..15bfc1f 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -2,24 +2,11 @@ import {type CommandContext} from 'gunshi'; import * as prompts from '@clack/prompts'; import colors from 'picocolors'; import {meta} from './migrate.meta.js'; -import {codemods} from 'module-replacements-codemods'; import {glob} from 'tinyglobby'; import {readFile, writeFile} from 'node:fs/promises'; +import {fixableReplacements} from './fixable-replacements.js'; +import type {Replacement} from '../types.js'; -interface Replacement { - from: string; - to: string; - condition?: (filename: string, source: string) => Promise; - factory: (typeof codemods)[keyof typeof codemods]; -} - -const fixableReplacements: Replacement[] = [ - { - from: 'chalk', - to: 'picocolors', - factory: codemods.chalk - } -]; const fixableReplacementsTargets = new Set( fixableReplacements.map((rep) => rep.from) ); diff --git a/src/scripts/generate-fixable-replacements.ts b/src/scripts/generate-fixable-replacements.ts new file mode 100644 index 0000000..4c1cf6a --- /dev/null +++ b/src/scripts/generate-fixable-replacements.ts @@ -0,0 +1,51 @@ +import {writeFile} from 'node:fs/promises'; +import {join, dirname} from 'node:path'; +import {fileURLToPath} from 'node:url'; +import {all} from 'module-replacements'; +import {codemods} from 'module-replacements-codemods'; +import {fixableReplacements} from '../commands/fixable-replacements.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +async function generateFixableReplacements() { + const existingReplacements = new Map( + fixableReplacements.map((r) => [r.from, r]) + ); + + let newCode = `import type { Replacement } from '../types.js';\n`; + newCode += `import { codemods } from 'module-replacements-codemods';\n\n`; + newCode += `export const fixableReplacements: Replacement[] = [\n`; + + let count = 0; + for (const replacement of all.moduleReplacements) { + if (replacement.moduleName in codemods) { + const existing = existingReplacements.get(replacement.moduleName); + const to = existing?.to ?? 'TODO'; + + newCode += ` {\n`; + newCode += ` from: "${replacement.moduleName}",\n`; + newCode += ` to: "${to}",\n`; + newCode += ` factory: codemods["${replacement.moduleName}"]\n`; + newCode += ` },\n`; + count++; + } + } + + newCode += `];\n`; + + const outputPath = join( + __dirname, + '..', + 'commands', + 'fixable-replacements.ts' + ); + await writeFile(outputPath, newCode); + + console.log(`✅ Generated fixable-replacements.ts with ${count} replacements`); + console.log(`📁 Output: ${outputPath}`); +} + +generateFixableReplacements().catch((error) => { + console.error('Failed to generate fixable replacements:', error); + process.exit(1); +}); diff --git a/src/types.ts b/src/types.ts index 5357995..43f86e3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import {codemods} from 'module-replacements-codemods'; + export interface PackFile { name: string; data: string | ArrayBuffer | Uint8Array; @@ -44,3 +46,10 @@ export interface PackageJsonLike { dependencies?: Record; devDependencies?: Record; } + +export interface Replacement { + from: string; + to: string; + condition?: (filename: string, source: string) => Promise; + factory: (typeof codemods)[keyof typeof codemods]; +}