@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 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 <route> <featurePath> [options]
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
 
@@ -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
- state.sections = state.sections.filter(s => s.route !== section.route);
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 (config.importAliases.features) {
1725
- const entryPart = effectiveOptions.entry === 'index' ? '' : `/${featureComponentName}`;
1726
- // In Astro, we can often omit the extension for .tsx files, but not for .astro files if using aliases sometimes.
1727
- // However, to be safe, we use the configured extension.
1728
- featureImportPath = `${config.importAliases.features}/${normalizedFeaturePath}${entryPart}${config.naming.featureExtension}`;
1729
- } else {
1730
- const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
1731
- // Remove extension for import
1732
- featureImportPath = relativeFeatureFile.replace(/\.[^/.]+$/, '');
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 (options.endpoint) {
1744
- routeContent = generateEndpointTemplate(featureComponentName);
1745
- routeSignature = getSignature(config, 'typescript');
1746
- } else {
1747
- routeContent = generateRouteTemplate(
1748
- options.layout,
1749
- layoutImportPath,
1750
- featureImportPath,
1751
- featureComponentName
1752
- );
1753
- routeSignature = getSignature(config, 'astro');
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 routeHash = await writeFileWithSignature(
1759
- routeFilePath,
1760
- routeContent,
1761
- routeSignature,
1762
- config.hashing?.normalization
1763
- );
1764
- await registerFile(routeFilePath, {
1765
- kind: 'route',
1766
- template: options.endpoint ? 'endpoint' : 'route',
1767
- hash: routeHash,
1768
- owner: normalizedRoute
1769
- });
1770
-
1771
- const writtenFiles = [routeFilePath];
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 <route> <featurePath>')
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')
@@ -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
- state.sections = state.sections.filter(s => s.route !== section.route);
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
- console.log(` Route: ${routeFilePath}`);
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
- await ensureNotExists(routeFilePath, options.force);
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 (config.importAliases.features) {
1524
- const entryPart = effectiveOptions.entry === 'index' ? '' : `/${featureComponentName}`;
1525
- // In Astro, we can often omit the extension for .tsx files, but not for .astro files if using aliases sometimes.
1526
- // However, to be safe, we use the configured extension.
1527
- featureImportPath = `${config.importAliases.features}/${normalizedFeaturePath}${entryPart}${config.naming.featureExtension}`;
1528
- }
1529
- else {
1530
- const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
1531
- // Remove extension for import
1532
- featureImportPath = relativeFeatureFile.replace(/\.[^/.]+$/, '');
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 (options.endpoint) {
1541
- routeContent = generateEndpointTemplate(featureComponentName);
1542
- routeSignature = getSignature(config, 'typescript');
1543
- }
1544
- else {
1545
- routeContent = generateRouteTemplate(options.layout, layoutImportPath, featureImportPath, featureComponentName);
1546
- routeSignature = getSignature(config, 'astro');
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 routeHash = await writeFileWithSignature(routeFilePath, routeContent, routeSignature, config.hashing?.normalization);
1550
- await registerFile(routeFilePath, {
1551
- kind: 'route',
1552
- template: options.endpoint ? 'endpoint' : 'route',
1553
- hash: routeHash,
1554
- owner: normalizedRoute
1555
- });
1556
- const writtenFiles = [routeFilePath];
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
- console.log(` Route: ${routeFilePath}`);
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
- await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${aliasPath}/${fromFeatureComponentName}'`, `import ${toFeatureComponentName} from '${aliasPath}/${toFeatureComponentName}'`);
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 oldImportPath = `import ${fromFeatureComponentName} from '${oldRelativeDir}/${fromFeatureComponentName}'`;
2047
- const newImportPath = `import ${toFeatureComponentName} from '${newRelativeDir}/${toFeatureComponentName}'`;
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;