@oamm/textor 1.0.5 → 1.0.7
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 +2 -1
- package/dist/bin/textor.js +145 -29
- package/dist/bin/textor.js.map +1 -1
- package/dist/index.cjs +126 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +126 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -370,7 +370,8 @@ The .textor/config.json file allows full control over the tool's behavior.
|
|
|
370
370
|
"createTypes": false,
|
|
371
371
|
"createReadme": false,
|
|
372
372
|
"createStories": false,
|
|
373
|
-
"createIndex": false
|
|
373
|
+
"createIndex": false,
|
|
374
|
+
"layout": "Main"
|
|
374
375
|
},
|
|
375
376
|
"components": {
|
|
376
377
|
"framework": "react",
|
package/dist/bin/textor.js
CHANGED
|
@@ -36,10 +36,12 @@ const CURRENT_CONFIG_VERSION = 2;
|
|
|
36
36
|
* @property {string} signatures.typescript
|
|
37
37
|
* @property {string} signatures.javascript
|
|
38
38
|
* @property {Object} features
|
|
39
|
+
* @property {string} features.framework
|
|
39
40
|
* @property {string} features.entry
|
|
40
41
|
* @property {boolean} features.createSubComponentsDir
|
|
41
42
|
* @property {boolean} features.createScriptsDir
|
|
42
43
|
* @property {string} features.scriptsIndexFile
|
|
44
|
+
* @property {string} features.layout
|
|
43
45
|
* @property {Object} components
|
|
44
46
|
* @property {boolean} components.createSubComponentsDir
|
|
45
47
|
* @property {boolean} components.createContext
|
|
@@ -105,7 +107,8 @@ const DEFAULT_CONFIG = {
|
|
|
105
107
|
createTypes: false,
|
|
106
108
|
createReadme: false,
|
|
107
109
|
createStories: false,
|
|
108
|
-
createIndex: false
|
|
110
|
+
createIndex: false,
|
|
111
|
+
layout: 'Main'
|
|
109
112
|
},
|
|
110
113
|
components: {
|
|
111
114
|
framework: 'react',
|
|
@@ -1794,6 +1797,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1794
1797
|
|
|
1795
1798
|
const {
|
|
1796
1799
|
framework,
|
|
1800
|
+
layout,
|
|
1797
1801
|
createSubComponentsDir: shouldCreateSubComponentsDir,
|
|
1798
1802
|
createScriptsDir: shouldCreateScriptsDir,
|
|
1799
1803
|
createApi: shouldCreateApi,
|
|
@@ -1965,7 +1969,13 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1965
1969
|
}
|
|
1966
1970
|
|
|
1967
1971
|
// Update imports in the moved file
|
|
1968
|
-
await updateImportsInFile(reorg.to, reorg.from, reorg.to);
|
|
1972
|
+
await updateImportsInFile$1(reorg.to, reorg.from, reorg.to);
|
|
1973
|
+
|
|
1974
|
+
// Update hash in state after import updates
|
|
1975
|
+
if (state.files[newRelative]) {
|
|
1976
|
+
const content = await readFile(reorg.to, 'utf-8');
|
|
1977
|
+
state.files[newRelative].hash = calculateHash(content, config.hashing?.normalization);
|
|
1978
|
+
}
|
|
1969
1979
|
|
|
1970
1980
|
console.log(`✓ Reorganized ${oldRelative} to ${newRelative}`);
|
|
1971
1981
|
}
|
|
@@ -1988,11 +1998,11 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1988
1998
|
if (shouldCreateScriptsDir) await ensureNotExists(scriptsIndexPath, options.force);
|
|
1989
1999
|
|
|
1990
2000
|
let layoutImportPath = null;
|
|
1991
|
-
if (routeFilePath &&
|
|
2001
|
+
if (routeFilePath && layout !== 'none') {
|
|
1992
2002
|
if (config.importAliases.layouts) {
|
|
1993
|
-
layoutImportPath = `${config.importAliases.layouts}/${
|
|
2003
|
+
layoutImportPath = `${config.importAliases.layouts}/${layout}.astro`;
|
|
1994
2004
|
} else {
|
|
1995
|
-
const layoutFilePath = secureJoin(layoutsRoot, `${
|
|
2005
|
+
const layoutFilePath = secureJoin(layoutsRoot, `${layout}.astro`);
|
|
1996
2006
|
layoutImportPath = getRelativeImportPath(routeFilePath, layoutFilePath);
|
|
1997
2007
|
}
|
|
1998
2008
|
}
|
|
@@ -2029,7 +2039,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
2029
2039
|
routeSignature = getSignature(config, 'typescript');
|
|
2030
2040
|
} else {
|
|
2031
2041
|
routeContent = generateRouteTemplate(
|
|
2032
|
-
|
|
2042
|
+
layout,
|
|
2033
2043
|
layoutImportPath,
|
|
2034
2044
|
featureImportPath,
|
|
2035
2045
|
featureComponentName,
|
|
@@ -2301,7 +2311,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
2301
2311
|
name: options.name || featureComponentName,
|
|
2302
2312
|
route: normalizedRoute,
|
|
2303
2313
|
featurePath: normalizedFeaturePath,
|
|
2304
|
-
layout:
|
|
2314
|
+
layout: layout,
|
|
2305
2315
|
extension: routeExtension
|
|
2306
2316
|
});
|
|
2307
2317
|
|
|
@@ -2318,7 +2328,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
2318
2328
|
}
|
|
2319
2329
|
}
|
|
2320
2330
|
|
|
2321
|
-
async function updateImportsInFile(filePath, oldFilePath, newFilePath) {
|
|
2331
|
+
async function updateImportsInFile$1(filePath, oldFilePath, newFilePath) {
|
|
2322
2332
|
if (!existsSync(filePath)) return;
|
|
2323
2333
|
|
|
2324
2334
|
let content = await readFile(filePath, 'utf-8');
|
|
@@ -2361,33 +2371,43 @@ async function removeSectionCommand(route, featurePath, options) {
|
|
|
2361
2371
|
|
|
2362
2372
|
const state = await loadState();
|
|
2363
2373
|
|
|
2364
|
-
let targetRoute = route;
|
|
2365
|
-
let targetFeaturePath = featurePath;
|
|
2366
2374
|
let section = findSection(state, route);
|
|
2375
|
+
if (!section && featurePath) {
|
|
2376
|
+
section = findSection(state, featurePath);
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
const targetRoute = section ? section.route : route;
|
|
2380
|
+
const targetFeaturePath = section ? section.featurePath : featurePath;
|
|
2367
2381
|
|
|
2368
2382
|
if (!targetFeaturePath) {
|
|
2369
|
-
|
|
2370
|
-
targetRoute = section.route;
|
|
2371
|
-
targetFeaturePath = section.featurePath;
|
|
2372
|
-
} else {
|
|
2373
|
-
throw new Error(`Section not found for identifier: ${route}. Please provide both route and featurePath.`);
|
|
2374
|
-
}
|
|
2383
|
+
throw new Error(`Section not found for identifier: ${route}. Please provide both route and featurePath.`);
|
|
2375
2384
|
}
|
|
2376
2385
|
|
|
2377
2386
|
const normalizedRoute = normalizeRoute(targetRoute);
|
|
2378
2387
|
const normalizedFeaturePath = featureToDirectoryPath(targetFeaturePath);
|
|
2379
2388
|
|
|
2380
|
-
const routeExtension = (section && section.extension) || config.naming.routeExtension;
|
|
2381
|
-
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
2382
|
-
extension: routeExtension,
|
|
2383
|
-
mode: config.routing.mode,
|
|
2384
|
-
indexFile: config.routing.indexFile
|
|
2385
|
-
});
|
|
2386
|
-
|
|
2387
2389
|
const pagesRoot = resolvePath(config, 'pages');
|
|
2388
2390
|
const featuresRoot = resolvePath(config, 'features');
|
|
2391
|
+
|
|
2392
|
+
// Find route file in state if possible
|
|
2393
|
+
let routeFilePath = null;
|
|
2394
|
+
const routeRelPath = Object.keys(state.files).find(f => {
|
|
2395
|
+
const data = state.files[f];
|
|
2396
|
+
return data.kind === 'route' && data.owner === normalizedRoute;
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2399
|
+
if (routeRelPath) {
|
|
2400
|
+
routeFilePath = path.resolve(process.cwd(), routeRelPath);
|
|
2401
|
+
} else {
|
|
2402
|
+
const routeExtension = (section && section.extension) || config.naming.routeExtension;
|
|
2403
|
+
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
2404
|
+
extension: routeExtension,
|
|
2405
|
+
mode: config.routing.mode,
|
|
2406
|
+
indexFile: config.routing.indexFile
|
|
2407
|
+
});
|
|
2408
|
+
routeFilePath = secureJoin(pagesRoot, routeFileName);
|
|
2409
|
+
}
|
|
2389
2410
|
|
|
2390
|
-
const routeFilePath = secureJoin(pagesRoot, routeFileName);
|
|
2391
2411
|
const featureDirPath = secureJoin(featuresRoot, normalizedFeaturePath);
|
|
2392
2412
|
|
|
2393
2413
|
const deletedFiles = [];
|
|
@@ -2474,8 +2494,67 @@ async function removeSectionCommand(route, featurePath, options) {
|
|
|
2474
2494
|
}
|
|
2475
2495
|
|
|
2476
2496
|
if (deletedFiles.length === 0 && deletedDirs.length === 0 && skippedFiles.length === 0) {
|
|
2477
|
-
|
|
2497
|
+
if (section) {
|
|
2498
|
+
console.log(`✓ Section ${normalizedRoute} removed from state (files were already missing on disk).`);
|
|
2499
|
+
state.sections = state.sections.filter(s => s.route !== normalizedRoute);
|
|
2500
|
+
await saveState(state);
|
|
2501
|
+
} else {
|
|
2502
|
+
console.log('No files to delete.');
|
|
2503
|
+
}
|
|
2478
2504
|
} else {
|
|
2505
|
+
// Reorganization (Flattening)
|
|
2506
|
+
if (!options.keepRoute && deletedFiles.length > 0 && config.routing.mode === 'flat') {
|
|
2507
|
+
const routeParts = normalizedRoute.split('/').filter(Boolean);
|
|
2508
|
+
if (routeParts.length > 1) {
|
|
2509
|
+
for (let i = routeParts.length - 1; i >= 1; i--) {
|
|
2510
|
+
const parentRoute = '/' + routeParts.slice(0, i).join('/');
|
|
2511
|
+
const parentDirName = routeParts.slice(0, i).join('/');
|
|
2512
|
+
const parentDirPath = secureJoin(pagesRoot, parentDirName);
|
|
2513
|
+
|
|
2514
|
+
if (existsSync(parentDirPath)) {
|
|
2515
|
+
const filesInDir = await readdir(parentDirPath);
|
|
2516
|
+
|
|
2517
|
+
if (filesInDir.length === 1) {
|
|
2518
|
+
const loneFile = filesInDir[0];
|
|
2519
|
+
const ext = path.extname(loneFile);
|
|
2520
|
+
const indexFile = ext === '.astro' ? config.routing.indexFile : `index${ext}`;
|
|
2521
|
+
|
|
2522
|
+
if (loneFile === indexFile) {
|
|
2523
|
+
const loneFilePath = path.join(parentDirPath, loneFile);
|
|
2524
|
+
const oldRelative = path.relative(process.cwd(), loneFilePath).replace(/\\/g, '/');
|
|
2525
|
+
|
|
2526
|
+
if (state.files[oldRelative] && state.files[oldRelative].kind === 'route') {
|
|
2527
|
+
const flatFileName = routeToFilePath(parentRoute, {
|
|
2528
|
+
extension: ext,
|
|
2529
|
+
mode: 'flat'
|
|
2530
|
+
});
|
|
2531
|
+
const flatFilePath = secureJoin(pagesRoot, flatFileName);
|
|
2532
|
+
|
|
2533
|
+
if (!existsSync(flatFilePath)) {
|
|
2534
|
+
await rename(loneFilePath, flatFilePath);
|
|
2535
|
+
|
|
2536
|
+
const newRelative = path.relative(process.cwd(), flatFilePath).replace(/\\/g, '/');
|
|
2537
|
+
|
|
2538
|
+
state.files[newRelative] = { ...state.files[oldRelative] };
|
|
2539
|
+
delete state.files[oldRelative];
|
|
2540
|
+
|
|
2541
|
+
await updateImportsInFile(flatFilePath, loneFilePath, flatFilePath);
|
|
2542
|
+
|
|
2543
|
+
// Update hash in state after import updates
|
|
2544
|
+
const content = await readFile(flatFilePath, 'utf-8');
|
|
2545
|
+
state.files[newRelative].hash = calculateHash(content, config.hashing?.normalization);
|
|
2546
|
+
|
|
2547
|
+
console.log(`✓ Reorganized ${oldRelative} to ${newRelative} (flattened)`);
|
|
2548
|
+
await cleanupEmptyDirs(parentDirPath, pagesRoot);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2479
2558
|
state.sections = state.sections.filter(s => s.route !== normalizedRoute);
|
|
2480
2559
|
await saveState(state);
|
|
2481
2560
|
}
|
|
@@ -2489,6 +2568,39 @@ async function removeSectionCommand(route, featurePath, options) {
|
|
|
2489
2568
|
}
|
|
2490
2569
|
}
|
|
2491
2570
|
|
|
2571
|
+
async function updateImportsInFile(filePath, oldFilePath, newFilePath) {
|
|
2572
|
+
if (!existsSync(filePath)) return;
|
|
2573
|
+
|
|
2574
|
+
let content = await readFile(filePath, 'utf-8');
|
|
2575
|
+
const oldDir = path.dirname(oldFilePath);
|
|
2576
|
+
const newDir = path.dirname(newFilePath);
|
|
2577
|
+
|
|
2578
|
+
if (oldDir === newDir) return;
|
|
2579
|
+
|
|
2580
|
+
// Find all relative imports
|
|
2581
|
+
const relativeImportRegex = /from\s+['"](\.\.?\/[^'"]+)['"]/g;
|
|
2582
|
+
let match;
|
|
2583
|
+
const replacements = [];
|
|
2584
|
+
|
|
2585
|
+
while ((match = relativeImportRegex.exec(content)) !== null) {
|
|
2586
|
+
const relativePath = match[1];
|
|
2587
|
+
const absoluteTarget = path.resolve(oldDir, relativePath);
|
|
2588
|
+
const newRelativePath = getRelativeImportPath(newFilePath, absoluteTarget);
|
|
2589
|
+
|
|
2590
|
+
replacements.push({
|
|
2591
|
+
full: match[0],
|
|
2592
|
+
oldRel: relativePath,
|
|
2593
|
+
newRel: newRelativePath
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
for (const repl of replacements) {
|
|
2598
|
+
content = content.replace(repl.full, `from '${repl.newRel}'`);
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
await writeFile(filePath, content, 'utf-8');
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2492
2604
|
/**
|
|
2493
2605
|
* Move a section (route + feature).
|
|
2494
2606
|
*
|
|
@@ -3402,9 +3514,13 @@ async function removeComponentCommand(identifier, options) {
|
|
|
3402
3514
|
owner: identifier
|
|
3403
3515
|
});
|
|
3404
3516
|
|
|
3405
|
-
if (result.deleted) {
|
|
3406
|
-
|
|
3407
|
-
|
|
3517
|
+
if (result.deleted || (result.reason === 'not-found' && component)) {
|
|
3518
|
+
if (result.deleted) {
|
|
3519
|
+
console.log(`✓ Deleted component: ${componentDir}/`);
|
|
3520
|
+
await cleanupEmptyDirs(path.dirname(componentDir), path.join(process.cwd(), config.paths.components));
|
|
3521
|
+
} else {
|
|
3522
|
+
console.log(`✓ Component ${identifier} removed from state (directory was already missing on disk).`);
|
|
3523
|
+
}
|
|
3408
3524
|
|
|
3409
3525
|
// Unregister files
|
|
3410
3526
|
const relComponentPath = path.relative(process.cwd(), componentDir).replace(/\\/g, '/');
|
|
@@ -4155,7 +4271,7 @@ program
|
|
|
4155
4271
|
.command('add-section [route] [featurePath]')
|
|
4156
4272
|
.description('Create a route + feature binding (route optional for standalone features)')
|
|
4157
4273
|
.option('--preset <name>', 'Scaffolding preset (minimal, standard, senior)')
|
|
4158
|
-
.option('--layout <name>', 'Layout component name (use "none" for no layout)'
|
|
4274
|
+
.option('--layout <name>', 'Layout component name (use "none" for no layout)')
|
|
4159
4275
|
.option('--name <name>', 'Section name for state tracking')
|
|
4160
4276
|
.option('--endpoint', 'Create an API endpoint (.ts) instead of an Astro page')
|
|
4161
4277
|
.option('--api', 'Create api directory')
|
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
|
@@ -35,10 +35,12 @@ const CURRENT_CONFIG_VERSION = 2;
|
|
|
35
35
|
* @property {string} signatures.typescript
|
|
36
36
|
* @property {string} signatures.javascript
|
|
37
37
|
* @property {Object} features
|
|
38
|
+
* @property {string} features.framework
|
|
38
39
|
* @property {string} features.entry
|
|
39
40
|
* @property {boolean} features.createSubComponentsDir
|
|
40
41
|
* @property {boolean} features.createScriptsDir
|
|
41
42
|
* @property {string} features.scriptsIndexFile
|
|
43
|
+
* @property {string} features.layout
|
|
42
44
|
* @property {Object} components
|
|
43
45
|
* @property {boolean} components.createSubComponentsDir
|
|
44
46
|
* @property {boolean} components.createContext
|
|
@@ -103,7 +105,8 @@ const DEFAULT_CONFIG = {
|
|
|
103
105
|
createTypes: false,
|
|
104
106
|
createReadme: false,
|
|
105
107
|
createStories: false,
|
|
106
|
-
createIndex: false
|
|
108
|
+
createIndex: false,
|
|
109
|
+
layout: 'Main'
|
|
107
110
|
},
|
|
108
111
|
components: {
|
|
109
112
|
framework: 'react',
|
|
@@ -1590,7 +1593,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1590
1593
|
const apiDirInside = secureJoin(featureDirPath, 'api');
|
|
1591
1594
|
const servicesDirInside = secureJoin(featureDirPath, 'services');
|
|
1592
1595
|
const schemasDirInside = secureJoin(featureDirPath, 'schemas');
|
|
1593
|
-
const { framework, createSubComponentsDir: shouldCreateSubComponentsDir, createScriptsDir: shouldCreateScriptsDir, createApi: shouldCreateApi, createServices: shouldCreateServices, createSchemas: shouldCreateSchemas, createHooks: shouldCreateHooks, createContext: shouldCreateContext, createTests: shouldCreateTests, createTypes: shouldCreateTypes, createReadme: shouldCreateReadme, createStories: shouldCreateStories, createIndex: shouldCreateIndex } = effectiveOptions;
|
|
1596
|
+
const { framework, layout, createSubComponentsDir: shouldCreateSubComponentsDir, createScriptsDir: shouldCreateScriptsDir, createApi: shouldCreateApi, createServices: shouldCreateServices, createSchemas: shouldCreateSchemas, createHooks: shouldCreateHooks, createContext: shouldCreateContext, createTests: shouldCreateTests, createTypes: shouldCreateTypes, createReadme: shouldCreateReadme, createStories: shouldCreateStories, createIndex: shouldCreateIndex } = effectiveOptions;
|
|
1594
1597
|
const featurePatterns = config.filePatterns?.features || {};
|
|
1595
1598
|
const patternData = {
|
|
1596
1599
|
componentName: featureComponentName,
|
|
@@ -1687,7 +1690,12 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1687
1690
|
delete state.files[oldRelative];
|
|
1688
1691
|
}
|
|
1689
1692
|
// Update imports in the moved file
|
|
1690
|
-
await updateImportsInFile(reorg.to, reorg.from, reorg.to);
|
|
1693
|
+
await updateImportsInFile$1(reorg.to, reorg.from, reorg.to);
|
|
1694
|
+
// Update hash in state after import updates
|
|
1695
|
+
if (state.files[newRelative]) {
|
|
1696
|
+
const content = await promises.readFile(reorg.to, 'utf-8');
|
|
1697
|
+
state.files[newRelative].hash = calculateHash(content, config.hashing?.normalization);
|
|
1698
|
+
}
|
|
1691
1699
|
console.log(`✓ Reorganized ${oldRelative} to ${newRelative}`);
|
|
1692
1700
|
}
|
|
1693
1701
|
await saveState(state);
|
|
@@ -1718,12 +1726,12 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1718
1726
|
if (shouldCreateScriptsDir)
|
|
1719
1727
|
await ensureNotExists(scriptsIndexPath, options.force);
|
|
1720
1728
|
let layoutImportPath = null;
|
|
1721
|
-
if (routeFilePath &&
|
|
1729
|
+
if (routeFilePath && layout !== 'none') {
|
|
1722
1730
|
if (config.importAliases.layouts) {
|
|
1723
|
-
layoutImportPath = `${config.importAliases.layouts}/${
|
|
1731
|
+
layoutImportPath = `${config.importAliases.layouts}/${layout}.astro`;
|
|
1724
1732
|
}
|
|
1725
1733
|
else {
|
|
1726
|
-
const layoutFilePath = secureJoin(layoutsRoot, `${
|
|
1734
|
+
const layoutFilePath = secureJoin(layoutsRoot, `${layout}.astro`);
|
|
1727
1735
|
layoutImportPath = getRelativeImportPath(routeFilePath, layoutFilePath);
|
|
1728
1736
|
}
|
|
1729
1737
|
}
|
|
@@ -1758,7 +1766,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1758
1766
|
routeSignature = getSignature(config, 'typescript');
|
|
1759
1767
|
}
|
|
1760
1768
|
else {
|
|
1761
|
-
routeContent = generateRouteTemplate(
|
|
1769
|
+
routeContent = generateRouteTemplate(layout, layoutImportPath, featureImportPath, featureComponentName, routeExtension);
|
|
1762
1770
|
routeSignature = getSignature(config, 'astro');
|
|
1763
1771
|
}
|
|
1764
1772
|
}
|
|
@@ -1959,7 +1967,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1959
1967
|
name: options.name || featureComponentName,
|
|
1960
1968
|
route: normalizedRoute,
|
|
1961
1969
|
featurePath: normalizedFeaturePath,
|
|
1962
|
-
layout:
|
|
1970
|
+
layout: layout,
|
|
1963
1971
|
extension: routeExtension
|
|
1964
1972
|
});
|
|
1965
1973
|
if (config.git?.stageChanges) {
|
|
@@ -1974,7 +1982,7 @@ async function addSectionCommand(route, featurePath, options) {
|
|
|
1974
1982
|
throw error;
|
|
1975
1983
|
}
|
|
1976
1984
|
}
|
|
1977
|
-
async function updateImportsInFile(filePath, oldFilePath, newFilePath) {
|
|
1985
|
+
async function updateImportsInFile$1(filePath, oldFilePath, newFilePath) {
|
|
1978
1986
|
if (!fs.existsSync(filePath))
|
|
1979
1987
|
return;
|
|
1980
1988
|
let content = await promises.readFile(filePath, 'utf-8');
|
|
@@ -2009,29 +2017,37 @@ async function removeSectionCommand(route, featurePath, options) {
|
|
|
2009
2017
|
throw new Error('Git repository is not clean. Please commit or stash your changes before proceeding.');
|
|
2010
2018
|
}
|
|
2011
2019
|
const state = await loadState();
|
|
2012
|
-
let targetRoute = route;
|
|
2013
|
-
let targetFeaturePath = featurePath;
|
|
2014
2020
|
let section = findSection(state, route);
|
|
2021
|
+
if (!section && featurePath) {
|
|
2022
|
+
section = findSection(state, featurePath);
|
|
2023
|
+
}
|
|
2024
|
+
const targetRoute = section ? section.route : route;
|
|
2025
|
+
const targetFeaturePath = section ? section.featurePath : featurePath;
|
|
2015
2026
|
if (!targetFeaturePath) {
|
|
2016
|
-
|
|
2017
|
-
targetRoute = section.route;
|
|
2018
|
-
targetFeaturePath = section.featurePath;
|
|
2019
|
-
}
|
|
2020
|
-
else {
|
|
2021
|
-
throw new Error(`Section not found for identifier: ${route}. Please provide both route and featurePath.`);
|
|
2022
|
-
}
|
|
2027
|
+
throw new Error(`Section not found for identifier: ${route}. Please provide both route and featurePath.`);
|
|
2023
2028
|
}
|
|
2024
2029
|
const normalizedRoute = normalizeRoute(targetRoute);
|
|
2025
2030
|
const normalizedFeaturePath = featureToDirectoryPath(targetFeaturePath);
|
|
2026
|
-
const routeExtension = (section && section.extension) || config.naming.routeExtension;
|
|
2027
|
-
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
2028
|
-
extension: routeExtension,
|
|
2029
|
-
mode: config.routing.mode,
|
|
2030
|
-
indexFile: config.routing.indexFile
|
|
2031
|
-
});
|
|
2032
2031
|
const pagesRoot = resolvePath(config, 'pages');
|
|
2033
2032
|
const featuresRoot = resolvePath(config, 'features');
|
|
2034
|
-
|
|
2033
|
+
// Find route file in state if possible
|
|
2034
|
+
let routeFilePath = null;
|
|
2035
|
+
const routeRelPath = Object.keys(state.files).find(f => {
|
|
2036
|
+
const data = state.files[f];
|
|
2037
|
+
return data.kind === 'route' && data.owner === normalizedRoute;
|
|
2038
|
+
});
|
|
2039
|
+
if (routeRelPath) {
|
|
2040
|
+
routeFilePath = path.resolve(process.cwd(), routeRelPath);
|
|
2041
|
+
}
|
|
2042
|
+
else {
|
|
2043
|
+
const routeExtension = (section && section.extension) || config.naming.routeExtension;
|
|
2044
|
+
const routeFileName = routeToFilePath(normalizedRoute, {
|
|
2045
|
+
extension: routeExtension,
|
|
2046
|
+
mode: config.routing.mode,
|
|
2047
|
+
indexFile: config.routing.indexFile
|
|
2048
|
+
});
|
|
2049
|
+
routeFilePath = secureJoin(pagesRoot, routeFileName);
|
|
2050
|
+
}
|
|
2035
2051
|
const featureDirPath = secureJoin(featuresRoot, normalizedFeaturePath);
|
|
2036
2052
|
const deletedFiles = [];
|
|
2037
2053
|
const skippedFiles = [];
|
|
@@ -2106,9 +2122,58 @@ async function removeSectionCommand(route, featurePath, options) {
|
|
|
2106
2122
|
});
|
|
2107
2123
|
}
|
|
2108
2124
|
if (deletedFiles.length === 0 && deletedDirs.length === 0 && skippedFiles.length === 0) {
|
|
2109
|
-
|
|
2125
|
+
if (section) {
|
|
2126
|
+
console.log(`✓ Section ${normalizedRoute} removed from state (files were already missing on disk).`);
|
|
2127
|
+
state.sections = state.sections.filter(s => s.route !== normalizedRoute);
|
|
2128
|
+
await saveState(state);
|
|
2129
|
+
}
|
|
2130
|
+
else {
|
|
2131
|
+
console.log('No files to delete.');
|
|
2132
|
+
}
|
|
2110
2133
|
}
|
|
2111
2134
|
else {
|
|
2135
|
+
// Reorganization (Flattening)
|
|
2136
|
+
if (!options.keepRoute && deletedFiles.length > 0 && config.routing.mode === 'flat') {
|
|
2137
|
+
const routeParts = normalizedRoute.split('/').filter(Boolean);
|
|
2138
|
+
if (routeParts.length > 1) {
|
|
2139
|
+
for (let i = routeParts.length - 1; i >= 1; i--) {
|
|
2140
|
+
const parentRoute = '/' + routeParts.slice(0, i).join('/');
|
|
2141
|
+
const parentDirName = routeParts.slice(0, i).join('/');
|
|
2142
|
+
const parentDirPath = secureJoin(pagesRoot, parentDirName);
|
|
2143
|
+
if (fs.existsSync(parentDirPath)) {
|
|
2144
|
+
const filesInDir = await promises.readdir(parentDirPath);
|
|
2145
|
+
if (filesInDir.length === 1) {
|
|
2146
|
+
const loneFile = filesInDir[0];
|
|
2147
|
+
const ext = path.extname(loneFile);
|
|
2148
|
+
const indexFile = ext === '.astro' ? config.routing.indexFile : `index${ext}`;
|
|
2149
|
+
if (loneFile === indexFile) {
|
|
2150
|
+
const loneFilePath = path.join(parentDirPath, loneFile);
|
|
2151
|
+
const oldRelative = path.relative(process.cwd(), loneFilePath).replace(/\\/g, '/');
|
|
2152
|
+
if (state.files[oldRelative] && state.files[oldRelative].kind === 'route') {
|
|
2153
|
+
const flatFileName = routeToFilePath(parentRoute, {
|
|
2154
|
+
extension: ext,
|
|
2155
|
+
mode: 'flat'
|
|
2156
|
+
});
|
|
2157
|
+
const flatFilePath = secureJoin(pagesRoot, flatFileName);
|
|
2158
|
+
if (!fs.existsSync(flatFilePath)) {
|
|
2159
|
+
await promises.rename(loneFilePath, flatFilePath);
|
|
2160
|
+
const newRelative = path.relative(process.cwd(), flatFilePath).replace(/\\/g, '/');
|
|
2161
|
+
state.files[newRelative] = { ...state.files[oldRelative] };
|
|
2162
|
+
delete state.files[oldRelative];
|
|
2163
|
+
await updateImportsInFile(flatFilePath, loneFilePath, flatFilePath);
|
|
2164
|
+
// Update hash in state after import updates
|
|
2165
|
+
const content = await promises.readFile(flatFilePath, 'utf-8');
|
|
2166
|
+
state.files[newRelative].hash = calculateHash(content, config.hashing?.normalization);
|
|
2167
|
+
console.log(`✓ Reorganized ${oldRelative} to ${newRelative} (flattened)`);
|
|
2168
|
+
await cleanupEmptyDirs(parentDirPath, pagesRoot);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2112
2177
|
state.sections = state.sections.filter(s => s.route !== normalizedRoute);
|
|
2113
2178
|
await saveState(state);
|
|
2114
2179
|
}
|
|
@@ -2121,6 +2186,33 @@ async function removeSectionCommand(route, featurePath, options) {
|
|
|
2121
2186
|
throw error;
|
|
2122
2187
|
}
|
|
2123
2188
|
}
|
|
2189
|
+
async function updateImportsInFile(filePath, oldFilePath, newFilePath) {
|
|
2190
|
+
if (!fs.existsSync(filePath))
|
|
2191
|
+
return;
|
|
2192
|
+
let content = await promises.readFile(filePath, 'utf-8');
|
|
2193
|
+
const oldDir = path.dirname(oldFilePath);
|
|
2194
|
+
const newDir = path.dirname(newFilePath);
|
|
2195
|
+
if (oldDir === newDir)
|
|
2196
|
+
return;
|
|
2197
|
+
// Find all relative imports
|
|
2198
|
+
const relativeImportRegex = /from\s+['"](\.\.?\/[^'"]+)['"]/g;
|
|
2199
|
+
let match;
|
|
2200
|
+
const replacements = [];
|
|
2201
|
+
while ((match = relativeImportRegex.exec(content)) !== null) {
|
|
2202
|
+
const relativePath = match[1];
|
|
2203
|
+
const absoluteTarget = path.resolve(oldDir, relativePath);
|
|
2204
|
+
const newRelativePath = getRelativeImportPath(newFilePath, absoluteTarget);
|
|
2205
|
+
replacements.push({
|
|
2206
|
+
full: match[0],
|
|
2207
|
+
oldRel: relativePath,
|
|
2208
|
+
newRel: newRelativePath
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
for (const repl of replacements) {
|
|
2212
|
+
content = content.replace(repl.full, `from '${repl.newRel}'`);
|
|
2213
|
+
}
|
|
2214
|
+
await promises.writeFile(filePath, content, 'utf-8');
|
|
2215
|
+
}
|
|
2124
2216
|
|
|
2125
2217
|
/**
|
|
2126
2218
|
* Move a section (route + feature).
|
|
@@ -2814,9 +2906,14 @@ async function removeComponentCommand(identifier, options) {
|
|
|
2814
2906
|
normalization: config.hashing?.normalization,
|
|
2815
2907
|
owner: identifier
|
|
2816
2908
|
});
|
|
2817
|
-
if (result.deleted) {
|
|
2818
|
-
|
|
2819
|
-
|
|
2909
|
+
if (result.deleted || (result.reason === 'not-found' && component)) {
|
|
2910
|
+
if (result.deleted) {
|
|
2911
|
+
console.log(`✓ Deleted component: ${componentDir}/`);
|
|
2912
|
+
await cleanupEmptyDirs(path.dirname(componentDir), path.join(process.cwd(), config.paths.components));
|
|
2913
|
+
}
|
|
2914
|
+
else {
|
|
2915
|
+
console.log(`✓ Component ${identifier} removed from state (directory was already missing on disk).`);
|
|
2916
|
+
}
|
|
2820
2917
|
// Unregister files
|
|
2821
2918
|
const relComponentPath = path.relative(process.cwd(), componentDir).replace(/\\/g, '/');
|
|
2822
2919
|
const dirPrefix = relComponentPath + '/';
|