@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.
Files changed (73) hide show
  1. package/README.md +438 -0
  2. package/README.zh-CN.md +554 -0
  3. package/assets/container/assets/index.css +1 -0
  4. package/assets/container/assets/index.js +137 -0
  5. package/assets/container/assets/pageFrame.css +1 -0
  6. package/assets/container/assets/pageFrame.js +56 -0
  7. package/assets/container/assets/service.js +6 -0
  8. package/assets/container/assets/vconsole.js +931 -0
  9. package/assets/container/favicon.ico +0 -0
  10. package/assets/container/images/icon-arrow.png +0 -0
  11. package/assets/container/images/mini-action-white.png +0 -0
  12. package/assets/container/images/mini-action.png +0 -0
  13. package/assets/container/images/mini-arrow-left-white.png +0 -0
  14. package/assets/container/images/mini-arrow-left.jpg +0 -0
  15. package/assets/container/images/mini-arrow-left.png +0 -0
  16. package/assets/container/images/mini-close-white.png +0 -0
  17. package/assets/container/images/mini-close.png +0 -0
  18. package/assets/container/images/more.png +0 -0
  19. package/assets/container/images/search.jpg +0 -0
  20. package/assets/container/index.html +1 -0
  21. package/assets/container/pageFrame.html +1 -0
  22. package/assets/init/tabbar/home-active.png +0 -0
  23. package/assets/init/tabbar/home.png +0 -0
  24. package/assets/init/tabbar/list-active.png +0 -0
  25. package/assets/init/tabbar/list.png +0 -0
  26. package/assets/init/types/neux-api.d.ts +1402 -0
  27. package/package.json +68 -0
  28. package/scripts/generate-init-types.js +210 -0
  29. package/scripts/sync-compiler.js +22 -0
  30. package/scripts/sync-web-container.js +34 -0
  31. package/src/bin/cli.js +646 -0
  32. package/src/core/brand.js +11 -0
  33. package/src/core/compiler.js +305 -0
  34. package/src/core/defaults.js +12 -0
  35. package/src/core/dev.js +15 -0
  36. package/src/core/errors.js +17 -0
  37. package/src/core/fs.js +66 -0
  38. package/src/core/i18n.js +37 -0
  39. package/src/core/init.js +866 -0
  40. package/src/core/lifecycle.js +32 -0
  41. package/src/core/manifest.js +72 -0
  42. package/src/core/pack.js +156 -0
  43. package/src/core/package-info.js +27 -0
  44. package/src/core/preview-server.js +123 -0
  45. package/src/core/project.js +101 -0
  46. package/src/core/prompts.js +71 -0
  47. package/src/core/proxy-security.js +141 -0
  48. package/src/core/proxy.js +156 -0
  49. package/src/core/qr.js +41 -0
  50. package/src/core/terminal-qr.js +72 -0
  51. package/src/core/update.js +278 -0
  52. package/src/core/watch.js +113 -0
  53. package/src/core/web.js +649 -0
  54. package/src/core/zip.js +120 -0
  55. package/src/index.js +20 -0
  56. package/src/providers/service-client.js +149 -0
  57. package/src/providers/service-config.js +264 -0
  58. package/src/providers/service.js +146 -0
  59. package/src/providers/upload.js +112 -0
  60. package/vendor/dimina-compiler/bin/index.cjs +265 -0
  61. package/vendor/dimina-compiler/bin/index.js +263 -0
  62. package/vendor/dimina-compiler/compatibility-B-DoZtUX.cjs +395 -0
  63. package/vendor/dimina-compiler/compatibility-Cl3-DO6V.js +366 -0
  64. package/vendor/dimina-compiler/core/logic-compiler.cjs +378 -0
  65. package/vendor/dimina-compiler/core/logic-compiler.js +374 -0
  66. package/vendor/dimina-compiler/core/style-compiler.cjs +392 -0
  67. package/vendor/dimina-compiler/core/style-compiler.js +377 -0
  68. package/vendor/dimina-compiler/core/view-compiler.cjs +1601 -0
  69. package/vendor/dimina-compiler/core/view-compiler.js +1582 -0
  70. package/vendor/dimina-compiler/index.cjs +762 -0
  71. package/vendor/dimina-compiler/index.js +751 -0
  72. package/vendor/dimina-compiler/sourcemap-BgtIgqkC.cjs +1377 -0
  73. package/vendor/dimina-compiler/sourcemap-CKjhV9h7.js +1135 -0
@@ -0,0 +1,32 @@
1
+ export function createLifecycleEvent(event, payload = {}) {
2
+ return {
3
+ event,
4
+ timestamp: Date.now(),
5
+ ...jsonSafe(payload),
6
+ }
7
+ }
8
+
9
+ export async function emitLifecycleEvent(options, event, payload = {}) {
10
+ const record = createLifecycleEvent(event, payload)
11
+ await options.onEvent?.(record)
12
+ return record
13
+ }
14
+
15
+ function jsonSafe(value) {
16
+ if (value instanceof Error) {
17
+ return {
18
+ name: value.name,
19
+ message: value.message,
20
+ code: value.code,
21
+ }
22
+ }
23
+ if (Array.isArray(value)) return value.map(jsonSafe)
24
+ if (!value || typeof value !== 'object') return value
25
+
26
+ const output = {}
27
+ for (const [key, item] of Object.entries(value)) {
28
+ if (typeof item === 'function') continue
29
+ output[key] = jsonSafe(item)
30
+ }
31
+ return output
32
+ }
@@ -0,0 +1,72 @@
1
+ export function createManifest(config, packageUrl, packageSha256, allowDowngrade = false) {
2
+ return {
3
+ schemaVersion: 2,
4
+ appId: config.appId,
5
+ name: config.name,
6
+ description: config.description || '',
7
+ versionCode: config.versionCode,
8
+ versionName: config.versionName,
9
+ path: config.path,
10
+ entryPath: config.path,
11
+ packageUrl,
12
+ sha256: packageSha256,
13
+ allowDowngrade,
14
+ address: {
15
+ id: config.appId,
16
+ manifestUrl: '',
17
+ version: config.versionCode,
18
+ path: config.path,
19
+ },
20
+ appInfo: {
21
+ description: config.description || '',
22
+ categories: config.categories || [],
23
+ icons: Object.values(config.icons || {}).filter(Boolean),
24
+ screenshots: config.screenshots || [],
25
+ },
26
+ signature: '',
27
+ algorithm: '',
28
+ publicKeyId: '',
29
+ offlineEnabled: true,
30
+ updateMode: 'manual',
31
+ minRuntimeVersion: '',
32
+ permissions: [],
33
+ domains: [],
34
+ }
35
+ }
36
+
37
+ export function createDeepLink({ appId, version, appPath, query = '', fragment = '' }) {
38
+ const encodedPath = String(appPath || '')
39
+ .split('/')
40
+ .filter(Boolean)
41
+ .map(segment => encodeURIComponent(segment))
42
+ .join('/')
43
+ const pathSegment = encodedPath ? `/${encodedPath}` : ''
44
+ const versionSegment = Number.isInteger(version) ? `;version=${version}` : ''
45
+ const querySegment = query ? `?${query}` : ''
46
+ const fragmentSegment = fragment ? `#${fragment}` : ''
47
+ return `dimina://miniapp/${encodeURIComponent(appId)}${versionSegment}${pathSegment}${querySegment}${fragmentSegment}`
48
+ }
49
+
50
+ export function createAppConfig({
51
+ appId,
52
+ appName,
53
+ appPath,
54
+ description = '',
55
+ versionCode = 1,
56
+ versionName = '1.0.0',
57
+ categories = [],
58
+ icons = {},
59
+ screenshots = [],
60
+ }) {
61
+ return {
62
+ appId,
63
+ name: appName || `App ${appId}`,
64
+ path: appPath || 'pages/index',
65
+ description,
66
+ categories,
67
+ icons,
68
+ screenshots,
69
+ versionCode,
70
+ versionName,
71
+ }
72
+ }
@@ -0,0 +1,156 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import { buildProject } from './compiler.js'
4
+ import { pathExists, readJson, resolvePath, writeJson } from './fs.js'
5
+ import { createAppConfig, createDeepLink, createManifest } from './manifest.js'
6
+ import { inspectProject } from './project.js'
7
+ import { createZip } from './zip.js'
8
+ import { defaultOut } from './defaults.js'
9
+
10
+ export async function packProject(options = {}) {
11
+ const projectInfo = await inspectProject(options)
12
+ const out = resolvePath(options.out || options.output || defaultOut(options.command || 'pack'))
13
+ let packageSourceDir = resolvePath(options.sourceDir || options.compiledDir || projectInfo.sourceRoot)
14
+ let buildResult = null
15
+ const packageExtension = String(options.packageExtension || 'zip').replace(/^\./, '')
16
+ const isWgtPackage = packageExtension === 'wgt'
17
+ const temporaryPaths = []
18
+
19
+ if (options.build) {
20
+ const buildOut = resolvePath(options.buildOut || (isWgtPackage ? path.join(out, '..', '.wgt-build') : path.join(out, '.build')))
21
+ buildResult = await buildProject({ ...options, out: buildOut })
22
+ packageSourceDir = await resolveBuiltAppDir(buildOut, projectInfo.appId, buildResult)
23
+ if (isWgtPackage && !options.buildOut) temporaryPaths.push(buildOut)
24
+ }
25
+
26
+ const appId = options.appId || projectInfo.appId
27
+ const compilerConfig = buildResult?.compilerResult && typeof buildResult.compilerResult === 'object'
28
+ ? buildResult.compilerResult
29
+ : {}
30
+ const cliConfig = projectInfo.cliConfig || {}
31
+ const appOutDir = isWgtPackage ? out : path.join(out, appId)
32
+ const config = createAppConfig({
33
+ appId,
34
+ appName: options.name || compilerConfig.name || projectInfo.name,
35
+ appPath: options.entryPath || compilerConfig.path || compilerConfig.entryPath || projectInfo.entryPath,
36
+ description: options.description || '',
37
+ versionCode: Number.parseInt(firstConfigValue(options.versionCode, cliConfig.versionCode, 1), 10),
38
+ versionName: firstConfigValue(
39
+ options.versionName,
40
+ options.version,
41
+ cliConfig.versionName,
42
+ cliConfig.version,
43
+ await readPackageVersion(projectInfo.projectPath),
44
+ '1.0.0',
45
+ ),
46
+ })
47
+
48
+ if (isWgtPackage && (options.command === 'build' || options.command === 'wgt')) {
49
+ await fs.rm(appOutDir, { recursive: true, force: true })
50
+ }
51
+ await fs.mkdir(appOutDir, { recursive: true })
52
+ if (isWgtPackage) {
53
+ const stagingDir = path.join(out, `.wgt-${appId}`)
54
+ temporaryPaths.push(stagingDir)
55
+ try {
56
+ await fs.rm(stagingDir, { recursive: true, force: true })
57
+ await fs.mkdir(stagingDir, { recursive: true })
58
+
59
+ const configPath = path.join(stagingDir, 'config.json')
60
+ await writeJson(configPath, config)
61
+ const innerPackagePath = path.join(stagingDir, `${appId}.zip`)
62
+ const innerZipInfo = await createZip(packageSourceDir, innerPackagePath)
63
+
64
+ const packagePath = path.join(appOutDir, `${appId}.wgt`)
65
+ const zipInfo = await createZip(stagingDir, packagePath)
66
+
67
+ return {
68
+ ok: true,
69
+ command: options.command || 'pack',
70
+ projectPath: projectInfo.projectPath,
71
+ sourceDir: packageSourceDir,
72
+ outputDir: appOutDir,
73
+ appId,
74
+ config,
75
+ configPath: null,
76
+ packagePath,
77
+ manifestPath: null,
78
+ sha256: zipInfo.sha256,
79
+ packageSize: zipInfo.size,
80
+ innerPackageName: `${appId}.zip`,
81
+ innerSha256: innerZipInfo.sha256,
82
+ innerPackageSize: innerZipInfo.size,
83
+ buildResult,
84
+ }
85
+ }
86
+ finally {
87
+ for (const temporaryPath of temporaryPaths.reverse()) {
88
+ await fs.rm(temporaryPath, { recursive: true, force: true })
89
+ }
90
+ }
91
+ }
92
+
93
+ const configPath = path.join(appOutDir, 'config.json')
94
+ await writeJson(configPath, config)
95
+
96
+ const zipPath = path.join(appOutDir, `${appId}.${packageExtension}`)
97
+ const configContent = `${JSON.stringify(config, null, 2)}\n`
98
+ const zipInfo = await createZip(packageSourceDir, zipPath, [{ name: 'config.json', content: configContent }])
99
+ const manifest = createManifest(config, `${appId}.${packageExtension}`, zipInfo.sha256, !!options.allowDowngrade)
100
+ const manifestPath = path.join(appOutDir, 'manifest.json')
101
+ await writeJson(manifestPath, manifest)
102
+
103
+ return {
104
+ ok: true,
105
+ command: 'pack',
106
+ projectPath: projectInfo.projectPath,
107
+ sourceDir: packageSourceDir,
108
+ outputDir: appOutDir,
109
+ appId,
110
+ config,
111
+ configPath,
112
+ packagePath: zipPath,
113
+ manifestPath,
114
+ sha256: zipInfo.sha256,
115
+ packageSize: zipInfo.size,
116
+ buildResult,
117
+ }
118
+ }
119
+
120
+ async function readPackageVersion(projectPath) {
121
+ const packageJson = await readJson(path.join(projectPath, 'package.json'), {})
122
+ return packageJson?.version
123
+ }
124
+
125
+ function firstConfigValue(...values) {
126
+ return values.find(value => value !== undefined && value !== null && value !== '')
127
+ }
128
+
129
+ async function resolveBuiltAppDir(buildOut, appId, buildResult) {
130
+ const compilerResult = buildResult?.compilerResult && typeof buildResult.compilerResult === 'object'
131
+ ? buildResult.compilerResult
132
+ : {}
133
+ const candidates = Array.from(new Set([
134
+ appId,
135
+ compilerResult.appId,
136
+ buildResult?.appId,
137
+ ].filter(Boolean)))
138
+
139
+ for (const candidate of candidates) {
140
+ const candidatePath = path.join(buildOut, candidate)
141
+ if (await pathExists(candidatePath)) return candidatePath
142
+ }
143
+
144
+ return path.join(buildOut, appId)
145
+ }
146
+
147
+ export function createPreviewUrls({ serverUrl, appId, versionCode, appPath, packageExtension = 'zip' }) {
148
+ const base = `${serverUrl}/${encodeURIComponent(appId)}`
149
+ const extension = String(packageExtension).replace(/^\./, '')
150
+ return {
151
+ serverUrl,
152
+ manifestUrl: `${base}/manifest.json`,
153
+ packageUrl: `${base}/${encodeURIComponent(appId)}.${extension}`,
154
+ deepLink: createDeepLink({ appId, version: versionCode, appPath }),
155
+ }
156
+ }
@@ -0,0 +1,27 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { brand } from './brand.js'
5
+
6
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..')
7
+ let packageInfoPromise
8
+
9
+ export async function getCliPackageInfo() {
10
+ packageInfoPromise ||= fs.readFile(path.join(packageRoot, 'package.json'), 'utf8')
11
+ .then(JSON.parse)
12
+
13
+ const packageJson = await packageInfoPromise
14
+ return {
15
+ packageName: packageJson.name || brand.packageName,
16
+ version: packageJson.version,
17
+ packageRoot,
18
+ }
19
+ }
20
+
21
+ export async function getCliVersion() {
22
+ return (await getCliPackageInfo()).version
23
+ }
24
+
25
+ export async function getServiceClientVersion() {
26
+ return `${brand.serviceClientName}/${await getCliVersion()}`
27
+ }
@@ -0,0 +1,123 @@
1
+ import fs from 'node:fs'
2
+ import http from 'node:http'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { DiminaCliError } from './errors.js'
6
+ import { emitLifecycleEvent } from './lifecycle.js'
7
+ import { packProject, createPreviewUrls } from './pack.js'
8
+ import { createLocalPreviewDeepLink, createQrPayloadResult } from './qr.js'
9
+
10
+ export function getLocalIPAddress() {
11
+ for (const addresses of Object.values(os.networkInterfaces())) {
12
+ for (const address of addresses || []) {
13
+ if (address.family === 'IPv4' && !address.internal) return address.address
14
+ }
15
+ }
16
+ return '127.0.0.1'
17
+ }
18
+
19
+ function contentType(filePath) {
20
+ const ext = path.extname(filePath)
21
+ if (ext === '.json') return 'application/json; charset=utf-8'
22
+ if (ext === '.zip') return 'application/zip'
23
+ if (ext === '.png') return 'image/png'
24
+ return 'application/octet-stream'
25
+ }
26
+
27
+ export async function startStaticServer(rootDir, host, port) {
28
+ const root = path.resolve(rootDir)
29
+ const server = http.createServer((req, res) => {
30
+ let pathname
31
+ try {
32
+ pathname = decodeURIComponent(new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`).pathname)
33
+ }
34
+ catch {
35
+ res.writeHead(400)
36
+ res.end('Bad request')
37
+ return
38
+ }
39
+
40
+ const resolved = path.resolve(path.join(root, path.normalize(pathname)))
41
+ if (resolved !== root && !resolved.startsWith(root + path.sep)) {
42
+ res.writeHead(403)
43
+ res.end('Forbidden')
44
+ return
45
+ }
46
+
47
+ fs.stat(resolved, (statError, stat) => {
48
+ if (statError || !stat.isFile()) {
49
+ res.writeHead(404)
50
+ res.end('Not found')
51
+ return
52
+ }
53
+ res.writeHead(200, { 'Content-Type': contentType(resolved) })
54
+ fs.createReadStream(resolved).pipe(res)
55
+ })
56
+ })
57
+
58
+ await new Promise((resolve, reject) => {
59
+ server.once('error', reject)
60
+ server.listen(port, host, () => {
61
+ server.off('error', reject)
62
+ resolve()
63
+ })
64
+ }).catch((error) => {
65
+ throw new DiminaCliError('DIMINA_PREVIEW_SERVER_FAILED', `Failed to start preview server: ${error.message}`, {
66
+ host,
67
+ port,
68
+ cause: error.code,
69
+ })
70
+ })
71
+
72
+ return server
73
+ }
74
+
75
+ export async function startPreview(options = {}) {
76
+ const command = options.command || 'preview'
77
+ const host = options.host || getLocalIPAddress()
78
+ const port = Number.parseInt(options.port ?? 5273, 10)
79
+ const runPack = options.withPackOutputRedirect || (action => action())
80
+ const pack = await runPack(() => packProject({ ...options, command }))
81
+ const wgtPack = await runPack(() => packProject({
82
+ ...options,
83
+ command,
84
+ out: pack.outputDir,
85
+ packageExtension: 'wgt',
86
+ }))
87
+ const server = await startStaticServer(path.dirname(pack.outputDir), host, port)
88
+ const address = server.address()
89
+ const resolvedPort = typeof address === 'object' && address ? address.port : port
90
+ const serverUrl = `http://${host}:${resolvedPort}`
91
+ const urls = createPreviewUrls({
92
+ serverUrl,
93
+ appId: pack.appId,
94
+ versionCode: wgtPack.config.versionCode,
95
+ appPath: pack.config.path,
96
+ packageExtension: 'wgt',
97
+ })
98
+ const deepLink = createLocalPreviewDeepLink({
99
+ appId: pack.appId,
100
+ download: urls.packageUrl,
101
+ version: wgtPack.config.versionCode,
102
+ pagePath: pack.config.path,
103
+ })
104
+
105
+ const session = {
106
+ ok: true,
107
+ command,
108
+ ...urls,
109
+ ...createQrPayloadResult(deepLink),
110
+ appId: pack.appId,
111
+ artifact: pack,
112
+ close: async () => {
113
+ await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve()))
114
+ await emitLifecycleEvent(options, 'close', {
115
+ command,
116
+ appId: pack.appId,
117
+ serverUrl,
118
+ })
119
+ },
120
+ }
121
+ await emitLifecycleEvent(options, 'ready', session)
122
+ return session
123
+ }
@@ -0,0 +1,101 @@
1
+ import path from 'node:path'
2
+ import { DiminaCliError } from './errors.js'
3
+ import { pathExists, readJson, resolvePath } from './fs.js'
4
+
5
+ export async function inspectProject(options = {}) {
6
+ const projectPath = resolvePath(options.project)
7
+ const projectConfigPath = path.join(projectPath, 'project.config.json')
8
+ const projectPrivateConfigPath = path.join(projectPath, 'project.private.config.json')
9
+
10
+ if (!(await pathExists(projectConfigPath))) {
11
+ throw new DiminaCliError('DIMINA_PROJECT_CONFIG_NOT_FOUND', `project.config.json not found: ${projectConfigPath}`, {
12
+ projectPath,
13
+ projectConfigPath,
14
+ })
15
+ }
16
+
17
+ const projectConfig = await readJson(projectConfigPath)
18
+ const projectPrivateConfig = await readJson(projectPrivateConfigPath, {})
19
+ const effectiveProjectConfig = mergeProjectConfig(projectConfig, projectPrivateConfig)
20
+ const cliConfig = await readProjectCliConfig(projectPath, effectiveProjectConfig)
21
+ const miniprogramRoot = effectiveProjectConfig.miniprogramRoot || ''
22
+ const sourceRoot = path.resolve(projectPath, miniprogramRoot)
23
+ const appConfigPath = path.join(sourceRoot, 'app.json')
24
+ const appConfig = await readJson(appConfigPath, {})
25
+ const pages = Array.isArray(appConfig.pages) ? appConfig.pages : []
26
+ const appId = options.appId || effectiveProjectConfig.appid || effectiveProjectConfig.appId || ''
27
+ const appName = options.name || effectiveProjectConfig.projectname || effectiveProjectConfig.projectName || `App ${appId || 'unknown'}`
28
+ const entryPath = options.entryPath || appConfig.entryPagePath || pages[0] || 'pages/index'
29
+ const hasPrivateConfig = await pathExists(projectPrivateConfigPath)
30
+
31
+ return {
32
+ ok: true,
33
+ command: 'inspect',
34
+ projectPath,
35
+ sourceRoot,
36
+ projectConfigPath,
37
+ projectPrivateConfigPath: hasPrivateConfig ? projectPrivateConfigPath : null,
38
+ appConfigPath,
39
+ appId,
40
+ name: appName,
41
+ entryPath,
42
+ pages,
43
+ compileType: effectiveProjectConfig.compileType || 'miniprogram',
44
+ libVersion: effectiveProjectConfig.libVersion || '',
45
+ miniprogramRoot,
46
+ setting: effectiveProjectConfig.setting || {},
47
+ packOptions: normalizePackOptions(effectiveProjectConfig.packOptions),
48
+ condition: effectiveProjectConfig.condition || {},
49
+ neuxCli: effectiveProjectConfig.neuxCli || {},
50
+ cliConfig,
51
+ }
52
+ }
53
+
54
+ function mergeProjectConfig(projectConfig = {}, privateConfig = {}) {
55
+ return {
56
+ ...projectConfig,
57
+ ...privateConfig,
58
+ setting: {
59
+ ...(projectConfig.setting || {}),
60
+ ...(privateConfig.setting || {}),
61
+ },
62
+ packOptions: {
63
+ ...(projectConfig.packOptions || {}),
64
+ ...(privateConfig.packOptions || {}),
65
+ },
66
+ neux: {
67
+ ...(projectConfig.neux || {}),
68
+ ...(privateConfig.neux || {}),
69
+ },
70
+ neuxCli: {
71
+ ...(projectConfig.neuxCli || {}),
72
+ ...(privateConfig.neuxCli || {}),
73
+ },
74
+ }
75
+ }
76
+
77
+ function normalizePackOptions(packOptions = {}) {
78
+ return {
79
+ ignore: Array.isArray(packOptions.ignore) ? packOptions.ignore : [],
80
+ include: Array.isArray(packOptions.include) ? packOptions.include : [],
81
+ }
82
+ }
83
+
84
+ async function readProjectCliConfig(projectPath, projectConfig) {
85
+ const neuxConfigPath = path.join(projectPath, 'neux.config.json')
86
+ const neuxConfig = await readJson(neuxConfigPath, {})
87
+ const source = {
88
+ ...pickCliConfig(projectConfig),
89
+ ...neuxConfig,
90
+ }
91
+
92
+ return pickCliConfig(source)
93
+ }
94
+
95
+ function pickCliConfig(source = {}) {
96
+ return {
97
+ version: source.version,
98
+ versionName: source.versionName,
99
+ versionCode: source.versionCode,
100
+ }
101
+ }
@@ -0,0 +1,71 @@
1
+ import readline from 'node:readline/promises'
2
+ import process from 'node:process'
3
+
4
+ export function shouldPrompt(options = {}) {
5
+ return options.interactive !== false
6
+ && options.noInteractive !== true
7
+ && !options.json
8
+ && !!process.stdin.isTTY
9
+ && !!process.stdout.isTTY
10
+ }
11
+
12
+ export async function promptLine(label, options = {}) {
13
+ const terminal = readline.createInterface({ input: process.stdin, output: process.stdout })
14
+ try {
15
+ if (options.secret) return await promptSecret(terminal, label)
16
+ return await terminal.question(label)
17
+ }
18
+ finally {
19
+ terminal.close()
20
+ }
21
+ }
22
+
23
+ export async function promptLines(prompts) {
24
+ const terminal = readline.createInterface({ input: process.stdin, output: process.stdout })
25
+ try {
26
+ const values = {}
27
+ for (const prompt of prompts) {
28
+ values[prompt.name] = prompt.secret
29
+ ? await promptSecret(terminal, prompt.label)
30
+ : await terminal.question(prompt.label)
31
+ }
32
+ return values
33
+ }
34
+ finally {
35
+ terminal.close()
36
+ }
37
+ }
38
+
39
+ async function promptSecret(terminal, label) {
40
+ if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function') {
41
+ return await terminal.question(label)
42
+ }
43
+
44
+ process.stdout.write(label)
45
+ process.stdin.setRawMode(true)
46
+ process.stdin.resume()
47
+ return await new Promise(resolve => {
48
+ let value = ''
49
+ const onData = chunk => {
50
+ const character = String(chunk)
51
+ if (character === '\u0003') {
52
+ process.stdin.setRawMode(false)
53
+ process.exit(130)
54
+ }
55
+ if (character === '\r' || character === '\n') {
56
+ process.stdin.setRawMode(false)
57
+ process.stdin.pause()
58
+ process.stdin.off('data', onData)
59
+ process.stdout.write('\n')
60
+ resolve(value)
61
+ }
62
+ else if (character === '\u007f' || character === '\b') {
63
+ value = value.slice(0, -1)
64
+ }
65
+ else if (character >= ' ') {
66
+ value += character
67
+ }
68
+ }
69
+ process.stdin.on('data', onData)
70
+ })
71
+ }