@screenbook/cli 1.7.0 → 1.7.1
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/dist/index.mjs +114 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -827,23 +827,22 @@ async function generateCoverageData(config, cwd, screens) {
|
|
|
827
827
|
cwd,
|
|
828
828
|
ignore: config.ignore
|
|
829
829
|
});
|
|
830
|
-
new Set(
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
830
|
+
const metaDirs = /* @__PURE__ */ new Set();
|
|
831
|
+
for (const screen of screens) if (screen.filePath) {
|
|
832
|
+
const absoluteDir = dirname(screen.filePath);
|
|
833
|
+
const relativeDir = absoluteDir.startsWith(cwd) ? absoluteDir.slice(cwd.length + 1) : absoluteDir;
|
|
834
|
+
metaDirs.add(relativeDir);
|
|
835
|
+
}
|
|
834
836
|
const missing = [];
|
|
835
837
|
for (const routeFile of routeFiles) {
|
|
836
838
|
const routeDir = dirname(routeFile);
|
|
837
|
-
if (!
|
|
838
|
-
const screenDir = s.id.replace(/\./g, "/");
|
|
839
|
-
return routeDir.includes(screenDir) || screenDir.includes(routeDir.replace(/^src\/pages\//, "").replace(/^app\//, ""));
|
|
840
|
-
})) missing.push({
|
|
839
|
+
if (!metaDirs.has(routeDir)) missing.push({
|
|
841
840
|
route: routeFile,
|
|
842
841
|
suggestedPath: join(dirname(routeFile), "screen.meta.ts")
|
|
843
842
|
});
|
|
844
843
|
}
|
|
845
844
|
const total = routeFiles.length > 0 ? routeFiles.length : screens.length;
|
|
846
|
-
const covered = screens.length;
|
|
845
|
+
const covered = routeFiles.length > 0 ? routeFiles.length - missing.length : screens.length;
|
|
847
846
|
const percentage = total > 0 ? Math.round(covered / total * 100) : 100;
|
|
848
847
|
const byOwner = {};
|
|
849
848
|
for (const screen of screens) {
|
|
@@ -3326,6 +3325,83 @@ function walkTemplateNodes(nodes, callback) {
|
|
|
3326
3325
|
|
|
3327
3326
|
//#endregion
|
|
3328
3327
|
//#region src/commands/generate.ts
|
|
3328
|
+
/**
|
|
3329
|
+
* Common paths where Vue Router configuration files are typically located
|
|
3330
|
+
*/
|
|
3331
|
+
const VUE_ROUTER_CONFIG_PATHS = [
|
|
3332
|
+
"src/router/routes.ts",
|
|
3333
|
+
"src/router/index.ts",
|
|
3334
|
+
"src/router.ts",
|
|
3335
|
+
"src/routes.ts",
|
|
3336
|
+
"router/routes.ts",
|
|
3337
|
+
"router/index.ts"
|
|
3338
|
+
];
|
|
3339
|
+
/**
|
|
3340
|
+
* Default patterns to exclude from screen.meta.ts generation.
|
|
3341
|
+
* These directories typically contain reusable components, not navigable screens.
|
|
3342
|
+
* @see https://github.com/wadakatu/screenbook/issues/170
|
|
3343
|
+
*/
|
|
3344
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
3345
|
+
"**/components/**",
|
|
3346
|
+
"**/shared/**",
|
|
3347
|
+
"**/utils/**",
|
|
3348
|
+
"**/hooks/**",
|
|
3349
|
+
"**/composables/**",
|
|
3350
|
+
"**/stores/**",
|
|
3351
|
+
"**/services/**",
|
|
3352
|
+
"**/helpers/**",
|
|
3353
|
+
"**/lib/**",
|
|
3354
|
+
"**/common/**"
|
|
3355
|
+
];
|
|
3356
|
+
/**
|
|
3357
|
+
* Check if a path matches any of the exclude patterns
|
|
3358
|
+
*/
|
|
3359
|
+
function matchesExcludePattern(filePath, excludePatterns) {
|
|
3360
|
+
for (const pattern of excludePatterns) {
|
|
3361
|
+
const cleanPattern = pattern.replace(/\*\*/g, "").replace(/\*/g, "").replace(/^\//, "").replace(/\/$/, "");
|
|
3362
|
+
if (cleanPattern && filePath.includes(`/${cleanPattern}/`)) return true;
|
|
3363
|
+
if (cleanPattern && filePath.startsWith(`${cleanPattern}/`)) return true;
|
|
3364
|
+
}
|
|
3365
|
+
return false;
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Detect Vue Router configuration file in the project
|
|
3369
|
+
*/
|
|
3370
|
+
function detectVueRouterConfigFile(cwd) {
|
|
3371
|
+
for (const configPath of VUE_ROUTER_CONFIG_PATHS) {
|
|
3372
|
+
const absolutePath = join(cwd, configPath);
|
|
3373
|
+
if (existsSync(absolutePath)) return absolutePath;
|
|
3374
|
+
}
|
|
3375
|
+
return null;
|
|
3376
|
+
}
|
|
3377
|
+
/**
|
|
3378
|
+
* Build a map from component file paths to their route information.
|
|
3379
|
+
* Normalizes paths for reliable matching.
|
|
3380
|
+
*/
|
|
3381
|
+
function buildRouteComponentMap(flatRoutes, cwd) {
|
|
3382
|
+
const map = /* @__PURE__ */ new Map();
|
|
3383
|
+
for (const route of flatRoutes) if (route.componentPath) {
|
|
3384
|
+
const componentDir = dirname(relative(cwd, route.componentPath));
|
|
3385
|
+
const componentNameWithoutExt = basename(route.componentPath).replace(/\.[^.]+$/, "");
|
|
3386
|
+
map.set(componentDir, route);
|
|
3387
|
+
map.set(basename(componentDir), route);
|
|
3388
|
+
map.set(componentNameWithoutExt, route);
|
|
3389
|
+
}
|
|
3390
|
+
return map;
|
|
3391
|
+
}
|
|
3392
|
+
/**
|
|
3393
|
+
* Find matching route from the component map for a given route file
|
|
3394
|
+
*/
|
|
3395
|
+
function findMatchingRoute(routeFile, routeComponentMap) {
|
|
3396
|
+
const routeDir = dirname(routeFile);
|
|
3397
|
+
const routeDirName = basename(routeDir);
|
|
3398
|
+
if (routeComponentMap.has(routeDir)) return routeComponentMap.get(routeDir) ?? null;
|
|
3399
|
+
if (routeComponentMap.has(routeDirName)) return routeComponentMap.get(routeDirName) ?? null;
|
|
3400
|
+
for (const [, route] of routeComponentMap) if (route.componentPath) {
|
|
3401
|
+
if (basename(dirname(route.componentPath)) === routeDirName) return route;
|
|
3402
|
+
}
|
|
3403
|
+
return null;
|
|
3404
|
+
}
|
|
3329
3405
|
const generateCommand = define({
|
|
3330
3406
|
name: "generate",
|
|
3331
3407
|
description: "Auto-generate screen.meta.ts files from route files",
|
|
@@ -3463,6 +3539,10 @@ async function generateFromRoutesFile(routesFile, cwd, options) {
|
|
|
3463
3539
|
for (const route of flatRoutes) {
|
|
3464
3540
|
const metaPath = determineMetaPath(route, cwd);
|
|
3465
3541
|
const absoluteMetaPath = resolve(cwd, metaPath);
|
|
3542
|
+
if (matchesExcludePattern(metaPath, DEFAULT_EXCLUDE_PATTERNS)) {
|
|
3543
|
+
skipped++;
|
|
3544
|
+
continue;
|
|
3545
|
+
}
|
|
3466
3546
|
if (!force && existsSync(absoluteMetaPath)) {
|
|
3467
3547
|
if (!interactive) {
|
|
3468
3548
|
skipped++;
|
|
@@ -3557,13 +3637,29 @@ async function generateFromRoutesPattern(routesPattern, cwd, options) {
|
|
|
3557
3637
|
logger.blank();
|
|
3558
3638
|
const routeFiles = await glob(routesPattern, {
|
|
3559
3639
|
cwd,
|
|
3560
|
-
ignore
|
|
3640
|
+
ignore: [...ignore, ...DEFAULT_EXCLUDE_PATTERNS]
|
|
3561
3641
|
});
|
|
3562
3642
|
if (routeFiles.length === 0) {
|
|
3563
3643
|
logger.warn(`No route files found matching: ${routesPattern}`);
|
|
3564
3644
|
return;
|
|
3565
3645
|
}
|
|
3566
3646
|
logger.log(`Found ${routeFiles.length} route files`);
|
|
3647
|
+
let routeComponentMap = null;
|
|
3648
|
+
if (routesPattern.includes(".vue")) {
|
|
3649
|
+
const vueRouterConfig = detectVueRouterConfigFile(cwd);
|
|
3650
|
+
if (vueRouterConfig) try {
|
|
3651
|
+
const parseResult = parseVueRouterConfig(vueRouterConfig);
|
|
3652
|
+
const flatRoutes = flattenRoutes(parseResult.routes);
|
|
3653
|
+
if (flatRoutes.length > 0) {
|
|
3654
|
+
routeComponentMap = buildRouteComponentMap(flatRoutes, cwd);
|
|
3655
|
+
logger.log(` ${logger.dim(`(using routes from ${relative(cwd, vueRouterConfig)})`)}`);
|
|
3656
|
+
}
|
|
3657
|
+
for (const warning of parseResult.warnings) logger.warn(warning);
|
|
3658
|
+
} catch (error) {
|
|
3659
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3660
|
+
logger.warn(`Could not parse Vue Router config: ${message}`);
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3567
3663
|
logger.blank();
|
|
3568
3664
|
let created = 0;
|
|
3569
3665
|
let skipped = 0;
|
|
@@ -3580,7 +3676,14 @@ async function generateFromRoutesPattern(routesPattern, cwd, options) {
|
|
|
3580
3676
|
skipped++;
|
|
3581
3677
|
continue;
|
|
3582
3678
|
}
|
|
3583
|
-
|
|
3679
|
+
let screenMeta;
|
|
3680
|
+
const matchedRoute = routeComponentMap ? findMatchingRoute(routeFile, routeComponentMap) : null;
|
|
3681
|
+
if (matchedRoute) screenMeta = {
|
|
3682
|
+
id: matchedRoute.screenId,
|
|
3683
|
+
title: matchedRoute.screenTitle,
|
|
3684
|
+
route: matchedRoute.fullPath
|
|
3685
|
+
};
|
|
3686
|
+
else screenMeta = inferScreenMeta(routeDir, routesPattern);
|
|
3584
3687
|
let detectedApis = [];
|
|
3585
3688
|
if (detectApi && apiIntegration) {
|
|
3586
3689
|
const absoluteRouteFile = join(cwd, routeFile);
|