@neuxnet/neux-cli 0.2.3 → 0.2.5

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.
@@ -0,0 +1,63 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://neuxnet.com/schemas/neux/app.schema.json",
4
+ "title": "Neux app configuration",
5
+ "description": "Application-level configuration consumed by the Neux mini program compiler.",
6
+ "type": "object",
7
+ "properties": {
8
+ "pages": { "type": "array", "description": "Page entry paths, relative to miniprogramRoot.", "items": { "type": "string", "minLength": 1 } },
9
+ "entryPagePath": { "type": "string", "description": "Optional initial page path." },
10
+ "window": { "$ref": "#/definitions/window" },
11
+ "tabBar": { "$ref": "#/definitions/tabBar" },
12
+ "subPackages": { "$ref": "#/definitions/subPackages" },
13
+ "subpackages": { "$ref": "#/definitions/subPackages" },
14
+ "preloadRule": { "type": "object", "description": "Page preload configuration.", "additionalProperties": { "type": "object" } },
15
+ "networkTimeout": { "type": "object", "properties": { "request": { "type": "number", "minimum": 0 }, "connectSocket": { "type": "number", "minimum": 0 }, "uploadFile": { "type": "number", "minimum": 0 }, "downloadFile": { "type": "number", "minimum": 0 } }, "additionalProperties": true },
16
+ "permission": { "type": "object", "description": "Permission declarations used by the host runtime.", "additionalProperties": true },
17
+ "usingComponents": { "$ref": "#/definitions/usingComponents" },
18
+ "style": { "type": "string" },
19
+ "sitemapLocation": { "type": "string" }
20
+ },
21
+ "additionalProperties": true,
22
+ "definitions": {
23
+ "window": {
24
+ "type": "object",
25
+ "properties": {
26
+ "navigationBarTitleText": { "type": "string" },
27
+ "navigationBarBackgroundColor": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
28
+ "navigationBarTextStyle": { "type": "string", "enum": ["black", "white"] },
29
+ "backgroundColor": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
30
+ "backgroundTextStyle": { "type": "string", "enum": ["dark", "light"] },
31
+ "enablePullDownRefresh": { "type": "boolean" },
32
+ "onReachBottomDistance": { "type": "number", "minimum": 0 },
33
+ "pageOrientation": { "type": "string" }
34
+ },
35
+ "additionalProperties": true
36
+ },
37
+ "tabBar": {
38
+ "type": "object",
39
+ "properties": {
40
+ "color": { "type": "string" },
41
+ "selectedColor": { "type": "string" },
42
+ "backgroundColor": { "type": "string" },
43
+ "borderStyle": { "type": "string", "enum": ["black", "white"] },
44
+ "position": { "type": "string", "enum": ["bottom", "top"] },
45
+ "custom": { "type": "boolean" },
46
+ "list": { "type": "array", "minItems": 1, "maxItems": 5, "items": { "$ref": "#/definitions/tabBarItem" } }
47
+ },
48
+ "required": ["list"],
49
+ "additionalProperties": true
50
+ },
51
+ "tabBarItem": {
52
+ "type": "object",
53
+ "properties": { "pagePath": { "type": "string" }, "text": { "type": "string" }, "iconPath": { "type": "string" }, "selectedIconPath": { "type": "string" } },
54
+ "required": ["pagePath", "text"],
55
+ "additionalProperties": true
56
+ },
57
+ "subPackages": {
58
+ "type": "array",
59
+ "items": { "type": "object", "properties": { "root": { "type": "string" }, "pages": { "type": "array", "items": { "type": "string" } }, "independent": { "type": "boolean" } }, "required": ["root", "pages"], "additionalProperties": true }
60
+ },
61
+ "usingComponents": { "type": "object", "description": "Maps component names to component files or package names.", "additionalProperties": { "type": "string", "minLength": 1 } }
62
+ }
63
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://neuxnet.com/schemas/neux/neux-config.schema.json",
4
+ "title": "Neux CLI project configuration",
5
+ "description": "Non-secret project settings read by the Neux CLI. Never put CLI keys in this file.",
6
+ "type": "object",
7
+ "properties": {
8
+ "serverUrl": { "type": "string", "format": "uri", "description": "Signed service base URL." },
9
+ "appId": { "type": "string" },
10
+ "appid": { "type": "string" },
11
+ "profile": { "type": "string" },
12
+ "version": { "type": "string" },
13
+ "versionName": { "type": "string" },
14
+ "versionCode": { "type": "integer", "minimum": 1 }
15
+ },
16
+ "not": { "anyOf": [ { "required": ["key"] }, { "required": ["cliKey"] }, { "required": ["neuxCliKey"] } ] },
17
+ "additionalProperties": true
18
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://neuxnet.com/schemas/neux/page.schema.json",
4
+ "title": "Neux page configuration",
5
+ "description": "Page-level configuration consumed by the Neux mini program compiler.",
6
+ "type": "object",
7
+ "properties": {
8
+ "navigationBarTitleText": { "type": "string" },
9
+ "navigationBarBackgroundColor": { "type": "string" },
10
+ "navigationBarTextStyle": { "type": "string", "enum": ["black", "white"] },
11
+ "backgroundColor": { "type": "string" },
12
+ "backgroundTextStyle": { "type": "string", "enum": ["dark", "light"] },
13
+ "enablePullDownRefresh": { "type": "boolean" },
14
+ "onReachBottomDistance": { "type": "number", "minimum": 0 },
15
+ "usingComponents": { "type": "object", "additionalProperties": { "type": "string", "minLength": 1 } },
16
+ "disableScroll": { "type": "boolean" },
17
+ "pageOrientation": { "type": "string" }
18
+ },
19
+ "additionalProperties": true
20
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://neuxnet.com/schemas/neux/project-config.schema.json",
4
+ "title": "Neux project configuration",
5
+ "description": "Project and compiler settings consumed by neux and the Neux compiler.",
6
+ "type": "object",
7
+ "properties": {
8
+ "description": { "type": "string" }, "appid": { "type": "string" }, "appId": { "type": "string" },
9
+ "projectname": { "type": "string" }, "projectName": { "type": "string" },
10
+ "compileType": { "type": "string", "enum": ["miniprogram"] }, "libVersion": { "type": "string" },
11
+ "miniprogramRoot": { "type": "string" }, "versionName": { "type": "string" }, "version": { "type": "string" }, "versionCode": { "type": "integer", "minimum": 1 },
12
+ "setting": { "$ref": "#/definitions/setting" }, "condition": { "type": "object", "additionalProperties": true },
13
+ "packOptions": { "$ref": "#/definitions/packOptions" }, "editorSetting": { "$ref": "#/definitions/editorSetting" },
14
+ "neux": { "$ref": "#/definitions/neux" }, "neuxCli": { "$ref": "#/definitions/neux" }
15
+ },
16
+ "additionalProperties": true,
17
+ "definitions": {
18
+ "setting": { "type": "object", "properties": { "urlCheck": { "type": "boolean" }, "es6": { "type": "boolean" }, "postcss": { "type": "boolean" }, "minified": { "type": "boolean" }, "newFeature": { "type": "boolean" }, "babelSetting": { "type": "object", "properties": { "ignore": { "type": "array", "items": { "type": "string" } }, "disablePlugins": { "type": "array", "items": { "type": "string" } }, "outputPath": { "type": "string" } }, "additionalProperties": true } }, "additionalProperties": true },
19
+ "packOptions": { "type": "object", "properties": { "ignore": { "type": "array", "items": { "type": "string" } }, "include": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": true },
20
+ "editorSetting": { "type": "object", "properties": { "tabIndent": { "type": "string", "enum": ["insertSpaces", "indexSize"] }, "tabSize": { "type": "integer", "minimum": 1 } }, "additionalProperties": true },
21
+ "neux": { "type": "object", "properties": { "serverUrl": { "type": "string", "format": "uri" }, "appId": { "type": "string" }, "profile": { "type": "string" } }, "additionalProperties": true }
22
+ }
23
+ }
@@ -0,0 +1,161 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://neuxnet.com/schemas/neux/project-private-config.schema.json",
4
+ "title": "Neux private project configuration",
5
+ "description": "Optional local overrides for project.config.json. All fields are optional.",
6
+ "type": "object",
7
+ "properties": {
8
+ "description": {
9
+ "type": "string"
10
+ },
11
+ "appid": {
12
+ "type": "string"
13
+ },
14
+ "appId": {
15
+ "type": "string"
16
+ },
17
+ "projectname": {
18
+ "type": "string"
19
+ },
20
+ "projectName": {
21
+ "type": "string"
22
+ },
23
+ "compileType": {
24
+ "type": "string",
25
+ "enum": [
26
+ "miniprogram"
27
+ ]
28
+ },
29
+ "libVersion": {
30
+ "type": "string"
31
+ },
32
+ "miniprogramRoot": {
33
+ "type": "string"
34
+ },
35
+ "versionName": {
36
+ "type": "string"
37
+ },
38
+ "version": {
39
+ "type": "string"
40
+ },
41
+ "versionCode": {
42
+ "type": "integer",
43
+ "minimum": 1
44
+ },
45
+ "setting": {
46
+ "$ref": "#/definitions/setting"
47
+ },
48
+ "condition": {
49
+ "type": "object",
50
+ "additionalProperties": true
51
+ },
52
+ "packOptions": {
53
+ "$ref": "#/definitions/packOptions"
54
+ },
55
+ "editorSetting": {
56
+ "$ref": "#/definitions/editorSetting"
57
+ },
58
+ "neux": {
59
+ "$ref": "#/definitions/neux"
60
+ },
61
+ "neuxCli": {
62
+ "$ref": "#/definitions/neux"
63
+ }
64
+ },
65
+ "additionalProperties": true,
66
+ "definitions": {
67
+ "setting": {
68
+ "type": "object",
69
+ "properties": {
70
+ "urlCheck": {
71
+ "type": "boolean"
72
+ },
73
+ "es6": {
74
+ "type": "boolean"
75
+ },
76
+ "postcss": {
77
+ "type": "boolean"
78
+ },
79
+ "minified": {
80
+ "type": "boolean"
81
+ },
82
+ "newFeature": {
83
+ "type": "boolean"
84
+ },
85
+ "babelSetting": {
86
+ "type": "object",
87
+ "properties": {
88
+ "ignore": {
89
+ "type": "array",
90
+ "items": {
91
+ "type": "string"
92
+ }
93
+ },
94
+ "disablePlugins": {
95
+ "type": "array",
96
+ "items": {
97
+ "type": "string"
98
+ }
99
+ },
100
+ "outputPath": {
101
+ "type": "string"
102
+ }
103
+ },
104
+ "additionalProperties": true
105
+ }
106
+ },
107
+ "additionalProperties": true
108
+ },
109
+ "packOptions": {
110
+ "type": "object",
111
+ "properties": {
112
+ "ignore": {
113
+ "type": "array",
114
+ "items": {
115
+ "type": "string"
116
+ }
117
+ },
118
+ "include": {
119
+ "type": "array",
120
+ "items": {
121
+ "type": "string"
122
+ }
123
+ }
124
+ },
125
+ "additionalProperties": true
126
+ },
127
+ "editorSetting": {
128
+ "type": "object",
129
+ "properties": {
130
+ "tabIndent": {
131
+ "type": "string",
132
+ "enum": [
133
+ "insertSpaces",
134
+ "indexSize"
135
+ ]
136
+ },
137
+ "tabSize": {
138
+ "type": "integer",
139
+ "minimum": 1
140
+ }
141
+ },
142
+ "additionalProperties": true
143
+ },
144
+ "neux": {
145
+ "type": "object",
146
+ "properties": {
147
+ "serverUrl": {
148
+ "type": "string",
149
+ "format": "uri"
150
+ },
151
+ "appId": {
152
+ "type": "string"
153
+ },
154
+ "profile": {
155
+ "type": "string"
156
+ }
157
+ },
158
+ "additionalProperties": true
159
+ }
160
+ }
161
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuxnet/neux-cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Standalone CLI and Node API for Neux mini program build, pack, preview, and CI automation.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,9 +24,12 @@
24
24
  "sync:compiler": "node ./scripts/sync-compiler.js",
25
25
  "prepare:publish": "npm run sync:compiler && npm run sync:container",
26
26
  "generate:init-types": "node ./scripts/generate-init-types.js",
27
+ "smoke:init-types": "node ./scripts/smoke-init-types.mjs",
28
+ "smoke:init-types:registry": "node ./scripts/smoke-init-types.mjs --registry",
27
29
  "test": "node --test",
28
30
  "smoke:help": "node ./src/bin/cli.js --help",
29
- "pack:dry": "npm pack --dry-run"
31
+ "pack:dry": "npm pack --dry-run",
32
+ "verify:schemas": "node ./scripts/verify-json-schemas.mjs"
30
33
  },
31
34
  "engines": {
32
35
  "node": ">=20"
@@ -58,6 +61,12 @@
58
61
  "preview"
59
62
  ],
60
63
  "license": "Apache-2.0",
64
+ "neux": {
65
+ "typesPackage": {
66
+ "name": "@neuxnet/neux-miniapp-types",
67
+ "versionRange": "^0.1.8"
68
+ }
69
+ },
61
70
  "publishConfig": {
62
71
  "access": "public"
63
72
  },
@@ -0,0 +1,114 @@
1
+ import fs from 'node:fs/promises'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { execFile } from 'node:child_process'
5
+ import { promisify } from 'node:util'
6
+ import { initProject } from '../src/core/init.js'
7
+
8
+ const execFileAsync = promisify(execFile)
9
+ const cliRoot = path.resolve(import.meta.dirname, '..')
10
+ const repoRoot = path.resolve(cliRoot, '..')
11
+ const typesRoot = path.join(repoRoot, 'neux-miniapp-types')
12
+ const tscPath = path.join(repoRoot, 'fe', 'node_modules', '.bin', 'tsc')
13
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'neux-cli-types-smoke-'))
14
+ const registryMode = process.argv.includes('--registry')
15
+
16
+ async function pack(directory, destination) {
17
+ const { stdout } = await execFileAsync('npm', [
18
+ 'pack',
19
+ '--pack-destination',
20
+ destination,
21
+ '--json',
22
+ ], { cwd: directory })
23
+ const result = JSON.parse(stdout)
24
+ return path.join(destination, result[0].filename)
25
+ }
26
+
27
+ try {
28
+ const project = path.join(tempRoot, 'starter-app')
29
+ await initProject({
30
+ _: [project],
31
+ appId: 'wxlocaltypes123',
32
+ name: 'Local Types Smoke',
33
+ })
34
+
35
+ const packageJsonPath = path.join(project, 'package.json')
36
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'))
37
+ const typesPackage = packageJson.devDependencies['@neuxnet/neux-miniapp-types']
38
+ const cliPackage = JSON.parse(await fs.readFile(
39
+ path.join(cliRoot, 'package.json'),
40
+ 'utf8',
41
+ ))
42
+ const expectedTypesRange = cliPackage.neux?.typesPackage?.versionRange
43
+ if (typesPackage !== expectedTypesRange) {
44
+ throw new Error(
45
+ `Unexpected generated types dependency: ${typesPackage}; ` +
46
+ `expected ${expectedTypesRange}`,
47
+ )
48
+ }
49
+
50
+ if (!registryMode) {
51
+ const typesTarball = await pack(typesRoot, tempRoot)
52
+ const cliTarball = await pack(cliRoot, tempRoot)
53
+ packageJson.devDependencies['@neuxnet/neux-cli'] = `file:${cliTarball}`
54
+ packageJson.devDependencies['@neuxnet/neux-miniapp-types'] = `file:${typesTarball}`
55
+ }
56
+ await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
57
+
58
+ await fs.writeFile(path.join(project, 'types', 'consumer.ts'), [
59
+ 'Page({',
60
+ ' data: { count: 0 },',
61
+ ' onLoad(query) {',
62
+ ' const id: string | undefined = query.id',
63
+ ' this.setData({ count: this.data.count + 1, dynamicallyAdded: true })',
64
+ ' void id',
65
+ ' },',
66
+ '})',
67
+ '',
68
+ 'wx.request({',
69
+ " url: '/api/profile',",
70
+ " method: 'GET',",
71
+ ' success(result) { result }',
72
+ '})',
73
+ '',
74
+ "Component({ properties: { title: String }, methods: { emitChange() {",
75
+ " this.triggerEvent<{ value: string }>('change', { value: this.properties.title })",
76
+ '} } })',
77
+ '',
78
+ ].join('\n'), 'utf8')
79
+
80
+ const installArgs = [
81
+ 'install',
82
+ '--ignore-scripts',
83
+ '--no-package-lock',
84
+ ]
85
+ if (registryMode) {
86
+ installArgs.push('--registry=https://registry.npmjs.org/')
87
+ }
88
+ await execFileAsync('npm', installArgs, {
89
+ cwd: project,
90
+ maxBuffer: 1024 * 1024,
91
+ })
92
+
93
+ await execFileAsync(tscPath, [
94
+ '-p',
95
+ path.join(project, 'jsconfig.json'),
96
+ '--noEmit',
97
+ ], { cwd: project, maxBuffer: 1024 * 1024 })
98
+
99
+ const installedTypesPackage = JSON.parse(await fs.readFile(
100
+ path.join(project, 'node_modules', '@neuxnet', 'neux-miniapp-types', 'package.json'),
101
+ 'utf8',
102
+ ))
103
+ if (installedTypesPackage.name !== '@neuxnet/neux-miniapp-types') {
104
+ throw new Error('Installed types package has an unexpected name')
105
+ }
106
+ if (installedTypesPackage.types !== './index.d.ts') {
107
+ throw new Error('Installed types package does not expose index.d.ts')
108
+ }
109
+
110
+ console.log(`Passed: neux init -> ${registryMode ? 'registry' : 'local'} npm install -> TypeScript consumer check.`)
111
+ }
112
+ finally {
113
+ await fs.rm(tempRoot, { recursive: true, force: true })
114
+ }
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
6
+ const schemaDir = path.join(root, 'assets', 'init', 'schemas')
7
+ const initSource = await fs.readFile(path.join(root, 'src', 'core', 'init.js'), 'utf8')
8
+ const projectSource = await fs.readFile(path.join(root, 'src', 'core', 'project.js'), 'utf8')
9
+ const serviceSource = await fs.readFile(path.join(root, 'src', 'providers', 'service-config.js'), 'utf8')
10
+
11
+ const manifest = {
12
+ 'app.schema.json': { fields: ['pages', 'entryPagePath', 'window', 'tabBar', 'subPackages', 'subpackages', 'preloadRule', 'networkTimeout', 'permission', 'usingComponents'] },
13
+ 'page.schema.json': { fields: ['navigationBarTitleText', 'navigationBarBackgroundColor', 'navigationBarTextStyle', 'backgroundColor', 'backgroundTextStyle', 'enablePullDownRefresh', 'onReachBottomDistance', 'usingComponents', 'disableScroll', 'pageOrientation'] },
14
+ 'project-config.schema.json': { fields: ['appid', 'projectname', 'compileType', 'libVersion', 'miniprogramRoot', 'versionName', 'versionCode', 'setting', 'condition', 'packOptions', 'editorSetting', 'neuxCli'] },
15
+ 'project-private-config.schema.json': { fields: ['appid', 'projectname', 'versionName', 'versionCode', 'setting', 'packOptions', 'neuxCli'] },
16
+ 'neux-config.schema.json': { fields: ['serverUrl', 'appId', 'appid', 'profile', 'version', 'versionName', 'versionCode'] },
17
+ }
18
+
19
+ const errors = []
20
+ for (const [name, entry] of Object.entries(manifest)) {
21
+ const file = path.join(schemaDir, name)
22
+ let schema
23
+ try { schema = JSON.parse(await fs.readFile(file, 'utf8')) } catch (error) { errors.push(`${name}: invalid JSON (${error.message})`); continue }
24
+ if (schema.$schema !== 'http://json-schema.org/draft-07/schema#') errors.push(`${name}: missing draft-07 $schema`)
25
+ if (!schema.title || !schema.description) errors.push(`${name}: title and description are required`)
26
+ const serialized = JSON.stringify(schema)
27
+ for (const field of entry.fields) if (!serialized.includes(`"${field}"`)) errors.push(`${name}: source field is missing from schema: ${field}`)
28
+ if (name === 'neux-config.schema.json') {
29
+ for (const secret of ['key', 'cliKey', 'neuxCliKey']) if (schema.properties?.[secret]) errors.push(`${name}: secret field must not be declared: ${secret}`)
30
+ if (!schema.not?.anyOf?.some(rule => rule.required?.includes('key'))) errors.push(`${name}: must reject key`)
31
+ }
32
+ }
33
+ const settings = initSource.match(/function createJsonSchemaSettings\(\)\s*\{([\s\S]*?)\n\}/)?.[1] || ''
34
+ for (const name of Object.keys(manifest)) {
35
+ if (!settings.includes(name)) errors.push(`init.js: schema is not registered: ${name}`)
36
+ try { await fs.access(path.join(schemaDir, name)) } catch { errors.push(`missing schema asset: ${name}`) }
37
+ }
38
+ if (!projectSource.includes('readProjectCliConfig') || !serviceSource.includes('assertNoKey')) errors.push('source guard: expected configuration readers were not found')
39
+ if (errors.length) { console.error(['JSON Schema verification failed:', ...errors.map(error => `- ${error}`)].join('\n')); process.exitCode = 1 }
40
+ else console.log(`JSON Schema verification passed (${Object.keys(manifest).length} schemas, ${Object.values(manifest).reduce((n, item) => n + item.fields.length, 0)} source-backed fields).`)
package/src/bin/cli.js CHANGED
@@ -307,6 +307,12 @@ async function writeResult(result, rawOptions) {
307
307
  process.stdout.write(`key: ${result.key?.present ? 'configured' : 'missing'}\n`)
308
308
  if (result.missing?.length) process.stdout.write(`missing: ${result.missing.join(', ')}\n`)
309
309
  }
310
+ if (Array.isArray(result.diagnostics) && result.diagnostics.length > 0) {
311
+ process.stdout.write(`diagnostics: ${result.diagnostics.length}\n`)
312
+ for (const diagnostic of result.diagnostics) {
313
+ process.stdout.write(` [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}\n`)
314
+ }
315
+ }
310
316
  if (result.qrPayload && qrFormat === 'terminal') {
311
317
  process.stdout.write('\n')
312
318
  process.stdout.write(await renderTerminalQr(result.qrPayload))
@@ -315,6 +321,12 @@ async function writeResult(result, rawOptions) {
315
321
 
316
322
  function formatErrorPayload(payload, locale = 'en') {
317
323
  const lines = [`${payload.code}: ${payload.message}`]
324
+ if (payload.details?.diagnostics?.length) {
325
+ lines.push('', 'Diagnostics:')
326
+ for (const diagnostic of payload.details.diagnostics) {
327
+ lines.push(` [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`)
328
+ }
329
+ }
318
330
  const suggestions = payload.details?.suggestions
319
331
  if (payload.code === 'NEUX_CLI_CONFIG_MISSING' && Array.isArray(suggestions) && suggestions.length > 0) {
320
332
  lines.push('', translate(locale, 'suggestedFixes'))
@@ -49,6 +49,7 @@ async function loadCompiler(moduleSpecifier) {
49
49
 
50
50
  export async function buildProject(options = {}) {
51
51
  const projectInfo = await inspectProject(options)
52
+ assertProjectConfiguration(projectInfo, options)
52
53
  const out = resolvePath(options.out || options.output || defaultOut('build'))
53
54
  const compilerModule = options.compiler || await loadCompiler(options.compilerModule || process.env.DIMINA_COMPILER_MODULE)
54
55
  const build = compilerModule.default || compilerModule.build || compilerModule
@@ -92,12 +93,22 @@ export async function buildProject(options = {}) {
92
93
  compilerInputCreated: compilerInput.created,
93
94
  outputPath: out,
94
95
  appId: projectInfo.appId,
96
+ diagnostics: projectInfo.diagnostics,
95
97
  compilerResult,
96
98
  logs,
97
99
  errors,
98
100
  }
99
101
  }
100
102
 
103
+ function assertProjectConfiguration(projectInfo, options = {}) {
104
+ if (options.validate === false || projectInfo.ok) return
105
+ throw new DiminaCliError(
106
+ 'NEUX_PROJECT_CONFIG_INVALID',
107
+ `Project configuration validation failed with ${projectInfo.diagnostics.length} error(s).`,
108
+ { diagnostics: projectInfo.diagnostics },
109
+ )
110
+ }
111
+
101
112
  export async function buildStyleOnlyProject(options = {}) {
102
113
  const projectInfo = await inspectProject(options)
103
114
  const out = resolvePath(options.out || options.output || defaultOut('build'))