@wevu/compiler 6.22.0 → 6.24.0

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 CHANGED
@@ -64,5 +64,5 @@ const nextCode = injectWevuPageFeaturesInJs(sourceCode, flags)
64
64
 
65
65
  ## 相关链接
66
66
 
67
- - weapp-vite 文档:https://vite.icebreaker.top/
67
+ - weapp-vite 文档:https://vite.weapp.dev/
68
68
  - 仓库:https://github.com/weapp-vite/weapp-vite
package/dist/index.d.mts CHANGED
@@ -320,8 +320,12 @@ interface TemplateCompileResult {
320
320
  */
321
321
  interface ForParseResult {
322
322
  listExp?: string;
323
+ rawListExp?: string;
323
324
  listExpAst?: Expression;
325
+ projectedListExp?: string;
326
+ projectedListExpAst?: Expression;
324
327
  item?: string;
328
+ itemAccess?: string;
325
329
  index?: string;
326
330
  key?: string;
327
331
  itemAliases?: Record<string, string>;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { WEAPP_I18N_RUNTIME_MARKER, WEAPP_VITE_RUNTIME_REACTIVITY_VIRTUAL_ID, WEAPP_VITE_RUNTIME_TEMPLATE_VIRTUAL_ID, WEAPP_VITE_RUNTIME_VIRTUAL_ID, WEVU_CLASS_STYLE_RUNTIME_FILE, WEVU_CLASS_STYLE_RUNTIME_MODULE, WEVU_CSS_MODULES_KEY, WEVU_CSS_VARS_STYLE_KEY, WEVU_EXPRESSION_ERROR_IDENTIFIER, WEVU_FUNCTION_PROP_PATHS_KEY, WEVU_INLINE_HANDLER, WEVU_INLINE_MAP_KEY, WEVU_IS_PAGE_KEY, WEVU_JSX_ISLAND_DATA_KEY, WEVU_JSX_ISLAND_HANDLER, WEVU_JSX_ISLAND_TEMPLATE_NAME, WEVU_LAYOUT_HOSTS_KEY, WEVU_LAYOUT_HOST_ID_PREFIX, WEVU_LAYOUT_HOST_REF_PREFIX, WEVU_MODEL_HANDLER, WEVU_OWNER_HANDLER, WEVU_PROPS_ALIASES_KEY, WEVU_PROPS_DERIVED_KEYS_KEY, WEVU_PROPS_KEY, WEVU_SLOT_FALLBACK_VIRTUAL_HOST_BASE, WEVU_SLOT_FALLBACK_VIRTUAL_HOST_TAG_NAME, WEVU_SLOT_FUNCTION_TOKEN, WEVU_SLOT_NAMES_ATTR, WEVU_SLOT_NAMES_PROP, WEVU_SLOT_OWNER_ID_ATTR, WEVU_SLOT_OWNER_ID_KEY, WEVU_SLOT_OWNER_ID_PROP, WEVU_SLOT_OWNER_KEY, WEVU_SLOT_OWNER_PROXY_KEY, WEVU_SLOT_PROPS_ATTR, WEVU_SLOT_PROPS_DATA_KEY, WEVU_SLOT_PROPS_KEY, WEVU_SLOT_SCOPE_ATTR, WEVU_SLOT_SCOPE_KEY, WEVU_TEMPLATE_REFS_KEY, WEVU_TEMPLATE_REF_CLASS_PREFIX } from "@weapp-core/constants";
3
- import { getMiniProgramRuntimeGlobalKeys, getMiniProgramTemplatePreset, removeExtensionDeep } from "@weapp-core/shared";
3
+ import { escapeWxmlAttribute, escapeWxmlText, getMiniProgramRuntimeGlobalKeys, getMiniProgramTemplatePreset, removeExtensionDeep } from "@weapp-core/shared";
4
4
  import path from "pathe";
5
5
  import { NodeTypes, baseParse } from "@vue/compiler-core";
6
6
  import { createHash } from "node:crypto";
@@ -3064,7 +3064,8 @@ function buildForExpression(binding, forStack, level, helpers) {
3064
3064
  if (level >= forStack.length) return buildNormalizedExpression(binding, helpers);
3065
3065
  const info = forStack[level];
3066
3066
  const listId = t.identifier(`__wv_list_${level}`);
3067
- const listExp = info.listExpAst ? t.cloneNode(info.listExpAst, true) : t.arrayExpression([]);
3067
+ const listExpAst = !binding.exp.startsWith("v-for :key ") && info.projectedListExpAst ? info.projectedListExpAst : info.listExpAst;
3068
+ const listExp = listExpAst ? t.cloneNode(listExpAst, true) : t.arrayExpression([]);
3068
3069
  const unrefHelper = helpers.unref ? t.cloneNode(helpers.unref) : t.identifier("unref");
3069
3070
  const listUnrefExp = t.callExpression(unrefHelper, [listExp]);
3070
3071
  const listDecl = t.variableDeclaration("let", [t.variableDeclarator(listId, t.arrayExpression([]))]);
@@ -4133,8 +4134,6 @@ function normalizeWxmlExpression(exp) {
4133
4134
  }
4134
4135
  //#endregion
4135
4136
  //#region src/plugins/jsx/compileJsx/ast.ts
4136
- const ESCAPED_TEXT_RE = /[&<>]/g;
4137
- const ESCAPED_ATTR_RE = /[&"<>]/g;
4138
4137
  const WXML_EXPRESSION_GENERATE_OPTIONS = {
4139
4138
  compact: true,
4140
4139
  jsescOption: {
@@ -4142,23 +4141,8 @@ const WXML_EXPRESSION_GENERATE_OPTIONS = {
4142
4141
  minimal: true
4143
4142
  }
4144
4143
  };
4145
- const ESCAPED_TEXT_MAP = {
4146
- "&": "&amp;",
4147
- "<": "&lt;",
4148
- ">": "&gt;"
4149
- };
4150
- const ESCAPED_ATTR_MAP = {
4151
- "&": "&amp;",
4152
- "\"": "&quot;",
4153
- "<": "&lt;",
4154
- ">": "&gt;"
4155
- };
4156
- function escapeText(value) {
4157
- return value.replace(ESCAPED_TEXT_RE, (ch) => ESCAPED_TEXT_MAP[ch] || ch);
4158
- }
4159
- function escapeAttr(value) {
4160
- return value.replace(ESCAPED_ATTR_RE, (ch) => ESCAPED_ATTR_MAP[ch] || ch);
4161
- }
4144
+ const escapeText = escapeWxmlText;
4145
+ const escapeAttr = escapeWxmlAttribute;
4162
4146
  const WHITESPACE_RE = /\s+/g;
4163
4147
  function normalizeJsxText(value) {
4164
4148
  return value.replace(WHITESPACE_RE, " ");
@@ -6975,11 +6959,93 @@ function getClassStyleWxsSource(options = {}) {
6975
6959
  ].join("\n");
6976
6960
  }
6977
6961
  //#endregion
6978
- //#region src/plugins/vue/compiler/template/elements/forExpression.ts
6962
+ //#region src/plugins/vue/compiler/template/expression/inlineShared.ts
6979
6963
  const IDENTIFIER_RE$5 = /^[A-Z_$][\w$]*$/i;
6964
+ const INLINE_GLOBALS = /* @__PURE__ */ new Set([
6965
+ "Math",
6966
+ "Number",
6967
+ "Date",
6968
+ "Array",
6969
+ "Object",
6970
+ "Boolean",
6971
+ "String",
6972
+ "RegExp",
6973
+ "Map",
6974
+ "Set",
6975
+ "JSON",
6976
+ "Intl",
6977
+ "Promise",
6978
+ "console",
6979
+ "Infinity",
6980
+ "undefined",
6981
+ "NaN",
6982
+ "isFinite",
6983
+ "isNaN",
6984
+ "parseFloat",
6985
+ "parseInt",
6986
+ "decodeURI",
6987
+ "decodeURIComponent",
6988
+ "encodeURI",
6989
+ "encodeURIComponent",
6990
+ "require",
6991
+ "arguments",
6992
+ WEVU_CLASS_STYLE_RUNTIME_MODULE,
6993
+ "__wevuUnref",
6994
+ "globalThis",
6995
+ "setTimeout",
6996
+ "clearTimeout",
6997
+ "setInterval",
6998
+ "clearInterval",
6999
+ "Page",
7000
+ "App",
7001
+ "Component",
7002
+ "requirePlugin",
7003
+ "getApp",
7004
+ "getCurrentPages",
7005
+ "ctx",
7006
+ "scope",
7007
+ ...getMiniProgramRuntimeGlobalKeys()
7008
+ ]);
7009
+ function createMemberAccess$1(target, prop) {
7010
+ if (IDENTIFIER_RE$5.test(prop)) return t.memberExpression(t.identifier(target), t.identifier(prop));
7011
+ return t.memberExpression(t.identifier(target), t.stringLiteral(prop), true);
7012
+ }
7013
+ function replaceIdentifierWithExpression$1(path, replacement) {
7014
+ const parent = path.parentPath;
7015
+ if (parent.isObjectProperty() && parent.node.shorthand && parent.node.key === path.node) {
7016
+ parent.node.shorthand = false;
7017
+ parent.node.value = replacement;
7018
+ return;
7019
+ }
7020
+ path.replaceWith(replacement);
7021
+ }
7022
+ //#endregion
7023
+ //#region src/plugins/vue/compiler/template/expression/forItemAccess.ts
7024
+ function getForItemAccess(context, name) {
7025
+ for (let index = context.forStack.length - 1; index >= 0; index -= 1) {
7026
+ const forInfo = context.forStack[index];
7027
+ if (forInfo?.item === name && forInfo.itemAccess) return forInfo.itemAccess;
7028
+ }
7029
+ return name;
7030
+ }
7031
+ function rewriteForItemAccess(ast, context) {
7032
+ const itemAccesses = /* @__PURE__ */ new Map();
7033
+ for (const forInfo of context.forStack) if (forInfo.item && forInfo.itemAccess) itemAccesses.set(forInfo.item, forInfo.itemAccess);
7034
+ if (!itemAccesses.size) return;
7035
+ traverse(ast, { Identifier(path) {
7036
+ if (!path.isReferencedIdentifier() || path.scope.hasBinding(path.node.name)) return;
7037
+ const replacement = itemAccesses.get(path.node.name);
7038
+ if (!replacement) return;
7039
+ replaceIdentifierWithExpression$1(path, replacement.split(".").slice(1).reduce((target, property) => t.memberExpression(target, t.identifier(property)), t.identifier(path.node.name)));
7040
+ path.skip();
7041
+ } });
7042
+ }
7043
+ //#endregion
7044
+ //#region src/plugins/vue/compiler/template/elements/forExpression.ts
7045
+ const IDENTIFIER_RE$4 = /^[A-Z_$][\w$]*$/i;
6980
7046
  const FOR_ITEM_ALIAS_PLACEHOLDER = "__wv_for_item__";
6981
7047
  function isIdentifier(value) {
6982
- return IDENTIFIER_RE$5.test(value);
7048
+ return IDENTIFIER_RE$4.test(value);
6983
7049
  }
6984
7050
  function splitTopLevelByComma(input) {
6985
7051
  const out = [];
@@ -7255,7 +7321,7 @@ function withSlotProps(context, mapping, fn) {
7255
7321
  popSlotProps(context);
7256
7322
  }
7257
7323
  }
7258
- const IDENTIFIER_RE$4 = /^[A-Z_$][\w$]*$/i;
7324
+ const IDENTIFIER_RE$3 = /^[A-Z_$][\w$]*$/i;
7259
7325
  const BIND_SHORTHAND_ARG_RE = /^[A-Z_$][\w$]*(?:-[A-Z_$][\w$]*)*$/i;
7260
7326
  const BACKSLASH_RE$2 = /\\/g;
7261
7327
  const SINGLE_QUOTE_RE$2 = /'/g;
@@ -7276,7 +7342,7 @@ function collectScopePropMapping(context) {
7276
7342
  const mapping = {};
7277
7343
  if (!context.slotMultipleInstance) return mapping;
7278
7344
  for (const scope of context.scopeStack) for (const name of scope) {
7279
- if (!IDENTIFIER_RE$4.test(name)) continue;
7345
+ if (!IDENTIFIER_RE$3.test(name)) continue;
7280
7346
  if (!hasOwn(mapping, name)) mapping[name] = name;
7281
7347
  }
7282
7348
  return mapping;
@@ -7288,7 +7354,7 @@ function buildScopePropsExpression(context) {
7288
7354
  const mapping = collectScopePropMapping(context);
7289
7355
  const keys = Object.keys(mapping);
7290
7356
  if (!keys.length) return null;
7291
- return `[${keys.map((key) => `${toWxmlStringLiteral$1(key)},${key}`).join(",")}]`;
7357
+ return `[${keys.map((key) => `${toWxmlStringLiteral$1(key)},${getForItemAccess(context, key)}`).join(",")}]`;
7292
7358
  }
7293
7359
  function hashString(input) {
7294
7360
  let hash = 0;
@@ -7357,7 +7423,7 @@ function collectForAliasMapping$2(context) {
7357
7423
  }
7358
7424
  return mapping;
7359
7425
  }
7360
- function replaceIdentifierWithExpression$1(path, replacement) {
7426
+ function replaceIdentifierWithExpression(path, replacement) {
7361
7427
  const parent = path.parentPath;
7362
7428
  if (parent.isObjectProperty() && parent.node.shorthand && parent.node.key === path.node) {
7363
7429
  parent.node.shorthand = false;
@@ -7366,7 +7432,7 @@ function replaceIdentifierWithExpression$1(path, replacement) {
7366
7432
  }
7367
7433
  path.replaceWith(replacement);
7368
7434
  }
7369
- const IDENTIFIER_RE$3 = /^[A-Z_$][\w$]*$/i;
7435
+ const IDENTIFIER_RE$2 = /^[A-Z_$][\w$]*$/i;
7370
7436
  function rewriteScopedSlotExpression(exp, context) {
7371
7437
  const normalized = normalizeWxmlExpression(exp);
7372
7438
  const parsed = parseBabelExpressionFile(normalized);
@@ -7377,7 +7443,7 @@ function rewriteScopedSlotExpression(exp, context) {
7377
7443
  const forAliases = collectForAliasMapping$2(context);
7378
7444
  const createMemberAccess = (target, prop) => {
7379
7445
  if (!prop) return t.identifier(target);
7380
- if (IDENTIFIER_RE$3.test(prop)) return t.memberExpression(t.identifier(target), t.identifier(prop));
7446
+ if (IDENTIFIER_RE$2.test(prop)) return t.memberExpression(t.identifier(target), t.identifier(prop));
7381
7447
  return t.memberExpression(t.identifier(target), t.stringLiteral(prop), true);
7382
7448
  };
7383
7449
  traverse(ast, { Identifier(path) {
@@ -7388,17 +7454,18 @@ function rewriteScopedSlotExpression(exp, context) {
7388
7454
  if (hasOwn(forAliases, name)) {
7389
7455
  const aliasExp = parseBabelExpression(forAliases[name]);
7390
7456
  if (aliasExp) {
7391
- replaceIdentifierWithExpression$1(path, t.cloneNode(aliasExp, true));
7457
+ replaceIdentifierWithExpression(path, t.cloneNode(aliasExp, true));
7392
7458
  return;
7393
7459
  }
7394
7460
  }
7395
7461
  if (locals.has(name)) return;
7396
7462
  if (hasOwn(slotProps, name)) {
7397
- replaceIdentifierWithExpression$1(path, createMemberAccess(WEVU_SLOT_PROPS_DATA_KEY, slotProps[name]));
7463
+ replaceIdentifierWithExpression(path, createMemberAccess(WEVU_SLOT_PROPS_DATA_KEY, slotProps[name]));
7398
7464
  return;
7399
7465
  }
7400
- replaceIdentifierWithExpression$1(path, createMemberAccess(WEVU_SLOT_OWNER_KEY, name));
7466
+ replaceIdentifierWithExpression(path, createMemberAccess(WEVU_SLOT_OWNER_KEY, name));
7401
7467
  } });
7468
+ rewriteForItemAccess(ast, context);
7402
7469
  const stmt = ast.program.body[0];
7403
7470
  const updatedExpression = stmt && "expression" in stmt ? stmt.expression : null;
7404
7471
  return updatedExpression ? generateExpression(updatedExpression) : normalized;
@@ -7406,7 +7473,7 @@ function rewriteScopedSlotExpression(exp, context) {
7406
7473
  function rewriteForAliasExpression(exp, context) {
7407
7474
  const normalized = normalizeWxmlExpression(exp);
7408
7475
  const forAliases = collectForAliasMapping$2(context);
7409
- if (!Object.keys(forAliases).length) return normalized;
7476
+ if (!Object.keys(forAliases).length && !context.forStack.some((forInfo) => forInfo.itemAccess)) return normalized;
7410
7477
  const parsed = parseBabelExpressionFile(normalized);
7411
7478
  if (!parsed) return normalized;
7412
7479
  const { ast } = parsed;
@@ -7416,9 +7483,10 @@ function rewriteForAliasExpression(exp, context) {
7416
7483
  if (path.scope.hasBinding(name)) return;
7417
7484
  if (hasOwn(forAliases, name)) {
7418
7485
  const aliasExp = parseBabelExpression(forAliases[name]);
7419
- if (aliasExp) replaceIdentifierWithExpression$1(path, t.cloneNode(aliasExp, true));
7486
+ if (aliasExp) replaceIdentifierWithExpression(path, t.cloneNode(aliasExp, true));
7420
7487
  }
7421
7488
  } });
7489
+ rewriteForItemAccess(ast, context);
7422
7490
  const stmt = ast.program.body[0];
7423
7491
  const updatedExpression = stmt && "expression" in stmt ? stmt.expression : null;
7424
7492
  return updatedExpression ? generateExpression(updatedExpression) : normalized;
@@ -7528,68 +7596,89 @@ function normalizeStyleBindingExpression(exp, context) {
7528
7596
  return out;
7529
7597
  }
7530
7598
  //#endregion
7531
- //#region src/plugins/vue/compiler/template/expression/inline.ts
7532
- const INLINE_GLOBALS = /* @__PURE__ */ new Set([
7533
- "Math",
7534
- "Number",
7535
- "Date",
7536
- "Array",
7537
- "Object",
7538
- "Boolean",
7539
- "String",
7540
- "RegExp",
7541
- "Map",
7542
- "Set",
7543
- "JSON",
7544
- "Intl",
7545
- "Promise",
7546
- "console",
7547
- "Infinity",
7548
- "undefined",
7549
- "NaN",
7550
- "isFinite",
7551
- "isNaN",
7552
- "parseFloat",
7553
- "parseInt",
7554
- "decodeURI",
7555
- "decodeURIComponent",
7556
- "encodeURI",
7557
- "encodeURIComponent",
7558
- "require",
7559
- "arguments",
7560
- WEVU_CLASS_STYLE_RUNTIME_MODULE,
7561
- "__wevuUnref",
7562
- "globalThis",
7563
- "setTimeout",
7564
- "clearTimeout",
7565
- "setInterval",
7566
- "clearInterval",
7567
- "Page",
7568
- "App",
7569
- "Component",
7570
- "requirePlugin",
7571
- "getApp",
7572
- "getCurrentPages",
7573
- "ctx",
7574
- "scope",
7575
- ...getMiniProgramRuntimeGlobalKeys()
7576
- ]);
7577
- const IDENTIFIER_RE$2 = /^[A-Z_$][\w$]*$/i;
7599
+ //#region src/plugins/vue/compiler/template/expression/forItemResolver.ts
7578
7600
  const SIMPLE_PATH_RE = /^[A-Z_$][\w$]*(?:\.[A-Z_$][\w$]*)*$/i;
7579
- const SCRIPT_SETUP_REF_BINDINGS = /* @__PURE__ */ new Set(["setup-ref", "setup-maybe-ref"]);
7580
- function createMemberAccess$1(target, prop) {
7581
- if (IDENTIFIER_RE$2.test(prop)) return t.memberExpression(t.identifier(target), t.identifier(prop));
7582
- return t.memberExpression(t.identifier(target), t.stringLiteral(prop), true);
7601
+ function rewriteForItemResolverExpression(rawExp, resolvedLocals, resolvedIndexes) {
7602
+ const parsed = parseBabelExpressionFile(rawExp);
7603
+ if (!parsed) return null;
7604
+ traverse(parsed.ast, {
7605
+ Identifier(path) {
7606
+ if (!path.isReferencedIdentifier() || path.scope.hasBinding(path.node.name)) return;
7607
+ const name = path.node.name;
7608
+ const localExpression = resolvedLocals.get(name) ?? resolvedIndexes.get(name);
7609
+ if (localExpression) {
7610
+ replaceIdentifierWithExpression$1(path, t.cloneNode(localExpression, true));
7611
+ path.skip();
7612
+ return;
7613
+ }
7614
+ if (INLINE_GLOBALS.has(name)) return;
7615
+ replaceIdentifierWithExpression$1(path, createMemberAccess$1("ctx", name));
7616
+ path.skip();
7617
+ },
7618
+ ThisExpression(path) {
7619
+ path.replaceWith(t.identifier("ctx"));
7620
+ }
7621
+ });
7622
+ const statement = parsed.ast.program.body[0];
7623
+ return statement && t.isExpressionStatement(statement) ? statement.expression : null;
7583
7624
  }
7584
- function replaceIdentifierWithExpression(path, replacement) {
7585
- const parent = path.parentPath;
7586
- if (parent.isObjectProperty() && parent.node.shorthand && parent.node.key === path.node) {
7587
- parent.node.shorthand = false;
7588
- parent.node.value = replacement;
7589
- return;
7625
+ function buildNestedForItemResolverExpression(targetKey, targetLevel, context, slotProps, indexBindings) {
7626
+ const resolvedLocals = /* @__PURE__ */ new Map();
7627
+ const resolvedIndexes = /* @__PURE__ */ new Map();
7628
+ for (let level = 0; level <= targetLevel; level += 1) {
7629
+ const forInfo = context.forStack[level];
7630
+ const listExp = forInfo?.listExp?.trim() ?? "";
7631
+ const indexBinding = indexBindings[level];
7632
+ if (!forInfo || !listExp || !indexBinding) return null;
7633
+ const root = SIMPLE_PATH_RE.test(listExp) ? listExp.split(".")[0] : "";
7634
+ if (root && hasOwn(slotProps, root) && !resolvedLocals.has(root)) return null;
7635
+ const listExpression = rewriteForItemResolverExpression(listExp, resolvedLocals, resolvedIndexes);
7636
+ if (!listExpression) return null;
7637
+ const indexExpression = createMemberAccess$1("scope", indexBinding.key);
7638
+ const itemExpression = t.memberExpression(listExpression, t.cloneNode(indexExpression), true);
7639
+ if (forInfo.item) resolvedLocals.set(forInfo.item, itemExpression);
7640
+ for (const [alias, aliasExp] of Object.entries(forInfo.itemAliases ?? {})) {
7641
+ const resolvedAlias = rewriteForItemResolverExpression(aliasExp, resolvedLocals, resolvedIndexes);
7642
+ if (resolvedAlias) resolvedLocals.set(alias, resolvedAlias);
7643
+ }
7644
+ const targetExpression = resolvedLocals.get(targetKey);
7645
+ if (level === targetLevel && targetExpression) return generateExpression(t.arrowFunctionExpression([t.identifier("ctx"), t.identifier("scope")], t.cloneNode(targetExpression, true)));
7646
+ resolvedIndexes.set(forInfo.index?.trim() || "index", indexExpression);
7647
+ if (forInfo.key) resolvedIndexes.set(forInfo.key, indexExpression);
7590
7648
  }
7591
- path.replaceWith(replacement);
7649
+ return null;
7592
7650
  }
7651
+ function buildForItemResolverExpression(targetKey, context, slotProps, indexBindings) {
7652
+ let targetLevel = -1;
7653
+ for (let level = context.forStack.length - 1; level >= 0; level -= 1) {
7654
+ const forInfo = context.forStack[level];
7655
+ if (forInfo?.item === targetKey || hasOwn(forInfo?.itemAliases ?? {}, targetKey)) {
7656
+ targetLevel = level;
7657
+ break;
7658
+ }
7659
+ }
7660
+ if (targetLevel < 0) return null;
7661
+ const forInfo = context.forStack[targetLevel];
7662
+ const listExp = forInfo?.listExp?.trim() ?? "";
7663
+ const indexBinding = indexBindings[targetLevel];
7664
+ if (!listExp || !indexBinding) return null;
7665
+ if (forInfo?.item === targetKey && SIMPLE_PATH_RE.test(listExp)) {
7666
+ const root = listExp.split(".")[0];
7667
+ const localRoots = new Set(Object.keys(slotProps));
7668
+ for (let level = 0; level <= targetLevel; level += 1) {
7669
+ const item = context.forStack[level]?.item?.trim();
7670
+ const index = context.forStack[level]?.index?.trim();
7671
+ if (item) localRoots.add(item);
7672
+ if (index) localRoots.add(index);
7673
+ localRoots.add("index");
7674
+ }
7675
+ if (!localRoots.has(root)) return `({type:'for-item',path:${JSON.stringify(listExp)},indexKey:${JSON.stringify(indexBinding.key)}})`;
7676
+ }
7677
+ return buildNestedForItemResolverExpression(targetKey, targetLevel, context, slotProps, indexBindings);
7678
+ }
7679
+ //#endregion
7680
+ //#region src/plugins/vue/compiler/template/expression/inline.ts
7681
+ const SCRIPT_SETUP_REF_BINDINGS = /* @__PURE__ */ new Set(["setup-ref", "setup-maybe-ref"]);
7593
7682
  function resolveSlotPropBinding(slotProps, name) {
7594
7683
  if (!hasOwn(slotProps, name)) return null;
7595
7684
  const prop = slotProps[name];
@@ -7606,11 +7695,11 @@ function rewriteExpressionAst(ast, locals, options) {
7606
7695
  if (path.scope.getBinding(name)) return;
7607
7696
  if (locals.has(name)) {
7608
7697
  options?.markLocal?.(name);
7609
- replaceIdentifierWithExpression(path, createMemberAccess$1("scope", name));
7698
+ replaceIdentifierWithExpression$1(path, createMemberAccess$1("scope", name));
7610
7699
  return;
7611
7700
  }
7612
7701
  if (INLINE_GLOBALS.has(name)) return;
7613
- replaceIdentifierWithExpression(path, createMemberAccess$1("ctx", name));
7702
+ replaceIdentifierWithExpression$1(path, createMemberAccess$1("ctx", name));
7614
7703
  },
7615
7704
  ThisExpression(path) {
7616
7705
  path.replaceWith(t.identifier("ctx"));
@@ -7624,28 +7713,6 @@ function buildInlineIndexBindings(context) {
7624
7713
  binding: forInfo.index?.trim() || "index"
7625
7714
  }));
7626
7715
  }
7627
- function buildForItemResolverExpression(targetKey, context, slotProps, indexBindings) {
7628
- let targetLevel = -1;
7629
- for (let level = context.forStack.length - 1; level >= 0; level -= 1) if (context.forStack[level]?.item === targetKey) {
7630
- targetLevel = level;
7631
- break;
7632
- }
7633
- if (targetLevel < 0) return null;
7634
- const listExp = context.forStack[targetLevel]?.listExp?.trim() ?? "";
7635
- const indexBinding = indexBindings[targetLevel];
7636
- if (!listExp || !indexBinding || !SIMPLE_PATH_RE.test(listExp)) return null;
7637
- const root = listExp.split(".")[0];
7638
- const localRoots = new Set(Object.keys(slotProps));
7639
- for (let level = 0; level <= targetLevel; level += 1) {
7640
- const item = context.forStack[level]?.item?.trim();
7641
- const index = context.forStack[level]?.index?.trim();
7642
- if (item) localRoots.add(item);
7643
- if (index) localRoots.add(index);
7644
- localRoots.add("index");
7645
- }
7646
- if (localRoots.has(root)) return null;
7647
- return `({type:'for-item',path:${JSON.stringify(listExp)},indexKey:${JSON.stringify(indexBinding.key)}})`;
7648
- }
7649
7716
  function buildScopeResolvers(usedLocals, context, slotProps, indexBindings) {
7650
7717
  const resolvers = [];
7651
7718
  for (const key of usedLocals) {
@@ -7807,7 +7874,7 @@ function createThisMemberAccess(prop) {
7807
7874
  function createUnrefCall(exp) {
7808
7875
  return t.callExpression(t.identifier("__wevuUnref"), [exp]);
7809
7876
  }
7810
- function createHasOwnPropertyCall(target, key) {
7877
+ function createHasOwnPropertyCall$1(target, key) {
7811
7878
  return t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.identifier("Object"), t.identifier("prototype")), t.identifier("hasOwnProperty")), t.identifier("call")), [target, t.stringLiteral(key)]);
7812
7879
  }
7813
7880
  function createResolvePropValueCall(name, fallback, preferProps = false) {
@@ -7823,7 +7890,7 @@ function createPropsKeyAccessFallback(name, fallback) {
7823
7890
  const propsObject = createThisMemberAccess(WEVU_PROPS_KEY);
7824
7891
  const propsAccess = createMemberAccess(propsObject, name);
7825
7892
  const hasPropsObject = t.binaryExpression("!=", propsObject, t.nullLiteral());
7826
- const hasPropsValue = t.logicalExpression("&&", hasPropsObject, t.logicalExpression("||", t.binaryExpression("!==", propsAccess, t.identifier("undefined")), createHasOwnPropertyCall(propsObject, name)));
7893
+ const hasPropsValue = t.logicalExpression("&&", hasPropsObject, t.logicalExpression("||", t.binaryExpression("!==", propsAccess, t.identifier("undefined")), createHasOwnPropertyCall$1(propsObject, name)));
7827
7894
  return t.conditionalExpression(hasPropsValue, propsAccess, fallback);
7828
7895
  }
7829
7896
  function createIdentifierAccessWithPropsFallback(name, context, useRuntimePropHelper = false) {
@@ -7845,10 +7912,10 @@ function createIdentifierAccessWithPropsFallback(name, context, useRuntimePropHe
7845
7912
  const stateObject = createThisMemberAccess("$state");
7846
7913
  const hasPropsObject = t.binaryExpression("!=", propsObject, t.nullLiteral());
7847
7914
  const hasDefinedPropsValue = t.binaryExpression("!==", propsAccess, t.identifier("undefined"));
7848
- const hasPropsKey = createHasOwnPropertyCall(propsObject, name);
7915
+ const hasPropsKey = createHasOwnPropertyCall$1(propsObject, name);
7849
7916
  const hasUsablePropsValue = t.logicalExpression("&&", hasPropsObject, t.logicalExpression("||", hasDefinedPropsValue, hasPropsKey));
7850
7917
  const hasStateObject = t.binaryExpression("!=", stateObject, t.nullLiteral());
7851
- const hasStateKey = createHasOwnPropertyCall(stateObject, name);
7918
+ const hasStateKey = createHasOwnPropertyCall$1(stateObject, name);
7852
7919
  const shouldUseStateAccess = Boolean(context.propsDerivedKeys?.includes(name)) ? t.booleanLiteral(false) : t.logicalExpression("&&", hasStateObject, hasStateKey);
7853
7920
  return t.conditionalExpression(shouldUseStateAccess, thisAccess, t.conditionalExpression(hasUsablePropsValue, propsAccess, thisAccess));
7854
7921
  }
@@ -7912,6 +7979,9 @@ function normalizeJsExpressionWithContext(exp, context, options) {
7912
7979
  }
7913
7980
  }
7914
7981
  if (locals.has(name)) return;
7982
+ if (options?.preserveForItems && context.forStack.some((forInfo) => {
7983
+ return forInfo.item === name || forInfo.index === name || forInfo.key === name;
7984
+ })) return;
7915
7985
  let replacement;
7916
7986
  if (context.rewriteScopedSlot) {
7917
7987
  if (hasOwn(slotProps, name)) {
@@ -7934,6 +8004,7 @@ function normalizeJsExpressionWithContext(exp, context, options) {
7934
8004
  path.replaceWith(replacement);
7935
8005
  path.skip();
7936
8006
  } });
8007
+ rewriteForItemAccess(ast, context);
7937
8008
  if (options?.unrefMemberAccess) traverse(ast, { MemberExpression: { exit(path) {
7938
8009
  if (!shouldUnrefMemberAccess(path.node, path.parentPath?.node)) return;
7939
8010
  path.replaceWith(createUnrefCall(t.cloneNode(path.node, true)));
@@ -8152,10 +8223,161 @@ function renderStyleAttribute(staticStyle, dynamicStyleExp, vShowExp, context) {
8152
8223
  function transformAttribute(node, _context) {
8153
8224
  const { name, value } = node;
8154
8225
  if (!value) return name;
8155
- if (value.type === NodeTypes.TEXT) return `${name}="${value.content}"`;
8226
+ if (value.type === NodeTypes.TEXT) return `${name}="${escapeWxmlAttribute(value.content)}"`;
8156
8227
  return `${name}=""`;
8157
8228
  }
8158
8229
  //#endregion
8230
+ //#region src/plugins/vue/compiler/template/directives/forKey/projection.ts
8231
+ function createConsoleErrorGuard(message, errorId) {
8232
+ return t.ifStatement(t.logicalExpression("&&", t.binaryExpression("!==", t.unaryExpression("typeof", t.identifier("console")), t.stringLiteral("undefined")), t.binaryExpression("===", t.unaryExpression("typeof", t.memberExpression(t.identifier("console"), t.identifier("error"))), t.stringLiteral("function"))), t.blockStatement([t.expressionStatement(t.callExpression(t.memberExpression(t.identifier("console"), t.identifier("error")), [t.stringLiteral(message), t.cloneNode(errorId)]))]));
8233
+ }
8234
+ function createConsoleWarnGuard(message, item) {
8235
+ return t.ifStatement(t.logicalExpression("&&", t.binaryExpression("!==", t.unaryExpression("typeof", t.identifier("console")), t.stringLiteral("undefined")), t.binaryExpression("===", t.unaryExpression("typeof", t.memberExpression(t.identifier("console"), t.identifier("warn"))), t.stringLiteral("function"))), t.blockStatement([t.expressionStatement(t.callExpression(t.memberExpression(t.identifier("console"), t.identifier("warn")), [t.stringLiteral(message), t.cloneNode(item)]))]));
8236
+ }
8237
+ function createHasOwnPropertyCall(item, field) {
8238
+ return t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.identifier("Object"), t.identifier("prototype")), t.identifier("hasOwnProperty")), t.identifier("call")), [t.cloneNode(item), t.stringLiteral(field)]);
8239
+ }
8240
+ function createSafeKeyExpression(exp, rawExp, seed, item) {
8241
+ const errorId = t.identifier(`__wv_for_key_error_${seed}`);
8242
+ const shouldReportError = item ? t.binaryExpression("!=", t.cloneNode(item), t.nullLiteral()) : t.booleanLiteral(true);
8243
+ return t.callExpression(t.arrowFunctionExpression([], t.blockStatement([t.tryStatement(t.blockStatement([t.returnStatement(t.cloneNode(exp, true))]), t.catchClause(errorId, t.blockStatement([t.ifStatement(shouldReportError, t.blockStatement([createConsoleErrorGuard(`[wevu] v-for :key 表达式执行失败: ${rawExp}`, errorId)])), t.returnStatement(t.identifier("undefined"))])), null)])), []);
8244
+ }
8245
+ function createProjectedItemExpression(item, keyExp, rawKeyExp, keyField, valueField, seed) {
8246
+ const isObject = t.logicalExpression("&&", t.binaryExpression("!=", t.cloneNode(item), t.nullLiteral()), t.binaryExpression("===", t.unaryExpression("typeof", t.cloneNode(item)), t.stringLiteral("object")));
8247
+ const projected = t.callExpression(t.arrowFunctionExpression([], t.blockStatement([t.ifStatement(t.logicalExpression("||", createHasOwnPropertyCall(item, keyField), createHasOwnPropertyCall(item, valueField)), t.blockStatement([createConsoleWarnGuard(`[wevu] v-for :key 内部字段冲突,投影将使用保留字段 ${keyField} / ${valueField}`, item)])), t.returnStatement(t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [
8248
+ t.objectExpression([]),
8249
+ t.cloneNode(item),
8250
+ t.objectExpression([t.objectProperty(t.stringLiteral(valueField), t.cloneNode(item)), t.objectProperty(t.stringLiteral(keyField), createSafeKeyExpression(keyExp, rawKeyExp, seed, item))])
8251
+ ]))])), []);
8252
+ const primitiveProjected = t.objectExpression([t.objectProperty(t.stringLiteral(valueField), t.cloneNode(item)), t.objectProperty(t.stringLiteral(keyField), createSafeKeyExpression(keyExp, rawKeyExp, seed, item))]);
8253
+ return t.conditionalExpression(isObject, projected, primitiveProjected);
8254
+ }
8255
+ function createArrayProjection(sourceId, keyExp, rawKeyExp, keyField, valueField, forInfo, seed) {
8256
+ const itemId = forInfo.item && t.isValidIdentifier(forInfo.item) ? t.identifier(forInfo.item) : t.identifier(`__wv_for_key_item_${seed}`);
8257
+ const indexId = forInfo.index && t.isValidIdentifier(forInfo.index) ? t.identifier(forInfo.index) : t.identifier(`__wv_for_key_index_${seed}`);
8258
+ const body = [];
8259
+ if (forInfo.key && t.isValidIdentifier(forInfo.key) && forInfo.key !== indexId.name) body.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier(forInfo.key), t.cloneNode(indexId))]));
8260
+ body.push(t.returnStatement(createProjectedItemExpression(itemId, keyExp, rawKeyExp, keyField, valueField, seed)));
8261
+ return t.callExpression(t.memberExpression(t.cloneNode(sourceId), t.identifier("map")), [t.arrowFunctionExpression([itemId, indexId], t.blockStatement(body))]);
8262
+ }
8263
+ function createObjectProjection(sourceId, keyExp, rawKeyExp, keyField, valueField, forInfo, seed) {
8264
+ const resultId = t.identifier(`__wv_for_key_result_${seed}`);
8265
+ const keysId = t.identifier(`__wv_for_key_keys_${seed}`);
8266
+ const loopIndexId = t.identifier(`__wv_for_key_loop_index_${seed}`);
8267
+ const sourceKeyId = t.identifier(`__wv_for_key_source_key_${seed}`);
8268
+ const sourceItemId = t.identifier(`__wv_for_key_source_item_${seed}`);
8269
+ const body = [t.variableDeclaration("const", [t.variableDeclarator(sourceKeyId, t.memberExpression(keysId, loopIndexId, true))]), t.variableDeclaration("const", [t.variableDeclarator(sourceItemId, t.memberExpression(sourceId, sourceKeyId, true))])];
8270
+ if (forInfo.item && t.isValidIdentifier(forInfo.item) && forInfo.item !== sourceItemId.name) body.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier(forInfo.item), t.cloneNode(sourceItemId))]));
8271
+ if (forInfo.key && t.isValidIdentifier(forInfo.key) && forInfo.key !== sourceKeyId.name) body.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier(forInfo.key), t.cloneNode(sourceKeyId))]));
8272
+ if (forInfo.index && t.isValidIdentifier(forInfo.index)) {
8273
+ const source = forInfo.key ? loopIndexId : sourceKeyId;
8274
+ if (forInfo.index !== source.name) body.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier(forInfo.index), t.cloneNode(source))]));
8275
+ }
8276
+ body.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(resultId, sourceKeyId, true), createProjectedItemExpression(sourceItemId, keyExp, rawKeyExp, keyField, valueField, seed))));
8277
+ return t.callExpression(t.arrowFunctionExpression([], t.blockStatement([
8278
+ t.variableDeclaration("const", [t.variableDeclarator(resultId, t.objectExpression([]))]),
8279
+ t.variableDeclaration("const", [t.variableDeclarator(keysId, t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("keys")), [t.cloneNode(sourceId)]))]),
8280
+ t.forStatement(t.variableDeclaration("let", [t.variableDeclarator(loopIndexId, t.numericLiteral(0))]), t.binaryExpression("<", loopIndexId, t.memberExpression(keysId, t.identifier("length"))), t.updateExpression("++", loopIndexId), t.blockStatement(body)),
8281
+ t.returnStatement(resultId)
8282
+ ])), []);
8283
+ }
8284
+ function createForKeyProjectionExpression(sourceExp, keyExp, rawKeyExp, keyField, valueField, forInfo, seed) {
8285
+ const sourceId = t.identifier(`__wv_for_key_source_${seed}`);
8286
+ const objectCheck = t.logicalExpression("&&", t.binaryExpression("!=", t.cloneNode(sourceId), t.nullLiteral()), t.binaryExpression("===", t.unaryExpression("typeof", t.cloneNode(sourceId)), t.stringLiteral("object")));
8287
+ return t.callExpression(t.arrowFunctionExpression([], t.blockStatement([
8288
+ t.variableDeclaration("const", [t.variableDeclarator(sourceId, t.cloneNode(sourceExp, true))]),
8289
+ t.ifStatement(t.callExpression(t.memberExpression(t.identifier("Array"), t.identifier("isArray")), [t.cloneNode(sourceId)]), t.blockStatement([t.returnStatement(createArrayProjection(sourceId, keyExp, rawKeyExp, keyField, valueField, forInfo, seed))])),
8290
+ t.ifStatement(objectCheck, t.blockStatement([t.returnStatement(createObjectProjection(sourceId, keyExp, rawKeyExp, keyField, valueField, forInfo, seed))])),
8291
+ t.returnStatement(t.cloneNode(sourceId))
8292
+ ])), []);
8293
+ }
8294
+ //#endregion
8295
+ //#region src/plugins/vue/compiler/template/directives/forKey/index.ts
8296
+ const SIMPLE_IDENTIFIER_RE$1 = /^[A-Z_$][\w$]*$/i;
8297
+ const SIMPLE_MEMBER_PATH_RE = /^[A-Z_$][\w$]*(?:\.[A-Z_$][\w$]*)*$/i;
8298
+ const RUNTIME_BINDING_REF_RE = /^__wv_bind_\d+(?:\[[A-Z_$][\w$]*\])*$/i;
8299
+ function createSourceContext(context) {
8300
+ return {
8301
+ ...context,
8302
+ forStack: context.forStack.map((forInfo) => ({
8303
+ ...forInfo,
8304
+ itemAccess: void 0
8305
+ }))
8306
+ };
8307
+ }
8308
+ function hasKnownPrimitiveItems(exp) {
8309
+ if (t.isStringLiteral(exp) || t.isNumericLiteral(exp) || t.isBooleanLiteral(exp) || t.isNullLiteral(exp)) return true;
8310
+ if (!t.isArrayExpression(exp)) return false;
8311
+ return exp.elements.every((element) => {
8312
+ return element == null || t.isStringLiteral(element) || t.isNumericLiteral(element) || t.isBooleanLiteral(element) || t.isNullLiteral(element);
8313
+ });
8314
+ }
8315
+ function resolveItemAliasKeyField(trimmed, forInfo) {
8316
+ const aliasExp = forInfo.itemAliases?.[trimmed]?.trim();
8317
+ if (!aliasExp || !forInfo.item || !aliasExp.startsWith(`${forInfo.item}.`)) return null;
8318
+ const remainder = aliasExp.slice(forInfo.item.length + 1);
8319
+ return SIMPLE_MEMBER_PATH_RE.test(remainder) && !remainder.includes(".") ? remainder : null;
8320
+ }
8321
+ function resolveNativeForKeyValue(exp, forInfo, keyThisValue) {
8322
+ const trimmed = exp.trim();
8323
+ if (!forInfo) return null;
8324
+ if (forInfo.item && trimmed === forInfo.item || forInfo.key && trimmed === forInfo.key) return keyThisValue;
8325
+ if (forInfo.item && trimmed.startsWith(`${forInfo.item}.`)) {
8326
+ const remainder = trimmed.slice(forInfo.item.length + 1);
8327
+ if (SIMPLE_MEMBER_PATH_RE.test(remainder) && !remainder.includes(".")) return remainder;
8328
+ return null;
8329
+ }
8330
+ if (SIMPLE_IDENTIFIER_RE$1.test(trimmed)) {
8331
+ if (forInfo.index === trimmed) return trimmed;
8332
+ return resolveItemAliasKeyField(trimmed, forInfo);
8333
+ }
8334
+ return null;
8335
+ }
8336
+ function createForKeyProjection(node, forInfo, context) {
8337
+ const rawKeyExp = getBindDirectiveExpression(node).trim();
8338
+ if (!rawKeyExp || !forInfo.listExp || resolveNativeForKeyValue(rawKeyExp, forInfo, context.platform.keyThisValue)) return null;
8339
+ const listExp = forInfo.listExp?.trim() ?? "";
8340
+ const rawListExp = forInfo.rawListExp?.trim() || listExp;
8341
+ const projectionListExp = RUNTIME_BINDING_REF_RE.test(listExp) ? listExp : rawListExp;
8342
+ const sourceContext = createSourceContext(context);
8343
+ const projectionSourceAst = RUNTIME_BINDING_REF_RE.test(projectionListExp) ? normalizeJsExpressionWithContext(projectionListExp, sourceContext, {
8344
+ hint: "v-for :key 数据源",
8345
+ runtimePropAccess: "helper",
8346
+ unrefMemberAccess: true,
8347
+ preserveForItems: true
8348
+ }) : normalizeJsExpressionWithContext(projectionListExp, sourceContext, {
8349
+ hint: "v-for :key 数据源",
8350
+ preserveForItems: true
8351
+ });
8352
+ if (!projectionSourceAst) return null;
8353
+ if (hasKnownPrimitiveItems(projectionSourceAst)) return null;
8354
+ const keyExpAst = normalizeJsExpressionWithContext(rawKeyExp, sourceContext, {
8355
+ hint: "v-for :key",
8356
+ runtimePropAccess: "helper",
8357
+ unrefMemberAccess: true,
8358
+ preserveForItems: true
8359
+ });
8360
+ if (!keyExpAst) return null;
8361
+ const bindingSeed = context.classStyleBindings.filter((binding) => binding.type === "bind").length;
8362
+ const bindingName = `__wv_bind_${bindingSeed}`;
8363
+ const keyField = `__wv_key_${bindingSeed}`;
8364
+ const valueField = `__wv_value_${bindingSeed}`;
8365
+ const outerForStack = context.forStack.slice(0, -1).map((info) => ({ ...info }));
8366
+ context.classStyleBindings.push({
8367
+ name: bindingName,
8368
+ type: "bind",
8369
+ exp: `v-for :key ${rawKeyExp}`,
8370
+ expAst: createForKeyProjectionExpression(projectionSourceAst, keyExpAst, rawKeyExp, keyField, valueField, forInfo, bindingSeed),
8371
+ forStack: outerForStack
8372
+ });
8373
+ const indexAccess = outerForStack.map((info) => `[${info.index ?? "index"}]`).join("");
8374
+ return {
8375
+ keyAttr: context.platform.keyAttr(keyField),
8376
+ listExp: `${bindingName}${indexAccess}`,
8377
+ itemAccess: `${forInfo.item}.${valueField}`
8378
+ };
8379
+ }
8380
+ //#endregion
8159
8381
  //#region src/plugins/vue/compiler/template/directives/bind.ts
8160
8382
  function unwrapTsExpression(node) {
8161
8383
  if (node.type === "TSAsExpression" || node.type === "TSNonNullExpression" || node.type === "TSTypeAssertion") return unwrapTsExpression(node.expression);
@@ -8202,10 +8424,6 @@ function createInlineObjectLiteralAttr(argValue, rawExpValue, context) {
8202
8424
  if (context.mustacheInterpolation === "spaced") return `${argValue}="${renderMustache(expValue, context)}"`;
8203
8425
  return `${argValue}="{{ ${expValue} }}"`;
8204
8426
  }
8205
- const SIMPLE_IDENTIFIER_RE$1 = /^[A-Z_$][\w$]*$/i;
8206
- const SIMPLE_MEMBER_PATH_RE = /^[A-Z_$][\w$]*(?:\.[A-Z_$][\w$]*)*$/i;
8207
- const isSimpleIdentifier = (value) => SIMPLE_IDENTIFIER_RE$1.test(value);
8208
- const isSimpleMemberPath = (value) => SIMPLE_MEMBER_PATH_RE.test(value);
8209
8427
  const COMPONENT_NON_PROP_BINDINGS = /* @__PURE__ */ new Set([
8210
8428
  "class",
8211
8429
  "style",
@@ -8277,24 +8495,10 @@ function transformBindDirective(node, context, forInfo, options) {
8277
8495
  if (argValue === "key") {
8278
8496
  const expValue = normalizeWxmlExpressionWithContext(rawExpValue, context);
8279
8497
  const trimmed = expValue.trim();
8280
- const warnKeyFallback = (reason) => {
8281
- if (!forInfo) return;
8282
- context.warnings.push(`v-for :key "${trimmed}" ${reason},已降级为 ${context.platform.keyAttr(context.platform.keyThisValue)}。建议使用稳定的基础类型 key(例如 item.id)。`);
8283
- };
8284
- if (forInfo?.item && trimmed === forInfo.item) return context.platform.keyAttr(context.platform.keyThisValue);
8285
- if (forInfo?.key && trimmed === forInfo.key) return context.platform.keyAttr(context.platform.keyThisValue);
8286
- if (forInfo?.item && trimmed.startsWith(`${forInfo.item}.`)) {
8287
- const remainder = trimmed.slice(forInfo.item.length + 1);
8288
- if (isSimpleMemberPath(remainder)) {
8289
- const firstSegment = remainder.split(".")[0] || remainder;
8290
- return context.platform.keyAttr(firstSegment);
8291
- }
8292
- warnKeyFallback("不是简单的成员路径");
8293
- return context.platform.keyAttr(context.platform.keyThisValue);
8294
- }
8295
- if (isSimpleIdentifier(trimmed)) return context.platform.keyAttr(trimmed);
8498
+ const nativeKeyValue = resolveNativeForKeyValue(trimmed, forInfo, context.platform.keyThisValue);
8499
+ if (nativeKeyValue) return context.platform.keyAttr(nativeKeyValue);
8296
8500
  if (forInfo) {
8297
- warnKeyFallback("是复杂表达式");
8501
+ context.warnings.push(`v-for :key "${trimmed}" 无法生成运行时 key 投影,已降级为 ${context.platform.keyAttr(context.platform.keyThisValue)}。建议使用稳定的基础类型 key。`);
8298
8502
  return context.platform.keyAttr(context.platform.keyThisValue);
8299
8503
  }
8300
8504
  return context.platform.keyAttr(expValue);
@@ -8347,7 +8551,6 @@ function getElementType(element) {
8347
8551
  }
8348
8552
  return "";
8349
8553
  }
8350
- const QUOTE_RE$1 = /"/g;
8351
8554
  const CAMELIZE_RE = /-([a-z0-9])/gi;
8352
8555
  const IDENTIFIER_RE = /^[A-Z_$][\w$]*$/i;
8353
8556
  const NATIVE_MODEL_TAGS = /* @__PURE__ */ new Set([
@@ -8371,7 +8574,7 @@ function isNativeModelElement(element) {
8371
8574
  return NATIVE_MODEL_TAGS.has(element.tag);
8372
8575
  }
8373
8576
  function transformVModel(element, expValue, context) {
8374
- const escapedModel = expValue.replace(QUOTE_RE$1, "&quot;");
8577
+ const escapedModel = escapeWxmlAttribute(expValue);
8375
8578
  const bindModel = (event) => {
8376
8579
  return `${context.platform.eventBindingAttr(event)}="${WEVU_MODEL_HANDLER}" data-wv-model="${escapedModel}"`;
8377
8580
  };
@@ -8449,15 +8652,14 @@ function isScriptSetupHandlerBinding(value, context) {
8449
8652
  function shouldUseDetailPayload(options) {
8450
8653
  return options?.isComponent === true;
8451
8654
  }
8452
- const QUOTE_RE = /"/g;
8453
8655
  function buildInlineScopeAttrs(scopeBindings, context) {
8454
8656
  return scopeBindings.map((binding, index) => {
8455
- return `data-wv-s${index}="${renderMustache(binding.replace(QUOTE_RE, "&quot;"), context)}"`;
8657
+ return `data-wv-s${index}="${renderMustache(escapeWxmlAttribute(binding), context)}"`;
8456
8658
  });
8457
8659
  }
8458
8660
  function buildInlineIndexAttrs(indexBindings, context) {
8459
8661
  return indexBindings.map((binding, index) => {
8460
- return `data-wv-i${index}="${renderMustache(binding.replace(QUOTE_RE, "&quot;"), context)}"`;
8662
+ return `data-wv-i${index}="${renderMustache(escapeWxmlAttribute(binding), context)}"`;
8461
8663
  });
8462
8664
  }
8463
8665
  function resolveEventPrefix(modifiers) {
@@ -8522,7 +8724,7 @@ function transformOnDirective(node, context, options) {
8522
8724
  }
8523
8725
  if (isInlineExpression) return [
8524
8726
  detailAttr,
8525
- `data-wv-inline-${eventSuffix}="${inlineSource.replace(QUOTE_RE, "&quot;")}"`,
8727
+ `data-wv-inline-${eventSuffix}="${escapeWxmlAttribute(inlineSource)}"`,
8526
8728
  `${bindAttr}="${WEVU_INLINE_HANDLER}"`
8527
8729
  ].filter(Boolean).join(" ");
8528
8730
  return [detailAttr, `${bindAttr}="${expValue}"`].filter(Boolean).join(" ");
@@ -9619,6 +9821,22 @@ function resolveConditionExpression$1(rawExpValue, context, hint) {
9619
9821
  function resolveListExpression(rawExpValue, context, hint) {
9620
9822
  return (context.rewriteScopedSlot || shouldFallbackToRuntimeBinding(rawExpValue, context.templateSafeCallNames) ? registerRuntimeBindingExpression(rawExpValue, context, { hint }) : null) ?? normalizeWxmlExpressionWithContext(rawExpValue, context);
9621
9823
  }
9824
+ function requiresRuntimeForKeyProjection(node, context) {
9825
+ const forDirective = node.props.find((prop) => {
9826
+ return prop.type === NodeTypes.DIRECTIVE && prop.name === "for";
9827
+ });
9828
+ const keyDirective = node.props.find((prop) => {
9829
+ return prop.type === NodeTypes.DIRECTIVE && prop.name === "bind" && prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION && prop.arg.content === "key";
9830
+ });
9831
+ if (forDirective?.exp?.type === NodeTypes.SIMPLE_EXPRESSION && keyDirective) {
9832
+ const forInfo = parseForExpression(forDirective.exp.content);
9833
+ const rawKeyExp = getBindDirectiveExpression(keyDirective).trim();
9834
+ if (rawKeyExp && !resolveNativeForKeyValue(rawKeyExp, forInfo, context.platform.keyThisValue)) return true;
9835
+ }
9836
+ return node.children.some((child) => {
9837
+ return child.type === NodeTypes.ELEMENT && requiresRuntimeForKeyProjection(child, context);
9838
+ });
9839
+ }
9622
9840
  function transformIfElement(node, context, transformNode) {
9623
9841
  const renderTemplateMustache = (exp) => renderMustache(exp, context);
9624
9842
  const ifDirective = node.props.find((prop) => prop.type === NodeTypes.DIRECTIVE && (prop.name === "if" || prop.name === "else-if" || prop.name === "else"));
@@ -9659,15 +9877,18 @@ function transformForElement(node, context, transformNode) {
9659
9877
  }));
9660
9878
  }
9661
9879
  }
9662
- if (context.classStyleRuntime === "js" && !forInfo.index) forInfo.index = `__wv_index_${context.forIndexSeed++}`;
9880
+ if ((context.classStyleRuntime === "js" || requiresRuntimeForKeyProjection(node, context)) && !forInfo.index) forInfo.index = `__wv_index_${context.forIndexSeed++}`;
9881
+ const rawListExp = forInfo.listExp?.trim();
9663
9882
  const listExp = forInfo.listExp ? resolveListExpression(forInfo.listExp, context, "v-for 列表") : void 0;
9664
9883
  const listExpAst = forInfo.listExp ? normalizeJsExpressionWithContext(forInfo.listExp, context, { hint: "v-for 列表" }) : void 0;
9665
9884
  const scopedForInfo = listExp ? {
9666
9885
  ...forInfo,
9667
9886
  listExp,
9887
+ rawListExp,
9668
9888
  listExpAst: listExpAst ?? void 0
9669
9889
  } : {
9670
9890
  ...forInfo,
9891
+ rawListExp,
9671
9892
  listExpAst: listExpAst ?? void 0
9672
9893
  };
9673
9894
  const scopeNames = [
@@ -9682,34 +9903,64 @@ function transformForElement(node, context, transformNode) {
9682
9903
  ...node,
9683
9904
  props: otherProps
9684
9905
  };
9685
- const extraAttrs = listExp ? context.platform.forAttrs(listExp, renderTemplateMustache, forInfo.item, forInfo.index) : [];
9686
- if (elementWithoutFor.tag === "slot") {
9687
- const keyDirective = elementWithoutFor.props.find((prop) => {
9688
- return prop.type === NodeTypes.DIRECTIVE && prop.name === "bind" && prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION && prop.arg.content === "key";
9906
+ const keyDirective = otherProps.find((prop) => {
9907
+ return prop.type === NodeTypes.DIRECTIVE && prop.name === "bind" && prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION && prop.arg.content === "key";
9908
+ });
9909
+ const keyProjection = keyDirective ? createForKeyProjection(keyDirective, scopedForInfo, context) : null;
9910
+ if (keyProjection) {
9911
+ scopedForInfo.itemAccess = keyProjection.itemAccess;
9912
+ const projectionContext = {
9913
+ ...context,
9914
+ forStack: context.forStack.map((forInfo) => ({
9915
+ ...forInfo,
9916
+ itemAccess: void 0
9917
+ }))
9918
+ };
9919
+ const projectedListExpAst = normalizeJsExpressionWithContext(keyProjection.listExp, projectionContext, {
9920
+ hint: "v-for 投影列表",
9921
+ runtimePropAccess: "helper",
9922
+ unrefMemberAccess: true,
9923
+ preserveForItems: true
9689
9924
  });
9925
+ scopedForInfo.projectedListExp = keyProjection.listExp;
9926
+ scopedForInfo.projectedListExpAst = projectedListExpAst ?? scopedForInfo.projectedListExpAst;
9927
+ const currentForInfo = context.forStack[context.forStack.length - 1];
9928
+ if (currentForInfo) {
9929
+ currentForInfo.itemAccess = keyProjection.itemAccess;
9930
+ currentForInfo.projectedListExp = scopedForInfo.projectedListExp;
9931
+ currentForInfo.projectedListExpAst = scopedForInfo.projectedListExpAst;
9932
+ }
9933
+ }
9934
+ const renderElement = keyProjection ? {
9935
+ ...elementWithoutFor,
9936
+ props: otherProps.filter((prop) => prop !== keyDirective)
9937
+ } : elementWithoutFor;
9938
+ const templateListExp = keyProjection?.listExp ?? listExp;
9939
+ const extraAttrs = templateListExp ? context.platform.forAttrs(templateListExp, renderTemplateMustache, forInfo.item, forInfo.index) : [];
9940
+ if (keyProjection) extraAttrs.push(keyProjection.keyAttr);
9941
+ if (renderElement.tag === "slot") {
9942
+ const slotKeyDirective = keyProjection ? void 0 : keyDirective;
9690
9943
  const content = transformSlotElement({
9691
- ...elementWithoutFor,
9692
- props: elementWithoutFor.props.filter((prop) => {
9693
- return prop !== keyDirective;
9694
- })
9944
+ ...renderElement,
9945
+ props: renderElement.props.filter((prop) => prop !== slotKeyDirective)
9695
9946
  }, context, transformNode);
9696
- const keyAttr = keyDirective ? transformBindDirective(keyDirective, context, forInfo) : null;
9947
+ const keyAttr = slotKeyDirective ? transformBindDirective(slotKeyDirective, context, scopedForInfo) : null;
9697
9948
  if (keyAttr) extraAttrs.push(keyAttr);
9698
9949
  const attrString = extraAttrs.length ? ` ${extraAttrs.join(" ")}` : "";
9699
9950
  return attrString ? `<block${attrString}>${content}</block>` : content;
9700
9951
  }
9701
- const resolvedTag = resolveTemplateTagName(elementWithoutFor.tag, context);
9702
- if (shouldTransformAsComponentWithSlots(elementWithoutFor, context, resolvedTag)) return transformComponentWithSlots(elementWithoutFor, context, transformNode, {
9952
+ const resolvedTag = resolveTemplateTagName(renderElement.tag, context);
9953
+ if (shouldTransformAsComponentWithSlots(renderElement, context, resolvedTag)) return transformComponentWithSlots(renderElement, context, transformNode, {
9703
9954
  extraAttrs,
9704
- forInfo
9955
+ forInfo: scopedForInfo
9705
9956
  });
9706
- const { attrs, vTextExp } = collectElementAttributes(elementWithoutFor, context, {
9707
- forInfo,
9957
+ const { attrs, vTextExp } = collectElementAttributes(renderElement, context, {
9958
+ forInfo: scopedForInfo,
9708
9959
  extraAttrs,
9709
9960
  resolvedTag
9710
9961
  });
9711
9962
  let children = "";
9712
- if (elementWithoutFor.children.length > 0) children = elementWithoutFor.children.map((child) => transformNode(child, context)).join("");
9963
+ if (renderElement.children.length > 0) children = renderElement.children.map((child) => transformNode(child, context)).join("");
9713
9964
  if (vTextExp !== void 0) children = renderMustache(vTextExp, context);
9714
9965
  const tag = resolvedTag;
9715
9966
  const attrString = attrs.length ? ` ${attrs.join(" ")}` : "";
@@ -9808,13 +10059,6 @@ function transformElement(node, context, transformNode) {
9808
10059
  }
9809
10060
  //#endregion
9810
10061
  //#region src/plugins/vue/compiler/template/nodes.ts
9811
- const AMP_RE = /&/g;
9812
- const LT_RE = /</g;
9813
- const GT_RE = />/g;
9814
- function escapeWxmlText(value) {
9815
- if (!value) return "";
9816
- return value.replace(AMP_RE, "&amp;").replace(LT_RE, "&lt;").replace(GT_RE, "&gt;");
9817
- }
9818
10062
  function transformText(node, _context) {
9819
10063
  return escapeWxmlText(node.content);
9820
10064
  }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@wevu/compiler",
3
3
  "type": "module",
4
- "version": "6.22.0",
4
+ "version": "6.24.0",
5
5
  "description": "wevu 编译器基础包,面向小程序模板的编译与转换",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
8
- "homepage": "https://vite.icebreaker.top/wevu/",
8
+ "homepage": "https://vite.weapp.dev/wevu/",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/weapp-vite/weapp-vite.git",
@@ -43,20 +43,20 @@
43
43
  "dependencies": {
44
44
  "@jridgewell/remapping": "^2.3.5",
45
45
  "@vue/babel-plugin-jsx": "3.0.0",
46
- "@vue/compiler-core": "^3.5.41",
47
- "@vue/compiler-dom": "^3.5.41",
46
+ "@vue/compiler-core": "^3.5.42",
47
+ "@vue/compiler-dom": "^3.5.42",
48
48
  "comment-json": "^5.0.0",
49
49
  "lru-cache": "^11.5.2",
50
- "magic-string": "^1.2.2",
50
+ "magic-string": "^1.2.3",
51
51
  "merge": "^2.1.1",
52
52
  "pathe": "^2.0.3",
53
53
  "postcss": "^8.5.26",
54
54
  "postcss-selector-parser": "^7.1.5",
55
- "vue": "^3.5.41",
56
- "@weapp-core/constants": "0.2.0",
57
- "@weapp-vite/ast": "6.22.0",
58
- "@weapp-core/shared": "3.1.1",
59
- "rolldown-require": "2.0.26"
55
+ "vue": "^3.5.42",
56
+ "@weapp-core/shared": "3.2.0",
57
+ "rolldown-require": "2.0.27",
58
+ "@weapp-vite/ast": "6.24.0",
59
+ "@weapp-core/constants": "0.2.0"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public",