@oamm/textor 1.0.1 → 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 +71 -51
- package/dist/bin/textor.js.map +1 -1
- package/dist/index.cjs +63 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +63 -40
- 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,19 +1733,21 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1720
1733
|
}
|
|
1721
1734
|
}
|
|
1722
1735
|
|
|
1723
|
-
let featureImportPath;
|
|
1724
|
-
if (
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
|
|
1731
|
-
// Remove extension for import if it's not an .astro file
|
|
1732
|
-
if (config.naming.featureExtension === '.astro') {
|
|
1733
|
-
featureImportPath = relativeFeatureFile;
|
|
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}`;
|
|
1734
1743
|
} else {
|
|
1735
|
-
|
|
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
|
+
}
|
|
1736
1751
|
}
|
|
1737
1752
|
}
|
|
1738
1753
|
|
|
@@ -1744,35 +1759,40 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1744
1759
|
let routeContent;
|
|
1745
1760
|
let routeSignature;
|
|
1746
1761
|
|
|
1747
|
-
if (
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
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
|
+
}
|
|
1758
1775
|
}
|
|
1759
1776
|
|
|
1760
1777
|
const featureContent = generateFeatureTemplate(featureComponentName, scriptImportPath, framework);
|
|
1761
1778
|
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
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
|
+
}
|
|
1776
1796
|
|
|
1777
1797
|
await ensureDir(featureDirPath);
|
|
1778
1798
|
|
|
@@ -1996,7 +2016,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1996
2016
|
}
|
|
1997
2017
|
|
|
1998
2018
|
console.log('✓ Section created successfully:');
|
|
1999
|
-
console.log(` Route: ${routeFilePath}`);
|
|
2019
|
+
if (routeFilePath) console.log(` Route: ${routeFilePath}`);
|
|
2000
2020
|
console.log(` Feature: ${featureFilePath}`);
|
|
2001
2021
|
|
|
2002
2022
|
if (shouldCreateIndex) console.log(` Index: ${indexFilePath}`);
|
|
@@ -3719,8 +3739,8 @@ program
|
|
|
3719
3739
|
.action(initCommand);
|
|
3720
3740
|
|
|
3721
3741
|
program
|
|
3722
|
-
.command('add-section
|
|
3723
|
-
.description('Create a route + feature binding')
|
|
3742
|
+
.command('add-section [route] [featurePath]')
|
|
3743
|
+
.description('Create a route + feature binding (route optional for standalone features)')
|
|
3724
3744
|
.option('--preset <name>', 'Scaffolding preset (minimal, standard, senior)')
|
|
3725
3745
|
.option('--layout <name>', 'Layout component name (use "none" for no layout)', 'Main')
|
|
3726
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,21 +1534,23 @@ 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
|
-
else {
|
|
1530
|
-
const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
|
|
1531
|
-
// Remove extension for import if it's not an .astro file
|
|
1532
|
-
if (config.naming.featureExtension === '.astro') {
|
|
1533
|
-
featureImportPath = relativeFeatureFile;
|
|
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}`;
|
|
1534
1544
|
}
|
|
1535
1545
|
else {
|
|
1536
|
-
|
|
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
|
+
}
|
|
1537
1554
|
}
|
|
1538
1555
|
}
|
|
1539
1556
|
let scriptImportPath;
|
|
@@ -1542,23 +1559,28 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1542
1559
|
}
|
|
1543
1560
|
let routeContent;
|
|
1544
1561
|
let routeSignature;
|
|
1545
|
-
if (
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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
|
+
}
|
|
1552
1571
|
}
|
|
1553
1572
|
const featureContent = generateFeatureTemplate(featureComponentName, scriptImportPath, framework);
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
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
|
+
}
|
|
1562
1584
|
await ensureDir(featureDirPath);
|
|
1563
1585
|
if (shouldCreateSubComponentsDir)
|
|
1564
1586
|
await ensureDir(subComponentsDir);
|
|
@@ -1713,7 +1735,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1713
1735
|
await formatFiles(writtenFiles, config.formatting.tool);
|
|
1714
1736
|
}
|
|
1715
1737
|
console.log('✓ Section created successfully:');
|
|
1716
|
-
|
|
1738
|
+
if (routeFilePath)
|
|
1739
|
+
console.log(` Route: ${routeFilePath}`);
|
|
1717
1740
|
console.log(` Feature: ${featureFilePath}`);
|
|
1718
1741
|
if (shouldCreateIndex)
|
|
1719
1742
|
console.log(` Index: ${indexFilePath}`);
|