@oamm/textor 1.0.0 → 1.0.2
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 +21 -2
- package/dist/bin/textor.js +88 -59
- package/dist/bin/textor.js.map +1 -1
- package/dist/index.cjs +78 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +78 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,10 +114,21 @@ If you have manually edited a Textor-generated file and wish to remove or move i
|
|
|
114
114
|
## 🛠️ Commands
|
|
115
115
|
|
|
116
116
|
### add-section
|
|
117
|
-
Create a route + feature binding.
|
|
117
|
+
Create a route + feature binding, or a standalone feature.
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
|
-
pnpm textor add-section
|
|
120
|
+
pnpm textor add-section [route] <featurePath> [options]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If `route` is provided, Textor creates both a route adapter (e.g., in `src/pages`) and a feature module. If `route` is omitted, Textor scaffolds only the feature module. This is useful for features that are shared across multiple pages or used as sub-parts of other features.
|
|
124
|
+
|
|
125
|
+
**Examples:**
|
|
126
|
+
```bash
|
|
127
|
+
# Create a section with a route
|
|
128
|
+
pnpm textor add-section /users users/catalog
|
|
129
|
+
|
|
130
|
+
# Create a standalone feature (no route file)
|
|
131
|
+
pnpm textor add-section auth/login
|
|
121
132
|
```
|
|
122
133
|
|
|
123
134
|
**Options:**
|
|
@@ -149,6 +160,14 @@ pnpm textor move-section /old /new
|
|
|
149
160
|
### remove-section / remove-component
|
|
150
161
|
Safely remove Textor-managed modules.
|
|
151
162
|
|
|
163
|
+
```bash
|
|
164
|
+
# Remove by route
|
|
165
|
+
pnpm textor remove-section /users
|
|
166
|
+
|
|
167
|
+
# Remove a standalone feature by its name or path
|
|
168
|
+
pnpm textor remove-section auth/login
|
|
169
|
+
```
|
|
170
|
+
|
|
152
171
|
### list-sections
|
|
153
172
|
List all Textor-managed modules, including their architectural capabilities (API, Hooks, etc.).
|
|
154
173
|
|
package/dist/bin/textor.js
CHANGED
|
@@ -415,6 +415,7 @@ function normalizeComponentName(name) {
|
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
function normalizeRoute(route) {
|
|
418
|
+
if (!route) return null;
|
|
418
419
|
let normalized = route.trim();
|
|
419
420
|
|
|
420
421
|
if (!normalized.startsWith('/')) {
|
|
@@ -1394,8 +1395,12 @@ async function registerFile(filePath, { kind, template, hash, templateVersion =
|
|
|
1394
1395
|
|
|
1395
1396
|
async function addSectionToState(section) {
|
|
1396
1397
|
const state = await loadState();
|
|
1397
|
-
// Avoid duplicates by route
|
|
1398
|
-
|
|
1398
|
+
// Avoid duplicates by route OR by featurePath if route is null
|
|
1399
|
+
if (section.route) {
|
|
1400
|
+
state.sections = state.sections.filter(s => s.route !== section.route);
|
|
1401
|
+
} else {
|
|
1402
|
+
state.sections = state.sections.filter(s => s.featurePath !== section.featurePath || s.route);
|
|
1403
|
+
}
|
|
1399
1404
|
state.sections.push(section);
|
|
1400
1405
|
await saveState(state);
|
|
1401
1406
|
}
|
|
@@ -1535,6 +1540,14 @@ async function stageFiles(filePaths) {
|
|
|
1535
1540
|
async function addSectionCommand(route, featurePath, options) {
|
|
1536
1541
|
try {
|
|
1537
1542
|
const config = await loadConfig();
|
|
1543
|
+
|
|
1544
|
+
// Handle optional route
|
|
1545
|
+
if (typeof featurePath === 'object' || featurePath === undefined) {
|
|
1546
|
+
options = featurePath || options || {};
|
|
1547
|
+
featurePath = route;
|
|
1548
|
+
route = null;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1538
1551
|
const effectiveOptions = getEffectiveOptions(options, config, 'features');
|
|
1539
1552
|
|
|
1540
1553
|
const normalizedRoute = normalizeRoute(route);
|
|
@@ -1549,7 +1562,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1549
1562
|
// Check if we should use nested mode even if config says flat
|
|
1550
1563
|
// (because the directory already exists, suggesting it should be an index file)
|
|
1551
1564
|
let effectiveRoutingMode = config.routing.mode;
|
|
1552
|
-
if (effectiveRoutingMode === 'flat') {
|
|
1565
|
+
if (normalizedRoute && effectiveRoutingMode === 'flat') {
|
|
1553
1566
|
const routeDirName = routeToFilePath(normalizedRoute, {
|
|
1554
1567
|
extension: '',
|
|
1555
1568
|
mode: 'flat'
|
|
@@ -1560,11 +1573,11 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1560
1573
|
}
|
|
1561
1574
|
}
|
|
1562
1575
|
|
|
1563
|
-
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
1576
|
+
const routeFileName = normalizedRoute ? routeToFilePath(normalizedRoute, {
|
|
1564
1577
|
extension: routeExtension,
|
|
1565
1578
|
mode: effectiveRoutingMode,
|
|
1566
1579
|
indexFile: config.routing.indexFile
|
|
1567
|
-
});
|
|
1580
|
+
}) : null;
|
|
1568
1581
|
|
|
1569
1582
|
const featureComponentName = getFeatureComponentName(normalizedFeaturePath);
|
|
1570
1583
|
const featureFileName = getFeatureFileName(normalizedFeaturePath, {
|
|
@@ -1572,7 +1585,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1572
1585
|
strategy: effectiveOptions.entry
|
|
1573
1586
|
});
|
|
1574
1587
|
|
|
1575
|
-
const routeFilePath = secureJoin(pagesRoot, routeFileName);
|
|
1588
|
+
const routeFilePath = routeFileName ? secureJoin(pagesRoot, routeFileName) : null;
|
|
1576
1589
|
const featureDirPath = secureJoin(featuresRoot, normalizedFeaturePath);
|
|
1577
1590
|
const featureFilePath = secureJoin(featureDirPath, featureFileName);
|
|
1578
1591
|
const scriptsIndexPath = secureJoin(featureDirPath, config.features.scriptsIndexFile);
|
|
@@ -1612,10 +1625,10 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1612
1625
|
const readmeFilePath = path.join(featureDirPath, 'README.md');
|
|
1613
1626
|
const storiesFilePath = path.join(featureDirPath, `${featureComponentName}.stories.tsx`);
|
|
1614
1627
|
|
|
1615
|
-
const routeParts = normalizedRoute.split('/').filter(Boolean);
|
|
1628
|
+
const routeParts = normalizedRoute ? normalizedRoute.split('/').filter(Boolean) : [];
|
|
1616
1629
|
const reorganizations = [];
|
|
1617
1630
|
|
|
1618
|
-
if (routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
1631
|
+
if (normalizedRoute && routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
1619
1632
|
const possibleExtensions = ['.astro', '.ts', '.js', '.md', '.mdx', '.html'];
|
|
1620
1633
|
for (let i = 1; i < routeParts.length; i++) {
|
|
1621
1634
|
const parentRoute = '/' + routeParts.slice(0, i).join('/');
|
|
@@ -1650,7 +1663,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1650
1663
|
|
|
1651
1664
|
if (options.dryRun) {
|
|
1652
1665
|
console.log('Dry run - would create:');
|
|
1653
|
-
console.log(` Route: ${routeFilePath}`);
|
|
1666
|
+
if (routeFilePath) console.log(` Route: ${routeFilePath}`);
|
|
1654
1667
|
console.log(` Feature: ${featureFilePath}`);
|
|
1655
1668
|
|
|
1656
1669
|
for (const reorg of reorganizations) {
|
|
@@ -1695,7 +1708,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1695
1708
|
await saveState(state);
|
|
1696
1709
|
}
|
|
1697
1710
|
|
|
1698
|
-
await ensureNotExists(routeFilePath, options.force);
|
|
1711
|
+
if (routeFilePath) await ensureNotExists(routeFilePath, options.force);
|
|
1699
1712
|
await ensureNotExists(featureFilePath, options.force);
|
|
1700
1713
|
|
|
1701
1714
|
if (shouldCreateIndex) await ensureNotExists(indexFilePath, options.force);
|
|
@@ -1711,7 +1724,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1711
1724
|
if (shouldCreateScriptsDir) await ensureNotExists(scriptsIndexPath, options.force);
|
|
1712
1725
|
|
|
1713
1726
|
let layoutImportPath = null;
|
|
1714
|
-
if (options.layout !== 'none') {
|
|
1727
|
+
if (routeFilePath && options.layout !== 'none') {
|
|
1715
1728
|
if (config.importAliases.layouts) {
|
|
1716
1729
|
layoutImportPath = `${config.importAliases.layouts}/${options.layout}.astro`;
|
|
1717
1730
|
} else {
|
|
@@ -1720,16 +1733,22 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1720
1733
|
}
|
|
1721
1734
|
}
|
|
1722
1735
|
|
|
1723
|
-
let featureImportPath;
|
|
1724
|
-
if (
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1736
|
+
let featureImportPath = null;
|
|
1737
|
+
if (routeFilePath) {
|
|
1738
|
+
if (config.importAliases.features) {
|
|
1739
|
+
const entryPart = effectiveOptions.entry === 'index' ? '' : `/${featureComponentName}`;
|
|
1740
|
+
// In Astro, we can often omit the extension for .tsx files, but not for .astro files if using aliases sometimes.
|
|
1741
|
+
// However, to be safe, we use the configured extension.
|
|
1742
|
+
featureImportPath = `${config.importAliases.features}/${normalizedFeaturePath}${entryPart}${config.naming.featureExtension}`;
|
|
1743
|
+
} else {
|
|
1744
|
+
const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
|
|
1745
|
+
// Remove extension for import if it's not an .astro file
|
|
1746
|
+
if (config.naming.featureExtension === '.astro') {
|
|
1747
|
+
featureImportPath = relativeFeatureFile;
|
|
1748
|
+
} else {
|
|
1749
|
+
featureImportPath = relativeFeatureFile.replace(/\.[^/.]+$/, '');
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1733
1752
|
}
|
|
1734
1753
|
|
|
1735
1754
|
let scriptImportPath;
|
|
@@ -1740,35 +1759,40 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1740
1759
|
let routeContent;
|
|
1741
1760
|
let routeSignature;
|
|
1742
1761
|
|
|
1743
|
-
if (
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1762
|
+
if (routeFilePath) {
|
|
1763
|
+
if (options.endpoint) {
|
|
1764
|
+
routeContent = generateEndpointTemplate(featureComponentName);
|
|
1765
|
+
routeSignature = getSignature(config, 'typescript');
|
|
1766
|
+
} else {
|
|
1767
|
+
routeContent = generateRouteTemplate(
|
|
1768
|
+
options.layout,
|
|
1769
|
+
layoutImportPath,
|
|
1770
|
+
featureImportPath,
|
|
1771
|
+
featureComponentName
|
|
1772
|
+
);
|
|
1773
|
+
routeSignature = getSignature(config, 'astro');
|
|
1774
|
+
}
|
|
1754
1775
|
}
|
|
1755
1776
|
|
|
1756
1777
|
const featureContent = generateFeatureTemplate(featureComponentName, scriptImportPath, framework);
|
|
1757
1778
|
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1779
|
+
const writtenFiles = [];
|
|
1780
|
+
|
|
1781
|
+
if (routeFilePath) {
|
|
1782
|
+
const routeHash = await writeFileWithSignature(
|
|
1783
|
+
routeFilePath,
|
|
1784
|
+
routeContent,
|
|
1785
|
+
routeSignature,
|
|
1786
|
+
config.hashing?.normalization
|
|
1787
|
+
);
|
|
1788
|
+
await registerFile(routeFilePath, {
|
|
1789
|
+
kind: 'route',
|
|
1790
|
+
template: options.endpoint ? 'endpoint' : 'route',
|
|
1791
|
+
hash: routeHash,
|
|
1792
|
+
owner: normalizedRoute
|
|
1793
|
+
});
|
|
1794
|
+
writtenFiles.push(routeFilePath);
|
|
1795
|
+
}
|
|
1772
1796
|
|
|
1773
1797
|
await ensureDir(featureDirPath);
|
|
1774
1798
|
|
|
@@ -1992,7 +2016,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1992
2016
|
}
|
|
1993
2017
|
|
|
1994
2018
|
console.log('✓ Section created successfully:');
|
|
1995
|
-
console.log(` Route: ${routeFilePath}`);
|
|
2019
|
+
if (routeFilePath) console.log(` Route: ${routeFilePath}`);
|
|
1996
2020
|
console.log(` Feature: ${featureFilePath}`);
|
|
1997
2021
|
|
|
1998
2022
|
if (shouldCreateIndex) console.log(` Index: ${indexFilePath}`);
|
|
@@ -2358,11 +2382,12 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
|
|
|
2358
2382
|
if (normalizedFromFeature !== targetFeature) {
|
|
2359
2383
|
const oldAliasPath = `${config.importAliases.features}/${normalizedFromFeature}`;
|
|
2360
2384
|
const newAliasPath = `${config.importAliases.features}/${targetFeature}`;
|
|
2385
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2361
2386
|
|
|
2362
2387
|
// Replace both the path and the component name if they are different
|
|
2363
2388
|
await updateSignature(toRoutePath,
|
|
2364
|
-
`import ${fromFeatureComponentName} from '${oldAliasPath}/${fromFeatureComponentName}'`,
|
|
2365
|
-
`import ${toFeatureComponentName} from '${newAliasPath}/${toFeatureComponentName}'`
|
|
2389
|
+
`import ${fromFeatureComponentName} from '${oldAliasPath}/${fromFeatureComponentName}${ext}'`,
|
|
2390
|
+
`import ${toFeatureComponentName} from '${newAliasPath}/${toFeatureComponentName}${ext}'`
|
|
2366
2391
|
);
|
|
2367
2392
|
|
|
2368
2393
|
// Fallback for prefix only replacement
|
|
@@ -2370,17 +2395,19 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
|
|
|
2370
2395
|
} else if (fromFeatureComponentName !== toFeatureComponentName) {
|
|
2371
2396
|
// Name changed but path didn't
|
|
2372
2397
|
const aliasPath = `${config.importAliases.features}/${targetFeature}`;
|
|
2398
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2373
2399
|
await updateSignature(toRoutePath,
|
|
2374
|
-
`import ${fromFeatureComponentName} from '${aliasPath}/${fromFeatureComponentName}'`,
|
|
2375
|
-
`import ${toFeatureComponentName} from '${aliasPath}/${toFeatureComponentName}'`
|
|
2400
|
+
`import ${fromFeatureComponentName} from '${aliasPath}/${fromFeatureComponentName}${ext}'`,
|
|
2401
|
+
`import ${toFeatureComponentName} from '${aliasPath}/${toFeatureComponentName}${ext}'`
|
|
2376
2402
|
);
|
|
2377
2403
|
}
|
|
2378
2404
|
} else {
|
|
2379
2405
|
const oldRelativeDir = getRelativeImportPath(fromRoutePath, fromFeatureDirPath);
|
|
2380
2406
|
const newRelativeDir = getRelativeImportPath(toRoutePath, toFeatureDirPath);
|
|
2407
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2381
2408
|
|
|
2382
|
-
const oldImportPath = `import ${fromFeatureComponentName} from '${oldRelativeDir}/${fromFeatureComponentName}'`;
|
|
2383
|
-
const newImportPath = `import ${toFeatureComponentName} from '${newRelativeDir}/${toFeatureComponentName}'`;
|
|
2409
|
+
const oldImportPath = `import ${fromFeatureComponentName} from '${oldRelativeDir}/${fromFeatureComponentName}${ext}'`;
|
|
2410
|
+
const newImportPath = `import ${toFeatureComponentName} from '${newRelativeDir}/${toFeatureComponentName}${ext}'`;
|
|
2384
2411
|
|
|
2385
2412
|
if (oldImportPath !== newImportPath) {
|
|
2386
2413
|
await updateSignature(toRoutePath, oldImportPath, newImportPath);
|
|
@@ -2470,14 +2497,16 @@ async function scanAndReplaceImports(config, state, fromInfo, toInfo, options) {
|
|
|
2470
2497
|
let content = await readFile(fullPath, 'utf-8');
|
|
2471
2498
|
let changed = false;
|
|
2472
2499
|
|
|
2500
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2501
|
+
|
|
2473
2502
|
// Handle Aliases
|
|
2474
2503
|
if (config.importAliases.features) {
|
|
2475
2504
|
const oldAlias = `${config.importAliases.features}/${fromFeaturePath}`;
|
|
2476
2505
|
const newAlias = `${config.importAliases.features}/${toFeaturePath}`;
|
|
2477
2506
|
|
|
2478
2507
|
// Update component name and path if both changed
|
|
2479
|
-
const oldFullImport = `from '${oldAlias}/${fromComponentName}'`;
|
|
2480
|
-
const newFullImport = `from '${newAlias}/${toComponentName}'`;
|
|
2508
|
+
const oldFullImport = `from '${oldAlias}/${fromComponentName}${ext}'`;
|
|
2509
|
+
const newFullImport = `from '${newAlias}/${toComponentName}${ext}'`;
|
|
2481
2510
|
|
|
2482
2511
|
if (content.includes(oldFullImport)) {
|
|
2483
2512
|
content = content.replace(new RegExp(oldFullImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newFullImport);
|
|
@@ -2495,8 +2524,8 @@ async function scanAndReplaceImports(config, state, fromInfo, toInfo, options) {
|
|
|
2495
2524
|
const oldRelPath = getRelativeImportPath(fullPath, fromFeatureDir);
|
|
2496
2525
|
const newRelPath = getRelativeImportPath(fullPath, toFeatureDir);
|
|
2497
2526
|
|
|
2498
|
-
const oldImport = `'${oldRelPath}/${fromComponentName}'`;
|
|
2499
|
-
const newImport = `'${newRelPath}/${toComponentName}'`;
|
|
2527
|
+
const oldImport = `'${oldRelPath}/${fromComponentName}${ext}'`;
|
|
2528
|
+
const newImport = `'${newRelPath}/${toComponentName}${ext}'`;
|
|
2500
2529
|
|
|
2501
2530
|
if (content.includes(oldImport)) {
|
|
2502
2531
|
content = content.replace(new RegExp(oldImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newImport);
|
|
@@ -3710,8 +3739,8 @@ program
|
|
|
3710
3739
|
.action(initCommand);
|
|
3711
3740
|
|
|
3712
3741
|
program
|
|
3713
|
-
.command('add-section
|
|
3714
|
-
.description('Create a route + feature binding')
|
|
3742
|
+
.command('add-section [route] [featurePath]')
|
|
3743
|
+
.description('Create a route + feature binding (route optional for standalone features)')
|
|
3715
3744
|
.option('--preset <name>', 'Scaffolding preset (minimal, standard, senior)')
|
|
3716
3745
|
.option('--layout <name>', 'Layout component name (use "none" for no layout)', 'Main')
|
|
3717
3746
|
.option('--name <name>', 'Section name for state tracking')
|
package/dist/bin/textor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textor.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"textor.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.cjs
CHANGED
|
@@ -384,6 +384,8 @@ function normalizeComponentName(name) {
|
|
|
384
384
|
return toPascalCase(name);
|
|
385
385
|
}
|
|
386
386
|
function normalizeRoute(route) {
|
|
387
|
+
if (!route)
|
|
388
|
+
return null;
|
|
387
389
|
let normalized = route.trim();
|
|
388
390
|
if (!normalized.startsWith('/')) {
|
|
389
391
|
normalized = '/' + normalized;
|
|
@@ -1228,8 +1230,13 @@ async function registerFile(filePath, { kind, template, hash, templateVersion =
|
|
|
1228
1230
|
}
|
|
1229
1231
|
async function addSectionToState(section) {
|
|
1230
1232
|
const state = await loadState();
|
|
1231
|
-
// Avoid duplicates by route
|
|
1232
|
-
|
|
1233
|
+
// Avoid duplicates by route OR by featurePath if route is null
|
|
1234
|
+
if (section.route) {
|
|
1235
|
+
state.sections = state.sections.filter(s => s.route !== section.route);
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
state.sections = state.sections.filter(s => s.featurePath !== section.featurePath || s.route);
|
|
1239
|
+
}
|
|
1233
1240
|
state.sections.push(section);
|
|
1234
1241
|
await saveState(state);
|
|
1235
1242
|
}
|
|
@@ -1351,6 +1358,12 @@ async function stageFiles(filePaths) {
|
|
|
1351
1358
|
async function addSectionCommand(route, featurePath, options) {
|
|
1352
1359
|
try {
|
|
1353
1360
|
const config = await loadConfig();
|
|
1361
|
+
// Handle optional route
|
|
1362
|
+
if (typeof featurePath === 'object' || featurePath === undefined) {
|
|
1363
|
+
options = featurePath || options || {};
|
|
1364
|
+
featurePath = route;
|
|
1365
|
+
route = null;
|
|
1366
|
+
}
|
|
1354
1367
|
const effectiveOptions = getEffectiveOptions(options, config, 'features');
|
|
1355
1368
|
const normalizedRoute = normalizeRoute(route);
|
|
1356
1369
|
const normalizedFeaturePath = featureToDirectoryPath(featurePath);
|
|
@@ -1361,7 +1374,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1361
1374
|
// Check if we should use nested mode even if config says flat
|
|
1362
1375
|
// (because the directory already exists, suggesting it should be an index file)
|
|
1363
1376
|
let effectiveRoutingMode = config.routing.mode;
|
|
1364
|
-
if (effectiveRoutingMode === 'flat') {
|
|
1377
|
+
if (normalizedRoute && effectiveRoutingMode === 'flat') {
|
|
1365
1378
|
const routeDirName = routeToFilePath(normalizedRoute, {
|
|
1366
1379
|
extension: '',
|
|
1367
1380
|
mode: 'flat'
|
|
@@ -1371,17 +1384,17 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1371
1384
|
effectiveRoutingMode = 'nested';
|
|
1372
1385
|
}
|
|
1373
1386
|
}
|
|
1374
|
-
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
1387
|
+
const routeFileName = normalizedRoute ? routeToFilePath(normalizedRoute, {
|
|
1375
1388
|
extension: routeExtension,
|
|
1376
1389
|
mode: effectiveRoutingMode,
|
|
1377
1390
|
indexFile: config.routing.indexFile
|
|
1378
|
-
});
|
|
1391
|
+
}) : null;
|
|
1379
1392
|
const featureComponentName = getFeatureComponentName(normalizedFeaturePath);
|
|
1380
1393
|
const featureFileName = getFeatureFileName(normalizedFeaturePath, {
|
|
1381
1394
|
extension: config.naming.featureExtension,
|
|
1382
1395
|
strategy: effectiveOptions.entry
|
|
1383
1396
|
});
|
|
1384
|
-
const routeFilePath = secureJoin(pagesRoot, routeFileName);
|
|
1397
|
+
const routeFilePath = routeFileName ? secureJoin(pagesRoot, routeFileName) : null;
|
|
1385
1398
|
const featureDirPath = secureJoin(featuresRoot, normalizedFeaturePath);
|
|
1386
1399
|
const featureFilePath = secureJoin(featureDirPath, featureFileName);
|
|
1387
1400
|
const scriptsIndexPath = secureJoin(featureDirPath, config.features.scriptsIndexFile);
|
|
@@ -1404,9 +1417,9 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1404
1417
|
const schemasFilePath = path.join(schemasDirInside, 'index.ts');
|
|
1405
1418
|
const readmeFilePath = path.join(featureDirPath, 'README.md');
|
|
1406
1419
|
const storiesFilePath = path.join(featureDirPath, `${featureComponentName}.stories.tsx`);
|
|
1407
|
-
const routeParts = normalizedRoute.split('/').filter(Boolean);
|
|
1420
|
+
const routeParts = normalizedRoute ? normalizedRoute.split('/').filter(Boolean) : [];
|
|
1408
1421
|
const reorganizations = [];
|
|
1409
|
-
if (routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
1422
|
+
if (normalizedRoute && routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
1410
1423
|
const possibleExtensions = ['.astro', '.ts', '.js', '.md', '.mdx', '.html'];
|
|
1411
1424
|
for (let i = 1; i < routeParts.length; i++) {
|
|
1412
1425
|
const parentRoute = '/' + routeParts.slice(0, i).join('/');
|
|
@@ -1437,7 +1450,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1437
1450
|
}
|
|
1438
1451
|
if (options.dryRun) {
|
|
1439
1452
|
console.log('Dry run - would create:');
|
|
1440
|
-
|
|
1453
|
+
if (routeFilePath)
|
|
1454
|
+
console.log(` Route: ${routeFilePath}`);
|
|
1441
1455
|
console.log(` Feature: ${featureFilePath}`);
|
|
1442
1456
|
for (const reorg of reorganizations) {
|
|
1443
1457
|
console.log(` Reorganize: ${reorg.from} -> ${reorg.to}`);
|
|
@@ -1485,7 +1499,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1485
1499
|
}
|
|
1486
1500
|
await saveState(state);
|
|
1487
1501
|
}
|
|
1488
|
-
|
|
1502
|
+
if (routeFilePath)
|
|
1503
|
+
await ensureNotExists(routeFilePath, options.force);
|
|
1489
1504
|
await ensureNotExists(featureFilePath, options.force);
|
|
1490
1505
|
if (shouldCreateIndex)
|
|
1491
1506
|
await ensureNotExists(indexFilePath, options.force);
|
|
@@ -1510,7 +1525,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1510
1525
|
if (shouldCreateScriptsDir)
|
|
1511
1526
|
await ensureNotExists(scriptsIndexPath, options.force);
|
|
1512
1527
|
let layoutImportPath = null;
|
|
1513
|
-
if (options.layout !== 'none') {
|
|
1528
|
+
if (routeFilePath && options.layout !== 'none') {
|
|
1514
1529
|
if (config.importAliases.layouts) {
|
|
1515
1530
|
layoutImportPath = `${config.importAliases.layouts}/${options.layout}.astro`;
|
|
1516
1531
|
}
|
|
@@ -1519,17 +1534,24 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1519
1534
|
layoutImportPath = getRelativeImportPath(routeFilePath, layoutFilePath);
|
|
1520
1535
|
}
|
|
1521
1536
|
}
|
|
1522
|
-
let featureImportPath;
|
|
1523
|
-
if (
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1537
|
+
let featureImportPath = null;
|
|
1538
|
+
if (routeFilePath) {
|
|
1539
|
+
if (config.importAliases.features) {
|
|
1540
|
+
const entryPart = effectiveOptions.entry === 'index' ? '' : `/${featureComponentName}`;
|
|
1541
|
+
// In Astro, we can often omit the extension for .tsx files, but not for .astro files if using aliases sometimes.
|
|
1542
|
+
// However, to be safe, we use the configured extension.
|
|
1543
|
+
featureImportPath = `${config.importAliases.features}/${normalizedFeaturePath}${entryPart}${config.naming.featureExtension}`;
|
|
1544
|
+
}
|
|
1545
|
+
else {
|
|
1546
|
+
const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
|
|
1547
|
+
// Remove extension for import if it's not an .astro file
|
|
1548
|
+
if (config.naming.featureExtension === '.astro') {
|
|
1549
|
+
featureImportPath = relativeFeatureFile;
|
|
1550
|
+
}
|
|
1551
|
+
else {
|
|
1552
|
+
featureImportPath = relativeFeatureFile.replace(/\.[^/.]+$/, '');
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1533
1555
|
}
|
|
1534
1556
|
let scriptImportPath;
|
|
1535
1557
|
if (shouldCreateScriptsDir) {
|
|
@@ -1537,23 +1559,28 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1537
1559
|
}
|
|
1538
1560
|
let routeContent;
|
|
1539
1561
|
let routeSignature;
|
|
1540
|
-
if (
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1562
|
+
if (routeFilePath) {
|
|
1563
|
+
if (options.endpoint) {
|
|
1564
|
+
routeContent = generateEndpointTemplate(featureComponentName);
|
|
1565
|
+
routeSignature = getSignature(config, 'typescript');
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
routeContent = generateRouteTemplate(options.layout, layoutImportPath, featureImportPath, featureComponentName);
|
|
1569
|
+
routeSignature = getSignature(config, 'astro');
|
|
1570
|
+
}
|
|
1547
1571
|
}
|
|
1548
1572
|
const featureContent = generateFeatureTemplate(featureComponentName, scriptImportPath, framework);
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1573
|
+
const writtenFiles = [];
|
|
1574
|
+
if (routeFilePath) {
|
|
1575
|
+
const routeHash = await writeFileWithSignature(routeFilePath, routeContent, routeSignature, config.hashing?.normalization);
|
|
1576
|
+
await registerFile(routeFilePath, {
|
|
1577
|
+
kind: 'route',
|
|
1578
|
+
template: options.endpoint ? 'endpoint' : 'route',
|
|
1579
|
+
hash: routeHash,
|
|
1580
|
+
owner: normalizedRoute
|
|
1581
|
+
});
|
|
1582
|
+
writtenFiles.push(routeFilePath);
|
|
1583
|
+
}
|
|
1557
1584
|
await ensureDir(featureDirPath);
|
|
1558
1585
|
if (shouldCreateSubComponentsDir)
|
|
1559
1586
|
await ensureDir(subComponentsDir);
|
|
@@ -1708,7 +1735,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1708
1735
|
await formatFiles(writtenFiles, config.formatting.tool);
|
|
1709
1736
|
}
|
|
1710
1737
|
console.log('✓ Section created successfully:');
|
|
1711
|
-
|
|
1738
|
+
if (routeFilePath)
|
|
1739
|
+
console.log(` Route: ${routeFilePath}`);
|
|
1712
1740
|
console.log(` Feature: ${featureFilePath}`);
|
|
1713
1741
|
if (shouldCreateIndex)
|
|
1714
1742
|
console.log(` Index: ${indexFilePath}`);
|
|
@@ -2029,22 +2057,25 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
|
|
|
2029
2057
|
if (normalizedFromFeature !== targetFeature) {
|
|
2030
2058
|
const oldAliasPath = `${config.importAliases.features}/${normalizedFromFeature}`;
|
|
2031
2059
|
const newAliasPath = `${config.importAliases.features}/${targetFeature}`;
|
|
2060
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2032
2061
|
// Replace both the path and the component name if they are different
|
|
2033
|
-
await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${oldAliasPath}/${fromFeatureComponentName}'`, `import ${toFeatureComponentName} from '${newAliasPath}/${toFeatureComponentName}'`);
|
|
2062
|
+
await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${oldAliasPath}/${fromFeatureComponentName}${ext}'`, `import ${toFeatureComponentName} from '${newAliasPath}/${toFeatureComponentName}${ext}'`);
|
|
2034
2063
|
// Fallback for prefix only replacement
|
|
2035
2064
|
await updateSignature(toRoutePath, oldAliasPath, newAliasPath);
|
|
2036
2065
|
}
|
|
2037
2066
|
else if (fromFeatureComponentName !== toFeatureComponentName) {
|
|
2038
2067
|
// Name changed but path didn't
|
|
2039
2068
|
const aliasPath = `${config.importAliases.features}/${targetFeature}`;
|
|
2040
|
-
|
|
2069
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2070
|
+
await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${aliasPath}/${fromFeatureComponentName}${ext}'`, `import ${toFeatureComponentName} from '${aliasPath}/${toFeatureComponentName}${ext}'`);
|
|
2041
2071
|
}
|
|
2042
2072
|
}
|
|
2043
2073
|
else {
|
|
2044
2074
|
const oldRelativeDir = getRelativeImportPath(fromRoutePath, fromFeatureDirPath);
|
|
2045
2075
|
const newRelativeDir = getRelativeImportPath(toRoutePath, toFeatureDirPath);
|
|
2046
|
-
const
|
|
2047
|
-
const
|
|
2076
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2077
|
+
const oldImportPath = `import ${fromFeatureComponentName} from '${oldRelativeDir}/${fromFeatureComponentName}${ext}'`;
|
|
2078
|
+
const newImportPath = `import ${toFeatureComponentName} from '${newRelativeDir}/${toFeatureComponentName}${ext}'`;
|
|
2048
2079
|
if (oldImportPath !== newImportPath) {
|
|
2049
2080
|
await updateSignature(toRoutePath, oldImportPath, newImportPath);
|
|
2050
2081
|
}
|
|
@@ -2117,13 +2148,14 @@ async function scanAndReplaceImports(config, state, fromInfo, toInfo, options) {
|
|
|
2117
2148
|
continue;
|
|
2118
2149
|
let content = await promises.readFile(fullPath, 'utf-8');
|
|
2119
2150
|
let changed = false;
|
|
2151
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2120
2152
|
// Handle Aliases
|
|
2121
2153
|
if (config.importAliases.features) {
|
|
2122
2154
|
const oldAlias = `${config.importAliases.features}/${fromFeaturePath}`;
|
|
2123
2155
|
const newAlias = `${config.importAliases.features}/${toFeaturePath}`;
|
|
2124
2156
|
// Update component name and path if both changed
|
|
2125
|
-
const oldFullImport = `from '${oldAlias}/${fromComponentName}'`;
|
|
2126
|
-
const newFullImport = `from '${newAlias}/${toComponentName}'`;
|
|
2157
|
+
const oldFullImport = `from '${oldAlias}/${fromComponentName}${ext}'`;
|
|
2158
|
+
const newFullImport = `from '${newAlias}/${toComponentName}${ext}'`;
|
|
2127
2159
|
if (content.includes(oldFullImport)) {
|
|
2128
2160
|
content = content.replace(new RegExp(oldFullImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newFullImport);
|
|
2129
2161
|
changed = true;
|
|
@@ -2140,8 +2172,8 @@ async function scanAndReplaceImports(config, state, fromInfo, toInfo, options) {
|
|
|
2140
2172
|
const toFeatureDir = secureJoin(featuresRoot, toFeaturePath);
|
|
2141
2173
|
const oldRelPath = getRelativeImportPath(fullPath, fromFeatureDir);
|
|
2142
2174
|
const newRelPath = getRelativeImportPath(fullPath, toFeatureDir);
|
|
2143
|
-
const oldImport = `'${oldRelPath}/${fromComponentName}'`;
|
|
2144
|
-
const newImport = `'${newRelPath}/${toComponentName}'`;
|
|
2175
|
+
const oldImport = `'${oldRelPath}/${fromComponentName}${ext}'`;
|
|
2176
|
+
const newImport = `'${newRelPath}/${toComponentName}${ext}'`;
|
|
2145
2177
|
if (content.includes(oldImport)) {
|
|
2146
2178
|
content = content.replace(new RegExp(oldImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newImport);
|
|
2147
2179
|
changed = true;
|