@elytracms/cli 0.0.90 → 0.0.91

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/doctor.js +80 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elytracms/cli",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.90",
4
+ "version": "0.0.91",
5
5
  "description": "The Elytra CLI — run a consuming repo's studio, frontend, and Convex backend under one command.",
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "node": ">=22.18.0"
18
18
  },
19
19
  "dependencies": {
20
- "@elytracms/core": "^0.0.90"
20
+ "@elytracms/core": "^0.0.91"
21
21
  },
22
22
  "devDependencies": {
23
23
  "vitest": "^4.1.10"
package/src/doctor.js CHANGED
@@ -497,6 +497,73 @@ export async function routeFactsFor({ outcome, configFacts = {}, routeFacts, roo
497
497
  return { ...enriched, source: 'native' }
498
498
  }
499
499
 
500
+ // --- Frontend: `cacheComponents` (EC-515) --------------------------------------
501
+
502
+ /** The `next.config` file names Next itself resolves, in its order. */
503
+ export const NEXT_CONFIG_FILES = ['next.config.js', 'next.config.mjs', 'next.config.ts', 'next.config.mts', 'next.config.cjs']
504
+
505
+ /**
506
+ * Read the frontend's `next.config` for the `cacheComponents` check — the file name and
507
+ * its text, or `{ configFile: null }` when the app has none. `undefined` when there is no
508
+ * frontend app to read (a studio-only repo: no row at all). Text only: the CLI runs
509
+ * without the consumer's `node_modules`, and a `next.config` imports Next-side modules.
510
+ * `readFile` is injectable for tests.
511
+ */
512
+ export function gatherFrontendCacheComponents(frontendDir, readFile = (p) => readFileSync(p, 'utf8')) {
513
+ if (!frontendDir) return undefined
514
+ for (const name of NEXT_CONFIG_FILES) {
515
+ try {
516
+ return { configFile: name, text: readFile(join(frontendDir, name)) }
517
+ } catch {
518
+ // not this name — try the next
519
+ }
520
+ }
521
+ return { configFile: null, text: '' }
522
+ }
523
+
524
+ /**
525
+ * The `frontend-cache-components` row (`app` bucket). Since EC-515 the delivery kit's
526
+ * cache is a `'use cache'` entry, which Next compiles only with `cacheComponents: true`
527
+ * — without it `next build` fails under both bundlers (measured: "To use "use cache",
528
+ * please enable the feature flag `cacheComponents`"). So a missing flag is a `fail`:
529
+ * deterministic in the repo, and the build would fail anyway — so is a literal `false`. A
530
+ * `cacheComponents` key that is neither literal (computed from env, say) is a `warn` —
531
+ * the doctor reads text and cannot evaluate it. No frontend → no row. Pure.
532
+ */
533
+ export function cacheComponentsDoctorChecks(facts, { frontendLabel = 'frontend' } = {}) {
534
+ if (!facts) return []
535
+ const base = { id: 'frontend-cache-components', bucket: 'app' }
536
+ const fix = `Set \`cacheComponents: true\` in ${frontendLabel}/next.config.mjs, and export \`generateStaticParams = route.generateStaticParams\` from the canvas catch-all page (see the upgrading notes for EC-515).`
537
+ if (facts.configFile === null) {
538
+ return [{ ...base, status: 'fail', detail: `${frontendLabel} has no next.config — the delivery kit needs \`cacheComponents: true\`.`, fix }]
539
+ }
540
+ const where = `${frontendLabel}/${facts.configFile}`
541
+ if (/\bcacheComponents\s*:\s*true\b/.test(facts.text)) {
542
+ return [{ ...base, status: 'ok', detail: `${where} enables \`cacheComponents\` (the kit's 'use cache' delivery cache).` }]
543
+ }
544
+ if (/\bcacheComponents\s*:\s*false\b/.test(facts.text)) {
545
+ return [{ ...base, status: 'fail', detail: `${where} sets \`cacheComponents: false\` — \`next build\` fails on the delivery kit's 'use cache' cache.`, fix }]
546
+ }
547
+ if (/\bcacheComponents\b/.test(facts.text)) {
548
+ return [
549
+ {
550
+ ...base,
551
+ status: 'warn',
552
+ detail: `${where} sets \`cacheComponents\` to something other than the literal \`true\` — the doctor cannot evaluate it. \`next build\` fails if it is not true.`,
553
+ fix,
554
+ },
555
+ ]
556
+ }
557
+ return [
558
+ {
559
+ ...base,
560
+ status: 'fail',
561
+ detail: `${where} does not enable \`cacheComponents\` — \`next build\` fails on the delivery kit's 'use cache' cache.`,
562
+ fix,
563
+ },
564
+ ]
565
+ }
566
+
500
567
  // --- Command -----------------------------------------------------------------
501
568
 
502
569
  /**
@@ -553,6 +620,12 @@ export async function routeFactsFor({ outcome, configFacts = {}, routeFacts, roo
553
620
  * - the route checks run over the RESOLVED config's routes whenever the harness
554
621
  * resolved it (the routes the studio and delivery actually serve); the native
555
622
  * `cms/index.ts` read is the fallback only when it could not, and the row says which.
623
+ *
624
+ * EC-515: when the layout resolved a frontend app, the `app` bucket reports
625
+ * `frontend-cache-components` — whether its `next.config` enables `cacheComponents`,
626
+ * which the delivery kit's `'use cache'` cache requires ({@link
627
+ * cacheComponentsDoctorChecks}). `frontendCacheComponents` (the gathered facts) is the
628
+ * test seam.
556
629
  */
557
630
  export async function runDoctor({
558
631
  env = {},
@@ -569,6 +642,7 @@ export async function runDoctor({
569
642
  routeFacts,
570
643
  configOutcome,
571
644
  installedEngineVersion,
645
+ frontendCacheComponents,
572
646
  } = {}) {
573
647
  const studioLabel = appDisplay(layout, 'studio')
574
648
  const target = resolveDoctorTarget(env, { url, envSource: `${studioLabel}/.env` })
@@ -638,9 +712,15 @@ export async function runDoctor({
638
712
  let checks = artifacts ? [...report.checks, ...previewsDoctorChecks(artifacts)] : [...report.checks]
639
713
 
640
714
  // EC-603: the layout and AI-route rows, in the `app` bucket (both repo facts).
715
+ // EC-515: the frontend's `cacheComponents` flag — a repo fact, any backend mode.
716
+ const frontend = layout?.entries?.frontend
641
717
  checks = spliceBucketChecks(checks, 'app', [
642
718
  ...layoutDoctorChecks(layout),
643
719
  ...aiRouteDoctorChecks(aiRoute, { studioLabel }),
720
+ ...cacheComponentsDoctorChecks(
721
+ frontendCacheComponents ?? gatherFrontendCacheComponents(frontend?.found ? frontend.path : undefined),
722
+ { frontendLabel: appDisplay(layout, 'frontend') },
723
+ ),
644
724
  ])
645
725
 
646
726
  // EC-469: the route-conflict check, spliced into the `app` bucket it belongs to.