@atlashub/smartstack-cli 4.16.1 → 4.17.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.
@@ -58448,6 +58448,8 @@ async function discoverPageFiles(webPath, routes) {
58448
58448
  const entry = { importPath, componentName };
58449
58449
  if (componentName.endsWith("CreatePage")) {
58450
58450
  discovery.create = entry;
58451
+ } else if (componentName.startsWith("Create") && componentName.endsWith("Page")) {
58452
+ discovery.create = entry;
58451
58453
  } else if (componentName.endsWith("EditPage")) {
58452
58454
  discovery.edit = entry;
58453
58455
  } else if (componentName.endsWith("DetailPage")) {
@@ -58505,17 +58507,29 @@ function generateApplicationRoutesConfig(routes, pageFiles, includeGuards) {
58505
58507
  for (const route of routes) {
58506
58508
  const discovery = pageFiles.get(route.navRoute);
58507
58509
  const { entityPlural, entitySingular, basePath } = deriveEntityNames(route.navRoute);
58508
- if (!discovery?.list) {
58509
- lines.push(`// TODO: const ${entityPlural}Page = lazy(() => import('${basePath}/${entityPlural}Page').then(m => ({ default: m.${entityPlural}Page })));`);
58510
- }
58511
- if (!discovery?.create) {
58512
- lines.push(`// TODO: const ${entitySingular}CreatePage = lazy(() => import('${basePath}/${entitySingular}CreatePage').then(m => ({ default: m.${entitySingular}CreatePage })));`);
58513
- }
58514
- if (!discovery?.detail) {
58515
- lines.push(`// TODO: const ${entitySingular}DetailPage = lazy(() => import('${basePath}/${entitySingular}DetailPage').then(m => ({ default: m.${entitySingular}DetailPage })));`);
58516
- }
58517
- if (!discovery?.edit) {
58518
- lines.push(`// TODO: const ${entitySingular}EditPage = lazy(() => import('${basePath}/${entitySingular}EditPage').then(m => ({ default: m.${entitySingular}EditPage })));`);
58510
+ if (!discovery?.list && !importedComponents.has(`${entityPlural}Page`)) {
58511
+ importedComponents.add(`${entityPlural}Page`);
58512
+ lines.push(`const ${entityPlural}Page = lazy(() =>`);
58513
+ lines.push(` import('${basePath}/${entityPlural}Page').then(m => ({ default: m.${entityPlural}Page }))`);
58514
+ lines.push(");");
58515
+ }
58516
+ if (!discovery?.create && !importedComponents.has(`${entitySingular}CreatePage`)) {
58517
+ importedComponents.add(`${entitySingular}CreatePage`);
58518
+ lines.push(`const ${entitySingular}CreatePage = lazy(() =>`);
58519
+ lines.push(` import('${basePath}/${entitySingular}CreatePage').then(m => ({ default: m.${entitySingular}CreatePage }))`);
58520
+ lines.push(");");
58521
+ }
58522
+ if (!discovery?.detail && !importedComponents.has(`${entitySingular}DetailPage`)) {
58523
+ importedComponents.add(`${entitySingular}DetailPage`);
58524
+ lines.push(`const ${entitySingular}DetailPage = lazy(() =>`);
58525
+ lines.push(` import('${basePath}/${entitySingular}DetailPage').then(m => ({ default: m.${entitySingular}DetailPage }))`);
58526
+ lines.push(");");
58527
+ }
58528
+ if (!discovery?.edit && !importedComponents.has(`${entitySingular}EditPage`)) {
58529
+ importedComponents.add(`${entitySingular}EditPage`);
58530
+ lines.push(`const ${entitySingular}EditPage = lazy(() =>`);
58531
+ lines.push(` import('${basePath}/${entitySingular}EditPage').then(m => ({ default: m.${entitySingular}EditPage }))`);
58532
+ lines.push(");");
58519
58533
  }
58520
58534
  }
58521
58535
  lines.push("");
@@ -58538,33 +58552,17 @@ function generateApplicationRoutesConfig(routes, pageFiles, includeGuards) {
58538
58552
  const permGuardClose = permGuardOpen ? "</PermissionGuard>" : "";
58539
58553
  lines.push(` // === ${entityPlural.toLowerCase()} (NavRoute: ${route.navRoute}) ===`);
58540
58554
  const listComp = discovery?.list?.componentName || `${entityPlural}Page`;
58541
- const listElement = discovery?.list ? `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${listComp} />${permGuardClose}</Suspense>` : void 0;
58542
- if (listElement) {
58543
- lines.push(` { path: '${modulePath}', element: ${listElement} },`);
58544
- } else {
58545
- lines.push(` // TODO: { path: '${modulePath}', element: <${listComp} /> },`);
58546
- }
58555
+ const listElement = `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${listComp} />${permGuardClose}</Suspense>`;
58556
+ lines.push(` { path: '${modulePath}', element: ${listElement} },`);
58547
58557
  const createComp = discovery?.create?.componentName || `${entitySingular}CreatePage`;
58548
- const createElement = discovery?.create ? `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${createComp} />${permGuardClose}</Suspense>` : void 0;
58549
- if (createElement) {
58550
- lines.push(` { path: '${modulePath}/create', element: ${createElement} },`);
58551
- } else {
58552
- lines.push(` // TODO: { path: '${modulePath}/create', element: <${createComp} /> },`);
58553
- }
58558
+ const createElement = `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${createComp} />${permGuardClose}</Suspense>`;
58559
+ lines.push(` { path: '${modulePath}/create', element: ${createElement} },`);
58554
58560
  const detailComp = discovery?.detail?.componentName || `${entitySingular}DetailPage`;
58555
- const detailElement = discovery?.detail ? `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${detailComp} />${permGuardClose}</Suspense>` : void 0;
58556
- if (detailElement) {
58557
- lines.push(` { path: '${modulePath}/:id', element: ${detailElement} },`);
58558
- } else {
58559
- lines.push(` // TODO: { path: '${modulePath}/:id', element: <${detailComp} /> },`);
58560
- }
58561
+ const detailElement = `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${detailComp} />${permGuardClose}</Suspense>`;
58562
+ lines.push(` { path: '${modulePath}/:id', element: ${detailElement} },`);
58561
58563
  const editComp = discovery?.edit?.componentName || `${entitySingular}EditPage`;
58562
- const editElement = discovery?.edit ? `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${editComp} />${permGuardClose}</Suspense>` : void 0;
58563
- if (editElement) {
58564
- lines.push(` { path: '${modulePath}/:id/edit', element: ${editElement} },`);
58565
- } else {
58566
- lines.push(` // TODO: { path: '${modulePath}/:id/edit', element: <${editComp} /> },`);
58567
- }
58564
+ const editElement = `<Suspense fallback={<PageLoader />}>${permGuardOpen}<${editComp} />${permGuardClose}</Suspense>`;
58565
+ lines.push(` { path: '${modulePath}/:id/edit', element: ${editElement} },`);
58568
58566
  }
58569
58567
  }
58570
58568
  lines.push("");
@@ -58795,6 +58793,9 @@ async function validateFrontendRoutes2(input, config2) {
58795
58793
  if (scope === "all" || scope === "routes") {
58796
58794
  await validateAppWiring(webPath, backendRoutes, result);
58797
58795
  }
58796
+ if (scope === "all" || scope === "routes") {
58797
+ await validateCrudRoutes(webPath, backendRoutes, result);
58798
+ }
58798
58799
  generateRecommendations2(result);
58799
58800
  result.valid = result.apiClients.issues.filter((i) => i.severity === "error").length === 0 && result.routes.missing.length === 0 && result.registry.exists && result.appWiring.issues.length === 0;
58800
58801
  return result;
@@ -59094,6 +59095,52 @@ async function validateAppWiring(webPath, backendRoutes, result) {
59094
59095
  }
59095
59096
  }
59096
59097
  }
59098
+ async function validateCrudRoutes(webPath, backendRoutes, result) {
59099
+ const routeCandidates = ["applicationRoutes.generated.tsx", "clientRoutes.generated.tsx"];
59100
+ let routesContent = "";
59101
+ for (const candidate of routeCandidates) {
59102
+ const candidatePath = path20.join(webPath, "src", "routes", candidate);
59103
+ if (await fileExists(candidatePath)) {
59104
+ try {
59105
+ routesContent = await readText(candidatePath);
59106
+ } catch {
59107
+ logger.debug(`Failed to read ${candidatePath}`);
59108
+ }
59109
+ break;
59110
+ }
59111
+ }
59112
+ if (!routesContent) {
59113
+ return;
59114
+ }
59115
+ for (const route of backendRoutes) {
59116
+ const modulePath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
59117
+ if (modulePath.length === 0) continue;
59118
+ const crudSuffixes = [
59119
+ { suffix: "/create", label: "create" },
59120
+ { suffix: "/:id", label: "detail" },
59121
+ { suffix: "/:id/edit", label: "edit" }
59122
+ ];
59123
+ for (const { suffix, label } of crudSuffixes) {
59124
+ const fullPath = `${modulePath}${suffix}`;
59125
+ const todoPattern = `// TODO:.*path:\\s*'${fullPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}'`;
59126
+ if (new RegExp(todoPattern).test(routesContent)) {
59127
+ result.appWiring.issues.push(
59128
+ `Route "${route.navRoute}" ${label} route is commented out (// TODO:). Run \`scaffold_routes\` to regenerate active routes.`
59129
+ );
59130
+ }
59131
+ if (label === "create") {
59132
+ const newPath = `${modulePath}/new`;
59133
+ const hasNewRoute = routesContent.includes(`path: '${newPath}'`) || routesContent.includes(`path="${newPath}"`);
59134
+ const hasCreateRoute = routesContent.includes(`path: '${modulePath}/create'`) || routesContent.includes(`path="${modulePath}/create"`);
59135
+ if (hasNewRoute && !hasCreateRoute) {
59136
+ result.appWiring.issues.push(
59137
+ `Route "${route.navRoute}" uses /new instead of /create. SmartStack convention is /create. Run \`scaffold_routes\` to fix.`
59138
+ );
59139
+ }
59140
+ }
59141
+ }
59142
+ }
59143
+ }
59097
59144
  function generateRecommendations2(result) {
59098
59145
  if (!result.registry.exists) {
59099
59146
  result.recommendations.push('Run `scaffold_routes source="controllers"` to generate route registry');