migrate-barrel-imports 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ npx migrate-barrel-imports <source-path> [target-path] [options]
30
30
 
31
31
  ### Arguments
32
32
 
33
- - `source-path`: Path to the package containing barrel files (required)
33
+ - `source-path`: Directory pattern for source packages (e.g. "libs/*", "packages/{ui,core}") (required)
34
34
  - `target-path`: Path to the directory where imports should be migrated (default: current directory)
35
35
 
36
36
  ### Options
@@ -44,11 +44,17 @@ Options can be specified either before or after the arguments:
44
44
  ## Example
45
45
 
46
46
  ```bash
47
- # Options after arguments
47
+ # Migrate a single package
48
48
  migrate-barrel-imports ./packages/my-lib --ignore-source-files "**/__tests__/**,**/__mocks__/**" --ignore-target-files "**/*.test.ts"
49
49
 
50
- # Options before arguments
51
- migrate-barrel-imports --no-extension ./packages/my-lib .
50
+ # Migrate multiple packages using glob pattern
51
+ migrate-barrel-imports "libs/*" --no-extension
52
+
53
+ # Migrate specific packages using glob pattern
54
+ migrate-barrel-imports "packages/{ui,core,utils}" --ignore-target-files "**/*.test.ts"
55
+
56
+ # Migrate all packages in a monorepo
57
+ migrate-barrel-imports "**/src" --no-extension
52
58
 
53
59
  # Mix of options before and after arguments
54
60
  migrate-barrel-imports --no-extension ./packages/my-lib --ignore-target-files "**/*.test.ts"
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{Command as e}from"commander";import{readFileSync as o,writeFileSync as t}from"node:fs";import r,{join as s}from"node:path";import i from"@babel/generator";import{parse as n}from"@babel/parser";import l from"@babel/traverse";import{isVariableDeclaration as a,isIdentifier as c,isExportSpecifier as p,isFunctionDeclaration as d,isImportSpecifier as u,importDeclaration as g,importSpecifier as f,stringLiteral as m}from"@babel/types";import h from"fast-glob";import x from"micromatch";let F=i.default||i,$=l.default||l;async function S(e){return JSON.parse(o(s(e,"package.json"),"utf-8"))}async function y({packagePath:e,ignoreSourceFiles:t=[],stats:r}){let i=[];console.log(`Scanning for TypeScript files in: ${e}`);let l=await h("**/*.{ts,tsx}",{cwd:e,ignore:["**/node_modules/**","**/dist/**","**/build/**"]});for(let u of(console.log(`Found ${l.length} TypeScript files`),l)){let l=t.some(e=>x.isMatch(u,e));l&&(console.log(`File matches ignore pattern but will be preserved: ${u}`),r&&r.sourceFilesSkipped++);let g=s(e,u);console.log(`
3
- Processing file: ${u}`);let f=o(g,"utf-8");try{let e=n(f,{sourceType:"module",plugins:["typescript","jsx","decorators-legacy","classProperties","classPrivateProperties","classPrivateMethods","exportDefaultFrom","exportNamespaceFrom","functionBind","functionSent","dynamicImport","nullishCoalescingOperator","optionalChaining","objectRestSpread","asyncGenerators","doExpressions","importMeta","logicalAssignment","moduleBlocks","moduleStringNames","numericSeparator","partialApplication","privateIn","throwExpressions","topLevelAwait"]});$(e,{ExportNamedDeclaration(e){if(console.log(`Found named export in ${u}`),e.node.source){console.log(`Skipping re-export from ${e.node.source.value}`);return}if(e.node.declaration){if(a(e.node.declaration)){let o=e.node.declaration.declarations.map(e=>c(e.id)?e.id.name:null).filter(e=>null!==e);o.length>0&&(console.log(`Named exports found: ${o.join(", ")}`),i.push({source:u,exports:o,isIgnored:l}))}return}let o=e.node.specifiers.map(e=>{if(p(e)){let o=e.exported;return c(o)?o.name:o.value}return null}).filter(e=>null!==e);o.length>0&&(console.log(`Named exports found: ${o.join(", ")}`),i.push({source:u,exports:o,isIgnored:l}))},ExportDefaultDeclaration(e){console.log(`Found default export in ${u}`);let o=e.node.declaration;c(o)?(console.log(`Default export name: ${o.name}`),i.push({source:u,exports:[o.name],isIgnored:l})):d(o)&&o.id?(console.log(`Default export name: ${o.id.name}`),i.push({source:u,exports:[o.id.name],isIgnored:l})):console.log("Default export is not an identifier or named function")}})}catch(e){console.error(`Error parsing ${u}:`,e)}}return console.log(`
4
- Total exports found: ${i.length}`),i}async function w({packageName:e,monorepoRoot:t}){try{let r=new Set,s=await h(["**/*.{ts,tsx}"],{cwd:t,absolute:!0,ignore:["**/node_modules/**","**/dist/**","**/build/**"],followSymbolicLinks:!1});for(let t of(console.log(`Found ${s.length} TypeScript files to scan`),s))try{let s=o(t,"utf-8"),i=n(s,{sourceType:"module",plugins:["typescript","jsx"]});$(i,{ImportDeclaration(o){let s=o.node.source.value;(s===e||s.startsWith(`${e}/`))&&r.add(t)}})}catch(e){console.error(`Error processing file ${t}:`,e)}let i=Array.from(r);if(i.length>0)for(let o of(console.log(`Found total of ${i.length} files with imports from ${e}`),console.log("Files found:"),i))console.log(` ${o}`);else console.log(`No files found importing from ${e}`);return i}catch(e){return console.error("Error finding imports:",e),[]}}async function E({filePath:e,packageName:r,exports:s,includeExtension:i=!0}){console.log(`
5
- Processing file: ${e}`);let l=o(e,"utf-8");try{let o=n(l,{sourceType:"module",plugins:["typescript","jsx","decorators-legacy","classProperties","classPrivateProperties","classPrivateMethods","exportDefaultFrom","exportNamespaceFrom","functionBind","functionSent","dynamicImport","nullishCoalescingOperator","optionalChaining","objectRestSpread","asyncGenerators","doExpressions","importMeta","logicalAssignment","moduleBlocks","moduleStringNames","numericSeparator","partialApplication","privateIn","throwExpressions","topLevelAwait"]}),a=!1,p=0;if($(o,{ImportDeclaration(e){if(e.node.source.value===r){p++,console.log(`Found import from ${r}`);let o=e.node.specifiers,t=[];for(let e of o)if(u(e)){let o=e.imported,n=c(o)?o.name:o.value,l=s.find(e=>e.exports.includes(n));if(l){if(console.log(` Found export ${n} in ${l.source}`),l.isIgnored){console.log(` Keeping original import for ignored file: ${l.source}`);continue}let o=i?`${r}/${l.source}`:`${r}/${l.source.replace(/\.(js|jsx|ts|tsx|mjs|cjs)$/,"")}`;t.push(g([f(e.local,e.imported)],m(o))),a=!0}else console.log(` Warning: Could not find export ${n}`)}if(t.length>0){let i=o.filter(e=>{if(!u(e))return!0;let o=c(e.imported)?e.imported.name:e.imported.value,t=s.find(e=>e.exports.includes(o));return!t||t.isIgnored});i.length>0&&t.unshift(g(i,m(r))),e.replaceWithMultiple(t),console.log(" Replaced import with direct imports from source files")}}}}),a){console.log(`Writing changes to ${e}`);let r=F(o,{retainLines:!1,retainFunctionParens:!0},l);t(e,r.code)}else p>0?console.log(`No changes needed for ${p} imports`):console.log("No imports found to update")}catch(o){console.error(`Error processing ${e}:`,o)}}async function P(e){let{sourcePath:t,targetPath:s,ignoreSourceFiles:i,ignoreTargetFiles:n}=e,l={totalFiles:0,filesProcessed:0,filesSkipped:0,importsUpdated:0,filesWithNoUpdates:0,errors:0,totalExports:0,sourceFilesScanned:0,sourceFilesWithExports:0,sourceFilesSkipped:0,targetFilesFound:[]},a=await S(t),c=await y({packagePath:t,ignoreSourceFiles:i,stats:l});l.totalExports=c.reduce((e,o)=>e+o.exports.length,0),l.sourceFilesWithExports=new Set(c.map(e=>e.source)).size,l.sourceFilesScanned=(await h("**/*.{ts,tsx}",{cwd:t,ignore:["**/node_modules/**","**/dist/**","**/build/**"]})).length;let p=await w({packageName:a.name,monorepoRoot:s});for(let t of(l.totalFiles=p.length,l.targetFilesFound=p,p)){let i=r.relative(s,t);if(n.some(e=>x.isMatch(i,e))){console.log(`Skipping ignored file: ${t} (matches pattern in ${n.join(", ")})`),l.filesSkipped++;continue}try{let r=o(t,"utf-8");await E({filePath:t,packageName:a.name,exports:c,includeExtension:e.includeExtension});let s=o(t,"utf-8");r!==s?l.importsUpdated++:l.filesWithNoUpdates++,l.filesProcessed++}catch(e){l.errors++,console.error(`Error processing ${t}:`,e)}}console.log("\nMigration Summary"),console.log(`Source files found: ${l.sourceFilesScanned}`),console.log(`Source files with exports: ${l.sourceFilesWithExports}`),console.log(`Source files skipped: ${l.sourceFilesSkipped}`),console.log(`Exports found: ${l.totalExports}`),console.log(`Target files found: ${l.totalFiles}`),console.log(`Target files processed: ${l.filesProcessed}`),console.log(`Target files with imports updated: ${l.importsUpdated}`),console.log(`Target files with no changes needed: ${l.filesWithNoUpdates}`),console.log(`Target files skipped: ${l.filesSkipped}`),l.errors>0&&console.log(`
6
- Warning: ${l.errors} errors encountered during processing`)}let b={targetPath:".",ignoreSourceFiles:[],ignoreTargetFiles:[],includeExtension:!1};async function k(){let o=new e;o.name("migrate-barrel-imports").description("CLI tool to migrate barrel files imports to direct imports").argument("<source-path>","Path to the package containing barrel files").argument("[target-path]","Path to the directory where imports should be migrated (default: current directory)").option("--ignore-source-files <patterns>","Comma-separated list of file patterns to ignore in source directory").option("--ignore-target-files <patterns>","Comma-separated list of file patterns to ignore in target directory").option("--no-extension","Exclude js|jsx|ts|tsx|mjs|cjs file extensions from import statements").allowUnknownOption(!1).parse(process.argv);let t=o.args;t[0]||(console.error("Error: source-path is required"),process.exit(1));let r=t[0],s=t[1]||b.targetPath,i=o.opts();await P({sourcePath:r,targetPath:s,ignoreSourceFiles:i.ignoreSourceFiles?i.ignoreSourceFiles.split(","):b.ignoreSourceFiles,ignoreTargetFiles:i.ignoreTargetFiles?i.ignoreTargetFiles.split(","):b.ignoreTargetFiles,includeExtension:!1!==i.extension||b.includeExtension})}(async()=>{await k()})();
2
+ import{Command as e}from"commander";import o,{readFileSync as r,writeFileSync as t}from"node:fs";import s from"node:path";import i from"@babel/generator";import{parse as n}from"@babel/parser";import l from"@babel/traverse";import{isVariableDeclaration as a,isIdentifier as c,isExportSpecifier as p,isFunctionDeclaration as d,isImportSpecifier as g,importDeclaration as u,importSpecifier as f,stringLiteral as m}from"@babel/types";import h from"fast-glob";import x from"micromatch";let $=i.default||i,S=l.default||l;async function F(e){return JSON.parse(r(s.join(e,"package.json"),"utf-8"))}async function k({packagePath:e,ignoreSourceFiles:o=[],stats:t}){let i=[];console.log(`Scanning for TypeScript and JavaScript files in: ${e}`);let l=await h("**/*.{ts,tsx,js,jsx}",{cwd:e,ignore:["**/node_modules/**","**/dist/**","**/build/**"]});for(let g of(console.log(`Found ${l.length} files`),l)){let l=o.some(e=>x.isMatch(g,e));l&&(console.log(`File matches ignore pattern but will be preserved: ${g}`),t&&t.sourceFilesSkipped++);let u=s.join(e,g);console.log(`
3
+ Processing file: ${g}`);let f=r(u,"utf-8");try{let e=n(f,{sourceType:"module",plugins:["typescript","jsx","decorators-legacy","classProperties","classPrivateProperties","classPrivateMethods","exportDefaultFrom","exportNamespaceFrom","functionBind","functionSent","dynamicImport","nullishCoalescingOperator","optionalChaining","objectRestSpread","asyncGenerators","doExpressions","importMeta","logicalAssignment","moduleBlocks","moduleStringNames","numericSeparator","partialApplication","privateIn","throwExpressions","topLevelAwait"]});S(e,{ExportNamedDeclaration(e){if(console.log(`Found named export in ${g}`),e.node.source){console.log(`Skipping re-export from ${e.node.source.value}`);return}if(e.node.declaration){if(a(e.node.declaration)){let o=e.node.declaration.declarations.map(e=>c(e.id)?e.id.name:null).filter(e=>null!==e);o.length>0&&(console.log(`Named exports found: ${o.join(", ")}`),i.push({source:g,exports:o,isIgnored:l}))}return}let o=e.node.specifiers.map(e=>{if(p(e)){let o=e.exported;return c(o)?o.name:o.value}return null}).filter(e=>null!==e);o.length>0&&(console.log(`Named exports found: ${o.join(", ")}`),i.push({source:g,exports:o,isIgnored:l}))},ExportDefaultDeclaration(e){console.log(`Found default export in ${g}`);let o=e.node.declaration;c(o)?(console.log(`Default export name: ${o.name}`),i.push({source:g,exports:[o.name],isIgnored:l})):d(o)&&o.id?(console.log(`Default export name: ${o.id.name}`),i.push({source:g,exports:[o.id.name],isIgnored:l})):console.log("Default export is not an identifier or named function")}})}catch(e){console.error(`Error parsing ${g}:`,e)}}return console.log(`
4
+ Total exports found: ${i.length}`),i}async function y({packageName:e,monorepoRoot:o}){try{let t=new Set,s=await h(["**/*.{ts,tsx,js,jsx}"],{cwd:o,absolute:!0,ignore:["**/node_modules/**","**/dist/**","**/build/**"],followSymbolicLinks:!1});for(let o of(console.log(`Found ${s.length} files to scan`),s))try{let s=r(o,"utf-8"),i=n(s,{sourceType:"module",plugins:["typescript","jsx"]});S(i,{ImportDeclaration(r){let s=r.node.source.value;(s===e||s.startsWith(`${e}/`))&&t.add(o)}})}catch(e){console.error(`Error processing file ${o}:`,e)}let i=Array.from(t);if(i.length>0)for(let o of(console.log(`Found total of ${i.length} files with imports from ${e}`),console.log("Files found:"),i))console.log(` ${o}`);else console.log(`No files found importing from ${e}`);return i}catch(e){return console.error("Error finding imports:",e),[]}}async function w({filePath:e,packageName:o,exports:s,includeExtension:i=!0}){console.log(`
5
+ Processing file: ${e}`);let l=r(e,"utf-8");try{let r=n(l,{sourceType:"module",plugins:["typescript","jsx","decorators-legacy","classProperties","classPrivateProperties","classPrivateMethods","exportDefaultFrom","exportNamespaceFrom","functionBind","functionSent","dynamicImport","nullishCoalescingOperator","optionalChaining","objectRestSpread","asyncGenerators","doExpressions","importMeta","logicalAssignment","moduleBlocks","moduleStringNames","numericSeparator","partialApplication","privateIn","throwExpressions","topLevelAwait"]}),a=!1,p=0;if(S(r,{ImportDeclaration(e){if(e.node.source.value===o){p++,console.log(`Found import from ${o}`);let r=e.node.specifiers,t=[];for(let e of r)if(g(e)){let r=e.imported,n=c(r)?r.name:r.value,l=s.find(e=>e.exports.includes(n));if(l){if(console.log(` Found export ${n} in ${l.source}`),l.isIgnored){console.log(` Keeping original import for ignored file: ${l.source}`);continue}let r=i?`${o}/${l.source}`:`${o}/${l.source.replace(/\.(js|jsx|ts|tsx|mjs|cjs)$/,"")}`;t.push(u([f(e.local,e.imported)],m(r))),a=!0}else console.log(` Warning: Could not find export ${n}`)}if(t.length>0){let i=r.filter(e=>{if(!g(e))return!0;let o=c(e.imported)?e.imported.name:e.imported.value,r=s.find(e=>e.exports.includes(o));return!r||r.isIgnored});i.length>0&&t.unshift(u(i,m(o))),e.replaceWithMultiple(t),console.log(" Replaced import with direct imports from source files")}}}}),a){console.log(`Writing changes to ${e}`);let o=$(r,{retainLines:!1,retainFunctionParens:!0},l);t(e,o.code)}else p>0?console.log(`No changes needed for ${p} imports`):console.log("No imports found to update")}catch(o){console.error(`Error processing ${e}:`,o)}}async function b(e){let{sourcePath:t,targetPath:i,ignoreSourceFiles:n,ignoreTargetFiles:l}=e,a={totalFiles:0,filesProcessed:0,filesSkipped:0,importsUpdated:0,filesWithNoUpdates:0,errors:0,totalExports:0,sourceFilesScanned:0,sourceFilesWithExports:0,sourceFilesSkipped:0,targetFilesFound:[],packagesProcessed:0,packagesSkipped:0},c=await h(t,{cwd:i,absolute:!0,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/target-app/**"],followSymbolicLinks:!1,onlyDirectories:!0}),p=[];for(let e of c){let r=s.join(e,"package.json");try{await o.promises.access(r),p.push(r)}catch{console.log(`No package.json found in ${e}, skipping`)}}for(let o of(console.log(`Found ${p.length} source packages to process`),p))try{let t=s.dirname(o);console.log(`
6
+ Processing package: ${t}`);let c=await F(t),p=await k({packagePath:t,ignoreSourceFiles:n,stats:a});a.totalExports+=p.reduce((e,o)=>e+o.exports.length,0),a.sourceFilesWithExports+=new Set(p.map(e=>e.source)).size,a.sourceFilesScanned+=(await h("**/*.{ts,tsx}",{cwd:t,ignore:["**/node_modules/**","**/dist/**","**/build/**"]})).length;let d=await y({packageName:c.name,monorepoRoot:i});for(let o of(a.totalFiles+=d.length,a.targetFilesFound.push(...d),d)){let t=s.relative(i,o);if(l.some(e=>x.isMatch(t,e))){console.log(`Skipping ignored file: ${o} (matches pattern in ${l.join(", ")})`),a.filesSkipped++;continue}try{let t=r(o,"utf-8");await w({filePath:o,packageName:c.name,exports:p,includeExtension:e.includeExtension});let s=r(o,"utf-8");t!==s?a.importsUpdated++:a.filesWithNoUpdates++,a.filesProcessed++}catch(e){a.errors++,console.error(`Error processing ${o}:`,e)}}a.packagesProcessed++}catch(e){a.packagesSkipped++,console.error(`Error processing package ${o}:`,e)}console.log("\nMigration Summary"),console.log(`Source packages found: ${p.length}`),console.log(`Source packages processed: ${a.packagesProcessed}`),console.log(`Source packages skipped: ${a.packagesSkipped}`),console.log(`Source files found: ${a.sourceFilesScanned}`),console.log(`Source files with exports: ${a.sourceFilesWithExports}`),console.log(`Source files skipped: ${a.sourceFilesSkipped}`),console.log(`Exports found: ${a.totalExports}`),console.log(`Target files found: ${a.totalFiles}`),console.log(`Target files processed: ${a.filesProcessed}`),console.log(`Target files with imports updated: ${a.importsUpdated}`),console.log(`Target files with no changes needed: ${a.filesWithNoUpdates}`),console.log(`Target files skipped: ${a.filesSkipped}`),a.errors>0&&console.log(`
7
+ Warning: ${a.errors} errors encountered during processing`)}let P={targetPath:".",ignoreSourceFiles:[],ignoreTargetFiles:[],includeExtension:!1};async function E(){let o=new e;o.name("migrate-barrel-imports").description("CLI tool to migrate barrel files imports to direct imports").argument("<source-path>",'Glob pattern for source packages containing barrel files (e.g. "libs/*")').argument("[target-path]","Path to the directory where imports should be migrated (default: current directory)").option("--ignore-source-files <patterns>","Comma-separated list of file patterns to ignore in source directory").option("--ignore-target-files <patterns>","Comma-separated list of file patterns to ignore in target directory").option("--no-extension","Exclude js|jsx|ts|tsx|mjs|cjs file extensions from import statements").allowUnknownOption(!1).parse(process.argv);let r=o.args;r[0]||(console.error("Error: source-path is required"),process.exit(1));let t=r[0],s=r[1]||P.targetPath,i=o.opts();await b({sourcePath:t,targetPath:s,ignoreSourceFiles:i.ignoreSourceFiles?i.ignoreSourceFiles.split(","):P.ignoreSourceFiles,ignoreTargetFiles:i.ignoreTargetFiles?i.ignoreTargetFiles.split(","):P.ignoreTargetFiles,includeExtension:!1!==i.extension||P.includeExtension})}(async()=>{await E()})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "migrate-barrel-imports",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "A CLI tool to migrate barrel imports in JavaScript/TypeScript monorepos",
5
5
  "files": [
6
6
  "dist/"