From b4077bd1f5cc260099adc416d905ee3cbac9c8ca Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 10 Jun 2026 10:29:56 +0100 Subject: [PATCH] (chore): add utility for refactoring --- utilsdeno/deno.json | 8 +++ utilsdeno/deno.lock | 68 ++++++++++++++++++ utilsdeno/refactor-imports.ts | 131 ++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 utilsdeno/deno.json create mode 100644 utilsdeno/deno.lock create mode 100644 utilsdeno/refactor-imports.ts diff --git a/utilsdeno/deno.json b/utilsdeno/deno.json new file mode 100644 index 0000000..4fa74eb --- /dev/null +++ b/utilsdeno/deno.json @@ -0,0 +1,8 @@ +{ + "tasks": { + "dev": "deno run --watch --allow-net main.ts" + }, + "imports": { + "@std/assert": "jsr:@std/assert@1" + } +} diff --git a/utilsdeno/deno.lock b/utilsdeno/deno.lock new file mode 100644 index 0000000..930a6fb --- /dev/null +++ b/utilsdeno/deno.lock @@ -0,0 +1,68 @@ +{ + "version": "5", + "specifiers": { + "npm:ts-morph@*": "28.0.0" + }, + "npm": { + "@ts-morph/common@0.29.0": { + "integrity": "sha512-35oUmphHbJvQ/+UTwFNme/t2p3FoKiGJ5auTjjpNTop2dyREspirjMy82PLSC1pnDJ8ah1GU98hwpVt64YXQsg==", + "dependencies": [ + "minimatch", + "path-browserify", + "tinyglobby" + ] + }, + "balanced-match@4.0.4": { + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==" + }, + "brace-expansion@5.0.6": { + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dependencies": [ + "balanced-match" + ] + }, + "code-block-writer@13.0.3": { + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==" + }, + "fdir@6.5.0_picomatch@4.0.4": { + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dependencies": [ + "picomatch" + ], + "optionalPeers": [ + "picomatch" + ] + }, + "minimatch@10.2.5": { + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dependencies": [ + "brace-expansion" + ] + }, + "path-browserify@1.0.1": { + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "picomatch@4.0.4": { + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==" + }, + "tinyglobby@0.2.16": { + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dependencies": [ + "fdir", + "picomatch" + ] + }, + "ts-morph@28.0.0": { + "integrity": "sha512-Wp3tnZ2bzwxyTZMtgWVzXDfm7lB1Drz+y9DmmYH/L702PQhPyVrp3pkou3yIz4qjS14GY9kcpmLiOOMvl8oG1g==", + "dependencies": [ + "@ts-morph/common", + "code-block-writer" + ] + } + }, + "workspace": { + "dependencies": [ + "jsr:@std/assert@1" + ] + } +} diff --git a/utilsdeno/refactor-imports.ts b/utilsdeno/refactor-imports.ts new file mode 100644 index 0000000..1e61115 --- /dev/null +++ b/utilsdeno/refactor-imports.ts @@ -0,0 +1,131 @@ +// Delete references to types.ts and replace them with new imports based on the importMap. It will also split imports if some are type-only and some are value imports. +// Use this script by running `deno run --allow-read --allow-write --allow-run refactor-imports.ts` from the utilsdeno directory. It will read all source files, find imports from types.ts, and replace them with the new paths based on the importMap. Make sure to review the changes before saving, as it will modify your source files. +import { Project } from "npm:ts-morph"; + +const project = new Project({ tsConfigFilePath: "../tsconfig.json" }); + +const importMap = new Map(); +// Build a map of types moved out of Models. +// Under src/lib/src/common/models. +for (const sourceFile of project.getSourceFiles()) { + if (sourceFile.getFilePath().includes("src/lib/src/common/models")) { + const exports = sourceFile.getExportedDeclarations(); + for (const [name, declarations] of exports) { + for (const declaration of declarations) { + if ( + // declaration.getKindName() === "TypeAliasDeclaration" || + // declaration.getKindName() === "InterfaceDeclaration" || + // declaration.getKindName() === "EnumDeclaration" || + true + ) { + console.log(`Found type export in ${sourceFile.getFilePath()}:`, name); + const relativePath = sourceFile.getFilePath().split("src/lib/src/")[1].replace(/\.ts$/, ""); + importMap.set(name, `@lib/${relativePath}`); + } + } + } + } +} +// Extras + +importMap.set("LOG_LEVEL_NOTICE", "@lib/common/logger"); +importMap.set("LOG_LEVEL_VERBOSE", "@lib/common/logger"); +importMap.set("LOG_LEVEL_INFO", "@lib/common/logger"); +importMap.set("LOG_LEVEL_DEBUG", "@lib/common/logger"); +importMap.set("LOG_LEVEL_URGENT", "@lib/common/logger"); +importMap.set("LOG_LEVEL", "@lib/common/logger"); +importMap.set("Logger", "@lib/common/logger"); + +console.log("Import map:", importMap); + +// Loop through all files that import from types.ts. +for (const sourceFile of project.getSourceFiles()) { + const imports = sourceFile.getImportDeclarations(); + + for (const imp of imports) { + if (imp.getModuleSpecifierValue().includes("types.ts") && imp.getModuleSpecifierValue().startsWith("@lib/common/")) { + // Collect imports from types.ts. + const namedImports = imp.getNamedImports(); + const defaultImport = imp.getDefaultImport(); + console.log(`Found import in ${sourceFile.getFilePath()}:`, { + namedImports: namedImports.map((ni) => ni.getText()), + defaultImport: defaultImport ? defaultImport.getText() : null, + }); + // Group imports by their names and generate new import paths based on the importMap + const importsToReplace: Record = {}; + for (const namedImport of namedImports) { + const name = namedImport.getName(); + const newPath = importMap.get(name); + if (newPath) { + console.log( + `Will replace import of ${name} in ${sourceFile.getFilePath()} with new path:`, + newPath + ); + if (!importsToReplace[newPath]) { + importsToReplace[newPath] = []; + } + importsToReplace[newPath].push({ name, newPath, isTypeOnly: namedImport.isTypeOnly() || imp.isTypeOnly() }); + } + } + + // For each import, generate a new path from importMap and replace it. + // Split the import when it needs to become multiple imports. + + for (const newPath in importsToReplace) { + // First, handle type-only imports. + const isTypeOnly = importsToReplace[newPath].filter((i) => i.isTypeOnly); + if (isTypeOnly.length > 0) { + sourceFile.insertImportDeclaration(imp.getChildIndex(), { + namedImports: isTypeOnly.map((i) => i.name), + moduleSpecifier: newPath, + isTypeOnly: true, + }); + } + // Then, handle non-type-only imports. + const isValueImport = importsToReplace[newPath].filter((i) => !i.isTypeOnly); + if (isValueImport.length > 0) { + + sourceFile.insertImportDeclaration(imp.getChildIndex(), { + namedImports: isValueImport.map((i) => i.name), + moduleSpecifier: newPath, + isTypeOnly: false, + }); + } + // Remove the replaced named imports from the old import. + for (const { name } of importsToReplace[newPath]) { + const namedImport = imp.getNamedImports().find((ni) => ni.getName() === name); + if (namedImport) { + namedImport.remove(); + } + } + } + // If there is also a default import and it exists in importMap, replace it too. + if (defaultImport) { + const name = defaultImport.getText(); + const newPath = importMap.get(name); + + if (newPath) { + console.log( + `Replacing default import of ${name} in ${sourceFile.getFilePath()} with new path:`, + newPath + ); + // Add the new import statement. + sourceFile.insertImportDeclaration(imp.getChildIndex(), { + defaultImport: name, + moduleSpecifier: newPath, + isTypeOnly: imp.isTypeOnly(), + }); + // Remove the default import from the old import. + imp.removeDefaultImport(); + } + } + if (imp.getNamedImports().length === 0 && !imp.getDefaultImport()) { + // Delete the entire import statement if nothing remains. + imp.remove(); + } + } + } +} + +// Save everything at the end. +project.saveSync();