@nuasite/cli 0.53.0 → 0.54.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuasite/cli",
3
- "version": "0.53.0",
3
+ "version": "0.54.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -25,8 +25,8 @@
25
25
  "dependencies": {
26
26
  "@astrojs/compiler": "^3.0.1",
27
27
  "@jridgewell/trace-mapping": "^0.3.31",
28
- "@nuasite/cms": "0.53.0",
29
- "@nuasite/cms-core": "0.53.0"
28
+ "@nuasite/cms": "0.54.0",
29
+ "@nuasite/cms-core": "0.54.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/bun": "1.3.11"
package/src/check.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { checkContent, type CheckReport, createNodeFs, formatCheckReport } from '@nuasite/cms-core'
1
+ import { checkContent, type CheckReport, createNodeFs, formatCheckReport, type LiveSchemas } from '@nuasite/cms-core'
2
2
  import { existsSync, readdirSync } from 'node:fs'
3
3
  import path from 'node:path'
4
4
  import { checkCode } from './check-code'
5
+ import { loadLiveSchemas } from './live-schema'
5
6
 
6
7
  export interface CheckOptions {
7
8
  cwd: string
@@ -10,6 +11,26 @@ export interface CheckOptions {
10
11
  strict: boolean
11
12
  /** Skip the code syntax pass — content only. */
12
13
  contentOnly: boolean
14
+ /** Validate against the project's real schemas, which means importing and running its content config. */
15
+ live: boolean
16
+ }
17
+
18
+ /**
19
+ * `nua check`'s argv, as options.
20
+ *
21
+ * Pure and exported so the flag combinations are testable: `index.ts` is a top-level-await
22
+ * script that reads `process.argv` and calls `process.exit`, so a test cannot import it.
23
+ */
24
+ export function parseCheckArgs(args: string[], cwd: string): CheckOptions {
25
+ const contentOnly = args.includes('--content-only')
26
+ return {
27
+ cwd,
28
+ json: args.includes('--json'),
29
+ strict: args.includes('--strict'),
30
+ contentOnly,
31
+ // Loading the real schemas executes the project's content config — the least content-only thing this command does.
32
+ live: !contentOnly && !args.includes('--no-live'),
33
+ }
13
34
  }
14
35
 
15
36
  const ASTRO_CONFIG_NAMES = ['astro.config.ts', 'astro.config.mts', 'astro.config.mjs', 'astro.config.js']
@@ -47,6 +68,25 @@ function prefixed(report: CheckReport, root: string): CheckReport {
47
68
  }
48
69
  }
49
70
 
71
+ /** The project's real schemas, or the sentence saying why the rules needing them did not run. */
72
+ type LiveResult = { schemas: LiveSchemas } | { skipped: string }
73
+
74
+ async function liveSchemasFor(options: CheckOptions, roots: string[]): Promise<LiveResult> {
75
+ if (options.contentOnly) {
76
+ return { skipped: "--content-only was passed, and reading the real schemas means executing the project's content config." }
77
+ }
78
+ if (!options.live) return { skipped: '--no-live was passed.' }
79
+
80
+ const first = roots[0]
81
+ // The `astro:content` stub is process-global and re-exports one project's zod, so a second
82
+ // project would be judged by the first project's schemas — quietly wrong is worse than skipped.
83
+ if (roots.length !== 1 || first === undefined) {
84
+ const where = roots.join(', ')
85
+ return { skipped: `They need exactly one Astro project; this run covers ${roots.length} (${where}). Check each one from its own directory.` }
86
+ }
87
+ return loadLiveSchemas(path.join(options.cwd, first))
88
+ }
89
+
50
90
  /** Validate the content collections and return the process exit code. */
51
91
  export async function check(options: CheckOptions): Promise<number> {
52
92
  const roots = astroRoots(options.cwd)
@@ -56,9 +96,12 @@ export async function check(options: CheckOptions): Promise<number> {
56
96
  return 1
57
97
  }
58
98
 
99
+ const live = await liveSchemasFor(options, roots)
100
+ const schemas = 'schemas' in live ? live.schemas : undefined
101
+
59
102
  const reports = await Promise.all(roots.map(async root => {
60
103
  const absolute = path.join(options.cwd, root)
61
- const content = prefixed(await checkContent(createNodeFs(absolute)), root)
104
+ const content = prefixed(await checkContent(createNodeFs(absolute), schemas ? { schemas } : {}), root)
62
105
  if (options.contentOnly) return { ...content, files: 0 }
63
106
  const code = await checkCode(absolute)
64
107
  return {
@@ -79,7 +122,8 @@ export async function check(options: CheckOptions): Promise<number> {
79
122
  const warnings = report.findings.length - errors
80
123
 
81
124
  if (options.json) {
82
- console.log(JSON.stringify({ ...report, roots }, null, 2))
125
+ // The reason belongs inside the payload: a stray line would break anything parsing this.
126
+ console.log(JSON.stringify({ ...report, roots, liveSchemas: 'schemas' in live ? 'ran' : live.skipped }, null, 2))
83
127
  } else {
84
128
  const formatted = formatCheckReport(report)
85
129
  if (formatted) console.log(formatted + '\n')
@@ -87,6 +131,8 @@ export async function check(options: CheckOptions): Promise<number> {
87
131
  const code = options.contentOnly ? '' : `, ${sourceFiles} source file(s)`
88
132
  const counts = `${report.collections} collection(s), ${report.entries} entries${code}${where}`
89
133
  console.log(errors === 0 && warnings === 0 ? `${counts} — no problems found` : `${counts} — ${errors} error(s), ${warnings} warning(s)`)
134
+ // A check that did not run must not read as a pass.
135
+ if ('skipped' in live) console.log(`Live schema rules did not run: ${live.skipped}`)
90
136
  }
91
137
 
92
138
  return errors > 0 || (options.strict && warnings > 0) ? 1 : 0
package/src/index.ts CHANGED
@@ -25,7 +25,7 @@ function printUsage() {
25
25
  console.log(' build Run astro build with the Nua defaults')
26
26
  console.log(' dev Run astro dev with the Nua defaults')
27
27
  console.log(' preview Run astro preview with the Nua defaults')
28
- console.log(' check Validate collections and parse the source (no build). --json, --strict, --content-only')
28
+ console.log(' check Validate collections and parse the source (no build). --json, --strict, --content-only, --no-live')
29
29
  console.log(' init Convert a standard Astro project to use Nua')
30
30
  console.log(' clean Eject to a standard Astro project (remove @nuasite/* deps)')
31
31
  console.log(' migrate <target> Run a content migration. Targets: astro-image')
@@ -38,15 +38,8 @@ if (command && ['build', 'dev', 'preview'].includes(command)) {
38
38
  } else {
39
39
  switch (command) {
40
40
  case 'check': {
41
- const { check } = await import('./check')
42
- process.exit(
43
- await check({
44
- cwd: process.cwd(),
45
- json: args.includes('--json'),
46
- strict: args.includes('--strict'),
47
- contentOnly: args.includes('--content-only'),
48
- }),
49
- )
41
+ const { check, parseCheckArgs } = await import('./check')
42
+ process.exit(await check(parseCheckArgs(args, process.cwd())))
50
43
  break
51
44
  }
52
45
  case 'init': {
Binary file