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