@vue/compiler-sfc 3.5.39 → 3.5.41

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,5 +1,5 @@
1
1
  /**
2
- * @vue/compiler-sfc v3.5.39
2
+ * @vue/compiler-sfc v3.5.41
3
3
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
4
4
  * @license MIT
5
5
  **/
@@ -1703,17 +1703,34 @@ function createCache(max = 500) {
1703
1703
  return new LRUCache({ max });
1704
1704
  }
1705
1705
 
1706
- function isImportUsed(local, sfc) {
1707
- return resolveTemplateUsedIdentifiers(sfc).has(local);
1706
+ function resolveTemplateAST(inAST, options) {
1707
+ if (!(inAST == null ? void 0 : inAST.transformed)) {
1708
+ return inAST;
1709
+ }
1710
+ const { compiler = CompilerDOM__namespace, compilerOptions, ssr, onError } = options;
1711
+ const newAST = (ssr ? CompilerDOM__namespace : compiler).parse(inAST.source, {
1712
+ prefixIdentifiers: true,
1713
+ ...compilerOptions,
1714
+ parseMode: "sfc",
1715
+ onError
1716
+ });
1717
+ const template = newAST.children.find(
1718
+ (node) => node.type === 1 && node.tag === "template"
1719
+ );
1720
+ return compilerCore.createRoot(template.children, inAST.source);
1721
+ }
1722
+
1723
+ function isImportUsed(local, sfc, options) {
1724
+ return resolveTemplateUsedIdentifiers(sfc, options).has(local);
1708
1725
  }
1709
1726
  const templateAnalysisCache = createCache();
1710
- function resolveTemplateVModelIdentifiers(sfc) {
1711
- return resolveTemplateAnalysisResult(sfc, false).vModelIds;
1727
+ function resolveTemplateVModelIdentifiers(sfc, options) {
1728
+ return resolveTemplateAnalysisResult(sfc, false, options).vModelIds;
1712
1729
  }
1713
- function resolveTemplateUsedIdentifiers(sfc) {
1714
- return resolveTemplateAnalysisResult(sfc).usedIds;
1730
+ function resolveTemplateUsedIdentifiers(sfc, options) {
1731
+ return resolveTemplateAnalysisResult(sfc, true, options).usedIds;
1715
1732
  }
1716
- function resolveTemplateAnalysisResult(sfc, collectUsedIds = true) {
1733
+ function resolveTemplateAnalysisResult(sfc, collectUsedIds = true, options) {
1717
1734
  const { content, ast } = sfc.template;
1718
1735
  const cached = templateAnalysisCache.get(content);
1719
1736
  if (cached && (!collectUsedIds || cached.usedIds)) {
@@ -1721,7 +1738,15 @@ function resolveTemplateAnalysisResult(sfc, collectUsedIds = true) {
1721
1738
  }
1722
1739
  const ids = collectUsedIds ? /* @__PURE__ */ new Set() : void 0;
1723
1740
  const vModelIds = /* @__PURE__ */ new Set();
1724
- ast.children.forEach(walk);
1741
+ const root = resolveTemplateAST(ast, {
1742
+ compiler: options == null ? void 0 : options.compiler,
1743
+ compilerOptions: options == null ? void 0 : options.compilerOptions,
1744
+ ssr: options == null ? void 0 : options.ssr,
1745
+ // ignore errors since they were already reported by the SFC parser
1746
+ onError: () => {
1747
+ }
1748
+ });
1749
+ root.children.forEach(walk);
1725
1750
  function walk(node) {
1726
1751
  var _a;
1727
1752
  switch (node.type) {
@@ -4299,18 +4324,12 @@ function doCompileTemplate({
4299
4324
  if (compiler !== defaultCompiler) {
4300
4325
  inAST = void 0;
4301
4326
  }
4302
- if (inAST == null ? void 0 : inAST.transformed) {
4303
- const newAST = (ssr ? CompilerDOM__namespace : compiler).parse(inAST.source, {
4304
- prefixIdentifiers: true,
4305
- ...compilerOptions,
4306
- parseMode: "sfc",
4307
- onError: (e) => errors.push(e)
4308
- });
4309
- const template = newAST.children.find(
4310
- (node) => node.type === 1 && node.tag === "template"
4311
- );
4312
- inAST = compilerCore.createRoot(template.children, inAST.source);
4313
- }
4327
+ inAST = resolveTemplateAST(inAST, {
4328
+ compiler,
4329
+ compilerOptions,
4330
+ ssr,
4331
+ onError: (e) => errors.push(e)
4332
+ });
4314
4333
  let { code, ast, preamble, map } = compiler.compile(inAST || source, {
4315
4334
  mode: "module",
4316
4335
  prefixIdentifiers: true,
@@ -20492,6 +20511,17 @@ const closePattern = /\\}/g;
20492
20511
  const commaPattern = /\\,/g;
20493
20512
  const periodPattern = /\\\./g;
20494
20513
  const EXPANSION_MAX = 100_000;
20514
+ // `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
20515
+ // input like `'{a,b}'.repeat(1500)` stays under that count - its output is
20516
+ // truncated to 100k results - while making every result ~1500 characters
20517
+ // long. The result set, and the intermediate arrays built while combining
20518
+ // brace sets, then grow large enough to exhaust memory and crash the process
20519
+ // (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
20520
+ // characters the accumulator may hold at any point, so memory stays flat no
20521
+ // matter how many brace groups are chained. The limit sits well above any
20522
+ // realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
20523
+ // characters) so legitimate input is unaffected.
20524
+ const EXPANSION_MAX_LENGTH = 4_000_000;
20495
20525
  function numeric(str) {
20496
20526
  return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
20497
20527
  }
@@ -20540,7 +20570,7 @@ function expand(str, options = {}) {
20540
20570
  if (!str) {
20541
20571
  return [];
20542
20572
  }
20543
- const { max = EXPANSION_MAX } = options;
20573
+ const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
20544
20574
  // I don't know why Bash 4.3 does this, but it does.
20545
20575
  // Anything starting with {} will have the first two bytes preserved
20546
20576
  // but *only* at the top level, so {},a}b will not expand to anything,
@@ -20550,7 +20580,7 @@ function expand(str, options = {}) {
20550
20580
  if (str.slice(0, 2) === '{}') {
20551
20581
  str = '\\{\\}' + str.slice(2);
20552
20582
  }
20553
- return expand_(escapeBraces(str), max, true).map(unescapeBraces);
20583
+ return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
20554
20584
  }
20555
20585
  function embrace(str) {
20556
20586
  return '{' + str + '}';
@@ -20564,22 +20594,117 @@ function lte(i, y) {
20564
20594
  function gte(i, y) {
20565
20595
  return i >= y;
20566
20596
  }
20567
- function expand_(str, max, isTop) {
20568
- /** @type {string[]} */
20569
- const expansions = [];
20570
- const m = balanced('{', '}', str);
20571
- if (!m)
20572
- return [str];
20573
- // no need to expand pre, since it is guaranteed to be free of brace-sets
20574
- const pre = m.pre;
20575
- const post = m.post.length ? expand_(m.post, max, false) : [''];
20576
- if (/\$$/.test(m.pre)) {
20577
- for (let k = 0; k < post.length && k < max; k++) {
20578
- const expansion = pre + '{' + m.body + '}' + post[k];
20579
- expansions.push(expansion);
20597
+ // Build `{ acc[a] + pre + values[v] }` for every combination, capping the
20598
+ // number of results at `max` and the total number of characters at `maxLength`.
20599
+ // This is the one place output grows, so bounding it here keeps the single
20600
+ // accumulator - and therefore memory - flat regardless of how many brace groups
20601
+ // are combined (CVE-2026-14257).
20602
+ function combine(acc, pre, values, max, maxLength, dropEmpties) {
20603
+ const out = [];
20604
+ let length = 0;
20605
+ for (let a = 0; a < acc.length; a++) {
20606
+ for (let v = 0; v < values.length; v++) {
20607
+ if (out.length >= max)
20608
+ return out;
20609
+ const expansion = acc[a] + pre + values[v];
20610
+ // Bash drops empty results at the top level. Skip them before they count
20611
+ // against `max`, so `max` bounds the number of *kept* results.
20612
+ if (dropEmpties && !expansion)
20613
+ continue;
20614
+ if (length + expansion.length > maxLength)
20615
+ return out;
20616
+ out.push(expansion);
20617
+ length += expansion.length;
20580
20618
  }
20581
20619
  }
20582
- else {
20620
+ return out;
20621
+ }
20622
+ // The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
20623
+ // sequence body.
20624
+ function expandSequence(body, isAlphaSequence, max, maxLength) {
20625
+ const n = body.split(/\.\./);
20626
+ const N = [];
20627
+ // A sequence body always splits into two or three parts, but the compiler
20628
+ // can't know that.
20629
+ /* c8 ignore start */
20630
+ if (n[0] === undefined || n[1] === undefined) {
20631
+ return N;
20632
+ }
20633
+ /* c8 ignore stop */
20634
+ const x = numeric(n[0]);
20635
+ const y = numeric(n[1]);
20636
+ const width = Math.max(n[0].length, n[1].length);
20637
+ let incr = n.length === 3 && n[2] !== undefined ?
20638
+ Math.max(Math.abs(numeric(n[2])), 1)
20639
+ : 1;
20640
+ let test = lte;
20641
+ const reverse = y < x;
20642
+ if (reverse) {
20643
+ incr *= -1;
20644
+ test = gte;
20645
+ }
20646
+ const pad = n.some(isPadded);
20647
+ let length = 0;
20648
+ for (let i = x; test(i, y) && N.length < max; i += incr) {
20649
+ let c;
20650
+ if (isAlphaSequence) {
20651
+ c = String.fromCharCode(i);
20652
+ if (c === '\\') {
20653
+ c = '';
20654
+ }
20655
+ }
20656
+ else {
20657
+ c = String(i);
20658
+ if (pad) {
20659
+ const need = width - c.length;
20660
+ if (need > 0) {
20661
+ const z = new Array(need + 1).join('0');
20662
+ if (i < 0) {
20663
+ c = '-' + z + c.slice(1);
20664
+ }
20665
+ else {
20666
+ c = z + c;
20667
+ }
20668
+ }
20669
+ }
20670
+ }
20671
+ if (length + c.length > maxLength)
20672
+ break;
20673
+ N.push(c);
20674
+ length += c.length;
20675
+ }
20676
+ return N;
20677
+ }
20678
+ function expand_(str, max, maxLength, isTop) {
20679
+ // Consume the string's top-level brace groups left to right, threading a
20680
+ // running set of combined prefixes (`acc`). Expanding the tail iteratively -
20681
+ // rather than recursing on `m.post` once per group - keeps the native stack
20682
+ // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
20683
+ // longer overflow the stack, and leaves a single accumulator whose size
20684
+ // `maxLength` bounds directly (CVE-2026-14257).
20685
+ let acc = [''];
20686
+ // Bash drops empty results, but only when the *first* top-level group is a
20687
+ // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
20688
+ // is on the final strings, so it is applied to whichever `combine` produces
20689
+ // them (the one with no brace set left in the tail).
20690
+ let dropEmpties = false;
20691
+ let firstGroup = true;
20692
+ for (;;) {
20693
+ const m = balanced('{', '}', str);
20694
+ // No brace set left: the rest of the string is literal.
20695
+ if (!m) {
20696
+ return combine(acc, str, [''], max, maxLength, dropEmpties);
20697
+ }
20698
+ // no need to expand pre, since it is guaranteed to be free of brace-sets
20699
+ const pre = m.pre;
20700
+ if (/\$$/.test(pre)) {
20701
+ acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
20702
+ firstGroup = false;
20703
+ if (!m.post.length)
20704
+ break;
20705
+ str = m.post;
20706
+ continue;
20707
+ }
20583
20708
  const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
20584
20709
  const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
20585
20710
  const isSequence = isNumericSequence || isAlphaSequence;
@@ -20588,87 +20713,69 @@ function expand_(str, max, isTop) {
20588
20713
  // {a},b}
20589
20714
  if (m.post.match(/,(?!,).*\}/)) {
20590
20715
  str = m.pre + '{' + m.body + escClose + m.post;
20591
- return expand_(str, max, true);
20716
+ isTop = true;
20717
+ continue;
20592
20718
  }
20593
- return [str];
20719
+ // Nothing here expands, so the whole remaining string is literal.
20720
+ return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
20594
20721
  }
20595
- let n;
20722
+ if (firstGroup) {
20723
+ dropEmpties = isTop && !isSequence;
20724
+ firstGroup = false;
20725
+ }
20726
+ let values;
20596
20727
  if (isSequence) {
20597
- n = m.body.split(/\.\./);
20728
+ values = expandSequence(m.body, isAlphaSequence, max, maxLength);
20598
20729
  }
20599
20730
  else {
20600
- n = parseCommaParts(m.body);
20731
+ let n = parseCommaParts(m.body);
20601
20732
  if (n.length === 1 && n[0] !== undefined) {
20602
20733
  // x{{a,b}}y ==> x{a}y x{b}y
20603
- n = expand_(n[0], max, false).map(embrace);
20734
+ n = expand_(n[0], max, maxLength, false).map(embrace);
20604
20735
  //XXX is this necessary? Can't seem to hit it in tests.
20605
20736
  /* c8 ignore start */
20606
20737
  if (n.length === 1) {
20607
- return post.map(p => m.pre + n[0] + p);
20738
+ acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
20739
+ if (!m.post.length)
20740
+ break;
20741
+ str = m.post;
20742
+ continue;
20608
20743
  }
20609
20744
  /* c8 ignore stop */
20610
20745
  }
20611
- }
20612
- // at this point, n is the parts, and we know it's not a comma set
20613
- // with a single entry.
20614
- let N;
20615
- if (isSequence && n[0] !== undefined && n[1] !== undefined) {
20616
- const x = numeric(n[0]);
20617
- const y = numeric(n[1]);
20618
- const width = Math.max(n[0].length, n[1].length);
20619
- let incr = n.length === 3 && n[2] !== undefined ?
20620
- Math.max(Math.abs(numeric(n[2])), 1)
20621
- : 1;
20622
- let test = lte;
20623
- const reverse = y < x;
20624
- if (reverse) {
20625
- incr *= -1;
20626
- test = gte;
20627
- }
20628
- const pad = n.some(isPadded);
20629
- N = [];
20630
- for (let i = x; test(i, y); i += incr) {
20631
- let c;
20632
- if (isAlphaSequence) {
20633
- c = String.fromCharCode(i);
20634
- if (c === '\\') {
20635
- c = '';
20636
- }
20746
+ // Values that `combine` is going to drop as empty produce no result, so
20747
+ // they must not count against `max` - otherwise `{a,,b}` with `max: 2`
20748
+ // would stop at `['a', '']` and yield one result instead of two. Skipping
20749
+ // them outright keeps `values` bounded while leaving `max` a bound on
20750
+ // *kept* results.
20751
+ let dropsEmpties = dropEmpties && !m.post.length && !pre;
20752
+ for (let d = 0; dropsEmpties && d < acc.length; d++) {
20753
+ if (acc[d]) {
20754
+ dropsEmpties = false;
20637
20755
  }
20638
- else {
20639
- c = String(i);
20640
- if (pad) {
20641
- const need = width - c.length;
20642
- if (need > 0) {
20643
- const z = new Array(need + 1).join('0');
20644
- if (i < 0) {
20645
- c = '-' + z + c.slice(1);
20646
- }
20647
- else {
20648
- c = z + c;
20649
- }
20650
- }
20651
- }
20652
- }
20653
- N.push(c);
20654
20756
  }
20655
- }
20656
- else {
20657
- N = [];
20658
- for (let j = 0; j < n.length; j++) {
20659
- N.push.apply(N, expand_(n[j], max, false));
20660
- }
20661
- }
20662
- for (let j = 0; j < N.length; j++) {
20663
- for (let k = 0; k < post.length && expansions.length < max; k++) {
20664
- const expansion = pre + N[j] + post[k];
20665
- if (!isTop || isSequence || expansion) {
20666
- expansions.push(expansion);
20757
+ values = [];
20758
+ let valuesLength = 0;
20759
+ outer: for (let j = 0; j < n.length; j++) {
20760
+ const expanded = expand_(n[j], max, maxLength, false);
20761
+ for (let k = 0; k < expanded.length; k++) {
20762
+ const v = expanded[k];
20763
+ if (dropsEmpties && !v)
20764
+ continue;
20765
+ if (values.length >= max || valuesLength + v.length > maxLength) {
20766
+ break outer;
20767
+ }
20768
+ values.push(v);
20769
+ valuesLength += v.length;
20667
20770
  }
20668
20771
  }
20669
20772
  }
20773
+ acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
20774
+ if (!m.post.length)
20775
+ break;
20776
+ str = m.post;
20670
20777
  }
20671
- return expansions;
20778
+ return acc;
20672
20779
  }
20673
20780
 
20674
20781
  const MAX_PATTERN_LENGTH = 1024 * 64;
@@ -25337,7 +25444,7 @@ Upgrade your vite or vue-loader version for compatibility with the latest experi
25337
25444
  function registerUserImport(source2, local, imported, isType, isFromSetup, needTemplateUsageCheck) {
25338
25445
  let isUsedInTemplate = needTemplateUsageCheck;
25339
25446
  if (needTemplateUsageCheck && ctx.isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
25340
- isUsedInTemplate = isImportUsed(local, sfc);
25447
+ isUsedInTemplate = isImportUsed(local, sfc, options.templateOptions);
25341
25448
  }
25342
25449
  ctx.userImports[local] = {
25343
25450
  isType,
@@ -25684,7 +25791,10 @@ const ${normalScriptDefaultVar} = ${defaultSpecifier.local.name}
25684
25791
  ctx.bindingMetadata[key] = setupBindings[key];
25685
25792
  }
25686
25793
  if (sfc.template && !sfc.template.src && sfc.template.ast) {
25687
- const vModelIds = resolveTemplateVModelIdentifiers(sfc);
25794
+ const vModelIds = resolveTemplateVModelIdentifiers(
25795
+ sfc,
25796
+ options.templateOptions
25797
+ );
25688
25798
  if (vModelIds.size) {
25689
25799
  const toDemote = /* @__PURE__ */ new Set();
25690
25800
  for (const id of vModelIds) {
@@ -26151,7 +26261,7 @@ function mergeSourceMaps(scriptMap, templateMap, templateLineOffset) {
26151
26261
  return generator.toJSON();
26152
26262
  }
26153
26263
 
26154
- const version = "3.5.39";
26264
+ const version = "3.5.41";
26155
26265
  const parseCache = parseCache$1;
26156
26266
  const errorMessages = {
26157
26267
  ...CompilerDOM.errorMessages,