@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,751 @@
1
+ import { A as resetAssetCache, E as collectAssets, F as DependencyGraph, S as getWorkPath, T as storeInfo, _ as getTargetPath, a as getAppConfigInfo, b as getViewScriptExts, c as getAppStyleScopeId, g as getStyleExts, h as getProjectConfig, l as getComponent, m as getPages, o as getAppId, p as getPageConfigInfo, s as getAppName, y as getTemplateExts } from "./sourcemap-CKjhV9h7.js";
2
+ import { compileSS } from "./core/style-compiler.js";
3
+ import { compileML, resetViewCompilerCache } from "./core/view-compiler.js";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+ import { fileURLToPath } from "node:url";
8
+ import { Worker } from "node:worker_threads";
9
+ import { Listr, PRESET_TIMER, isUnicodeSupported } from "listr2";
10
+ import os from "node:os";
11
+ //#region src/common/compile-progress.js
12
+ var DEFAULT_TERMINAL_COLUMNS = 80;
13
+ var MIN_PROGRESS_WIDTH = 8;
14
+ var MAX_PROGRESS_WIDTH = 28;
15
+ var RENDERER_CHROME_WIDTH = 14;
16
+ var PROGRESS_BRACKETS_WIDTH = 2;
17
+ /**
18
+ * Format worker progress without assuming a fixed terminal width.
19
+ * The completed count remains the source of truth. Whole cells avoid the
20
+ * visible gap that partial block glyphs leave before the unfilled section.
21
+ */
22
+ function formatCompileProgress(completed, total, options = {}) {
23
+ const unicode = options.unicode ?? isUnicodeSupported();
24
+ const columns = normalizePositiveInteger(options.columns) || process.stdout.columns || DEFAULT_TERMINAL_COLUMNS;
25
+ const safeTotal = normalizePositiveInteger(total);
26
+ const safeCompleted = Math.min(normalizePositiveInteger(completed), safeTotal);
27
+ const ratio = safeTotal === 0 ? 0 : safeCompleted / safeTotal;
28
+ const percentage = Math.round(ratio * 100);
29
+ const separator = unicode ? "·" : "|";
30
+ const metadata = `${String(safeCompleted).padStart(String(safeTotal).length)}/${safeTotal} ${separator} ${String(percentage).padStart(3)}%`;
31
+ const availableWidth = columns - metadata.length - RENDERER_CHROME_WIDTH - PROGRESS_BRACKETS_WIDTH;
32
+ if (availableWidth < MIN_PROGRESS_WIDTH) return metadata;
33
+ const width = Math.min(availableWidth, MAX_PROGRESS_WIDTH);
34
+ return `[${unicode ? createUnicodeBar(ratio, width) : createAsciiBar(ratio, width)}] ${metadata}`;
35
+ }
36
+ function createUnicodeBar(ratio, width) {
37
+ const completeWidth = Math.round(ratio * width);
38
+ const emptyWidth = width - completeWidth;
39
+ return `${"█".repeat(completeWidth)}${"░".repeat(emptyWidth)}`;
40
+ }
41
+ function createAsciiBar(ratio, width) {
42
+ const completeWidth = Math.floor(ratio * width);
43
+ const showHead = ratio > 0 && ratio < 1;
44
+ const bodyWidth = Math.max(0, completeWidth - (showHead ? 1 : 0));
45
+ const emptyWidth = width - bodyWidth - (showHead ? 1 : 0);
46
+ return `${"=".repeat(bodyWidth)}${showHead ? ">" : ""}${"-".repeat(emptyWidth)}`;
47
+ }
48
+ function normalizePositiveInteger(value) {
49
+ return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
50
+ }
51
+ //#endregion
52
+ //#region src/common/publish.js
53
+ function createDist(seedPath) {
54
+ const distPath = getTargetPath();
55
+ if (fs.existsSync(distPath)) fs.rmSync(distPath, {
56
+ recursive: true,
57
+ force: true
58
+ });
59
+ fs.mkdirSync(distPath, { recursive: true });
60
+ if (seedPath && fs.existsSync(seedPath)) fs.cpSync(seedPath, distPath, { recursive: true });
61
+ }
62
+ /**
63
+ * 发布到指定目录
64
+ * @param {string} dist 目标路径
65
+ * @param {boolean} useAppIdDir 是否在路径中包含appId
66
+ */
67
+ function publishToDist(dist, useAppIdDir = true) {
68
+ const distPath = getTargetPath();
69
+ const appId = getAppId();
70
+ const absolutePath = useAppIdDir ? `${path.resolve(process.cwd(), dist)}${path.sep}${appId}` : `${path.resolve(process.cwd(), dist)}`;
71
+ if (fs.existsSync(absolutePath)) fs.rmSync(absolutePath, {
72
+ recursive: true,
73
+ force: true
74
+ });
75
+ fs.mkdirSync(absolutePath, { recursive: true });
76
+ function copyDir(src, dest) {
77
+ fs.mkdirSync(dest, { recursive: true });
78
+ const entries = fs.readdirSync(src, { withFileTypes: true });
79
+ for (const entry of entries) {
80
+ const srcPath = path.join(src, entry.name);
81
+ const destPath = path.join(dest, entry.name);
82
+ if (entry.isDirectory()) copyDir(srcPath, destPath);
83
+ else fs.copyFileSync(srcPath, destPath);
84
+ }
85
+ }
86
+ copyDir(distPath, absolutePath);
87
+ }
88
+ //#endregion
89
+ //#region src/common/worker-pool.js
90
+ function getCGroupCPUCount() {
91
+ try {
92
+ const quotaPath = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us";
93
+ const periodPath = "/sys/fs/cgroup/cpu/cpu.cfs_period_us";
94
+ if (fs.existsSync(quotaPath) && fs.existsSync(periodPath)) {
95
+ const quota = Number.parseInt(fs.readFileSync(quotaPath, "utf8"));
96
+ const period = Number.parseInt(fs.readFileSync(periodPath, "utf8"));
97
+ if (quota > 0) return Math.max(1, Math.floor(quota / period));
98
+ }
99
+ } catch (e) {
100
+ console.warn("Failed to read CPU limits from cgroup:", e.message);
101
+ }
102
+ return os.cpus().length;
103
+ }
104
+ function getCGroupMemoryLimit() {
105
+ try {
106
+ const memLimitPath = "/sys/fs/cgroup/memory/memory.limit_in_bytes";
107
+ if (fs.existsSync(memLimitPath)) {
108
+ const memLimit = Number.parseInt(fs.readFileSync(memLimitPath, "utf8"));
109
+ if (memLimit < Number.Infinity && memLimit > 0) return memLimit;
110
+ }
111
+ } catch (e) {
112
+ console.warn("Failed to read memory limits from cgroup:", e.message);
113
+ }
114
+ return os.totalmem();
115
+ }
116
+ var MAX_WORKERS = Math.max(1, Math.min(4, Math.floor(getCGroupCPUCount() / 4)));
117
+ var WorkerPool = class {
118
+ constructor(maxWorkers = MAX_WORKERS) {
119
+ this.maxWorkers = maxWorkers;
120
+ this.activeWorkers = 0;
121
+ this.queue = [];
122
+ this.memoryLimit = Math.floor(getCGroupMemoryLimit() * .6 / maxWorkers);
123
+ }
124
+ async runWorker(workerCreator) {
125
+ if (this.activeWorkers >= this.maxWorkers) await new Promise((resolve) => this.queue.push(resolve));
126
+ this.activeWorkers++;
127
+ try {
128
+ return await workerCreator();
129
+ } finally {
130
+ this.activeWorkers--;
131
+ if (this.queue.length > 0) this.queue.shift()();
132
+ }
133
+ }
134
+ getWorkerOptions() {
135
+ const memoryMb = Math.floor(this.memoryLimit / (1024 * 1024));
136
+ return { resourceLimits: {
137
+ maxOldGenerationSizeMb: Math.max(256, memoryMb),
138
+ maxYoungGenerationSizeMb: Math.max(64, Math.floor(memoryMb / 4)),
139
+ codeRangeSizeMb: 64
140
+ } };
141
+ }
142
+ };
143
+ var workerPool = new WorkerPool();
144
+ //#endregion
145
+ //#region src/common/npm-builder.js
146
+ /**
147
+ * npm 构建工具
148
+ * 用于处理小程序 npm 包的构建和管理
149
+ */
150
+ var NpmBuilder = class {
151
+ constructor(workPath, targetPath, dependencyGraph = null) {
152
+ this.workPath = workPath;
153
+ this.targetPath = targetPath;
154
+ this.dependencyGraph = dependencyGraph;
155
+ this.builtPackages = /* @__PURE__ */ new Set();
156
+ this.packageDependencies = /* @__PURE__ */ new Map();
157
+ }
158
+ /**
159
+ * 构建 npm 包
160
+ * 扫描 miniprogram_npm 目录并构建相关包
161
+ */
162
+ async buildNpmPackages() {
163
+ const miniprogramNpmPaths = this.findMiniprogramNpmDirs();
164
+ for (const npmPath of miniprogramNpmPaths) await this.buildNpmDir(npmPath);
165
+ }
166
+ /**
167
+ * 查找所有 miniprogram_npm 目录
168
+ * @returns {string[]} miniprogram_npm 目录路径数组
169
+ */
170
+ findMiniprogramNpmDirs() {
171
+ const npmDirs = [];
172
+ const scanDir = (dir, relativePath = "") => {
173
+ if (!fs.existsSync(dir)) return;
174
+ const items = fs.readdirSync(dir, { withFileTypes: true });
175
+ for (const item of items) if (item.isDirectory()) {
176
+ const itemPath = path.join(dir, item.name);
177
+ const itemRelativePath = relativePath ? `${relativePath}/${item.name}` : item.name;
178
+ if (item.name === "miniprogram_npm") npmDirs.push(itemRelativePath);
179
+ else scanDir(itemPath, itemRelativePath);
180
+ }
181
+ };
182
+ scanDir(this.workPath);
183
+ return npmDirs;
184
+ }
185
+ /**
186
+ * 构建指定的 miniprogram_npm 目录
187
+ * @param {string} npmDirPath miniprogram_npm 目录路径
188
+ */
189
+ async buildNpmDir(npmDirPath) {
190
+ const fullNpmPath = path.join(this.workPath, npmDirPath);
191
+ if (!fs.existsSync(fullNpmPath)) return;
192
+ const packages = fs.readdirSync(fullNpmPath, { withFileTypes: true }).filter((item) => item.isDirectory()).map((item) => item.name);
193
+ for (const packageName of packages) await this.buildPackage(packageName, npmDirPath);
194
+ }
195
+ /**
196
+ * 构建单个 npm 包
197
+ * @param {string} packageName 包名
198
+ * @param {string} npmDirPath miniprogram_npm 目录路径
199
+ */
200
+ async buildPackage(packageName, npmDirPath) {
201
+ const packageKey = `${npmDirPath}/${packageName}`;
202
+ if (this.builtPackages.has(packageKey)) return;
203
+ const packagePath = path.join(this.workPath, npmDirPath, packageName);
204
+ const targetPackagePath = path.join(this.targetPath, npmDirPath, packageName);
205
+ if (!fs.existsSync(path.dirname(targetPackagePath))) fs.mkdirSync(path.dirname(targetPackagePath), { recursive: true });
206
+ await this.copyPackageFiles(packagePath, targetPackagePath);
207
+ await this.processDependencies(packageName, packagePath, npmDirPath);
208
+ this.builtPackages.add(packageKey);
209
+ }
210
+ /**
211
+ * 复制包文件
212
+ * @param {string} sourcePath 源路径
213
+ * @param {string} targetPath 目标路径
214
+ */
215
+ async copyPackageFiles(sourcePath, targetPath) {
216
+ if (!fs.existsSync(sourcePath)) return;
217
+ if (!fs.existsSync(targetPath)) fs.mkdirSync(targetPath, { recursive: true });
218
+ const items = fs.readdirSync(sourcePath, { withFileTypes: true });
219
+ for (const item of items) {
220
+ const sourceItemPath = path.join(sourcePath, item.name);
221
+ const targetItemPath = path.join(targetPath, item.name);
222
+ if (item.isDirectory()) await this.copyPackageFiles(sourceItemPath, targetItemPath);
223
+ else if (this.isMiniprogramFile(item.name)) {
224
+ this.dependencyGraph?.addFile("app", sourceItemPath, "config");
225
+ fs.copyFileSync(sourceItemPath, targetItemPath);
226
+ }
227
+ }
228
+ }
229
+ /**
230
+ * 检查是否为小程序相关文件
231
+ * @param {string} filename 文件名
232
+ * @returns {boolean} 是否为小程序文件
233
+ */
234
+ isMiniprogramFile(filename) {
235
+ const miniprogramExts = [
236
+ ".js",
237
+ ".json",
238
+ ".ts",
239
+ ...getTemplateExts(),
240
+ ...getStyleExts(),
241
+ ...getViewScriptExts()
242
+ ];
243
+ const ext = path.extname(filename).toLowerCase();
244
+ return miniprogramExts.includes(ext) || filename === "package.json" || filename === "README.md" || filename.startsWith(".");
245
+ }
246
+ /**
247
+ * 处理包依赖
248
+ * @param {string} packageName 包名
249
+ * @param {string} packagePath 包路径
250
+ * @param {string} npmDirPath npm 目录路径
251
+ */
252
+ async processDependencies(packageName, packagePath, npmDirPath) {
253
+ const packageJsonPath = path.join(packagePath, "package.json");
254
+ if (!fs.existsSync(packageJsonPath)) return;
255
+ try {
256
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
257
+ const dependencies = {
258
+ ...packageJson.dependencies,
259
+ ...packageJson.peerDependencies
260
+ };
261
+ if (dependencies && Object.keys(dependencies).length > 0) {
262
+ this.packageDependencies.set(packageName, dependencies);
263
+ for (const depName of Object.keys(dependencies)) await this.buildPackage(depName, npmDirPath);
264
+ }
265
+ } catch (e) {
266
+ console.warn(`[npm-builder] 解析 package.json 失败: ${packageJsonPath}`, e.message);
267
+ }
268
+ }
269
+ /**
270
+ * 验证 npm 包的完整性
271
+ * @param {string} packageName 包名
272
+ * @param {string} packagePath 包路径
273
+ * @returns {boolean} 是否有效
274
+ */
275
+ validatePackage(packageName, packagePath) {
276
+ for (const file of ["package.json"]) if (!fs.existsSync(path.join(packagePath, file))) {
277
+ console.warn(`[npm-builder] 包 ${packageName} 缺少必要文件: ${file}`);
278
+ return false;
279
+ }
280
+ try {
281
+ const packageJsonPath = path.join(packagePath, "package.json");
282
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
283
+ if (!packageJson.name || !packageJson.version) {
284
+ console.warn(`[npm-builder] 包 ${packageName} 的 package.json 格式不正确`);
285
+ return false;
286
+ }
287
+ } catch (e) {
288
+ console.warn(`[npm-builder] 包 ${packageName} 的 package.json 解析失败:`, e.message);
289
+ return false;
290
+ }
291
+ return true;
292
+ }
293
+ /**
294
+ * 获取已构建的包列表
295
+ * @returns {string[]} 已构建的包列表
296
+ */
297
+ getBuiltPackages() {
298
+ return Array.from(this.builtPackages);
299
+ }
300
+ /**
301
+ * 获取包依赖关系
302
+ * @returns {Map} 包依赖关系映射
303
+ */
304
+ getPackageDependencies() {
305
+ return this.packageDependencies;
306
+ }
307
+ /**
308
+ * 清理构建缓存
309
+ */
310
+ clearCache() {
311
+ this.builtPackages.clear();
312
+ this.packageDependencies.clear();
313
+ }
314
+ };
315
+ //#endregion
316
+ //#region src/core/config-compiler.js
317
+ /**
318
+ * 处理 tabBar.list 中的 iconPath / selectedIconPath。
319
+ * 视为相对小程序根目录的资源,复用 collectAssets 拷贝到 main/static/,
320
+ * 并把 list 中的路径改写成产物 URL,避免容器侧再做特殊解析。
321
+ *
322
+ * 注意:会在原 app 配置上原地修改,不影响后续输出(compileConfig 是
323
+ * 整个流水线最后才走到的环节,不会被再次读取)。
324
+ */
325
+ function processTabBarIcons(app) {
326
+ const list = app?.tabBar?.list;
327
+ if (!Array.isArray(list) || list.length === 0) return;
328
+ const workPath = getWorkPath();
329
+ const targetPath = getTargetPath();
330
+ const appId = getAppId();
331
+ for (const item of list) {
332
+ if (item.iconPath) item.iconPath = collectAssets(workPath, "", item.iconPath, targetPath, appId);
333
+ if (item.selectedIconPath) item.selectedIconPath = collectAssets(workPath, "", item.selectedIconPath, targetPath, appId);
334
+ }
335
+ }
336
+ /**
337
+ *
338
+ * 编译项目配置文件 app-config.json
339
+ */
340
+ function compileConfig() {
341
+ const app = getAppConfigInfo();
342
+ processTabBarIcons(app);
343
+ const compileResInfo = {
344
+ app,
345
+ modules: getPageConfigInfo(),
346
+ projectName: getAppName()
347
+ };
348
+ const apiNamespaces = getProjectConfig().apiNamespaces;
349
+ if (Array.isArray(apiNamespaces) && apiNamespaces.length > 0) compileResInfo.apiNamespaces = apiNamespaces;
350
+ const json = JSON.stringify(compileResInfo, null, 4);
351
+ const mainDir = `${getTargetPath()}/main`;
352
+ if (!fs.existsSync(mainDir)) fs.mkdirSync(mainDir, { recursive: true });
353
+ fs.writeFileSync(`${mainDir}/app-config.json`, json);
354
+ }
355
+ //#endregion
356
+ //#region src/index.js
357
+ var previousCompatibilityWarnings = /* @__PURE__ */ new Map();
358
+ var COMPILE_STAGE_ORDER = [
359
+ "view",
360
+ "logic",
361
+ "style"
362
+ ];
363
+ var BUILD_LABELS = {
364
+ en: {
365
+ initialize: "Prepare project build environment",
366
+ collectConfig: "Collect project configuration",
367
+ prepareOutput: "Prepare output directory",
368
+ compileConfig: "Compile project configuration",
369
+ buildNpm: "Build npm packages",
370
+ compile: "Compile project",
371
+ view: "Compile view files",
372
+ logic: "Compile logic files",
373
+ style: "Compile style files",
374
+ writeOutput: "Write build output"
375
+ },
376
+ "zh-CN": {
377
+ initialize: "初始化项目",
378
+ collectConfig: "收集配置信息",
379
+ prepareOutput: "准备产物目录",
380
+ compileConfig: "编译配置信息",
381
+ buildNpm: "构建 npm 包",
382
+ compile: "编译项目",
383
+ view: "编译视图",
384
+ logic: "编译逻辑",
385
+ style: "编译样式",
386
+ writeOutput: "写入编译产物"
387
+ }
388
+ };
389
+ function resolveBuildLabels(options = {}) {
390
+ return BUILD_LABELS[String(options.lang || process.env.NEUX_CLI_LANG || "en").toLowerCase().startsWith("zh") ? "zh-CN" : "en"];
391
+ }
392
+ function getStyleOnlyChangedPathPattern() {
393
+ return createExtensionPattern([
394
+ ...getStyleExts(),
395
+ ".acss",
396
+ ".css"
397
+ ]);
398
+ }
399
+ function getViewOnlyChangedPathPattern() {
400
+ return createExtensionPattern([...getTemplateExts(), ".axml"]);
401
+ }
402
+ /**
403
+ * 构建命令入口
404
+ * @param {string} targetPath 编译产物目标路径
405
+ * @param {string} workPath 编译工作目录
406
+ * @param {boolean} useAppIdDir 产物根目录是否包含 appId
407
+ * @param {object} [options] 构建选项
408
+ * @param {boolean} [options.sourcemap] 是否生成 sourcemap
409
+ * @param {{ template?: string[], style?: string[], viewScript?: string[] }} [options.fileTypes]
410
+ * 自定义文件类型,在内置 wx/dd 类型基础上追加;template 为模板扩展名,style 为样式扩展名,
411
+ * viewScript 为视图脚本扩展名和内联标签名
412
+ * @param {string[]} [options.affectedEntries] 仅重编这些页面的视图和样式;逻辑仍按包重建
413
+ * @param {string} [options.seedPath] 增量构建前用于保留未受影响产物的已发布目录
414
+ * @param {object} [options.dependencyGraph] 上一次构建的依赖图快照
415
+ * @param {Array<'view'|'logic'|'style'>} [options.stages] 仅运行指定编译阶段
416
+ */
417
+ async function build(targetPath, workPath, useAppIdDir = true, options = {}) {
418
+ const { sourcemap = false, fileTypes, affectedEntries, seedPath, dependencyGraph, stages } = options;
419
+ const labels = resolveBuildLabels(options);
420
+ if (stages !== void 0 && (!Array.isArray(stages) || stages.some((stage) => !COMPILE_STAGE_ORDER.includes(stage)))) throw new TypeError(`Invalid compiler stages: ${JSON.stringify(stages)}`);
421
+ const enabledStages = new Set(stages === void 0 ? COMPILE_STAGE_ORDER : COMPILE_STAGE_ORDER.filter((stage) => stages.includes(stage)));
422
+ resetAssetCache();
423
+ const context = await new Listr([
424
+ {
425
+ title: labels.initialize,
426
+ task: (_, task) => task.newListr([
427
+ {
428
+ title: labels.collectConfig,
429
+ task: (ctx) => {
430
+ ctx.storeInfo = storeInfo(workPath, {
431
+ fileTypes,
432
+ dependencyGraph
433
+ });
434
+ ctx.dependencyGraph = new DependencyGraph(ctx.storeInfo.dependencyGraph);
435
+ }
436
+ },
437
+ {
438
+ title: labels.prepareOutput,
439
+ task: () => {
440
+ createDist(seedPath);
441
+ }
442
+ },
443
+ {
444
+ title: labels.compileConfig,
445
+ task: () => {
446
+ compileConfig();
447
+ }
448
+ },
449
+ {
450
+ title: labels.buildNpm,
451
+ task: async (ctx) => {
452
+ await new NpmBuilder(getWorkPath(), getTargetPath(), ctx.dependencyGraph).buildNpmPackages();
453
+ }
454
+ }
455
+ ], { concurrent: false })
456
+ },
457
+ {
458
+ title: `${labels.compile} · ${path.basename(path.resolve(workPath))}`,
459
+ task: (ctx, task) => {
460
+ const allPages = getPages();
461
+ ctx.allPages = allPages;
462
+ ctx.pages = filterPagesByEntries(allPages, affectedEntries);
463
+ ctx.compatibilityWarnings = /* @__PURE__ */ new Set();
464
+ const compileTasks = [];
465
+ if (enabledStages.has("view")) compileTasks.push({
466
+ title: labels.view,
467
+ rendererOptions: {
468
+ outputBar: true,
469
+ persistentOutput: false
470
+ },
471
+ task: async (ctx, task) => {
472
+ return runCompileInWorker("view", ctx, task, { sourcemap });
473
+ }
474
+ });
475
+ if (enabledStages.has("logic")) compileTasks.push({
476
+ title: labels.logic,
477
+ rendererOptions: {
478
+ outputBar: true,
479
+ persistentOutput: false
480
+ },
481
+ task: async (ctx, task) => {
482
+ return runCompileInWorker("logic", ctx, task, {
483
+ sourcemap,
484
+ pages: ctx.allPages
485
+ });
486
+ }
487
+ });
488
+ if (enabledStages.has("style")) compileTasks.push({
489
+ title: labels.style,
490
+ rendererOptions: {
491
+ outputBar: true,
492
+ persistentOutput: false
493
+ },
494
+ task: async (ctx, task) => {
495
+ const stylePages = {
496
+ ...ctx.pages,
497
+ mainPages: [{
498
+ path: "app",
499
+ id: getAppStyleScopeId()
500
+ }, ...ctx.pages.mainPages]
501
+ };
502
+ return runCompileInWorker("style", ctx, task, {
503
+ sourcemap,
504
+ pages: stylePages
505
+ });
506
+ }
507
+ });
508
+ if (compileTasks.length > 0) return task.newListr(compileTasks, { concurrent: true });
509
+ }
510
+ },
511
+ {
512
+ title: labels.writeOutput,
513
+ task: () => {
514
+ publishToDist(targetPath, useAppIdDir);
515
+ }
516
+ }
517
+ ], {
518
+ concurrent: false,
519
+ rendererOptions: {
520
+ collapseSubtasks: true,
521
+ formatOutput: "truncate",
522
+ timer: PRESET_TIMER
523
+ },
524
+ fallbackRendererOptions: { timer: PRESET_TIMER }
525
+ }).run();
526
+ printCompatibilityWarnings(workPath, context.compatibilityWarnings);
527
+ return {
528
+ appId: getAppId(),
529
+ name: getAppName(),
530
+ path: getAppConfigInfo().entryPagePath || context.allPages.mainPages[0].path,
531
+ dependencyGraph: context.dependencyGraph.toJSON()
532
+ };
533
+ }
534
+ function filterPagesByEntries(pages, affectedEntries) {
535
+ if (!Array.isArray(affectedEntries)) return pages;
536
+ const selected = new Set(affectedEntries);
537
+ return {
538
+ mainPages: pages.mainPages.filter((page) => selected.has(page.path)),
539
+ subPages: Object.fromEntries(Object.entries(pages.subPages).map(([root, subPackage]) => [root, {
540
+ ...subPackage,
541
+ info: subPackage.info.filter((page) => selected.has(page.path))
542
+ }]).filter(([, subPackage]) => subPackage.info.length > 0))
543
+ };
544
+ }
545
+ async function buildStyleOnly(targetPath, workPath, useAppIdDir = true, options = {}) {
546
+ storeInfo(workPath, { fileTypes: options.fileTypes });
547
+ const pages = getPages();
548
+ const affectedPages = getAffectedPages(pages, options.changedPath, getStyleOnlyChangedPathPattern());
549
+ const progress = { completedTasks: 0 };
550
+ await compileSS(affectedPages.mainPages, null, progress);
551
+ for (const [root, subPages] of Object.entries(affectedPages.subPages)) await compileSS(subPages.info, root, progress);
552
+ publishCssToDist(targetPath, useAppIdDir);
553
+ return {
554
+ appId: getAppId(),
555
+ name: getAppName(),
556
+ path: getAppConfigInfo().entryPagePath || pages.mainPages[0]?.path,
557
+ styleOnly: true,
558
+ compiledTasks: progress.completedTasks
559
+ };
560
+ }
561
+ async function buildViewOnly(targetPath, workPath, useAppIdDir = true, options = {}) {
562
+ resetViewCompilerCache();
563
+ storeInfo(workPath, { fileTypes: options.fileTypes });
564
+ const pages = getPages();
565
+ const affectedPages = getAffectedPages(pages, options.changedPath, getViewOnlyChangedPathPattern());
566
+ const progress = { completedTasks: 0 };
567
+ await compileML(affectedPages.mainPages, null, progress);
568
+ for (const [root, subPages] of Object.entries(affectedPages.subPages)) await compileML(subPages.info, root, progress);
569
+ publishFilesToDist(targetPath, useAppIdDir, ".js");
570
+ return {
571
+ appId: getAppId(),
572
+ name: getAppName(),
573
+ path: getAppConfigInfo().entryPagePath || pages.mainPages[0].path,
574
+ viewOnly: true,
575
+ compiledTasks: progress.completedTasks,
576
+ updatedPages: collectUpdatedPages(affectedPages)
577
+ };
578
+ }
579
+ async function buildLogicOnly(targetPath, workPath, useAppIdDir = true, options = {}) {
580
+ const currentStoreInfo = storeInfo(workPath, { fileTypes: options.fileTypes });
581
+ const pages = getPages();
582
+ await runCompileInWorker("logic", {
583
+ pages,
584
+ storeInfo: currentStoreInfo
585
+ }, {}, { sourcemap: options.sourcemap });
586
+ publishFilesToDist(targetPath, useAppIdDir, ".js");
587
+ return {
588
+ appId: getAppId(),
589
+ name: getAppName(),
590
+ path: getAppConfigInfo().entryPagePath || pages.mainPages[0].path,
591
+ logicOnly: true,
592
+ compiledTasks: pages.mainPages.length + Object.values(pages.subPages).reduce((sum, item) => sum + item.info.length, 0),
593
+ updatedPages: collectUpdatedPages(pages)
594
+ };
595
+ }
596
+ function collectUpdatedPages(pages) {
597
+ return [...pages.mainPages.map((page) => ({
598
+ pagePath: page.path,
599
+ root: "main"
600
+ })), ...Object.entries(pages.subPages).flatMap(([root, subPackage]) => subPackage.info.map((page) => ({
601
+ pagePath: page.path,
602
+ root
603
+ })))];
604
+ }
605
+ function getAffectedPages(pages, changedPath, extensionPattern) {
606
+ const appStylePage = {
607
+ path: "app",
608
+ id: ""
609
+ };
610
+ if (!changedPath) return {
611
+ mainPages: extensionPattern.test("app.wxss") ? [appStylePage, ...pages.mainPages] : pages.mainPages,
612
+ subPages: pages.subPages
613
+ };
614
+ const modulePath = normalizeChangedModulePath(changedPath, extensionPattern);
615
+ if (!modulePath) return {
616
+ mainPages: extensionPattern.test("app.wxss") ? [appStylePage, ...pages.mainPages] : pages.mainPages,
617
+ subPages: pages.subPages
618
+ };
619
+ if (modulePath === "app") return {
620
+ mainPages: [appStylePage],
621
+ subPages: {}
622
+ };
623
+ const mainPages = pages.mainPages.filter((page) => isStyleModuleAffected(page, modulePath));
624
+ const subPages = {};
625
+ for (const [root, subPackage] of Object.entries(pages.subPages)) {
626
+ const info = subPackage.info.filter((page) => isStyleModuleAffected(page, modulePath));
627
+ if (info.length) subPages[root] = {
628
+ ...subPackage,
629
+ info
630
+ };
631
+ }
632
+ if (!mainPages.length && !Object.keys(subPages).length) return {
633
+ mainPages: extensionPattern.test("app.wxss") ? [appStylePage, ...pages.mainPages] : pages.mainPages,
634
+ subPages: pages.subPages
635
+ };
636
+ return {
637
+ mainPages,
638
+ subPages
639
+ };
640
+ }
641
+ function normalizeChangedModulePath(changedPath, extensionPattern) {
642
+ const relativePath = path.relative(getWorkPath(), changedPath);
643
+ if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath)) return null;
644
+ return relativePath.replace(/\\/g, "/").replace(extensionPattern, "");
645
+ }
646
+ function isStyleModuleAffected(page, modulePath, visited = /* @__PURE__ */ new Set()) {
647
+ if (isSameStyleModulePath(page.path, modulePath)) return true;
648
+ if (!page.usingComponents || visited.has(page.path)) return false;
649
+ visited.add(page.path);
650
+ for (const componentPath of Object.values(page.usingComponents)) {
651
+ if (isSameStyleModulePath(componentPath, modulePath)) return true;
652
+ const component = getComponent(componentPath);
653
+ if (component && isStyleModuleAffected(component, modulePath, visited)) return true;
654
+ }
655
+ return false;
656
+ }
657
+ function isSameStyleModulePath(left, right) {
658
+ return String(left || "").replace(/^\/+/, "") === String(right || "").replace(/^\/+/, "");
659
+ }
660
+ function createExtensionPattern(extensions) {
661
+ const normalizedExts = [...new Set(extensions)].map((ext) => String(ext).trim().toLowerCase()).filter(Boolean).map((ext) => ext.startsWith(".") ? ext.slice(1) : ext).map((ext) => ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
662
+ return new RegExp(`\\.(?:${normalizedExts.join("|")})$`, "i");
663
+ }
664
+ function publishCssToDist(dist, useAppIdDir = true) {
665
+ publishFilesToDist(dist, useAppIdDir, ".css");
666
+ }
667
+ function publishFilesToDist(dist, useAppIdDir = true, extension) {
668
+ const sourceRoot = getTargetPath();
669
+ const targetRoot = useAppIdDir ? path.join(path.resolve(process.cwd(), dist), getAppId()) : path.resolve(process.cwd(), dist);
670
+ function copyFiles(src, dest) {
671
+ if (!fs.existsSync(src)) return;
672
+ fs.mkdirSync(dest, { recursive: true });
673
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
674
+ const srcPath = path.join(src, entry.name);
675
+ const destPath = path.join(dest, entry.name);
676
+ if (entry.isDirectory()) copyFiles(srcPath, destPath);
677
+ else if (entry.isFile() && entry.name.endsWith(extension)) fs.copyFileSync(srcPath, destPath);
678
+ }
679
+ }
680
+ copyFiles(sourceRoot, targetRoot);
681
+ }
682
+ function runCompileInWorker(script, ctx, task, options = {}) {
683
+ return workerPool.runWorker(() => new Promise((resolve, reject) => {
684
+ const worker = new Worker(path.join(path.dirname(fileURLToPath(import.meta.url)), `core/${script}-compiler.js`), workerPool.getWorkerOptions());
685
+ const pages = options.pages || ctx.pages;
686
+ const totalTasks = Object.keys(pages.mainPages).length + Object.values(pages.subPages).reduce((sum, item) => sum + item.info.length, 0);
687
+ let isResolved = false;
688
+ let workerError = null;
689
+ const handleError = (error) => {
690
+ if (isResolved) return;
691
+ isResolved = true;
692
+ worker.terminate();
693
+ reject(error);
694
+ };
695
+ worker.postMessage({
696
+ pages,
697
+ storeInfo: ctx.storeInfo,
698
+ sourcemap: !!options.sourcemap
699
+ });
700
+ worker.on("message", (message) => {
701
+ try {
702
+ for (const warning of message.compatibilityWarnings || []) ctx.compatibilityWarnings.add(warning);
703
+ if (process.stdout.isTTY && message.completedTasks !== void 0) task.output = formatCompileProgress(message.completedTasks, totalTasks);
704
+ if (message.success) {
705
+ if (isResolved) return;
706
+ isResolved = true;
707
+ if (process.stdout.isTTY && totalTasks > 0) task.output = formatCompileProgress(totalTasks, totalTasks);
708
+ ctx.dependencyGraph.merge(message.dependencyGraph);
709
+ worker.terminate();
710
+ resolve();
711
+ } else if (message.error) {
712
+ const error = new Error(message.error.message || message.error);
713
+ if (message.error.name) error.name = message.error.name;
714
+ if (message.error.stack) error.stack = message.error.stack;
715
+ if (message.error.file) error.file = message.error.file;
716
+ if (message.error.line != null) error.line = message.error.line;
717
+ if (message.error.column != null) error.column = message.error.column;
718
+ if (message.error.stage) error.stage = message.error.stage;
719
+ handleError(error);
720
+ }
721
+ } catch (err) {
722
+ handleError(/* @__PURE__ */ new Error(`Error processing worker message: ${err.message}\n${err.stack}`));
723
+ }
724
+ });
725
+ worker.on("error", (err) => {
726
+ workerError = err;
727
+ handleError(err);
728
+ });
729
+ worker.on("exit", (code) => {
730
+ if (code !== 0 && !isResolved) {
731
+ const error = workerError || /* @__PURE__ */ new Error(code === 1 ? "Worker terminated due to reaching memory limit: JS heap out of memory" : `Worker stopped with exit code ${code}`);
732
+ handleError(error);
733
+ }
734
+ });
735
+ }));
736
+ }
737
+ function printCompatibilityWarnings(workPath, warnings = /* @__PURE__ */ new Set()) {
738
+ const projectPath = path.resolve(workPath);
739
+ const hasPreviousResult = previousCompatibilityWarnings.has(projectPath);
740
+ const previousWarnings = previousCompatibilityWarnings.get(projectPath) || /* @__PURE__ */ new Set();
741
+ const currentWarnings = new Set(warnings);
742
+ const newWarnings = [...currentWarnings].filter((warning) => !previousWarnings.has(warning));
743
+ previousCompatibilityWarnings.set(projectPath, currentWarnings);
744
+ if (newWarnings.length === 0) return;
745
+ const qualifier = hasPreviousResult ? " new" : "";
746
+ const suffix = newWarnings.length === 1 ? "" : "s";
747
+ console.warn(`\n[compat] ${newWarnings.length}${qualifier} compatibility warning${suffix}`);
748
+ for (const warning of newWarnings) console.warn(` - ${warning.replace(/^\[compat\]\s*/, "")}`);
749
+ }
750
+ //#endregion
751
+ export { buildLogicOnly, buildStyleOnly, buildViewOnly, build as default };