@dimina/compiler 1.0.14-beta.0 → 1.0.14

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.
@@ -1,20 +1,19 @@
1
1
  import { _ as tagWhiteList, a as getContentByPath, d as resetStoreInfo, h as getAbsolutePath, i as getComponent, l as getTargetPath, m as collectAssets, n as getAppId, u as getWorkPath, v as transformRpx } from "../env-DgCLbrQb.js";
2
+ import { t as checkTemplateCompatibility } from "../compatibility-B_5UilxZ.js";
2
3
  import path from "node:path";
3
4
  import { isMainThread, parentPort } from "node:worker_threads";
4
5
  import fs from "node:fs";
5
- import babel from "@babel/core";
6
- import _traverse from "@babel/traverse";
7
- import types from "@babel/types";
8
- import generate from "@babel/generator";
6
+ import { parseSync } from "oxc-parser";
7
+ import { walk } from "oxc-walker";
8
+ import MagicString from "magic-string";
9
9
  import { compileTemplate } from "@vue/compiler-sfc";
10
10
  import * as cheerio from "cheerio";
11
11
  import { transform } from "esbuild";
12
12
  import * as htmlparser2 from "htmlparser2";
13
13
  //#region src/common/expression-parser.js
14
14
  /**
15
- * 表达式解析器 - 使用 Babel AST 解析器提取依赖
15
+ * 表达式解析器 - 使用 Oxc AST 解析器提取依赖
16
16
  */
17
- var traverse$1 = _traverse.default ? _traverse.default : _traverse;
18
17
  var KEYWORDS = new Set([
19
18
  "true",
20
19
  "false",
@@ -48,7 +47,7 @@ var KEYWORDS = new Set([
48
47
  ]);
49
48
  /**
50
49
  * 提取表达式中的所有变量依赖路径
51
- * 使用 Babel AST 解析器进行精确分析
50
+ * 使用 Oxc AST 解析器进行精确分析
52
51
  * @param {string} expression - 表达式字符串,如 "count || defaultValue" 或 "item.name"
53
52
  * @returns {Array<string>} - 依赖的变量名数组,如 ["count", "defaultValue"] 或 ["item"]
54
53
  */
@@ -56,36 +55,42 @@ function extractDependencies(expression) {
56
55
  if (!expression || typeof expression !== "string") return [];
57
56
  const dependencies = /* @__PURE__ */ new Set();
58
57
  try {
59
- const code = `(${expression})`;
60
- traverse$1(babel.parseSync(code, {
58
+ const ast = parseSync("expression.js", `(${expression})`, {
61
59
  sourceType: "module",
62
- plugins: []
63
- }), {
64
- Identifier(path) {
65
- const name = path.node.name;
66
- if (KEYWORDS.has(name)) return;
67
- const parent = path.parent;
68
- if (parent.type === "MemberExpression" && parent.property === path.node && !parent.computed) return;
69
- if (parent.type === "CallExpression" && parent.callee === path.node) {
70
- dependencies.add(name);
71
- return;
72
- }
73
- if (parent.type === "ObjectProperty" && parent.key === path.node && !parent.computed) return;
74
- dependencies.add(name);
75
- },
76
- MemberExpression(path) {
77
- let root = path.node.object;
78
- while (root.type === "MemberExpression") root = root.object;
79
- if (root.type === "Identifier" && !KEYWORDS.has(root.name)) dependencies.add(root.name);
80
- path.skip();
81
- }
82
- });
60
+ lang: "js"
61
+ }).program;
62
+ visitExpressionAst(ast, null, dependencies);
83
63
  } catch (error) {
84
64
  console.warn("[expression-parser] AST 解析失败,表达式:", expression, "错误:", error.message);
85
65
  return [];
86
66
  }
87
67
  return Array.from(dependencies);
88
68
  }
69
+ function visitExpressionAst(node, parent, dependencies) {
70
+ if (!node || typeof node !== "object") return;
71
+ if (node.type === "Identifier") {
72
+ collectIdentifier(node, parent, dependencies);
73
+ return;
74
+ }
75
+ if (node.type === "MemberExpression") {
76
+ let root = node.object;
77
+ while (root?.type === "MemberExpression" || root?.type === "ChainExpression") root = root.type === "ChainExpression" ? root.expression : root.object;
78
+ if (root?.type === "Identifier" && !KEYWORDS.has(root.name)) dependencies.add(root.name);
79
+ return;
80
+ }
81
+ for (const [key, value] of Object.entries(node)) {
82
+ if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
83
+ if (Array.isArray(value)) for (const child of value) visitExpressionAst(child, node, dependencies);
84
+ else visitExpressionAst(value, node, dependencies);
85
+ }
86
+ }
87
+ function collectIdentifier(node, parent, dependencies) {
88
+ const name = node.name;
89
+ if (KEYWORDS.has(name)) return;
90
+ if (parent?.type === "MemberExpression" && parent.property === node && !parent.computed) return;
91
+ if (parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand) return;
92
+ dependencies.add(name);
93
+ }
89
94
  /**
90
95
  * 解析表达式并生成依赖路径信息
91
96
  * @param {string} expression - 表达式字符串
@@ -118,20 +123,53 @@ function parseBindings(bindings) {
118
123
  }
119
124
  //#endregion
120
125
  //#region src/core/view-compiler.js
121
- var traverse = _traverse.default ? _traverse.default : _traverse;
122
- var generateCode = generate.default ? generate.default : generate;
123
126
  var fileType = [".wxml", ".ddml"];
124
127
  /**
125
- * AST 生成代码,如果顶层是单个表达式语句,则只生成表达式部分
126
- * @param {*} ast - Babel AST
127
- * @returns {string} 生成的表达式代码
128
+ * 解析 JavaScript 代码
129
+ * @param {string} code
130
+ * @param {string} filename
131
+ * @param {'module'|'script'} sourceType
132
+ * @returns {*} Oxc Program AST
128
133
  */
129
- function generateCodeFromAst(ast) {
130
- if (ast.type === "File" && ast.program.body.length === 1) {
131
- const statement = ast.program.body[0];
132
- if (statement.type === "ExpressionStatement") return generateCode(statement.expression, { comments: false }).code;
133
- }
134
- return generateCode(ast, { comments: false }).code;
134
+ function parseJs(code, filename = "view-compiler.js", sourceType = "module") {
135
+ return parseSync(filename, code, {
136
+ sourceType,
137
+ lang: "js"
138
+ }).program;
139
+ }
140
+ /**
141
+ * 如果顶层是单个表达式语句,则只返回表达式源码
142
+ * @param {string} code
143
+ * @param {*} ast - Oxc Program AST
144
+ * @returns {string} Program code or the source of the single top-level expression.
145
+ */
146
+ function getProgramCode(code, ast) {
147
+ const statement = ast.body?.[0];
148
+ if (ast.body?.length === 1 && statement?.type === "ExpressionStatement") return code.slice(statement.expression.start, statement.expression.end);
149
+ return code;
150
+ }
151
+ function isStringLiteral(node) {
152
+ return node?.type === "StringLiteral" || node?.type === "Literal" && typeof node.value === "string";
153
+ }
154
+ function getStringLiteralRawValue(node) {
155
+ if (!isStringLiteral(node)) return "";
156
+ if (typeof node.raw === "string") return node.raw.slice(1, -1);
157
+ return String(node.value);
158
+ }
159
+ function getSource(code, node) {
160
+ return code.slice(node.start, node.end);
161
+ }
162
+ function applyCodeReplacements(source, replacements) {
163
+ if (replacements.length === 0) return source;
164
+ const selected = [];
165
+ const byLargestRange = [...replacements].sort((a, b) => {
166
+ return b.end - b.start - (a.end - a.start) || b.start - a.start;
167
+ });
168
+ for (const replacement of byLargestRange) if (!selected.some((item) => replacement.start < item.end && item.start < replacement.end)) selected.push(replacement);
169
+ const s = new MagicString(source);
170
+ for (const replacement of selected.sort((a, b) => b.start - a.start)) if (replacement.type === "insert") s.appendLeft(replacement.start, replacement.value);
171
+ else s.overwrite(replacement.start, replacement.end, replacement.value);
172
+ return s.toString();
135
173
  }
136
174
  /**
137
175
  * 为模板表达式中的成员访问补充空值保护,避免生成的 render 函数直接访问 null/undefined 属性
@@ -142,12 +180,30 @@ function generateCodeFromAst(ast) {
142
180
  function addOptionalChaining(expression) {
143
181
  if (!expression || typeof expression !== "string") return expression;
144
182
  try {
145
- const ast = babel.parseSync(`(${expression})`, { parserOpts: { plugins: ["optionalChaining", "nullishCoalescingOperator"] } });
146
- traverse(ast, { MemberExpression: { exit(astPath) {
147
- if (astPath.node.optional) return;
148
- astPath.replaceWith(types.optionalMemberExpression(astPath.node.object, astPath.node.property, astPath.node.computed, true));
149
- } } });
150
- return generateCodeFromAst(ast);
183
+ const code = `(${expression})`;
184
+ const ast = parseJs(code);
185
+ const insertions = [];
186
+ walk(ast, { enter(node) {
187
+ if (node.type !== "MemberExpression" || node.optional) return;
188
+ if (node.computed) {
189
+ const bracketIndex = code.lastIndexOf("[", node.property.start);
190
+ if (bracketIndex >= 0) insertions.push({
191
+ type: "insert",
192
+ start: bracketIndex,
193
+ end: bracketIndex,
194
+ value: "?."
195
+ });
196
+ } else {
197
+ const dotIndex = code.lastIndexOf(".", node.property.start);
198
+ if (dotIndex >= 0) insertions.push({
199
+ type: "insert",
200
+ start: dotIndex,
201
+ end: dotIndex,
202
+ value: "?"
203
+ });
204
+ }
205
+ } });
206
+ return applyCodeReplacements(code, insertions).slice(1, -1);
151
207
  } catch (error) {
152
208
  return expression;
153
209
  }
@@ -385,15 +441,11 @@ function compileModule(module, isComponent, scriptRes, options = {}) {
385
441
  inline: true
386
442
  }
387
443
  });
388
- const ast = babel.parseSync(code);
389
- insertWxsToRenderAst(ast, compileInstruction.scriptModule, scriptRes);
390
- code = generateCodeFromAst(ast);
444
+ code = insertWxsToRenderCode(code, compileInstruction.scriptModule, scriptRes, tm.path);
391
445
  tplComponents += `'${tm.path}':${code},`;
392
446
  }
393
447
  tplComponents += "}";
394
- const tplAst = babel.parseSync(tplCode.code);
395
- insertWxsToRenderAst(tplAst, compileInstruction.scriptModule, scriptRes);
396
- const transCode = generateCodeFromAst(tplAst);
448
+ const transCode = insertWxsToRenderCode(tplCode.code, compileInstruction.scriptModule, scriptRes, module.path);
397
449
  const code = `Module({
398
450
  path: '${module.path}',
399
451
  id: '${module.id}',
@@ -430,44 +482,42 @@ function compileModule(module, isComponent, scriptRes, options = {}) {
430
482
  function processWxsContent(wxsContent, wxsFilePath, scriptModule, workPath, filePath) {
431
483
  let wxsAst;
432
484
  try {
433
- wxsAst = babel.parseSync(wxsContent);
485
+ wxsAst = parseJs(wxsContent, wxsFilePath || "inline.wxs", "script");
434
486
  } catch (error) {
435
487
  console.error(`[view] 解析 wxs 文件失败: ${wxsFilePath}`, error.message);
436
488
  return wxsContent;
437
489
  }
438
- traverse(wxsAst, {
439
- CallExpression(astPath) {
440
- const calleeName = astPath.node.callee.name;
490
+ const replacements = [];
491
+ walk(wxsAst, { enter(node) {
492
+ if (node.type === "CallExpression") {
493
+ const calleeName = node.callee?.name;
441
494
  if (calleeName === "getRegExp") {
442
- const args = astPath.node.arguments;
443
- if (args.length > 0) if (args[0].type === "StringLiteral" && (!args[1] || args[1].type === "StringLiteral")) {
444
- let pattern = "";
445
- let flags = "";
446
- const arg = args[0];
447
- if (arg.extra && arg.extra.raw) pattern = arg.extra.raw.slice(1, -1);
448
- else if (arg.value !== void 0) pattern = arg.value;
449
- else pattern = "";
450
- if (args.length > 1) {
451
- const flagArg = args[1];
452
- if (flagArg.extra && flagArg.extra.raw) flags = flagArg.extra.raw.slice(1, -1);
453
- else if (flagArg.value !== void 0) flags = flagArg.value;
454
- else flags = "";
455
- }
456
- const regexLiteral = types.regExpLiteral(pattern, flags);
457
- astPath.replaceWith(regexLiteral);
495
+ const args = node.arguments;
496
+ if (args.length > 0) if (isStringLiteral(args[0]) && (!args[1] || isStringLiteral(args[1]))) {
497
+ const pattern = getStringLiteralRawValue(args[0]);
498
+ const flags = args.length > 1 ? getStringLiteralRawValue(args[1]) : "";
499
+ replacements.push({
500
+ start: node.start,
501
+ end: node.end,
502
+ value: `/${pattern}/${flags}`
503
+ });
458
504
  } else {
459
- const newRegExpArgs = [args[0]];
460
- if (args.length > 1) newRegExpArgs.push(args[1]);
461
- const newRegExpCall = types.newExpression(types.identifier("RegExp"), newRegExpArgs);
462
- astPath.replaceWith(newRegExpCall);
505
+ const newRegExpArgs = args.map((arg) => getSource(wxsContent, arg)).join(", ");
506
+ replacements.push({
507
+ start: node.start,
508
+ end: node.end,
509
+ value: `new RegExp(${newRegExpArgs})`
510
+ });
463
511
  }
464
512
  } else if (calleeName === "getDate") {
465
- const args = [];
466
- for (let i = 0; i < astPath.node.arguments.length; i++) args.push(astPath.node.arguments[i]);
467
- const newExpr = types.newExpression(types.identifier("Date"), args);
468
- astPath.replaceWith(newExpr);
469
- } else if (calleeName === "require" && astPath.node.arguments.length > 0 && wxsFilePath) {
470
- const requirePath = astPath.node.arguments[0].value;
513
+ const args = node.arguments.map((arg) => getSource(wxsContent, arg)).join(", ");
514
+ replacements.push({
515
+ start: node.start,
516
+ end: node.end,
517
+ value: `new Date(${args})`
518
+ });
519
+ } else if (calleeName === "require" && node.arguments.length > 0 && wxsFilePath) {
520
+ const requirePath = node.arguments[0].value;
471
521
  if (requirePath && typeof requirePath === "string") {
472
522
  let resolvedWxsPath;
473
523
  if (filePath && filePath.includes("/miniprogram_npm/")) {
@@ -475,25 +525,37 @@ function processWxsContent(wxsContent, wxsFilePath, scriptModule, workPath, file
475
525
  resolvedWxsPath = path.resolve(currentWxsDir, requirePath);
476
526
  const moduleName = resolvedWxsPath.replace(workPath, "").replace(/\.wxs$/, "").replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
477
527
  processWxsDependency(resolvedWxsPath, moduleName, scriptModule, workPath, filePath);
478
- astPath.node.arguments[0] = types.stringLiteral(moduleName);
528
+ replacements.push({
529
+ start: node.arguments[0].start,
530
+ end: node.arguments[0].end,
531
+ value: JSON.stringify(moduleName)
532
+ });
479
533
  } else {
480
534
  const currentWxsDir = path.dirname(wxsFilePath);
481
535
  resolvedWxsPath = path.resolve(currentWxsDir, requirePath);
482
536
  const depModuleName = resolvedWxsPath.replace(workPath, "").replace(/\.wxs$/, "").replace(/[\/\\@\-]/g, "_").replace(/^_/, "");
483
537
  processWxsDependency(resolvedWxsPath, depModuleName, scriptModule, workPath, filePath);
484
- astPath.node.arguments[0] = types.stringLiteral(depModuleName);
538
+ replacements.push({
539
+ start: node.arguments[0].start,
540
+ end: node.arguments[0].end,
541
+ value: JSON.stringify(depModuleName)
542
+ });
485
543
  }
486
544
  }
487
545
  }
488
- },
489
- MemberExpression(astPath) {
490
- if (astPath.node.property.name === "constructor" && !astPath.node.computed) {
491
- const getTypeString = types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.memberExpression(types.memberExpression(types.identifier("Object"), types.identifier("prototype")), types.identifier("toString")), types.identifier("call")), [astPath.node.object]), types.identifier("slice")), [types.numericLiteral(8), types.numericLiteral(-1)]);
492
- astPath.replaceWith(getTypeString);
546
+ }
547
+ if (node.type === "MemberExpression") {
548
+ if (node.property?.name === "constructor" && !node.computed) {
549
+ const objectCode = getSource(wxsContent, node.object);
550
+ replacements.push({
551
+ start: node.start,
552
+ end: node.end,
553
+ value: `Object.prototype.toString.call(${objectCode}).slice(8, -1)`
554
+ });
493
555
  }
494
556
  }
495
- });
496
- return generateCodeFromAst(wxsAst);
557
+ } });
558
+ return applyCodeReplacements(wxsContent, replacements);
497
559
  }
498
560
  /**
499
561
  * 通过代码内容判断是否为 wxs 模块
@@ -523,9 +585,9 @@ function processWxsDependency(wxsFilePath, moduleName, scriptModule, workPath, f
523
585
  }
524
586
  /**
525
587
  * 重新编译模块,包含所有收集到的 wxs 模块
526
- * @param {*} module
527
- * @param {*} scriptRes
528
- * @param {*} allScriptModules
588
+ * @param {*} module
589
+ * @param {*} scriptRes
590
+ * @param {*} allScriptModules
529
591
  */
530
592
  function compileModuleWithAllWxs(module, scriptRes, allScriptModules) {
531
593
  const { tpl, instruction } = toCompileTemplate(false, module.path, module.usingComponents, module.componentPlaceholder);
@@ -564,15 +626,11 @@ function compileModuleWithAllWxs(module, scriptRes, allScriptModules) {
564
626
  inline: true
565
627
  }
566
628
  });
567
- const ast = babel.parseSync(code);
568
- insertWxsToRenderAst(ast, allScriptModules, scriptRes);
569
- code = generateCodeFromAst(ast);
629
+ code = insertWxsToRenderCode(code, allScriptModules, scriptRes, tm.path);
570
630
  tplComponents += `'${tm.path}':${code},`;
571
631
  }
572
632
  tplComponents += "}";
573
- const tplAst = babel.parseSync(tplCode.code);
574
- insertWxsToRenderAst(tplAst, allScriptModules, scriptRes);
575
- const transCode = generateCodeFromAst(tplAst);
633
+ const transCode = insertWxsToRenderCode(tplCode.code, allScriptModules, scriptRes, module.path);
576
634
  const code = `Module({
577
635
  path: '${module.path}',
578
636
  id: '${module.id}',
@@ -659,13 +717,17 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
659
717
  const workPath = getWorkPath();
660
718
  const fullPath = getViewPath(workPath, path);
661
719
  if (!fullPath) return { tpl: void 0 };
720
+ const diagnosticSource = fullPath.startsWith(workPath) ? fullPath.slice(workPath.length) : path;
662
721
  let content = getContentByPath(fullPath).trim();
663
722
  if (!content) content = "<block></block>";
664
- else if (isComponent) content = `<wrapper name="${path}">${content}</wrapper>`;
665
- else if (cheerio.load(content, {
666
- xmlMode: true,
667
- decodeEntities: false
668
- }).root().children().toArray().filter((node) => node.type !== "comment").length > 1) content = `<view>${content}</view>`;
723
+ else {
724
+ checkTemplateCompatibility(content, diagnosticSource, components);
725
+ if (isComponent) content = `<wrapper name="${path}">${content}</wrapper>`;
726
+ else if (cheerio.load(content, {
727
+ xmlMode: true,
728
+ decodeEntities: false
729
+ }).root().children().toArray().filter((node) => node.type !== "comment").length > 1) content = `<view>${content}</view>`;
730
+ }
669
731
  const templateModule = [];
670
732
  const scriptModule = [];
671
733
  const $ = cheerio.load(content, {
@@ -680,9 +742,11 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
680
742
  if (src) {
681
743
  const includeFullPath = getAbsolutePath(workPath, path, src);
682
744
  let includePath = includeFullPath.replace(workPath, "").replace(/\.(wxml|ddml)$/, "");
745
+ const includeDiagnosticSource = includeFullPath.startsWith(workPath) ? includeFullPath.slice(workPath.length) : includePath;
683
746
  if (!includePath.startsWith("/")) includePath = "/" + includePath;
684
747
  const includeContent = getContentByPath(includeFullPath).trim();
685
748
  if (includeContent) {
749
+ checkTemplateCompatibility(includeContent, includeDiagnosticSource, components);
686
750
  const $includeContent = cheerio.load(includeContent, {
687
751
  xmlMode: true,
688
752
  decodeEntities: false
@@ -706,9 +770,11 @@ function toCompileTemplate(isComponent, path, components, componentPlaceholder,
706
770
  if (src) {
707
771
  const importFullPath = getAbsolutePath(workPath, path, src);
708
772
  let importPath = importFullPath.replace(workPath, "").replace(/\.(wxml|ddml)$/, "");
773
+ const importDiagnosticSource = importFullPath.startsWith(workPath) ? importFullPath.slice(workPath.length) : importPath;
709
774
  if (!importPath.startsWith("/")) importPath = "/" + importPath;
710
775
  const importContent = getContentByPath(importFullPath).trim();
711
776
  if (importContent) {
777
+ checkTemplateCompatibility(importContent, importDiagnosticSource, components);
712
778
  const $$ = cheerio.load(importContent, {
713
779
  xmlMode: true,
714
780
  decodeEntities: false
@@ -826,7 +892,7 @@ function transTag(opts) {
826
892
  }
827
893
  /**
828
894
  * 处理动态slot指令生成,比如 slot="{{xxx}}"
829
- *
895
+ *
830
896
  * @param {string} slotValue - slot属性值
831
897
  * @returns {string} 生成的slot指令
832
898
  */
@@ -1241,28 +1307,43 @@ function extractWxsDependencies(moduleCode) {
1241
1307
  }
1242
1308
  return dependencies;
1243
1309
  }
1244
- function insertWxsToRenderAst(ast, scriptModule, scriptRes) {
1245
- const replacements = [];
1310
+ function insertWxsToRenderCode(code, scriptModule, scriptRes, filename = "render.js") {
1311
+ const wxsBindings = [];
1312
+ const codeReplacements = [];
1313
+ const ast = parseJs(code, filename);
1314
+ const statement = ast.body?.[0];
1315
+ const renderBody = (statement?.type === "ExpressionStatement" ? statement.expression : null)?.body;
1316
+ const declarations = [];
1246
1317
  for (const [index, sm] of scriptModule.entries()) {
1247
1318
  if (!scriptRes.has(sm.path)) scriptRes.set(sm.path, sm.code);
1248
1319
  const templatePropertyName = sm.originalName || sm.path;
1249
1320
  const requireModuleName = sm.path;
1250
1321
  const localIdentifier = `__wxs_${index}`;
1251
- replacements.push({
1322
+ wxsBindings.push({
1252
1323
  localIdentifier,
1253
1324
  templatePropertyName
1254
1325
  });
1255
- const variableDeclaration = types.variableDeclaration("const", [types.variableDeclarator(types.identifier(localIdentifier), types.callExpression(types.identifier("require"), [types.stringLiteral(requireModuleName)]))]);
1256
- ast.program.body[0].expression.body.body.unshift(variableDeclaration);
1326
+ declarations.push(`const ${localIdentifier} = require(${JSON.stringify(requireModuleName)});`);
1257
1327
  }
1258
- if (replacements.length === 0) return;
1259
- traverse(ast, { MemberExpression(astPath) {
1260
- const { node } = astPath;
1261
- if (node.object?.type === "Identifier" && node.object.name === "_ctx" && !node.computed && node.property?.type === "Identifier") {
1262
- const replacement = replacements.find((item) => item.templatePropertyName === node.property.name);
1263
- if (replacement) astPath.replaceWith(types.identifier(replacement.localIdentifier));
1328
+ if (wxsBindings.length === 0) return getProgramCode(code, ast);
1329
+ if (renderBody?.type === "BlockStatement") codeReplacements.push({
1330
+ type: "insert",
1331
+ start: renderBody.start + 1,
1332
+ end: renderBody.start + 1,
1333
+ value: `\n${declarations.join("\n")}`
1334
+ });
1335
+ walk(ast, { enter(node) {
1336
+ if (node.type === "MemberExpression" && node.object?.type === "Identifier" && node.object.name === "_ctx" && !node.computed && node.property?.type === "Identifier") {
1337
+ const replacement = wxsBindings.find((item) => item.templatePropertyName === node.property.name);
1338
+ if (replacement) codeReplacements.push({
1339
+ start: node.start,
1340
+ end: node.end,
1341
+ value: replacement.localIdentifier
1342
+ });
1264
1343
  }
1265
1344
  } });
1345
+ const transformed = applyCodeReplacements(code, codeReplacements);
1346
+ return getProgramCode(transformed, parseJs(transformed, filename));
1266
1347
  }
1267
1348
  //#endregion
1268
1349
  export { compileML, generateSlotDirective, generateVModelTemplate, parseBraceExp, parseClassRules, parseKeyExpression, parseTemplateDataExp, processIncludeConditionalAttrs, processWxsContent, splitWithBraces };
package/dist/index.cjs CHANGED
@@ -274,12 +274,33 @@ var NpmBuilder = class {
274
274
  //#endregion
275
275
  //#region src/core/config-compiler.js
276
276
  /**
277
+ * 处理 tabBar.list 中的 iconPath / selectedIconPath。
278
+ * 视为相对小程序根目录的资源,复用 collectAssets 拷贝到 main/static/,
279
+ * 并把 list 中的路径改写成产物 URL,避免容器侧再做特殊解析。
280
+ *
281
+ * 注意:会在原 app 配置上原地修改,不影响后续输出(compileConfig 是
282
+ * 整个流水线最后才走到的环节,不会被再次读取)。
283
+ */
284
+ function processTabBarIcons(app) {
285
+ const list = app?.tabBar?.list;
286
+ if (!Array.isArray(list) || list.length === 0) return;
287
+ const workPath = require_env.getWorkPath();
288
+ const targetPath = require_env.getTargetPath();
289
+ const appId = require_env.getAppId();
290
+ for (const item of list) {
291
+ if (item.iconPath) item.iconPath = require_env.collectAssets(workPath, "", item.iconPath, targetPath, appId);
292
+ if (item.selectedIconPath) item.selectedIconPath = require_env.collectAssets(workPath, "", item.selectedIconPath, targetPath, appId);
293
+ }
294
+ }
295
+ /**
277
296
  *
278
297
  * 编译项目配置文件 app-config.json
279
298
  */
280
299
  function compileConfig() {
300
+ const app = require_env.getAppConfigInfo();
301
+ processTabBarIcons(app);
281
302
  const compileResInfo = {
282
- app: require_env.getAppConfigInfo(),
303
+ app,
283
304
  modules: require_env.getPageConfigInfo(),
284
305
  projectName: require_env.getAppName()
285
306
  };
@@ -297,7 +318,8 @@ var isPrinted = false;
297
318
  * @param {string} workPath 编译工作目录
298
319
  * @param {boolean} useAppIdDir 产物根目录是否包含appId
299
320
  */
300
- async function build(targetPath, workPath, useAppIdDir = true) {
321
+ async function build(targetPath, workPath, useAppIdDir = true, options = {}) {
322
+ const { sourcemap = false } = options;
301
323
  if (!isPrinted) {
302
324
  require_env.art_default();
303
325
  isPrinted = true;
@@ -341,13 +363,13 @@ async function build(targetPath, workPath, useAppIdDir = true) {
341
363
  {
342
364
  title: "编译页面文件",
343
365
  task: async (ctx, task) => {
344
- return runCompileInWorker("view", ctx, task);
366
+ return runCompileInWorker("view", ctx, task, { sourcemap });
345
367
  }
346
368
  },
347
369
  {
348
370
  title: "编译页面逻辑",
349
371
  task: async (ctx, task) => {
350
- return runCompileInWorker("logic", ctx, task);
372
+ return runCompileInWorker("logic", ctx, task, { sourcemap });
351
373
  }
352
374
  },
353
375
  {
@@ -357,7 +379,7 @@ async function build(targetPath, workPath, useAppIdDir = true) {
357
379
  path: "app",
358
380
  id: ""
359
381
  });
360
- return runCompileInWorker("style", ctx, task);
382
+ return runCompileInWorker("style", ctx, task, { sourcemap });
361
383
  }
362
384
  }
363
385
  ], { concurrent: true });
@@ -381,7 +403,7 @@ async function build(targetPath, workPath, useAppIdDir = true) {
381
403
  console.error(`${workPath} 编译出错: ${e.message}\n${e.stack}`);
382
404
  }
383
405
  }
384
- function runCompileInWorker(script, ctx, task) {
406
+ function runCompileInWorker(script, ctx, task, options = {}) {
385
407
  return workerPool.runWorker(() => new Promise((resolve, reject) => {
386
408
  const worker = new node_worker_threads.Worker(node_path.default.join(node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), `core/${script}-compiler.js`), workerPool.getWorkerOptions());
387
409
  const totalTasks = Object.keys(ctx.pages.mainPages).length + Object.values(ctx.pages.subPages).reduce((sum, item) => sum + item.info.length, 0);
@@ -395,7 +417,8 @@ function runCompileInWorker(script, ctx, task) {
395
417
  };
396
418
  worker.postMessage({
397
419
  pages: ctx.pages,
398
- storeInfo: ctx.storeInfo
420
+ storeInfo: ctx.storeInfo,
421
+ sourcemap: !!options.sourcemap
399
422
  });
400
423
  worker.on("message", (message) => {
401
424
  try {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { c as getPages, l as getTargetPath, n as getAppId, p as storeInfo, r as getAppName, s as getPageConfigInfo, t as getAppConfigInfo, u as getWorkPath, y as art_default } from "./env-DgCLbrQb.js";
1
+ import { c as getPages, l as getTargetPath, m as collectAssets, n as getAppId, p as storeInfo, r as getAppName, s as getPageConfigInfo, t as getAppConfigInfo, u as getWorkPath, y as art_default } from "./env-DgCLbrQb.js";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { Worker } from "node:worker_threads";
@@ -270,12 +270,33 @@ var NpmBuilder = class {
270
270
  //#endregion
271
271
  //#region src/core/config-compiler.js
272
272
  /**
273
+ * 处理 tabBar.list 中的 iconPath / selectedIconPath。
274
+ * 视为相对小程序根目录的资源,复用 collectAssets 拷贝到 main/static/,
275
+ * 并把 list 中的路径改写成产物 URL,避免容器侧再做特殊解析。
276
+ *
277
+ * 注意:会在原 app 配置上原地修改,不影响后续输出(compileConfig 是
278
+ * 整个流水线最后才走到的环节,不会被再次读取)。
279
+ */
280
+ function processTabBarIcons(app) {
281
+ const list = app?.tabBar?.list;
282
+ if (!Array.isArray(list) || list.length === 0) return;
283
+ const workPath = getWorkPath();
284
+ const targetPath = getTargetPath();
285
+ const appId = getAppId();
286
+ for (const item of list) {
287
+ if (item.iconPath) item.iconPath = collectAssets(workPath, "", item.iconPath, targetPath, appId);
288
+ if (item.selectedIconPath) item.selectedIconPath = collectAssets(workPath, "", item.selectedIconPath, targetPath, appId);
289
+ }
290
+ }
291
+ /**
273
292
  *
274
293
  * 编译项目配置文件 app-config.json
275
294
  */
276
295
  function compileConfig() {
296
+ const app = getAppConfigInfo();
297
+ processTabBarIcons(app);
277
298
  const compileResInfo = {
278
- app: getAppConfigInfo(),
299
+ app,
279
300
  modules: getPageConfigInfo(),
280
301
  projectName: getAppName()
281
302
  };
@@ -293,7 +314,8 @@ var isPrinted = false;
293
314
  * @param {string} workPath 编译工作目录
294
315
  * @param {boolean} useAppIdDir 产物根目录是否包含appId
295
316
  */
296
- async function build(targetPath, workPath, useAppIdDir = true) {
317
+ async function build(targetPath, workPath, useAppIdDir = true, options = {}) {
318
+ const { sourcemap = false } = options;
297
319
  if (!isPrinted) {
298
320
  art_default();
299
321
  isPrinted = true;
@@ -337,13 +359,13 @@ async function build(targetPath, workPath, useAppIdDir = true) {
337
359
  {
338
360
  title: "编译页面文件",
339
361
  task: async (ctx, task) => {
340
- return runCompileInWorker("view", ctx, task);
362
+ return runCompileInWorker("view", ctx, task, { sourcemap });
341
363
  }
342
364
  },
343
365
  {
344
366
  title: "编译页面逻辑",
345
367
  task: async (ctx, task) => {
346
- return runCompileInWorker("logic", ctx, task);
368
+ return runCompileInWorker("logic", ctx, task, { sourcemap });
347
369
  }
348
370
  },
349
371
  {
@@ -353,7 +375,7 @@ async function build(targetPath, workPath, useAppIdDir = true) {
353
375
  path: "app",
354
376
  id: ""
355
377
  });
356
- return runCompileInWorker("style", ctx, task);
378
+ return runCompileInWorker("style", ctx, task, { sourcemap });
357
379
  }
358
380
  }
359
381
  ], { concurrent: true });
@@ -377,7 +399,7 @@ async function build(targetPath, workPath, useAppIdDir = true) {
377
399
  console.error(`${workPath} 编译出错: ${e.message}\n${e.stack}`);
378
400
  }
379
401
  }
380
- function runCompileInWorker(script, ctx, task) {
402
+ function runCompileInWorker(script, ctx, task, options = {}) {
381
403
  return workerPool.runWorker(() => new Promise((resolve, reject) => {
382
404
  const worker = new Worker(path.join(path.dirname(fileURLToPath(import.meta.url)), `core/${script}-compiler.js`), workerPool.getWorkerOptions());
383
405
  const totalTasks = Object.keys(ctx.pages.mainPages).length + Object.values(ctx.pages.subPages).reduce((sum, item) => sum + item.info.length, 0);
@@ -391,7 +413,8 @@ function runCompileInWorker(script, ctx, task) {
391
413
  };
392
414
  worker.postMessage({
393
415
  pages: ctx.pages,
394
- storeInfo: ctx.storeInfo
416
+ storeInfo: ctx.storeInfo,
417
+ sourcemap: !!options.sourcemap
395
418
  });
396
419
  worker.on("message", (message) => {
397
420
  try {