@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.
- package/package.json +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.
|
|
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.
|
|
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.
|