@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/dist/index.js
CHANGED
|
@@ -382,6 +382,8 @@ function normalizeComponentName(name) {
|
|
|
382
382
|
return toPascalCase(name);
|
|
383
383
|
}
|
|
384
384
|
function normalizeRoute(route) {
|
|
385
|
+
if (!route)
|
|
386
|
+
return null;
|
|
385
387
|
let normalized = route.trim();
|
|
386
388
|
if (!normalized.startsWith('/')) {
|
|
387
389
|
normalized = '/' + normalized;
|
|
@@ -1226,8 +1228,13 @@ async function registerFile(filePath, { kind, template, hash, templateVersion =
|
|
|
1226
1228
|
}
|
|
1227
1229
|
async function addSectionToState(section) {
|
|
1228
1230
|
const state = await loadState();
|
|
1229
|
-
// Avoid duplicates by route
|
|
1230
|
-
|
|
1231
|
+
// Avoid duplicates by route OR by featurePath if route is null
|
|
1232
|
+
if (section.route) {
|
|
1233
|
+
state.sections = state.sections.filter(s => s.route !== section.route);
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
state.sections = state.sections.filter(s => s.featurePath !== section.featurePath || s.route);
|
|
1237
|
+
}
|
|
1231
1238
|
state.sections.push(section);
|
|
1232
1239
|
await saveState(state);
|
|
1233
1240
|
}
|
|
@@ -1349,6 +1356,12 @@ async function stageFiles(filePaths) {
|
|
|
1349
1356
|
async function addSectionCommand(route, featurePath, options) {
|
|
1350
1357
|
try {
|
|
1351
1358
|
const config = await loadConfig();
|
|
1359
|
+
// Handle optional route
|
|
1360
|
+
if (typeof featurePath === 'object' || featurePath === undefined) {
|
|
1361
|
+
options = featurePath || options || {};
|
|
1362
|
+
featurePath = route;
|
|
1363
|
+
route = null;
|
|
1364
|
+
}
|
|
1352
1365
|
const effectiveOptions = getEffectiveOptions(options, config, 'features');
|
|
1353
1366
|
const normalizedRoute = normalizeRoute(route);
|
|
1354
1367
|
const normalizedFeaturePath = featureToDirectoryPath(featurePath);
|
|
@@ -1359,7 +1372,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1359
1372
|
// Check if we should use nested mode even if config says flat
|
|
1360
1373
|
// (because the directory already exists, suggesting it should be an index file)
|
|
1361
1374
|
let effectiveRoutingMode = config.routing.mode;
|
|
1362
|
-
if (effectiveRoutingMode === 'flat') {
|
|
1375
|
+
if (normalizedRoute && effectiveRoutingMode === 'flat') {
|
|
1363
1376
|
const routeDirName = routeToFilePath(normalizedRoute, {
|
|
1364
1377
|
extension: '',
|
|
1365
1378
|
mode: 'flat'
|
|
@@ -1369,17 +1382,17 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1369
1382
|
effectiveRoutingMode = 'nested';
|
|
1370
1383
|
}
|
|
1371
1384
|
}
|
|
1372
|
-
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
1385
|
+
const routeFileName = normalizedRoute ? routeToFilePath(normalizedRoute, {
|
|
1373
1386
|
extension: routeExtension,
|
|
1374
1387
|
mode: effectiveRoutingMode,
|
|
1375
1388
|
indexFile: config.routing.indexFile
|
|
1376
|
-
});
|
|
1389
|
+
}) : null;
|
|
1377
1390
|
const featureComponentName = getFeatureComponentName(normalizedFeaturePath);
|
|
1378
1391
|
const featureFileName = getFeatureFileName(normalizedFeaturePath, {
|
|
1379
1392
|
extension: config.naming.featureExtension,
|
|
1380
1393
|
strategy: effectiveOptions.entry
|
|
1381
1394
|
});
|
|
1382
|
-
const routeFilePath = secureJoin(pagesRoot, routeFileName);
|
|
1395
|
+
const routeFilePath = routeFileName ? secureJoin(pagesRoot, routeFileName) : null;
|
|
1383
1396
|
const featureDirPath = secureJoin(featuresRoot, normalizedFeaturePath);
|
|
1384
1397
|
const featureFilePath = secureJoin(featureDirPath, featureFileName);
|
|
1385
1398
|
const scriptsIndexPath = secureJoin(featureDirPath, config.features.scriptsIndexFile);
|
|
@@ -1402,9 +1415,9 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1402
1415
|
const schemasFilePath = path.join(schemasDirInside, 'index.ts');
|
|
1403
1416
|
const readmeFilePath = path.join(featureDirPath, 'README.md');
|
|
1404
1417
|
const storiesFilePath = path.join(featureDirPath, `${featureComponentName}.stories.tsx`);
|
|
1405
|
-
const routeParts = normalizedRoute.split('/').filter(Boolean);
|
|
1418
|
+
const routeParts = normalizedRoute ? normalizedRoute.split('/').filter(Boolean) : [];
|
|
1406
1419
|
const reorganizations = [];
|
|
1407
|
-
if (routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
1420
|
+
if (normalizedRoute && routeParts.length > 1 && config.routing.mode === 'flat') {
|
|
1408
1421
|
const possibleExtensions = ['.astro', '.ts', '.js', '.md', '.mdx', '.html'];
|
|
1409
1422
|
for (let i = 1; i < routeParts.length; i++) {
|
|
1410
1423
|
const parentRoute = '/' + routeParts.slice(0, i).join('/');
|
|
@@ -1435,7 +1448,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1435
1448
|
}
|
|
1436
1449
|
if (options.dryRun) {
|
|
1437
1450
|
console.log('Dry run - would create:');
|
|
1438
|
-
|
|
1451
|
+
if (routeFilePath)
|
|
1452
|
+
console.log(` Route: ${routeFilePath}`);
|
|
1439
1453
|
console.log(` Feature: ${featureFilePath}`);
|
|
1440
1454
|
for (const reorg of reorganizations) {
|
|
1441
1455
|
console.log(` Reorganize: ${reorg.from} -> ${reorg.to}`);
|
|
@@ -1483,7 +1497,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1483
1497
|
}
|
|
1484
1498
|
await saveState(state);
|
|
1485
1499
|
}
|
|
1486
|
-
|
|
1500
|
+
if (routeFilePath)
|
|
1501
|
+
await ensureNotExists(routeFilePath, options.force);
|
|
1487
1502
|
await ensureNotExists(featureFilePath, options.force);
|
|
1488
1503
|
if (shouldCreateIndex)
|
|
1489
1504
|
await ensureNotExists(indexFilePath, options.force);
|
|
@@ -1508,7 +1523,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1508
1523
|
if (shouldCreateScriptsDir)
|
|
1509
1524
|
await ensureNotExists(scriptsIndexPath, options.force);
|
|
1510
1525
|
let layoutImportPath = null;
|
|
1511
|
-
if (options.layout !== 'none') {
|
|
1526
|
+
if (routeFilePath && options.layout !== 'none') {
|
|
1512
1527
|
if (config.importAliases.layouts) {
|
|
1513
1528
|
layoutImportPath = `${config.importAliases.layouts}/${options.layout}.astro`;
|
|
1514
1529
|
}
|
|
@@ -1517,17 +1532,24 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1517
1532
|
layoutImportPath = getRelativeImportPath(routeFilePath, layoutFilePath);
|
|
1518
1533
|
}
|
|
1519
1534
|
}
|
|
1520
|
-
let featureImportPath;
|
|
1521
|
-
if (
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1535
|
+
let featureImportPath = null;
|
|
1536
|
+
if (routeFilePath) {
|
|
1537
|
+
if (config.importAliases.features) {
|
|
1538
|
+
const entryPart = effectiveOptions.entry === 'index' ? '' : `/${featureComponentName}`;
|
|
1539
|
+
// In Astro, we can often omit the extension for .tsx files, but not for .astro files if using aliases sometimes.
|
|
1540
|
+
// However, to be safe, we use the configured extension.
|
|
1541
|
+
featureImportPath = `${config.importAliases.features}/${normalizedFeaturePath}${entryPart}${config.naming.featureExtension}`;
|
|
1542
|
+
}
|
|
1543
|
+
else {
|
|
1544
|
+
const relativeFeatureFile = getRelativeImportPath(routeFilePath, featureFilePath);
|
|
1545
|
+
// Remove extension for import if it's not an .astro file
|
|
1546
|
+
if (config.naming.featureExtension === '.astro') {
|
|
1547
|
+
featureImportPath = relativeFeatureFile;
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
featureImportPath = relativeFeatureFile.replace(/\.[^/.]+$/, '');
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1531
1553
|
}
|
|
1532
1554
|
let scriptImportPath;
|
|
1533
1555
|
if (shouldCreateScriptsDir) {
|
|
@@ -1535,23 +1557,28 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1535
1557
|
}
|
|
1536
1558
|
let routeContent;
|
|
1537
1559
|
let routeSignature;
|
|
1538
|
-
if (
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1560
|
+
if (routeFilePath) {
|
|
1561
|
+
if (options.endpoint) {
|
|
1562
|
+
routeContent = generateEndpointTemplate(featureComponentName);
|
|
1563
|
+
routeSignature = getSignature(config, 'typescript');
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
routeContent = generateRouteTemplate(options.layout, layoutImportPath, featureImportPath, featureComponentName);
|
|
1567
|
+
routeSignature = getSignature(config, 'astro');
|
|
1568
|
+
}
|
|
1545
1569
|
}
|
|
1546
1570
|
const featureContent = generateFeatureTemplate(featureComponentName, scriptImportPath, framework);
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1571
|
+
const writtenFiles = [];
|
|
1572
|
+
if (routeFilePath) {
|
|
1573
|
+
const routeHash = await writeFileWithSignature(routeFilePath, routeContent, routeSignature, config.hashing?.normalization);
|
|
1574
|
+
await registerFile(routeFilePath, {
|
|
1575
|
+
kind: 'route',
|
|
1576
|
+
template: options.endpoint ? 'endpoint' : 'route',
|
|
1577
|
+
hash: routeHash,
|
|
1578
|
+
owner: normalizedRoute
|
|
1579
|
+
});
|
|
1580
|
+
writtenFiles.push(routeFilePath);
|
|
1581
|
+
}
|
|
1555
1582
|
await ensureDir(featureDirPath);
|
|
1556
1583
|
if (shouldCreateSubComponentsDir)
|
|
1557
1584
|
await ensureDir(subComponentsDir);
|
|
@@ -1706,7 +1733,8 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1706
1733
|
await formatFiles(writtenFiles, config.formatting.tool);
|
|
1707
1734
|
}
|
|
1708
1735
|
console.log('✓ Section created successfully:');
|
|
1709
|
-
|
|
1736
|
+
if (routeFilePath)
|
|
1737
|
+
console.log(` Route: ${routeFilePath}`);
|
|
1710
1738
|
console.log(` Feature: ${featureFilePath}`);
|
|
1711
1739
|
if (shouldCreateIndex)
|
|
1712
1740
|
console.log(` Index: ${indexFilePath}`);
|
|
@@ -2027,22 +2055,25 @@ async function moveSectionCommand(fromRoute, fromFeature, toRoute, toFeature, op
|
|
|
2027
2055
|
if (normalizedFromFeature !== targetFeature) {
|
|
2028
2056
|
const oldAliasPath = `${config.importAliases.features}/${normalizedFromFeature}`;
|
|
2029
2057
|
const newAliasPath = `${config.importAliases.features}/${targetFeature}`;
|
|
2058
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2030
2059
|
// Replace both the path and the component name if they are different
|
|
2031
|
-
await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${oldAliasPath}/${fromFeatureComponentName}'`, `import ${toFeatureComponentName} from '${newAliasPath}/${toFeatureComponentName}'`);
|
|
2060
|
+
await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${oldAliasPath}/${fromFeatureComponentName}${ext}'`, `import ${toFeatureComponentName} from '${newAliasPath}/${toFeatureComponentName}${ext}'`);
|
|
2032
2061
|
// Fallback for prefix only replacement
|
|
2033
2062
|
await updateSignature(toRoutePath, oldAliasPath, newAliasPath);
|
|
2034
2063
|
}
|
|
2035
2064
|
else if (fromFeatureComponentName !== toFeatureComponentName) {
|
|
2036
2065
|
// Name changed but path didn't
|
|
2037
2066
|
const aliasPath = `${config.importAliases.features}/${targetFeature}`;
|
|
2038
|
-
|
|
2067
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2068
|
+
await updateSignature(toRoutePath, `import ${fromFeatureComponentName} from '${aliasPath}/${fromFeatureComponentName}${ext}'`, `import ${toFeatureComponentName} from '${aliasPath}/${toFeatureComponentName}${ext}'`);
|
|
2039
2069
|
}
|
|
2040
2070
|
}
|
|
2041
2071
|
else {
|
|
2042
2072
|
const oldRelativeDir = getRelativeImportPath(fromRoutePath, fromFeatureDirPath);
|
|
2043
2073
|
const newRelativeDir = getRelativeImportPath(toRoutePath, toFeatureDirPath);
|
|
2044
|
-
const
|
|
2045
|
-
const
|
|
2074
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2075
|
+
const oldImportPath = `import ${fromFeatureComponentName} from '${oldRelativeDir}/${fromFeatureComponentName}${ext}'`;
|
|
2076
|
+
const newImportPath = `import ${toFeatureComponentName} from '${newRelativeDir}/${toFeatureComponentName}${ext}'`;
|
|
2046
2077
|
if (oldImportPath !== newImportPath) {
|
|
2047
2078
|
await updateSignature(toRoutePath, oldImportPath, newImportPath);
|
|
2048
2079
|
}
|
|
@@ -2115,13 +2146,14 @@ async function scanAndReplaceImports(config, state, fromInfo, toInfo, options) {
|
|
|
2115
2146
|
continue;
|
|
2116
2147
|
let content = await readFile(fullPath, 'utf-8');
|
|
2117
2148
|
let changed = false;
|
|
2149
|
+
const ext = config.naming.featureExtension === '.astro' ? '.astro' : '';
|
|
2118
2150
|
// Handle Aliases
|
|
2119
2151
|
if (config.importAliases.features) {
|
|
2120
2152
|
const oldAlias = `${config.importAliases.features}/${fromFeaturePath}`;
|
|
2121
2153
|
const newAlias = `${config.importAliases.features}/${toFeaturePath}`;
|
|
2122
2154
|
// Update component name and path if both changed
|
|
2123
|
-
const oldFullImport = `from '${oldAlias}/${fromComponentName}'`;
|
|
2124
|
-
const newFullImport = `from '${newAlias}/${toComponentName}'`;
|
|
2155
|
+
const oldFullImport = `from '${oldAlias}/${fromComponentName}${ext}'`;
|
|
2156
|
+
const newFullImport = `from '${newAlias}/${toComponentName}${ext}'`;
|
|
2125
2157
|
if (content.includes(oldFullImport)) {
|
|
2126
2158
|
content = content.replace(new RegExp(oldFullImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newFullImport);
|
|
2127
2159
|
changed = true;
|
|
@@ -2138,8 +2170,8 @@ async function scanAndReplaceImports(config, state, fromInfo, toInfo, options) {
|
|
|
2138
2170
|
const toFeatureDir = secureJoin(featuresRoot, toFeaturePath);
|
|
2139
2171
|
const oldRelPath = getRelativeImportPath(fullPath, fromFeatureDir);
|
|
2140
2172
|
const newRelPath = getRelativeImportPath(fullPath, toFeatureDir);
|
|
2141
|
-
const oldImport = `'${oldRelPath}/${fromComponentName}'`;
|
|
2142
|
-
const newImport = `'${newRelPath}/${toComponentName}'`;
|
|
2173
|
+
const oldImport = `'${oldRelPath}/${fromComponentName}${ext}'`;
|
|
2174
|
+
const newImport = `'${newRelPath}/${toComponentName}${ext}'`;
|
|
2143
2175
|
if (content.includes(oldImport)) {
|
|
2144
2176
|
content = content.replace(new RegExp(oldImport.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newImport);
|
|
2145
2177
|
changed = true;
|