@neuxnet/neux-cli 0.2.3
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/README.md +438 -0
- package/README.zh-CN.md +554 -0
- package/assets/container/assets/index.css +1 -0
- package/assets/container/assets/index.js +137 -0
- package/assets/container/assets/pageFrame.css +1 -0
- package/assets/container/assets/pageFrame.js +56 -0
- package/assets/container/assets/service.js +6 -0
- package/assets/container/assets/vconsole.js +931 -0
- package/assets/container/favicon.ico +0 -0
- package/assets/container/images/icon-arrow.png +0 -0
- package/assets/container/images/mini-action-white.png +0 -0
- package/assets/container/images/mini-action.png +0 -0
- package/assets/container/images/mini-arrow-left-white.png +0 -0
- package/assets/container/images/mini-arrow-left.jpg +0 -0
- package/assets/container/images/mini-arrow-left.png +0 -0
- package/assets/container/images/mini-close-white.png +0 -0
- package/assets/container/images/mini-close.png +0 -0
- package/assets/container/images/more.png +0 -0
- package/assets/container/images/search.jpg +0 -0
- package/assets/container/index.html +1 -0
- package/assets/container/pageFrame.html +1 -0
- package/assets/init/tabbar/home-active.png +0 -0
- package/assets/init/tabbar/home.png +0 -0
- package/assets/init/tabbar/list-active.png +0 -0
- package/assets/init/tabbar/list.png +0 -0
- package/assets/init/types/neux-api.d.ts +1402 -0
- package/package.json +68 -0
- package/scripts/generate-init-types.js +210 -0
- package/scripts/sync-compiler.js +22 -0
- package/scripts/sync-web-container.js +34 -0
- package/src/bin/cli.js +646 -0
- package/src/core/brand.js +11 -0
- package/src/core/compiler.js +305 -0
- package/src/core/defaults.js +12 -0
- package/src/core/dev.js +15 -0
- package/src/core/errors.js +17 -0
- package/src/core/fs.js +66 -0
- package/src/core/i18n.js +37 -0
- package/src/core/init.js +866 -0
- package/src/core/lifecycle.js +32 -0
- package/src/core/manifest.js +72 -0
- package/src/core/pack.js +156 -0
- package/src/core/package-info.js +27 -0
- package/src/core/preview-server.js +123 -0
- package/src/core/project.js +101 -0
- package/src/core/prompts.js +71 -0
- package/src/core/proxy-security.js +141 -0
- package/src/core/proxy.js +156 -0
- package/src/core/qr.js +41 -0
- package/src/core/terminal-qr.js +72 -0
- package/src/core/update.js +278 -0
- package/src/core/watch.js +113 -0
- package/src/core/web.js +649 -0
- package/src/core/zip.js +120 -0
- package/src/index.js +20 -0
- package/src/providers/service-client.js +149 -0
- package/src/providers/service-config.js +264 -0
- package/src/providers/service.js +146 -0
- package/src/providers/upload.js +112 -0
- package/vendor/dimina-compiler/bin/index.cjs +265 -0
- package/vendor/dimina-compiler/bin/index.js +263 -0
- package/vendor/dimina-compiler/compatibility-B-DoZtUX.cjs +395 -0
- package/vendor/dimina-compiler/compatibility-Cl3-DO6V.js +366 -0
- package/vendor/dimina-compiler/core/logic-compiler.cjs +378 -0
- package/vendor/dimina-compiler/core/logic-compiler.js +374 -0
- package/vendor/dimina-compiler/core/style-compiler.cjs +392 -0
- package/vendor/dimina-compiler/core/style-compiler.js +377 -0
- package/vendor/dimina-compiler/core/view-compiler.cjs +1601 -0
- package/vendor/dimina-compiler/core/view-compiler.js +1582 -0
- package/vendor/dimina-compiler/index.cjs +762 -0
- package/vendor/dimina-compiler/index.js +751 -0
- package/vendor/dimina-compiler/sourcemap-BgtIgqkC.cjs +1377 -0
- package/vendor/dimina-compiler/sourcemap-CKjhV9h7.js +1135 -0
package/src/bin/cli.js
ADDED
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import { buildProject, createLifecycleEvent, createProjectWatcher, DEFAULT_SERVER_URL, getCliPackageInfo, getCliVersion, initProject, inspectProject, maybeAutoUpdateCli, packProject, saveCliKey, startDev, startPreview, startWeb, updateCli } from '../index.js'
|
|
7
|
+
import { DiminaCliError, toErrorPayload } from '../core/errors.js'
|
|
8
|
+
import { brand } from '../core/brand.js'
|
|
9
|
+
import { runProviderCommand, unsupportedProviderCommand } from '../providers/upload.js'
|
|
10
|
+
import { resolveServiceConfig, resolveServiceConfigDiagnostics } from '../providers/service-config.js'
|
|
11
|
+
import { renderQrPng, renderTerminalQr } from '../core/terminal-qr.js'
|
|
12
|
+
|
|
13
|
+
const signalExitCodes = {
|
|
14
|
+
SIGHUP: 129,
|
|
15
|
+
SIGINT: 0,
|
|
16
|
+
SIGTERM: 143,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createSessionSignalController() {
|
|
20
|
+
let session = null
|
|
21
|
+
let pendingSignal = null
|
|
22
|
+
let shuttingDown = false
|
|
23
|
+
|
|
24
|
+
const shutdown = signal => {
|
|
25
|
+
if (shuttingDown) return
|
|
26
|
+
if (!session) {
|
|
27
|
+
pendingSignal = signal
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
shuttingDown = true
|
|
32
|
+
void session.close()
|
|
33
|
+
.then(() => process.exit(signalExitCodes[signal] ?? 1))
|
|
34
|
+
.catch(error => {
|
|
35
|
+
console.error(error)
|
|
36
|
+
process.exit(1)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const signal of Object.keys(signalExitCodes)) {
|
|
41
|
+
process.once(signal, () => shutdown(signal))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
setSession(nextSession) {
|
|
46
|
+
session = nextSession
|
|
47
|
+
if (pendingSignal) shutdown(pendingSignal)
|
|
48
|
+
},
|
|
49
|
+
wait() {
|
|
50
|
+
return new Promise(() => {})
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
import { resolveLocale, translate } from '../core/i18n.js'
|
|
55
|
+
import { promptLine } from '../core/prompts.js'
|
|
56
|
+
|
|
57
|
+
function help(locale = 'en') {
|
|
58
|
+
return `${translate(locale, 'usage')}: ${brand.commandName} <command> [options]
|
|
59
|
+
|
|
60
|
+
${translate(locale, 'commands')}:
|
|
61
|
+
init Create a new mini program project
|
|
62
|
+
inspect Read mini program metadata
|
|
63
|
+
dev Compile, open the H5 container, watch files, and live reload
|
|
64
|
+
build Build and create a client .wgt package in dist/release/
|
|
65
|
+
upload Upload a signed service trial package
|
|
66
|
+
debug Upload a signed debug preview package and print a QR code
|
|
67
|
+
submit Submit a signed service trial version for audit
|
|
68
|
+
status Query signed service version information
|
|
69
|
+
meta Alias of status for signed service version metadata
|
|
70
|
+
config Diagnose signed service configuration
|
|
71
|
+
update Check for or install a newer CLI version
|
|
72
|
+
cli-version Show the installed CLI version
|
|
73
|
+
compile Compile a mini program with ${brand.compilerPackageName}
|
|
74
|
+
wgt Alias of build for compatibility
|
|
75
|
+
preview Legacy local static preview server
|
|
76
|
+
web Legacy H5 container command; prefer dev
|
|
77
|
+
login Reserved provider-backed login command (unsupported by signed service)
|
|
78
|
+
debug-preview Upload a signed debug preview package and print a QR code
|
|
79
|
+
service-preview Generate signed service trial preview information
|
|
80
|
+
submit-audit Submit a signed service trial version for audit
|
|
81
|
+
version Query signed service version information
|
|
82
|
+
audit-status Query signed service audit status
|
|
83
|
+
audit Alias of submit-audit for provider integrations
|
|
84
|
+
|
|
85
|
+
${translate(locale, 'commonOptions')}:
|
|
86
|
+
--project <path> Mini program project path, defaults to current directory
|
|
87
|
+
--out <path> Output directory, defaults to dist/<command>
|
|
88
|
+
--app-id <id> Override project app id
|
|
89
|
+
--name <name> Project name for init or metadata override
|
|
90
|
+
--entry-path <path> Override entry path
|
|
91
|
+
--source-dir <path> Already-produced mini app output directory for packaging
|
|
92
|
+
--compiled-dir <path> Alias of --source-dir
|
|
93
|
+
--version-code <n> Package version code
|
|
94
|
+
--version <version> Package/service version
|
|
95
|
+
--version-name <name> Package version name
|
|
96
|
+
--host <host> Preview host
|
|
97
|
+
--port <port> Preview port, use 0 for a random free port
|
|
98
|
+
--container-dir <path> Web container dist directory
|
|
99
|
+
--compiler-module <path> Use a local compiler module instead of ${brand.compilerPackageName}
|
|
100
|
+
--provider <module> Provider module or "local-file" for provider commands
|
|
101
|
+
--provider-out <path> Output directory for provider-generated records
|
|
102
|
+
--server-url <url> Signed service base URL
|
|
103
|
+
--profile <name> User-level service profile name
|
|
104
|
+
--key <key> Signed service key (prefer NEUX_CLI_KEY in CI)
|
|
105
|
+
--config <path> User-level CLI config path, defaults to ~/.neux/config.json
|
|
106
|
+
--registry <url> npm registry for CLI self-update
|
|
107
|
+
--check Check updates without installing
|
|
108
|
+
--dry-run Check update/install intent without installing
|
|
109
|
+
--auto-update Enable automatic CLI update for this run
|
|
110
|
+
--no-auto-update Disable automatic CLI update for this run
|
|
111
|
+
--desc <text> Upload description
|
|
112
|
+
--changelog <text> Upload changelog, also used as description fallback
|
|
113
|
+
--channel <channel> Service channel for preview/upload/audit
|
|
114
|
+
--page-path <path> Debug/preview launch page path
|
|
115
|
+
--query <query> Debug/preview launch query string
|
|
116
|
+
--scene <scene> Debug/preview launch scene
|
|
117
|
+
--launch-from <source> Debug/preview launch source
|
|
118
|
+
--location <location> Debug/preview launch location
|
|
119
|
+
--qr-format <format> QR output format: png, terminal, or none
|
|
120
|
+
--qr-output <path> Write QR output to a file
|
|
121
|
+
--copy Copy deeplink/QR payload to clipboard when supported
|
|
122
|
+
--proxy <url> Proxy hint for service/provider integrations
|
|
123
|
+
--preview Return trial preview during upload
|
|
124
|
+
--submit-audit Submit trial version after upload
|
|
125
|
+
--auto-publish Ask audit service to publish automatically when supported
|
|
126
|
+
--version-id <id> Service version id
|
|
127
|
+
--version-number <v> Service version number
|
|
128
|
+
--build Build before pack/preview
|
|
129
|
+
--no-open Do not open the browser automatically
|
|
130
|
+
--json Print machine-readable JSON
|
|
131
|
+
--lang <locale> Output language: en or zh-CN
|
|
132
|
+
--interactive Enable interactive prompts
|
|
133
|
+
--no-interactive Disable interactive prompts
|
|
134
|
+
--stay Keep preview/web server running
|
|
135
|
+
--watch, -w Rebuild on project file changes
|
|
136
|
+
--force Allow init to write into a non-empty directory
|
|
137
|
+
--version, -v Show CLI version
|
|
138
|
+
--help Show help
|
|
139
|
+
`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parse(argv) {
|
|
143
|
+
const args = [...argv]
|
|
144
|
+
const command = args.shift()
|
|
145
|
+
const options = { _: [] }
|
|
146
|
+
|
|
147
|
+
for (let i = 0; i < args.length; i++) {
|
|
148
|
+
const arg = args[i]
|
|
149
|
+
const next = () => args[++i]
|
|
150
|
+
if (arg === '--project' || arg === '-p') options.project = next()
|
|
151
|
+
else if (arg === '--out' || arg === '-o') options.out = next()
|
|
152
|
+
else if (arg === '--app-id') options.appId = next()
|
|
153
|
+
else if (arg === '--name') options.name = next()
|
|
154
|
+
else if (arg === '--entry-path') options.entryPath = next()
|
|
155
|
+
else if (arg === '--source-dir') options.sourceDir = next()
|
|
156
|
+
else if (arg === '--compiled-dir') options.compiledDir = next()
|
|
157
|
+
else if (arg === '--version-code') options.versionCode = next()
|
|
158
|
+
else if (arg === '--version') options.version = next()
|
|
159
|
+
else if (arg === '--version-name') options.versionName = next()
|
|
160
|
+
else if (arg === '--host') options.host = next()
|
|
161
|
+
else if (arg === '--port') options.port = next()
|
|
162
|
+
else if (arg === '--container-dir') options.containerDir = next()
|
|
163
|
+
else if (arg === '--compiler-module') options.compilerModule = next()
|
|
164
|
+
else if (arg === '--provider') options.provider = next()
|
|
165
|
+
else if (arg === '--provider-out') options.providerOut = next()
|
|
166
|
+
else if (arg === '--server-url') options.serverUrl = next()
|
|
167
|
+
else if (arg === '--lang' || arg === '--locale') options.lang = next()
|
|
168
|
+
else if (arg === '--registry') options.registry = next()
|
|
169
|
+
else if (arg === '--npm') options.npmCommand = next()
|
|
170
|
+
else if (arg === '--profile') options.profile = next()
|
|
171
|
+
else if (arg === '--key') options.key = next()
|
|
172
|
+
else if (arg === '--config') options.config = next()
|
|
173
|
+
else if (arg === '--desc') options.desc = next()
|
|
174
|
+
else if (arg === '--changelog' || arg === '--app-changelog') options.changelog = next()
|
|
175
|
+
else if (arg === '--channel') options.channel = next()
|
|
176
|
+
else if (arg === '--page-path' || arg === '--miniapp-path') options.pagePath = next()
|
|
177
|
+
else if (arg === '--query' || arg === '--miniapp-query') options.query = next()
|
|
178
|
+
else if (arg === '--scene' || arg === '--miniapp-scene') options.scene = next()
|
|
179
|
+
else if (arg === '--launch-from' || arg === '--miniapp-launch-from') options.launchFrom = next()
|
|
180
|
+
else if (arg === '--location' || arg === '--miniapp-location') options.location = next()
|
|
181
|
+
else if (arg === '--qr-format' || arg === '--qrcode-format') options.qrFormat = next()
|
|
182
|
+
else if (arg === '--qr-output' || arg === '--qrcode-output') options.qrOutput = next()
|
|
183
|
+
else if (arg === '--proxy') options.proxy = next()
|
|
184
|
+
else if (arg === '--version-id') options.versionId = next()
|
|
185
|
+
else if (arg === '--version-number') options.versionNumber = next()
|
|
186
|
+
else if (arg === '--preview') options.preview = true
|
|
187
|
+
else if (arg === '--submit-audit') options.submitAudit = true
|
|
188
|
+
else if (arg === '--auto-publish') options.autoPublish = true
|
|
189
|
+
else if (arg === '--auto-update') options.autoUpdate = true
|
|
190
|
+
else if (arg === '--no-auto-update') options.autoUpdate = false
|
|
191
|
+
else if (arg === '--check') options.check = true
|
|
192
|
+
else if (arg === '--dry-run') options.dryRun = true
|
|
193
|
+
else if (arg === '--copy') options.copy = true
|
|
194
|
+
else if (arg === '--build') options.build = true
|
|
195
|
+
else if (arg === '--no-build') options.build = false
|
|
196
|
+
else if (arg === '--no-open') options.open = false
|
|
197
|
+
else if (arg === '--json') options.json = true
|
|
198
|
+
else if (arg === '--stay') options.stay = true
|
|
199
|
+
else if (arg === '--watch' || arg === '-w') options.watch = true
|
|
200
|
+
else if (arg === '--force') options.force = true
|
|
201
|
+
else if (arg === '--interactive') options.interactive = true
|
|
202
|
+
else if (arg === '--no-interactive') options.noInteractive = true
|
|
203
|
+
else if (arg === '--sourcemap') options.sourcemap = true
|
|
204
|
+
else if (arg === '--help' || arg === '-h') options.help = true
|
|
205
|
+
else if (arg.startsWith('--')) {
|
|
206
|
+
const [key, value] = arg.slice(2).split('=')
|
|
207
|
+
options[key.replaceAll('-', '_')] = value ?? true
|
|
208
|
+
}
|
|
209
|
+
else options._.push(arg)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { command, options }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function jsonSafe(value) {
|
|
216
|
+
if (!value || typeof value !== 'object') return value
|
|
217
|
+
const { close, artifact, ...rest } = value
|
|
218
|
+
return artifact ? { ...rest, artifact } : rest
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function outputOptions(value) {
|
|
222
|
+
return typeof value === 'object' && value !== null ? value : { json: !!value }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function copyToClipboard(value) {
|
|
226
|
+
if (!value || process.platform !== 'darwin') return false
|
|
227
|
+
const copied = spawnSync('pbcopy', { input: String(value), encoding: 'utf8' })
|
|
228
|
+
return copied.status === 0
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function withQrOutput(result, options) {
|
|
232
|
+
if (!result?.qrPayload) return result
|
|
233
|
+
const next = { ...result }
|
|
234
|
+
const qrFormat = resolveQrFormat(result, options)
|
|
235
|
+
if (options.copy) next.copied = copyToClipboard(result.qrPayload)
|
|
236
|
+
const shouldWriteDefaultPng = qrFormat === 'png' && (!options.json || options.qrFormat)
|
|
237
|
+
const defaultPngPath = options.defaultQrOutput
|
|
238
|
+
|| path.join(options.out || 'dist/release', `${result.command || 'preview'}-qr.png`)
|
|
239
|
+
const outputPath = options.qrOutput
|
|
240
|
+
? path.resolve(options.qrOutput)
|
|
241
|
+
: (shouldWriteDefaultPng ? path.resolve(defaultPngPath) : null)
|
|
242
|
+
if (outputPath) {
|
|
243
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
244
|
+
if (qrFormat === 'png') {
|
|
245
|
+
await fs.writeFile(outputPath, await renderQrPng(result.qrPayload))
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
const content = qrFormat === 'none'
|
|
249
|
+
? `${String(result.qrPayload)}\n`
|
|
250
|
+
: await renderTerminalQr(result.qrPayload)
|
|
251
|
+
await fs.writeFile(outputPath, content)
|
|
252
|
+
}
|
|
253
|
+
next.qrOutputPath = outputPath
|
|
254
|
+
next.qrFormat = qrFormat
|
|
255
|
+
}
|
|
256
|
+
return next
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function resolveQrFormat(result, options) {
|
|
260
|
+
const explicitFormat = options.qrFormat && normalizeQrFormat(options.qrFormat)
|
|
261
|
+
if (explicitFormat) return explicitFormat
|
|
262
|
+
if (options.qrOutput) return 'terminal'
|
|
263
|
+
return 'terminal'
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function normalizeQrFormat(value) {
|
|
267
|
+
const qrFormat = String(value).trim().toLowerCase()
|
|
268
|
+
if (!['png', 'terminal', 'none'].includes(qrFormat)) {
|
|
269
|
+
throw new DiminaCliError('NEUX_CLI_QR_FORMAT_INVALID', `Unsupported QR output format: ${value}`, {
|
|
270
|
+
qrFormat: value,
|
|
271
|
+
supported: ['png', 'terminal', 'none'],
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
return qrFormat
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function writeResult(result, rawOptions) {
|
|
278
|
+
const options = outputOptions(rawOptions)
|
|
279
|
+
const locale = resolveLocale(options)
|
|
280
|
+
result = await withQrOutput(result, options)
|
|
281
|
+
const qrFormat = resolveQrFormat(result, options)
|
|
282
|
+
if (options.json) {
|
|
283
|
+
process.stdout.write(`${JSON.stringify(jsonSafe(result), null, 2)}\n`)
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
process.stdout.write(`${result.command === 'init' ? translate(locale, 'initOk') : `${result.command} ok`}\n`)
|
|
287
|
+
if (result.outputPath) process.stdout.write(`${translate(locale, 'outputPath')}: ${result.outputPath}\n`)
|
|
288
|
+
if (result.outputDir) process.stdout.write(`${translate(locale, 'outputDir')}: ${result.outputDir}\n`)
|
|
289
|
+
if (result.packagePath) process.stdout.write(`${translate(locale, 'packagePath')}: ${result.packagePath}\n`)
|
|
290
|
+
if (result.manifestPath) process.stdout.write(`manifestPath: ${result.manifestPath}\n`)
|
|
291
|
+
if (result.sha256) process.stdout.write(`sha256: ${result.sha256}\n`)
|
|
292
|
+
if (result.innerPackageName) process.stdout.write(`innerPackageName: ${result.innerPackageName}\n`)
|
|
293
|
+
if (result.packageName) process.stdout.write(`packageName: ${result.packageName}\n`)
|
|
294
|
+
if (result.packageRoot) process.stdout.write(`packageRoot: ${result.packageRoot}\n`)
|
|
295
|
+
if (result.currentVersion) process.stdout.write(`currentVersion: ${result.currentVersion}\n`)
|
|
296
|
+
if (result.latestVersion) process.stdout.write(`latestVersion: ${result.latestVersion}\n`)
|
|
297
|
+
if (result.updateAvailable !== undefined) process.stdout.write(`updateAvailable: ${result.updateAvailable}\n`)
|
|
298
|
+
if (result.installed !== undefined) process.stdout.write(`installed: ${result.installed}\n`)
|
|
299
|
+
if (result.target) process.stdout.write(`target: ${result.target}\n`)
|
|
300
|
+
if (result.registry) process.stdout.write(`registry: ${result.registry}\n`)
|
|
301
|
+
if (result.serverUrl) process.stdout.write(`${translate(locale, 'serverUrl')}: ${result.serverUrl}\n`)
|
|
302
|
+
if (result.deepLink) process.stdout.write(`deepLink: ${result.deepLink}\n`)
|
|
303
|
+
if (result.qrOutputPath) process.stdout.write(`qrOutputPath: ${result.qrOutputPath}\n`)
|
|
304
|
+
if (result.qrFormat) process.stdout.write(`qrFormat: ${result.qrFormat}\n`)
|
|
305
|
+
if (result.copied !== undefined) process.stdout.write(`copied: ${result.copied}\n`)
|
|
306
|
+
if (result.command === 'config doctor') {
|
|
307
|
+
process.stdout.write(`key: ${result.key?.present ? 'configured' : 'missing'}\n`)
|
|
308
|
+
if (result.missing?.length) process.stdout.write(`missing: ${result.missing.join(', ')}\n`)
|
|
309
|
+
}
|
|
310
|
+
if (result.qrPayload && qrFormat === 'terminal') {
|
|
311
|
+
process.stdout.write('\n')
|
|
312
|
+
process.stdout.write(await renderTerminalQr(result.qrPayload))
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function formatErrorPayload(payload, locale = 'en') {
|
|
317
|
+
const lines = [`${payload.code}: ${payload.message}`]
|
|
318
|
+
const suggestions = payload.details?.suggestions
|
|
319
|
+
if (payload.code === 'NEUX_CLI_CONFIG_MISSING' && Array.isArray(suggestions) && suggestions.length > 0) {
|
|
320
|
+
lines.push('', translate(locale, 'suggestedFixes'))
|
|
321
|
+
for (const suggestion of suggestions) lines.push(` ${suggestion}`)
|
|
322
|
+
}
|
|
323
|
+
return `${lines.join('\n')}\n`
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function writeJsonEvent(event, payload = {}) {
|
|
327
|
+
process.stdout.write(`${JSON.stringify(jsonSafe(createLifecycleEvent(event, payload)))}\n`)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function writeLifecycleEvent(enabled, payload) {
|
|
331
|
+
if (!enabled) return
|
|
332
|
+
process.stdout.write(`${JSON.stringify(jsonSafe(payload))}\n`)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function withJsonLogRedirect(enabled, action) {
|
|
336
|
+
if (!enabled) return await action()
|
|
337
|
+
|
|
338
|
+
const originalWrite = process.stdout.write.bind(process.stdout)
|
|
339
|
+
process.stdout.write = (chunk, encoding, callback) => process.stderr.write(chunk, encoding, callback)
|
|
340
|
+
try {
|
|
341
|
+
return await action()
|
|
342
|
+
}
|
|
343
|
+
finally {
|
|
344
|
+
process.stdout.write = originalWrite
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function main() {
|
|
349
|
+
const { command, options } = parse(process.argv.slice(2))
|
|
350
|
+
const locale = resolveLocale(options)
|
|
351
|
+
if (command === '--version' || command === '-v') {
|
|
352
|
+
process.stdout.write(`${await getCliVersion()}\n`)
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
if (!command || command === '--help' || options.help) {
|
|
356
|
+
process.stdout.write(help(locale))
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
if (options.qrFormat) normalizeQrFormat(options.qrFormat)
|
|
360
|
+
|
|
361
|
+
if (command !== 'update') {
|
|
362
|
+
try {
|
|
363
|
+
await maybeAutoUpdateCli({ ...options, silent: true })
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
process.stderr.write(`NEUX_CLI_AUTO_UPDATE_SKIPPED: ${error.message}\n`)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function runCompileCommand(commandName) {
|
|
371
|
+
const compileOptions = { ...options, out: options.out || 'dist/build' }
|
|
372
|
+
if (options.watch && options.json) writeJsonEvent('build-start', { command: commandName })
|
|
373
|
+
let result
|
|
374
|
+
try {
|
|
375
|
+
result = await withJsonLogRedirect(options.json, () => buildProject(compileOptions))
|
|
376
|
+
result = { ...result, command: commandName }
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
if (options.watch && options.json) {
|
|
380
|
+
writeJsonEvent('build-error', { command: commandName, error: toErrorPayload(error) })
|
|
381
|
+
process.exitCode = 1
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
throw error
|
|
385
|
+
}
|
|
386
|
+
if (options.watch && options.json) writeJsonEvent('build-success', result)
|
|
387
|
+
else await writeResult(result, options)
|
|
388
|
+
if (options.watch) {
|
|
389
|
+
let rebuilding = false
|
|
390
|
+
let rebuildAgain = false
|
|
391
|
+
const rebuild = async (event) => {
|
|
392
|
+
if (rebuilding) {
|
|
393
|
+
rebuildAgain = true
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
rebuilding = true
|
|
397
|
+
try {
|
|
398
|
+
if (options.json) writeJsonEvent('build-start', { command: commandName, changedPath: event?.path })
|
|
399
|
+
const nextResult = await withJsonLogRedirect(options.json, () => buildProject(compileOptions))
|
|
400
|
+
const nextCommandResult = { ...nextResult, command: commandName }
|
|
401
|
+
if (options.json) writeJsonEvent('build-success', nextCommandResult)
|
|
402
|
+
else await writeResult(nextCommandResult, options)
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
const payload = toErrorPayload(error)
|
|
406
|
+
if (options.json) writeJsonEvent('build-error', { command: commandName, error: payload })
|
|
407
|
+
else process.stderr.write(`${payload.code}: ${payload.message}\n`)
|
|
408
|
+
}
|
|
409
|
+
finally {
|
|
410
|
+
rebuilding = false
|
|
411
|
+
if (rebuildAgain) {
|
|
412
|
+
rebuildAgain = false
|
|
413
|
+
await rebuild()
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const watcher = await createProjectWatcher({
|
|
418
|
+
project: options.project,
|
|
419
|
+
ignorePaths: [result.outputPath],
|
|
420
|
+
onChange: rebuild,
|
|
421
|
+
})
|
|
422
|
+
process.once('SIGINT', () => {
|
|
423
|
+
watcher.close()
|
|
424
|
+
if (options.json) writeJsonEvent('close', { command: commandName })
|
|
425
|
+
process.exit(0)
|
|
426
|
+
})
|
|
427
|
+
return await new Promise(() => {})
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function runDeliveryBuild(commandName) {
|
|
432
|
+
return writeResult(await withJsonLogRedirect(options.json, () => packProject({
|
|
433
|
+
...options,
|
|
434
|
+
command: commandName,
|
|
435
|
+
out: options.out || 'dist/release',
|
|
436
|
+
build: options.build !== false,
|
|
437
|
+
packageExtension: 'wgt',
|
|
438
|
+
})), options)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (command === 'inspect') return writeResult(await withJsonLogRedirect(options.json, () => inspectProject(options)), options)
|
|
442
|
+
if (command === 'init') return writeResult(await initProject(options), options)
|
|
443
|
+
if (command === 'cli-version') {
|
|
444
|
+
const info = await getCliPackageInfo()
|
|
445
|
+
return writeResult({
|
|
446
|
+
ok: true,
|
|
447
|
+
command: 'cli-version',
|
|
448
|
+
packageName: info.packageName,
|
|
449
|
+
currentVersion: info.version,
|
|
450
|
+
packageRoot: info.packageRoot,
|
|
451
|
+
}, options)
|
|
452
|
+
}
|
|
453
|
+
if (command === 'update') {
|
|
454
|
+
const subcommand = options._[0]
|
|
455
|
+
return writeResult(await updateCli({
|
|
456
|
+
...options,
|
|
457
|
+
check: options.check || subcommand === 'check',
|
|
458
|
+
dryRun: options.dryRun || options.dry_run,
|
|
459
|
+
}), options)
|
|
460
|
+
}
|
|
461
|
+
if (command === 'compile') return await runCompileCommand('compile')
|
|
462
|
+
if (command === 'build') return await runDeliveryBuild('build')
|
|
463
|
+
if (command === 'pack') return writeResult(await withJsonLogRedirect(options.json, () => packProject({ ...options, command: 'pack' })), options)
|
|
464
|
+
if (command === 'wgt') return await runDeliveryBuild('wgt')
|
|
465
|
+
if (command === 'preview') {
|
|
466
|
+
const eventStream = options.json && options.stay
|
|
467
|
+
const signalController = options.stay ? createSessionSignalController() : null
|
|
468
|
+
const session = await startPreview({
|
|
469
|
+
...options,
|
|
470
|
+
command: 'preview',
|
|
471
|
+
withPackOutputRedirect: action => withJsonLogRedirect(options.json, action),
|
|
472
|
+
onEvent: event => {
|
|
473
|
+
writeLifecycleEvent(eventStream, event)
|
|
474
|
+
return options.onEvent?.(event)
|
|
475
|
+
},
|
|
476
|
+
})
|
|
477
|
+
signalController?.setSession(session)
|
|
478
|
+
if (!eventStream) await writeResult(session, options)
|
|
479
|
+
if (options.stay) {
|
|
480
|
+
return await signalController.wait()
|
|
481
|
+
}
|
|
482
|
+
return await session.close()
|
|
483
|
+
}
|
|
484
|
+
if (command === 'web') {
|
|
485
|
+
const eventStream = options.json && options.watch
|
|
486
|
+
let sawBuildErrorEvent = false
|
|
487
|
+
let session
|
|
488
|
+
const signalController = options.stay || options.watch || options.open !== false
|
|
489
|
+
? createSessionSignalController()
|
|
490
|
+
: null
|
|
491
|
+
try {
|
|
492
|
+
session = await startWeb({
|
|
493
|
+
...options,
|
|
494
|
+
withBuildOutputRedirect: action => withJsonLogRedirect(options.json, action),
|
|
495
|
+
onEvent: event => {
|
|
496
|
+
if (event.event === 'build-error') sawBuildErrorEvent = true
|
|
497
|
+
writeLifecycleEvent(eventStream, event)
|
|
498
|
+
return options.onEvent?.(event)
|
|
499
|
+
},
|
|
500
|
+
onBuildStart: payload => {
|
|
501
|
+
return options.onBuildStart?.(payload)
|
|
502
|
+
},
|
|
503
|
+
onRebuild: payload => {
|
|
504
|
+
return options.onRebuild?.(payload)
|
|
505
|
+
},
|
|
506
|
+
onBuildError: error => {
|
|
507
|
+
return options.onBuildError?.(error)
|
|
508
|
+
},
|
|
509
|
+
onReload: payload => {
|
|
510
|
+
return options.onReload?.(payload)
|
|
511
|
+
},
|
|
512
|
+
})
|
|
513
|
+
signalController?.setSession(session)
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
if (eventStream) {
|
|
517
|
+
if (!sawBuildErrorEvent) writeJsonEvent('build-error', { command: 'web', error: toErrorPayload(error) })
|
|
518
|
+
process.exitCode = 1
|
|
519
|
+
return
|
|
520
|
+
}
|
|
521
|
+
throw error
|
|
522
|
+
}
|
|
523
|
+
if (!eventStream) {
|
|
524
|
+
await writeResult(session, options)
|
|
525
|
+
}
|
|
526
|
+
if (options.stay || options.watch || options.open !== false) {
|
|
527
|
+
return await signalController.wait()
|
|
528
|
+
}
|
|
529
|
+
return await session.close()
|
|
530
|
+
}
|
|
531
|
+
if (command === 'dev') {
|
|
532
|
+
const eventStream = !!options.json
|
|
533
|
+
let sawBuildErrorEvent = false
|
|
534
|
+
let session
|
|
535
|
+
const signalController = createSessionSignalController()
|
|
536
|
+
try {
|
|
537
|
+
session = await startDev({
|
|
538
|
+
...options,
|
|
539
|
+
withBuildOutputRedirect: action => withJsonLogRedirect(options.json, action),
|
|
540
|
+
onEvent: event => {
|
|
541
|
+
if (event.event === 'build-error') sawBuildErrorEvent = true
|
|
542
|
+
writeLifecycleEvent(eventStream, event)
|
|
543
|
+
return options.onEvent?.(event)
|
|
544
|
+
},
|
|
545
|
+
onBuildStart: payload => {
|
|
546
|
+
return options.onBuildStart?.(payload)
|
|
547
|
+
},
|
|
548
|
+
onRebuild: payload => {
|
|
549
|
+
return options.onRebuild?.(payload)
|
|
550
|
+
},
|
|
551
|
+
onBuildError: error => {
|
|
552
|
+
return options.onBuildError?.(error)
|
|
553
|
+
},
|
|
554
|
+
onReload: payload => {
|
|
555
|
+
return options.onReload?.(payload)
|
|
556
|
+
},
|
|
557
|
+
})
|
|
558
|
+
signalController.setSession(session)
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
if (eventStream) {
|
|
562
|
+
if (!sawBuildErrorEvent) writeJsonEvent('build-error', { command: 'dev', error: toErrorPayload(error) })
|
|
563
|
+
process.exitCode = 1
|
|
564
|
+
return
|
|
565
|
+
}
|
|
566
|
+
throw error
|
|
567
|
+
}
|
|
568
|
+
if (!eventStream) {
|
|
569
|
+
await writeResult(session, options)
|
|
570
|
+
}
|
|
571
|
+
return await signalController.wait()
|
|
572
|
+
}
|
|
573
|
+
if (command === 'login') {
|
|
574
|
+
if (!options.provider) return await unsupportedProviderCommand(command)
|
|
575
|
+
return writeResult(await runProviderCommand(command, options, {
|
|
576
|
+
project: options.project ? await inspectProject(options) : null,
|
|
577
|
+
}), options)
|
|
578
|
+
}
|
|
579
|
+
if (command === 'config') {
|
|
580
|
+
const subcommand = options._[0] || 'doctor'
|
|
581
|
+
if (subcommand === 'set') {
|
|
582
|
+
const project = await inspectProject(options)
|
|
583
|
+
if (!options.key && options.noInteractive === true) {
|
|
584
|
+
throw new DiminaCliError('NEUX_CLI_KEY_REQUIRED', 'CLI Key is required in non-interactive mode. Pass --key <key>.')
|
|
585
|
+
}
|
|
586
|
+
const key = options.key || await promptLine('CLI Key: ', { secret: true })
|
|
587
|
+
const result = await saveCliKey({ appId: options.appId || project.appId, key, configPath: options.config })
|
|
588
|
+
return writeResult({ ok: true, command: 'config set', ...result, serverUrl: project.neuxCli?.serverUrl || DEFAULT_SERVER_URL }, options)
|
|
589
|
+
}
|
|
590
|
+
if (subcommand !== 'doctor') {
|
|
591
|
+
throw new DiminaCliError('DIMINA_UNKNOWN_COMMAND', `Unknown config command: ${subcommand}`, { command: 'config', subcommand })
|
|
592
|
+
}
|
|
593
|
+
return writeResult(await resolveServiceConfigDiagnostics(options), options)
|
|
594
|
+
}
|
|
595
|
+
if ((command === 'audit' || command === 'submit') && (!options.provider || options.provider === 'service')) {
|
|
596
|
+
return writeResult(await runProviderCommand('submit-audit', options, {}), options)
|
|
597
|
+
}
|
|
598
|
+
if (command === 'status' || command === 'meta') {
|
|
599
|
+
return writeResult(await runProviderCommand('version', options, {}), options)
|
|
600
|
+
}
|
|
601
|
+
if (command === 'upload' || command === 'debug' || command === 'debug-preview' || command === 'audit' || command === 'submit') {
|
|
602
|
+
const providerCommand = command === 'debug' ? 'debug-preview' : command
|
|
603
|
+
const serviceProvider = !options.provider || options.provider === 'service'
|
|
604
|
+
const servicePackage = serviceProvider
|
|
605
|
+
if (servicePackage) await resolveServiceConfig(options)
|
|
606
|
+
const shouldBuild = servicePackage && !options.sourceDir && !options.compiledDir
|
|
607
|
+
? options.build !== false
|
|
608
|
+
: options.build === true
|
|
609
|
+
const artifact = await withJsonLogRedirect(options.json, () => packProject({
|
|
610
|
+
...options,
|
|
611
|
+
command: servicePackage ? 'wgt' : command,
|
|
612
|
+
out: servicePackage ? options.out || 'dist/release' : options.out,
|
|
613
|
+
build: shouldBuild,
|
|
614
|
+
packageExtension: servicePackage ? 'wgt' : undefined,
|
|
615
|
+
}))
|
|
616
|
+
const result = await runProviderCommand(providerCommand, options, {
|
|
617
|
+
artifact,
|
|
618
|
+
project: {
|
|
619
|
+
projectPath: artifact.projectPath,
|
|
620
|
+
appId: artifact.appId,
|
|
621
|
+
},
|
|
622
|
+
})
|
|
623
|
+
return writeResult(result, {
|
|
624
|
+
...options,
|
|
625
|
+
defaultQrOutput: providerCommand === 'debug-preview'
|
|
626
|
+
? path.join(artifact.outputDir, 'debug-preview-qr.png')
|
|
627
|
+
: undefined,
|
|
628
|
+
})
|
|
629
|
+
}
|
|
630
|
+
if (command === 'service-preview' || command === 'submit-audit' || command === 'version' || command === 'audit-status') {
|
|
631
|
+
return writeResult(await runProviderCommand(command, options, {}), options)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
throw new DiminaCliError('DIMINA_UNKNOWN_COMMAND', `Unknown command: ${command}`, { command })
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
main().catch((error) => {
|
|
638
|
+
const payload = toErrorPayload(error)
|
|
639
|
+
if (process.argv.includes('--json')) {
|
|
640
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`)
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
process.stderr.write(formatErrorPayload(payload, resolveLocale(parse(process.argv.slice(2)).options)))
|
|
644
|
+
}
|
|
645
|
+
process.exitCode = 1
|
|
646
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const brand = {
|
|
2
|
+
packageName: '@neuxnet/neux-cli',
|
|
3
|
+
commandName: 'neux',
|
|
4
|
+
serviceClientName: 'neux-cli',
|
|
5
|
+
displayName: 'Neux CLI',
|
|
6
|
+
compilerPackageName: '@dimina/compiler',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function commandExample(command) {
|
|
10
|
+
return `${brand.commandName} ${command}`
|
|
11
|
+
}
|