migrate-barrel-imports 1.0.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/index.js +5 -469
  2. package/package.json +86 -81
package/dist/index.js CHANGED
@@ -1,470 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import {Command as $cUHbR$Command} from "commander";
3
- import {readFileSync as $cUHbR$readFileSync, writeFileSync as $cUHbR$writeFileSync} from "node:fs";
4
- import $cUHbR$nodepath, {join as $cUHbR$join} from "node:path";
5
- import $cUHbR$babelgenerator from "@babel/generator";
6
- import {parse as $cUHbR$parse} from "@babel/parser";
7
- import $cUHbR$babeltraverse from "@babel/traverse";
8
- import {isVariableDeclaration as $cUHbR$isVariableDeclaration, isIdentifier as $cUHbR$isIdentifier, isExportSpecifier as $cUHbR$isExportSpecifier, isFunctionDeclaration as $cUHbR$isFunctionDeclaration, isImportSpecifier as $cUHbR$isImportSpecifier, importDeclaration as $cUHbR$importDeclaration, importSpecifier as $cUHbR$importSpecifier, stringLiteral as $cUHbR$stringLiteral} from "@babel/types";
9
- import $cUHbR$fastglob from "fast-glob";
10
- import $cUHbR$micromatch from "micromatch";
11
-
12
-
13
- /**
14
- * @fileoverview Tool for migrating TypeScript projects from barrel file exports to direct file imports
15
- *
16
- * This tool helps migrate TypeScript projects that use barrel files (index.ts files that re-export)
17
- * to use direct imports from source files instead. This improves:
18
- * - Tree-shaking efficiency
19
- * - Build performance
20
- * - Code maintainability
21
- * - TypeScript compilation speed
22
- *
23
- * The migration process:
24
- * 1. Scans source package for all exports
25
- * 2. Finds all files importing from the package
26
- * 3. Updates imports to point directly to source files
27
- * 4. Preserves original import names and types
28
- */
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
- // @ts-expect-error
37
- const $1d95f64f52ade133$var$generate = (0, $cUHbR$babelgenerator).default || (0, $cUHbR$babelgenerator);
38
- // @ts-expect-error
39
- const $1d95f64f52ade133$var$traverse = (0, $cUHbR$babeltraverse).default || (0, $cUHbR$babeltraverse);
40
- /**
41
- * Reads and parses the package.json file for a given package path
42
- *
43
- * @param {string} packagePath - The path to the package directory
44
- * @returns {Promise<PackageJson>} The parsed package.json contents
45
- * @throws {Error} If package.json cannot be read or parsed
46
- */ async function $1d95f64f52ade133$var$getPackageInfo(packagePath) {
47
- const packageJsonPath = (0, $cUHbR$join)(packagePath, 'package.json');
48
- const content = (0, $cUHbR$readFileSync)(packageJsonPath, 'utf-8');
49
- return JSON.parse(content);
50
- }
51
- /**
52
- * Recursively finds all exports in a package by scanning all TypeScript files
53
- *
54
- * This function:
55
- * 1. Scans all .ts and .tsx files in the package
56
- * 2. Identifies both named exports and default exports
57
- * 3. Skips re-exports to avoid circular dependencies
58
- * 4. Filters out ignored files based on patterns
59
- *
60
- * @param {FindExportsParams} params - Parameters for finding exports
61
- * @returns {Promise<ExportInfo[]>} Array of export information, including source file and exported names
62
- */ async function $1d95f64f52ade133$var$findExports({ packagePath: packagePath, ignoreSourceFiles: ignoreSourceFiles = [], stats: stats }) {
63
- const exports = [];
64
- console.log(`Scanning for TypeScript files in: ${packagePath}`);
65
- const allFiles = await (0, $cUHbR$fastglob)('**/*.{ts,tsx}', {
66
- cwd: packagePath,
67
- ignore: [
68
- '**/node_modules/**',
69
- '**/dist/**',
70
- '**/build/**'
71
- ]
72
- });
73
- console.log(`Found ${allFiles.length} TypeScript files`);
74
- for (const file of allFiles){
75
- // Mark files that match ignore patterns but still process them
76
- const isIgnored = ignoreSourceFiles.some((pattern)=>(0, $cUHbR$micromatch).isMatch(file, pattern));
77
- if (isIgnored) {
78
- console.log(`File matches ignore pattern but will be preserved: ${file}`);
79
- if (stats) stats.sourceFilesSkipped++;
80
- }
81
- const fullPath = (0, $cUHbR$join)(packagePath, file);
82
- console.log(`\nProcessing file: ${file}`);
83
- const content = (0, $cUHbR$readFileSync)(fullPath, 'utf-8');
84
- try {
85
- const ast = (0, $cUHbR$parse)(content, {
86
- sourceType: 'module',
87
- plugins: [
88
- 'typescript',
89
- 'jsx',
90
- 'decorators-legacy',
91
- 'classProperties',
92
- 'classPrivateProperties',
93
- 'classPrivateMethods',
94
- 'exportDefaultFrom',
95
- 'exportNamespaceFrom',
96
- 'functionBind',
97
- 'functionSent',
98
- 'dynamicImport',
99
- 'nullishCoalescingOperator',
100
- 'optionalChaining',
101
- 'objectRestSpread',
102
- 'asyncGenerators',
103
- 'doExpressions',
104
- 'importMeta',
105
- 'logicalAssignment',
106
- 'moduleBlocks',
107
- 'moduleStringNames',
108
- 'numericSeparator',
109
- 'partialApplication',
110
- 'privateIn',
111
- 'throwExpressions',
112
- 'topLevelAwait'
113
- ]
114
- });
115
- $1d95f64f52ade133$var$traverse(ast, {
116
- ExportNamedDeclaration (path) {
117
- console.log(`Found named export in ${file}`);
118
- // Skip exports from other files (we only want direct exports)
119
- if (path.node.source) {
120
- console.log(`Skipping re-export from ${path.node.source.value}`);
121
- return;
122
- }
123
- // Handle variable declarations with exports
124
- if (path.node.declaration) {
125
- if ((0, $cUHbR$isVariableDeclaration)(path.node.declaration)) {
126
- const declarations = path.node.declaration.declarations;
127
- const namedExports = declarations.map((d)=>{
128
- if ((0, $cUHbR$isIdentifier)(d.id)) return d.id.name;
129
- return null;
130
- }).filter((name)=>name !== null);
131
- if (namedExports.length > 0) {
132
- console.log(`Named exports found: ${namedExports.join(', ')}`);
133
- exports.push({
134
- source: file,
135
- exports: namedExports,
136
- isIgnored: isIgnored
137
- });
138
- }
139
- }
140
- return;
141
- }
142
- // Handle export specifiers
143
- const namedExports = path.node.specifiers.map((s)=>{
144
- if ((0, $cUHbR$isExportSpecifier)(s)) {
145
- const exported = s.exported;
146
- return (0, $cUHbR$isIdentifier)(exported) ? exported.name : exported.value;
147
- }
148
- return null;
149
- }).filter((name)=>name !== null);
150
- if (namedExports.length > 0) {
151
- console.log(`Named exports found: ${namedExports.join(', ')}`);
152
- exports.push({
153
- source: file,
154
- exports: namedExports,
155
- isIgnored: isIgnored
156
- });
157
- }
158
- },
159
- ExportDefaultDeclaration (path) {
160
- console.log(`Found default export in ${file}`);
161
- const exported = path.node.declaration;
162
- if ((0, $cUHbR$isIdentifier)(exported)) {
163
- console.log(`Default export name: ${exported.name}`);
164
- exports.push({
165
- source: file,
166
- exports: [
167
- exported.name
168
- ],
169
- isIgnored: isIgnored
170
- });
171
- } else if ((0, $cUHbR$isFunctionDeclaration)(exported) && exported.id) {
172
- console.log(`Default export name: ${exported.id.name}`);
173
- exports.push({
174
- source: file,
175
- exports: [
176
- exported.id.name
177
- ],
178
- isIgnored: isIgnored
179
- });
180
- } else console.log('Default export is not an identifier or named function');
181
- }
182
- });
183
- } catch (error) {
184
- console.error(`Error parsing ${file}:`, error);
185
- }
186
- }
187
- console.log(`\nTotal exports found: ${exports.length}`);
188
- return exports;
189
- }
190
- /**
191
- * Finds all files in the monorepo that import from a specific package
192
- *
193
- * This function:
194
- * 1. Uses fast-glob to find all TypeScript files
195
- * 2. Parses each file's AST to find imports
196
- * 3. Handles both direct package imports and subpath imports
197
- * 4. Excludes node_modules, dist, and build directories
198
- *
199
- * @param {FindImportsParams} params - Parameters for finding imports
200
- * @returns {Promise<string[]>} Array of file paths that import from the package
201
- */ async function $1d95f64f52ade133$var$findImports({ packageName: packageName, monorepoRoot: monorepoRoot }) {
202
- try {
203
- const allFiles = new Set();
204
- // Find all TypeScript files in the monorepo
205
- const files = await (0, $cUHbR$fastglob)([
206
- '**/*.{ts,tsx}'
207
- ], {
208
- cwd: monorepoRoot,
209
- absolute: true,
210
- ignore: [
211
- '**/node_modules/**',
212
- '**/dist/**',
213
- '**/build/**'
214
- ],
215
- followSymbolicLinks: false
216
- });
217
- console.log(`Found ${files.length} TypeScript files to scan`);
218
- // Scan each file for imports
219
- for (const file of files)try {
220
- const content = (0, $cUHbR$readFileSync)(file, 'utf-8');
221
- const ast = (0, $cUHbR$parse)(content, {
222
- sourceType: 'module',
223
- plugins: [
224
- 'typescript',
225
- 'jsx'
226
- ]
227
- });
228
- $1d95f64f52ade133$var$traverse(ast, {
229
- ImportDeclaration (path) {
230
- const source = path.node.source.value;
231
- // Check for exact package import or subpath import
232
- if (source === packageName || source.startsWith(`${packageName}/`)) allFiles.add(file);
233
- }
234
- });
235
- } catch (error) {
236
- console.error(`Error processing file ${file}:`, error);
237
- }
238
- const uniqueFiles = Array.from(allFiles);
239
- if (uniqueFiles.length > 0) {
240
- console.log(`Found total of ${uniqueFiles.length} files with imports from ${packageName}`);
241
- console.log('Files found:');
242
- for (const file of uniqueFiles)console.log(` ${file}`);
243
- } else console.log(`No files found importing from ${packageName}`);
244
- return uniqueFiles;
245
- } catch (error) {
246
- console.error('Error finding imports:', error);
247
- return [];
248
- }
249
- }
250
- /**
251
- * Updates imports in a file to point directly to source files instead of using barrel files
252
- *
253
- * This function:
254
- * 1. Parses the file's AST to find imports from the package
255
- * 2. For each import, finds the source file containing the export
256
- * 3. Updates the import to point directly to the source file
257
- * 4. Preserves original import names and types
258
- * 5. Only modifies the file if changes are needed
259
- *
260
- * @param {UpdateImportsParams} params - Parameters for updating imports
261
- * @returns {Promise<void>}
262
- */ async function $1d95f64f52ade133$var$updateImports({ filePath: filePath, packageName: packageName, exports: exports, includeExtension: includeExtension = true }) {
263
- console.log(`\nProcessing file: ${filePath}`);
264
- const content = (0, $cUHbR$readFileSync)(filePath, 'utf-8');
265
- try {
266
- const ast = (0, $cUHbR$parse)(content, {
267
- sourceType: 'module',
268
- plugins: [
269
- 'typescript',
270
- 'jsx',
271
- 'decorators-legacy',
272
- 'classProperties',
273
- 'classPrivateProperties',
274
- 'classPrivateMethods',
275
- 'exportDefaultFrom',
276
- 'exportNamespaceFrom',
277
- 'functionBind',
278
- 'functionSent',
279
- 'dynamicImport',
280
- 'nullishCoalescingOperator',
281
- 'optionalChaining',
282
- 'objectRestSpread',
283
- 'asyncGenerators',
284
- 'doExpressions',
285
- 'importMeta',
286
- 'logicalAssignment',
287
- 'moduleBlocks',
288
- 'moduleStringNames',
289
- 'numericSeparator',
290
- 'partialApplication',
291
- 'privateIn',
292
- 'throwExpressions',
293
- 'topLevelAwait'
294
- ]
295
- });
296
- let modified = false;
297
- let importCount = 0;
298
- $1d95f64f52ade133$var$traverse(ast, {
299
- ImportDeclaration (path) {
300
- if (path.node.source.value === packageName) {
301
- importCount++;
302
- console.log(`Found import from ${packageName}`);
303
- const specifiers = path.node.specifiers;
304
- const newImports = [];
305
- for (const specifier of specifiers)if ((0, $cUHbR$isImportSpecifier)(specifier)) {
306
- const imported = specifier.imported;
307
- const importName = (0, $cUHbR$isIdentifier)(imported) ? imported.name : imported.value;
308
- const exportInfo = exports.find((e)=>e.exports.includes(importName));
309
- if (exportInfo) {
310
- console.log(` Found export ${importName} in ${exportInfo.source}`);
311
- // Skip modifying imports from ignored files
312
- if (exportInfo.isIgnored) {
313
- console.log(` Keeping original import for ignored file: ${exportInfo.source}`);
314
- continue;
315
- }
316
- const importPath = includeExtension ? `${packageName}/${exportInfo.source}` : `${packageName}/${exportInfo.source.replace(/\.(js|jsx|ts|tsx|mjs|cjs)$/, '')}`;
317
- newImports.push((0, $cUHbR$importDeclaration)([
318
- (0, $cUHbR$importSpecifier)(specifier.local, specifier.imported)
319
- ], (0, $cUHbR$stringLiteral)(importPath)));
320
- modified = true;
321
- } else console.log(` Warning: Could not find export ${importName}`);
322
- }
323
- if (newImports.length > 0) {
324
- // If there are any remaining specifiers that weren't moved to direct imports,
325
- // create a new import declaration for them
326
- const remainingSpecifiers = specifiers.filter((s)=>{
327
- if (!(0, $cUHbR$isImportSpecifier)(s)) return true;
328
- const importName = (0, $cUHbR$isIdentifier)(s.imported) ? s.imported.name : s.imported.value;
329
- const exportInfo = exports.find((e)=>e.exports.includes(importName));
330
- return !exportInfo || exportInfo.isIgnored;
331
- });
332
- if (remainingSpecifiers.length > 0) newImports.unshift((0, $cUHbR$importDeclaration)(remainingSpecifiers, (0, $cUHbR$stringLiteral)(packageName)));
333
- path.replaceWithMultiple(newImports);
334
- console.log(' Replaced import with direct imports from source files');
335
- }
336
- }
337
- }
338
- });
339
- if (modified) {
340
- console.log(`Writing changes to ${filePath}`);
341
- const output = $1d95f64f52ade133$var$generate(ast, {
342
- retainLines: false,
343
- retainFunctionParens: true
344
- }, content);
345
- (0, $cUHbR$writeFileSync)(filePath, output.code);
346
- } else if (importCount > 0) console.log(`No changes needed for ${importCount} imports`);
347
- else console.log('No imports found to update');
348
- } catch (error) {
349
- console.error(`Error processing ${filePath}:`, error);
350
- }
351
- }
352
- async function $1d95f64f52ade133$export$eb4d0c836e660010(options) {
353
- const { sourcePath: sourcePath, targetPath: targetPath, ignoreSourceFiles: ignoreSourceFiles, ignoreTargetFiles: ignoreTargetFiles } = options;
354
- // Track migration statistics
355
- const stats = {
356
- totalFiles: 0,
357
- filesProcessed: 0,
358
- filesSkipped: 0,
359
- importsUpdated: 0,
360
- filesWithNoUpdates: 0,
361
- errors: 0,
362
- totalExports: 0,
363
- sourceFilesScanned: 0,
364
- sourceFilesWithExports: 0,
365
- sourceFilesSkipped: 0,
366
- targetFilesFound: []
367
- };
368
- const packageInfo = await $1d95f64f52ade133$var$getPackageInfo(sourcePath);
369
- const exports = await $1d95f64f52ade133$var$findExports({
370
- packagePath: sourcePath,
371
- ignoreSourceFiles: ignoreSourceFiles,
372
- stats: stats
373
- });
374
- // Calculate total number of unique exports and source files
375
- stats.totalExports = exports.reduce((total, exp)=>total + exp.exports.length, 0);
376
- stats.sourceFilesWithExports = new Set(exports.map((exp)=>exp.source)).size;
377
- stats.sourceFilesScanned = (await (0, $cUHbR$fastglob)('**/*.{ts,tsx}', {
378
- cwd: sourcePath,
379
- ignore: [
380
- '**/node_modules/**',
381
- '**/dist/**',
382
- '**/build/**'
383
- ]
384
- })).length;
385
- const files = await $1d95f64f52ade133$var$findImports({
386
- packageName: packageInfo.name,
387
- monorepoRoot: targetPath
388
- });
389
- stats.totalFiles = files.length;
390
- stats.targetFilesFound = files;
391
- for (const file of files){
392
- const relativeFile = (0, $cUHbR$nodepath).relative(targetPath, file);
393
- if (ignoreTargetFiles.some((pattern)=>(0, $cUHbR$micromatch).isMatch(relativeFile, pattern))) {
394
- console.log(`Skipping ignored file: ${file} (matches pattern in ${ignoreTargetFiles.join(', ')})`);
395
- stats.filesSkipped++;
396
- continue;
397
- }
398
- try {
399
- const originalContent = (0, $cUHbR$readFileSync)(file, 'utf-8');
400
- await $1d95f64f52ade133$var$updateImports({
401
- filePath: file,
402
- packageName: packageInfo.name,
403
- exports: exports,
404
- includeExtension: options.includeExtension
405
- });
406
- const updatedContent = (0, $cUHbR$readFileSync)(file, 'utf-8');
407
- if (originalContent !== updatedContent) stats.importsUpdated++;
408
- else stats.filesWithNoUpdates++;
409
- stats.filesProcessed++;
410
- } catch (error) {
411
- stats.errors++;
412
- console.error(`Error processing ${file}:`, error);
413
- }
414
- }
415
- // Print migration summary
416
- console.log('\nMigration Summary');
417
- console.log(`Source files found: ${stats.sourceFilesScanned}`);
418
- console.log(`Source files with exports: ${stats.sourceFilesWithExports}`);
419
- console.log(`Source files skipped: ${stats.sourceFilesSkipped}`);
420
- console.log(`Exports found: ${stats.totalExports}`);
421
- console.log(`Target files found: ${stats.totalFiles}`);
422
- console.log(`Target files processed: ${stats.filesProcessed}`);
423
- console.log(`Target files with imports updated: ${stats.importsUpdated}`);
424
- console.log(`Target files with no changes needed: ${stats.filesWithNoUpdates}`);
425
- console.log(`Target files skipped: ${stats.filesSkipped}`);
426
- if (stats.errors > 0) console.log(`\nWarning: ${stats.errors} errors encountered during processing`);
427
- }
428
-
429
-
430
- /**
431
- * Configuration options for the migration process
432
- * @property {string} sourcePath - Path to the package being migrated
433
- * @property {string} targetPath - Path to the monorepo root to search for imports
434
- * @property {string[]} ignoreSourceFiles - Patterns to ignore when scanning source files
435
- * @property {string[]} ignoreTargetFiles - Patterns to ignore when scanning target files
436
- * @property {boolean} [includeExtension] - Whether to include file extensions in imports
437
- */ const $bf36973fbb2420ae$export$ba43bf67f3d48107 = {
438
- targetPath: '.',
439
- ignoreSourceFiles: [],
440
- ignoreTargetFiles: [],
441
- includeExtension: false
442
- };
443
-
444
-
445
- async function $f2a4372f35794f3d$export$f22da7240b7add18() {
446
- const program = new (0, $cUHbR$Command)();
447
- program.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(false).parse(process.argv);
448
- const args = program.args;
449
- if (!args[0]) {
450
- console.error('Error: source-path is required');
451
- process.exit(1);
452
- }
453
- const sourcePath = args[0];
454
- const targetPath = args[1] || (0, $bf36973fbb2420ae$export$ba43bf67f3d48107).targetPath;
455
- const options = program.opts();
456
- await (0, $1d95f64f52ade133$export$eb4d0c836e660010)({
457
- sourcePath: sourcePath,
458
- targetPath: targetPath,
459
- ignoreSourceFiles: options.ignoreSourceFiles ? options.ignoreSourceFiles.split(',') : (0, $bf36973fbb2420ae$export$ba43bf67f3d48107).ignoreSourceFiles,
460
- ignoreTargetFiles: options.ignoreTargetFiles ? options.ignoreTargetFiles.split(',') : (0, $bf36973fbb2420ae$export$ba43bf67f3d48107).ignoreTargetFiles,
461
- includeExtension: options.extension !== false ? true : (0, $bf36973fbb2420ae$export$ba43bf67f3d48107).includeExtension
462
- });
463
- }
464
-
465
-
466
- (async ()=>{
467
- await (0, $f2a4372f35794f3d$export$f22da7240b7add18)();
468
- })();
469
-
470
-
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()})();
package/package.json CHANGED
@@ -1,83 +1,88 @@
1
1
  {
2
- "name": "migrate-barrel-imports",
3
- "version": "1.0.0",
4
- "description": "A CLI tool to migrate barrel imports in JavaScript/TypeScript monorepos",
5
- "files": [
6
- "dist/"
7
- ],
8
- "main": "./dist/index.js",
9
- "keywords": [
10
- "typescript",
11
- "javascript",
12
- "monorepo",
13
- "cli",
14
- "tool",
15
- "migrate",
16
- "barrel",
17
- "import",
18
- "imports"
19
- ],
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/brandhaug/migrate-barrel-imports.git"
23
- },
24
- "bugs": {
25
- "url": "https://github.com/brandhaug/migrate-barrel-imports/issues"
26
- },
27
- "homepage": "https://github.com/brandhaug/migrate-barrel-imports#readme",
28
- "type": "module",
29
- "bin": {
30
- "migrate-barrel-imports": "dist/index.js"
31
- },
32
- "scripts": {
33
- "build": "parcel build src/index.ts --no-source-maps",
34
- "start": "node dist/index.js",
35
- "lint": "biome lint",
36
- "check-write": "biome check --write",
37
- "typecheck": "tsc",
38
- "prepare": "husky",
39
- "test": "vitest",
40
- "validate": "npm run lint && npm run typecheck && npm run test"
41
- },
42
- "dependencies": {
43
- "@babel/generator": "7.26.10",
44
- "@babel/parser": "7.26.10",
45
- "@babel/traverse": "7.26.10",
46
- "@babel/types": "7.26.10",
47
- "commander": "13.1.0",
48
- "fast-glob": "3.3.3",
49
- "micromatch": "4.0.8",
50
- "ts-morph": "25.0.1"
51
- },
52
- "devDependencies": {
53
- "@biomejs/biome": "1.9.4",
54
- "@commitlint/cli": "19.8.0",
55
- "@commitlint/config-conventional": "19.8.0",
56
- "@semantic-release/changelog": "6.0.3",
57
- "@types/babel__generator": "7.6.8",
58
- "@types/babel__traverse": "7.20.6",
59
- "@types/micromatch": "4.0.9",
60
- "execa": "9.5.2",
61
- "husky": "9.1.7",
62
- "lint-staged": "15.5.0",
63
- "parcel": "2.14.1",
64
- "semantic-release": "24.2.3",
65
- "tsc-files": "1.1.4",
66
- "tsx": "4.19.3",
67
- "typescript": "5.8.2",
68
- "vitest": "3.0.9"
69
- },
70
- "author": "Martin Brandhaug",
71
- "license": "MIT",
72
- "lint-staged": {
73
- "*.{ts,json}": [
74
- "biome check --write"
75
- ],
76
- "*.ts": [
77
- "tsc-files"
78
- ]
79
- },
80
- "engines": {
81
- "node": ">=20"
82
- }
2
+ "name": "migrate-barrel-imports",
3
+ "version": "1.2.0",
4
+ "description": "A CLI tool to migrate barrel imports in JavaScript/TypeScript monorepos",
5
+ "files": [
6
+ "dist/"
7
+ ],
8
+ "main": "./dist/index.js",
9
+ "targets": {
10
+ "main": {
11
+ "optimize": true
12
+ }
13
+ },
14
+ "keywords": [
15
+ "typescript",
16
+ "javascript",
17
+ "monorepo",
18
+ "cli",
19
+ "tool",
20
+ "migrate",
21
+ "barrel",
22
+ "import",
23
+ "imports"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/brandhaug/migrate-barrel-imports.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/brandhaug/migrate-barrel-imports/issues"
31
+ },
32
+ "homepage": "https://github.com/brandhaug/migrate-barrel-imports#readme",
33
+ "type": "module",
34
+ "bin": {
35
+ "migrate-barrel-imports": "dist/index.js"
36
+ },
37
+ "scripts": {
38
+ "build": "parcel build src/index.ts --no-source-maps",
39
+ "start": "node dist/index.js",
40
+ "lint": "biome lint",
41
+ "check-write": "biome check --write",
42
+ "typecheck": "tsc",
43
+ "prepare": "husky",
44
+ "test": "vitest",
45
+ "validate": "npm run lint && npm run typecheck && npm run test"
46
+ },
47
+ "dependencies": {
48
+ "@babel/generator": "7.26.10",
49
+ "@babel/parser": "7.26.10",
50
+ "@babel/traverse": "7.26.10",
51
+ "@babel/types": "7.26.10",
52
+ "commander": "13.1.0",
53
+ "fast-glob": "3.3.3",
54
+ "micromatch": "4.0.8",
55
+ "ts-morph": "25.0.1"
56
+ },
57
+ "devDependencies": {
58
+ "@biomejs/biome": "1.9.4",
59
+ "@commitlint/cli": "19.8.0",
60
+ "@commitlint/config-conventional": "19.8.0",
61
+ "@semantic-release/changelog": "6.0.3",
62
+ "@types/babel__generator": "7.6.8",
63
+ "@types/babel__traverse": "7.20.6",
64
+ "@types/micromatch": "4.0.9",
65
+ "execa": "9.5.2",
66
+ "husky": "9.1.7",
67
+ "lint-staged": "15.5.0",
68
+ "parcel": "2.14.1",
69
+ "semantic-release": "24.2.3",
70
+ "tsc-files": "1.1.4",
71
+ "tsx": "4.19.3",
72
+ "typescript": "5.8.2",
73
+ "vitest": "3.0.9"
74
+ },
75
+ "author": "Martin Brandhaug",
76
+ "license": "MIT",
77
+ "lint-staged": {
78
+ "*.{ts,json}": [
79
+ "biome check --write"
80
+ ],
81
+ "*.ts": [
82
+ "tsc-files"
83
+ ]
84
+ },
85
+ "engines": {
86
+ "node": ">=20"
87
+ }
83
88
  }