@svelte-vitals/core 0.55.2 → 0.55.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/internal.js CHANGED
@@ -1,37 +1,4 @@
1
- import { $ as performanceMinifyDisabled, $n as applyRuleSeverities, $t as correctnessOrphanLifecycle, A as a11yAbbrTitle, An as seoImageAlt, Ar as defaultProject, At as walkEstree, B as a11yUseList, Bn as seoSitemapInRobots, Bt as parseSvelte, C as a11yDisallowedElement, Cn as foldOccurrences, Cr as effectiveSeverity, Ct as collectSvelteLifecycleImports, D as a11yDeprecatedElement, Dn as stripTextDirective, Dr as isPenalized, Dt as scopeIntroducedNames, E as a11yDeprecatedAttr, En as splitTokens, Er as docsUrlFor, Et as rootObjectName, F as isInteractiveContainer, Fn as seoJsonLdPlaceholder, Ft as attrTextOf, G as a11yInvalidAriaValue, Gn as seoIndexability, Gt as securitySharedStateImport, H as a11yAccessibleName, Hn as seoOgUrl, Ht as valueFromNodes, I as isInteractiveElement, In as seoJsonLdDateFormat, It as attrValue, J as a11yInvalidRole, Jn as resolveRuleOptions, Jt as securityJavascriptUrl, K as a11yRequiredAriaProps, Kn as performancePreconnect, Kt as securityServerModuleState, L as literalTabindexValue, Ln as seoJsonLdRelativeUrl, Lt as attrValueOf, M as a11yNoAutofocus, Mn as seoDescriptionLength, Mt as walkScoped, N as a11yNoAccesskey, Nn as seoTitleLength, Nt as CHILD_NODE_KEYS, O as HTML_SPEC, On as resolveRole, Ot as unwrapExport, P as a11yPositiveTabindex, Pn as seoJsonLdRequiredProps, Pt as attrText, Q as performanceLoadWaterfall, Qn as applyOverrides, Qt as correctnessBasePathNavigation, R as a11yRequireDatetime, Rn as seoJsonLdDeprecatedType, Rt as findAttr, S as a11yPermittedContents, Sn as decodeFragmentId, Sr as classify, St as collectSuppressions, T as a11yDisallowedAriaProps, Tn as resolveLandmark, Tr as summarize, Tt as parseModuleProgram, U as a11yAriaHiddenFocus, Un as seoOgDescription, Ut as architecturePropCount, V as a11yLabelHasControl, Vn as seoViewport, Vt as textFromNodes, W as a11yInteractiveNesting, Wn as seoTwitterCard, Wt as architectureComponentSize, X as performanceStateRaw, Xn as validateRuleSetting, Xt as correctnessInstanceBrowserGlobal, Y as performanceIframeLoading, Yn as shouldSkipRangeCheck, Yt as securityRawHtml, Z as performanceSequentialAwaits, Zn as SEARCH_RESULT_RULES, Zt as correctnessServerBrowserGlobal, _ as a11yIdDuplication, _n as ANCESTRY_DEPENDENT_TAGS, _r as seoOgTitle, _t as collectBrowserGlobalRefs, a as formatJsonReport, an as correctnessPropMutation, ar as withIndexableOff, at as architectureReservedDirectoryNames, b as a11yDoctype, bn as NAMING_ATTRS, br as seoDescriptionPresence, bt as collectNamedImportAliases, c as formatGithubReport, cn as correctnessEffectAsDerived, cr as performanceFontPreloadCrossorigin, ct as architecturePrivateScopeImport, d as ruleMetaById, dn as seoSsrDisabled, dr as performanceImageLoadingHint, dt as parseInMemoryExports, en as correctnessOrphanEffect, er as compileOverrides, et as performanceNamespaceImport, f as severityToSarifLevel, fn as routeNeverSsr, fr as performanceImageDimensions, ft as parseKitModuleFacts, g as a11yNoMissingIdRef, gn as seoSingleH1, gr as seoJsonLd, gt as addBoundNames, h as a11yUnverifiedIdRef, hn as seoDuplicateTitle, hr as seoRobotsTxt, ht as WALK_IGNORED_KEYS, i as buildJsonReport, in as correctnessStalePropDerivation, ir as withFailedRulesOff, it as architectureReservedNamePlacement, j as a11yNoDuplicateDt, jn as seoCharset, jt as walkEvalScope, k as a11yPatternTitle, kn as seoHreflang, kr as defaultConfig, kt as unwrapTs, l as SEVERITY_RANK, ln as correctnessEachIndexKey, lr as performancePreloadMissingAs, lt as DEFAULT_KIT_ALIASES, m as explainRule, mn as seoDuplicateDescription, mr as seoSitemapXml, mt as isRootRelativePath, n as mdEscape, nn as correctnessCheckableBindValue, nr as selectRules, nt as architectureDocLinkTarget, o as computeHealth, on as correctnessUnmutatedState, or as performanceRenderBlockingScript, ot as architectureDirectoryNaming, p as allRules, pn as seoHeadingLevelSkip, pr as seoHtmlLang, pt as resolveRepoLocalPath, q as a11yUnknownAriaAttribute, qn as isPlainObject, qt as securityHandlerStateWrite, r as terminalSafe, rn as correctnessNonreactiveBuiltinState, rr as settingSeverity, rt as architectureRouteComponentImport, s as computeScore, sn as correctnessEffectAsOnMount, sr as performanceLcpImage, st as architectureUnitEntryFile, t as formatMarkdownReport, tn as correctnessAutoplayMuted, tr as formatFailedRuleWarning, tt as performanceHeavyImport, u as messageText, un as correctnessEachKey, ur as performanceResponsiveImage, ut as collectTopLevelBindings, v as a11yTopLevelLandmark, vn as ASIDE_DEMOTING_TAGS, vr as seoOgImage, vt as collectBrowserGuardImports, w as a11yDeprecatedAria, wn as isTopFragment, wr as hasFailureAtOrAbove, wt as matchLifecycleCall, x as a11yRequiredElement, xn as SECTIONING_TAGS, xr as seoTitlePresence, xt as collectProgramBindings, y as a11yDuplicateLandmark, yn as IDREF_ATTRS, yr as seoCanonicalUrl, yt as collectDerivedGuardBindings, z as a11yPlaceholderLabelOption, zn as seoJsonLdValidity, zt as lineOf } from "./markdown-DgHBAvYl.js";
2
- //#region src/head.ts
3
- const JS_MIME_TYPES = /* @__PURE__ */ new Set([
4
- "application/ecmascript",
5
- "application/javascript",
6
- "application/x-ecmascript",
7
- "application/x-javascript",
8
- "text/ecmascript",
9
- "text/javascript",
10
- "text/javascript1.0",
11
- "text/javascript1.1",
12
- "text/javascript1.2",
13
- "text/javascript1.3",
14
- "text/javascript1.4",
15
- "text/javascript1.5",
16
- "text/jscript",
17
- "text/livescript",
18
- "text/x-ecmascript",
19
- "text/x-javascript"
20
- ]);
21
- /** HTML strips only ASCII whitespace from a `type` before matching — `trim()` would also strip
22
- * U+00A0 etc. and misread a data block as a classic script. */
23
- const ASCII_WHITESPACE_EDGES = /^[\t\n\f\r ]+|[\t\n\f\r ]+$/g;
24
- /**
25
- * Whether a `type` attribute (undefined = absent) makes a `<script>` a classic,
26
- * render-blocking-capable script. Per the HTML spec, absent and the literal empty string are
27
- * classic; any other value is ASCII-whitespace-stripped and must match a JavaScript MIME type —
28
- * so a whitespace-only `type` is a data block, not a classic script.
29
- */
30
- function isClassicScriptType(type) {
31
- if (type === void 0 || type === "") return true;
32
- return JS_MIME_TYPES.has(type.replace(ASCII_WHITESPACE_EDGES, "").toLowerCase());
33
- }
34
- //#endregion
1
+ import { $ as performanceLoadWaterfall, $n as shouldSkipRangeCheck, $t as securityJavascriptUrl, A as a11yPatternTitle, An as splitTokens, Ar as seoTitlePresence, At as parseModuleProgram, B as a11yPlaceholderLabelOption, Bn as seoJsonLdDateFormat, Bt as attrTextOf, C as a11yPermittedContents, Cn as IDREF_ATTRS, Cr as seoCanonicalUrl, Ct as collectBrowserGuardImports, D as a11yDeprecatedAttr, Dn as foldOccurrences, Dr as layoutNeverRenders, Dt as collectSuppressions, E as a11yDisallowedAriaProps, En as decodeFragmentId, Er as seoDescriptionPresence, Et as collectProgramBindings, F as a11yPositiveTabindex, Fn as seoCharset, Fr as docsUrlFor, Ft as walkEstree, G as a11yInteractiveNesting, Gn as seoViewport, Gt as parseSvelte, H as a11yLabelHasControl, Hn as seoJsonLdDeprecatedType, Ht as attrValueOf, I as isInteractiveContainer, In as seoDescriptionLength, Ir as isPenalized, It as walkEvalScope, J as a11yUnknownAriaAttribute, Jn as seoTwitterCard, Jt as architecturePropCount, K as a11yInvalidAriaValue, Kn as seoOgUrl, Kt as textFromNodes, L as isInteractiveElement, Ln as seoTitleLength, Lt as walkScoped, M as a11yNoDuplicateDt, Mn as resolveRole, Mr as effectiveSeverity, Mt as scopeIntroducedNames, N as a11yNoAutofocus, Nn as seoHreflang, Nr as hasFailureAtOrAbove, Nt as unwrapExport, O as a11yDeprecatedElement, On as isTopFragment, Or as pageRendersOnlyWhenBuilding, Ot as collectSvelteLifecycleImports, P as a11yNoAccesskey, Pn as seoImageAlt, Pr as summarize, Pt as unwrapTs, Q as performanceSequentialAwaits, Qn as resolveRuleOptions, Qt as securityHandlerStateWrite, R as literalTabindexValue, Rn as seoJsonLdRequiredProps, Rr as defaultConfig, Rt as CHILD_NODE_KEYS, S as a11yRequiredElement, Sn as ASIDE_DEMOTING_TAGS, Sr as seoOgImage, St as collectBrowserGlobalRefs, T as a11yDeprecatedAria, Tn as SECTIONING_TAGS, Tr as isClassicScriptType, Tt as collectNamedImportAliases, U as a11yAccessibleName, Un as seoJsonLdValidity, Ut as findAttr, V as a11yUseList, Vn as seoJsonLdRelativeUrl, Vt as attrValue, W as a11yAriaHiddenFocus, Wn as seoSitemapInRobots, Wt as lineOf, X as performanceIframeLoading, Xn as performancePreconnect, Xt as securitySharedStateImport, Y as a11yInvalidRole, Yn as seoIndexability, Yt as architectureComponentSize, Z as performanceStateRaw, Zn as isPlainObject, Zt as securityServerModuleState, _ as a11yNoMissingIdRef, _n as seoHeadingLevelSkip, _r as seoHtmlLang, _t as parseKitModuleFacts, a as formatJsonReport, an as correctnessOrphanEffect, ar as formatFailedRuleWarning, at as architectureReservedNamePlacement, b as a11yDuplicateLandmark, bn as seoSingleH1, br as seoJsonLd, bt as WALK_IGNORED_KEYS, c as computeScore, cn as correctnessNonreactiveBuiltinState, cr as withFailedRulesOff, ct as architectureUnitEntryFile, d as messageText, dn as correctnessUnmutatedState, dr as performanceLcpImage, dt as collectFalseExports, en as securityRawHtml, er as validateRuleSetting, et as performanceMinifyDisabled, f as ruleMetaById, fn as correctnessEffectAsOnMount, fr as performanceFontPreloadCrossorigin, ft as collectTopLevelBindings, g as a11yUnverifiedIdRef, gn as seoSsrDisabled, gr as performanceImageDimensions, gt as parseInMemoryExports, h as explainRule, hn as correctnessEachKey, hr as performanceImageLoadingHint, ht as parseFalseExports, i as buildJsonReport, in as correctnessOrphanLifecycle, ir as compileOverrides, it as architectureRouteComponentImport, j as a11yAbbrTitle, jn as stripTextDirective, jr as classify, jt as rootObjectName, k as HTML_SPEC, kn as resolveLandmark, kr as routeNeverSsr, kt as matchLifecycleCall, l as formatGithubReport, ln as correctnessStalePropDerivation, lr as withIndexableOff, lt as architecturePrivateScopeImport, m as allRules, mn as correctnessEachIndexKey, mr as performanceResponsiveImage, mt as parseExitFactoryExports, n as mdEscape, nn as correctnessServerBrowserGlobal, nr as applyOverrides, nt as performanceHeavyImport, o as formatJsonReportChunks, on as correctnessAutoplayMuted, or as selectRules, ot as architectureReservedDirectoryNames, p as severityToSarifLevel, pn as correctnessEffectAsDerived, pr as performancePreloadMissingAs, pt as parseClientExports, q as a11yRequiredAriaProps, qn as seoOgDescription, qt as valueFromNodes, r as terminalSafe, rn as correctnessBasePathNavigation, rr as applyRuleSeverities, rt as architectureDocLinkTarget, s as computeHealth, sn as correctnessCheckableBindValue, sr as settingSeverity, st as architectureDirectoryNaming, t as formatMarkdownReport, tn as correctnessInstanceBrowserGlobal, tr as SEARCH_RESULT_RULES, tt as performanceNamespaceImport, u as SEVERITY_RANK, un as correctnessPropMutation, ur as performanceRenderBlockingScript, ut as DEFAULT_KIT_ALIASES, v as a11yIdDuplication, vn as seoDuplicateDescription, vr as seoSitemapXml, vt as resolveRepoLocalPath, w as a11yDisallowedElement, wn as NAMING_ATTRS, wr as hasRel, wt as collectDerivedGuardBindings, x as a11yDoctype, xn as ANCESTRY_DEPENDENT_TAGS, xr as seoOgTitle, xt as addBoundNames, y as a11yTopLevelLandmark, yn as seoDuplicateTitle, yr as seoRobotsTxt, yt as isRootRelativePath, z as a11yRequireDatetime, zn as seoJsonLdPlaceholder, zr as defaultProject, zt as attrText } from "./markdown-CsQNXeWj.js";
35
2
  //#region src/images.ts
36
3
  /** A `.svg` path (query and fragment ignored) or an inline `data:image/svg+xml` URI. */
37
4
  function isSvgSrc(src) {
@@ -161,21 +128,46 @@ function collectConstantLists(programs, fragment, opts = {}) {
161
128
  const stmt = opts.exported ? unwrapExport(top) : top;
162
129
  if (stmt?.type !== "VariableDeclaration" || stmt.kind !== "const") continue;
163
130
  for (const d of stmt.declarations ?? []) {
164
- const init = unwrapTs(d?.init);
131
+ const init = opts.sets ? setLiteralList(unwrapTs(d?.init)) : unwrapTs(d?.init);
165
132
  if (d?.id?.type === "Identifier" && init?.type === "ArrayExpression") {
166
133
  if (!(init.elements ?? []).some((el) => el?.type === "SpreadElement")) candidates.set(d.id.name, d.id);
167
134
  }
168
135
  }
169
136
  }
170
137
  if (candidates.size === 0) return /* @__PURE__ */ new Set();
138
+ const aliases = /* @__PURE__ */ new Map();
139
+ if (opts.exported) for (const program of programs) walkEstree(program, (n) => {
140
+ if (n.type !== "VariableDeclaration" || n.kind !== "const") return;
141
+ for (const d of n.declarations ?? []) {
142
+ const init = unwrapTs(d?.init);
143
+ const list = (init?.type === "ConditionalExpression" ? [init.consequent, init.alternate] : init?.type === "LogicalExpression" && init.operator !== "&&" ? [init.left, init.right] : [init]).map((b) => unwrapTs(b)).find((b) => b?.type === "Identifier" && candidates.has(b.name));
144
+ if (d?.id?.type !== "Identifier" || !list || candidates.has(d.id.name)) continue;
145
+ safe.add(list);
146
+ aliases.set(d.id.name, list.name);
147
+ candidates.set(d.id.name, d.id);
148
+ }
149
+ });
171
150
  for (const node of candidates.values()) safe.add(node);
172
151
  const namespaces = new Set([...opts.imports ?? []].filter(([, b]) => b.imported === "*").map(([local]) => local));
173
152
  const unsafe = /* @__PURE__ */ new Set();
174
153
  const roots = [...programs, fragment];
175
154
  const listOf = (n) => n?.type === "Identifier" && candidates.has(n.name) ? n.name : void 0;
155
+ const outerList = listOf;
176
156
  const nsMember = (n) => n?.type === "MemberExpression" && !n.computed && namespaces.has(listOf(n.object)) ? `${n.object.name}.${n.property.name}` : void 0;
157
+ const outerMember = nsMember;
177
158
  const members = /* @__PURE__ */ new Set();
178
- for (const root of roots) walkEstree(root, (n) => {
159
+ const declared = /* @__PURE__ */ new Set();
160
+ for (const program of programs) {
161
+ const imported = /* @__PURE__ */ new Set();
162
+ for (const top of program.body ?? []) if (top?.type === "ImportDeclaration") for (const s of top.specifiers ?? []) imported.add(s?.local?.name);
163
+ for (const name of collectProgramBindings(program)) if (!imported.has(name)) declared.add(name);
164
+ }
165
+ for (const root of roots) walkScoped(root, (n, shadowed) => {
166
+ const listOf = (x) => {
167
+ const name = outerList(x);
168
+ return name && !shadowed.has(name) ? name : void 0;
169
+ };
170
+ const nsMember = (x) => x?.type === "MemberExpression" && listOf(x.object) ? outerMember(x) : void 0;
179
171
  if (n.type === "EachBlock" && (listOf(n.expression) || nsMember(n.expression))) safe.add(n.expression);
180
172
  if (n.type === "ForOfStatement" && (listOf(n.right) || nsMember(n.right))) safe.add(n.right);
181
173
  if (n.type === "ArrayExpression") {
@@ -183,29 +175,46 @@ function collectConstantLists(programs, fragment, opts = {}) {
183
175
  }
184
176
  if (nsMember(n)) members.add(nsMember(n));
185
177
  if (n.type === "MemberExpression") {
186
- if (listOf(n.object) && !(n.computed && namespaces.has(n.object.name))) safe.add(n.object);
187
- if (nsMember(n.object)) safe.add(n.object);
178
+ const object = n.object ? unwrapTs(n.object) : n.object;
179
+ if (listOf(object) && !(n.computed && namespaces.has(object.name))) safe.add(object);
180
+ if (nsMember(object)) safe.add(object);
188
181
  if (!n.computed) safe.add(n.property);
189
182
  }
190
183
  const target = n.type === "AssignmentExpression" ? n.left : n.type === "UpdateExpression" ? n.argument : void 0;
191
- if (target?.type === "MemberExpression" && listOf(rootObjectNode(target))) unsafe.add(listOf(rootObjectNode(target)));
192
- const called = n.type === "CallExpression" && n.callee?.type === "MemberExpression" ? n.callee.object : void 0;
184
+ let root = target;
185
+ while (root && (root.type === "MemberExpression" || root.type.startsWith("TS"))) root = root.type === "MemberExpression" ? root.object : root.expression;
186
+ if (target?.type === "MemberExpression" && listOf(root)) unsafe.add(listOf(root));
187
+ const called = n.type === "CallExpression" && n.callee?.type === "MemberExpression" ? unwrapTs(n.callee.object) : void 0;
193
188
  const calledList = listOf(called) ?? nsMember(called);
194
189
  if (calledList) {
195
190
  const { computed, property } = n.callee;
196
191
  const method = !computed ? property?.name : property?.type === "Literal" && typeof property.value === "string" ? property.value : void 0;
197
192
  if (computed && method === void 0 || method !== void 0 && MUTATING_METHODS.has(method)) unsafe.add(calledList);
198
193
  }
194
+ const callee = n.type === "CallExpression" || n.type === "NewExpression" ? rootObjectName(n.callee) : void 0;
195
+ if (opts.exported && callee !== void 0 && !declared.has(callee) && !shadowed.has(callee)) {
196
+ for (const arg of n.arguments ?? []) if (listOf(unwrapTs(arg))) safe.add(unwrapTs(arg));
197
+ }
199
198
  if (n.type === "BindDirective" && listOf(rootObjectNode(n.expression))) unsafe.add(listOf(rootObjectNode(n.expression)));
200
199
  if (n.type === "Property" && !n.computed && !n.shorthand) safe.add(n.key);
201
200
  });
202
- for (const root of roots) walkEvalScope(root, (n) => {
201
+ for (const root of roots) walkEvalScope(root, (n, shadowed) => {
203
202
  if (n.type.startsWith("TS") && !TS_VALUE_NODES.has(n.type)) return true;
204
203
  const name = listOf(n) ?? nsMember(n);
205
- if (name && !safe.has(n)) unsafe.add(name);
204
+ if (name && !safe.has(n) && !shadowed.has(name.split(".")[0])) unsafe.add(name);
206
205
  return false;
207
206
  }, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
208
- return new Set([...candidates.keys(), ...members].filter((key) => !unsafe.has(key) && !unsafe.has(key.split(".")[0])));
207
+ for (const [alias, list] of aliases) if (unsafe.has(alias)) unsafe.add(list);
208
+ return new Set([...candidates.keys(), ...members].filter((key) => !aliases.has(key) && !unsafe.has(key) && !unsafe.has(key.split(".")[0])));
209
+ }
210
+ /** A component's script `const` lists that nothing in its scripts or template can change. */
211
+ function collectComponentConstantLists(ast, opts = {}) {
212
+ return collectConstantLists([ast.module?.content, ast.instance?.content].filter(Boolean), ast.fragment ?? ast, opts);
213
+ }
214
+ /** The array literal of `new Set([…])`, or the node itself. */
215
+ function setLiteralList(n) {
216
+ const arg = n?.type === "NewExpression" && n.callee?.type === "Identifier" && n.callee.name === "Set" ? n.arguments : void 0;
217
+ return arg?.length === 1 && unwrapTs(arg[0])?.type === "ArrayExpression" ? unwrapTs(arg[0]) : n;
209
218
  }
210
219
  /**
211
220
  * Export names of a module's constant lists — `export const X = [ … ]`, or a local one exported by
@@ -288,6 +297,14 @@ function isEffectRootCall(node) {
288
297
  * `$state.raw(...)`, or `$state.frozen(...)` — but NOT readers like
289
298
  * `$state.snapshot(...)`, which would otherwise pollute the state-name set (correctness/effect-as-derived).
290
299
  */
300
+ /** An absent argument or a literal no reader can write through: `0`, `'x'`, `null`, `-1`, `` `x` ``. */
301
+ function isPrimitiveLiteral(node) {
302
+ if (node === void 0) return true;
303
+ if (node.type === "Literal") return !("regex" in node);
304
+ if (node.type === "TemplateLiteral") return node.expressions.length === 0;
305
+ if (node.type === "UnaryExpression") return node.argument?.type === "Literal";
306
+ return node.type === "Identifier" && node.name === "undefined";
307
+ }
291
308
  function isStateDeclaration(node) {
292
309
  const c = node?.callee;
293
310
  if (c?.type === "Identifier") return c.name === "$state";
@@ -296,7 +313,18 @@ function isStateDeclaration(node) {
296
313
  }
297
314
  /** True when a function's body does nothing but assign to `$state` identifiers (correctness/effect-as-derived). */
298
315
  function bodyOnlyAssignsState(fn, stateNames) {
299
- const isStateAssign = (expr) => expr?.type === "AssignmentExpression" && expr.operator === "=" && expr.left?.type === "Identifier" && stateNames.has(expr.left.name);
316
+ const readsItself = (expr) => {
317
+ let reads = false;
318
+ const names = /* @__PURE__ */ new Set();
319
+ walkEstree(expr.right, (n) => {
320
+ if (n?.type === "MemberExpression" && !n.computed) names.add(n.property);
321
+ if (n?.type === "Property" && !n.computed && !n.shorthand) names.add(n.key);
322
+ if (n?.type === "TSTypeQuery") names.add(n.exprName);
323
+ reads ||= n?.type === "Identifier" && n.name === expr.left.name && !names.has(n);
324
+ });
325
+ return reads;
326
+ };
327
+ const isStateAssign = (expr) => expr?.type === "AssignmentExpression" && expr.operator === "=" && expr.left?.type === "Identifier" && stateNames.has(expr.left.name) && !readsItself(expr);
300
328
  const body = fn?.body;
301
329
  if (!body) return false;
302
330
  if (body.type !== "BlockStatement") return isStateAssign(body);
@@ -310,6 +338,36 @@ function isDerivedDeclaration(node) {
310
338
  if (c?.type === "MemberExpression" && c.object?.type === "Identifier" && c.object.name === "$derived") return c.property?.type === "Identifier" && c.property.name === "by";
311
339
  return false;
312
340
  }
341
+ const OBJECT_TYPES = /* @__PURE__ */ new Set([
342
+ "TSArrayType",
343
+ "TSTupleType",
344
+ "TSTypeLiteral"
345
+ ]);
346
+ const OBJECT_TYPE_NAMES = /* @__PURE__ */ new Set([
347
+ "Array",
348
+ "ReadonlyArray",
349
+ "Record",
350
+ "Map",
351
+ "Set",
352
+ "WeakMap",
353
+ "WeakSet",
354
+ "Date"
355
+ ]);
356
+ const OBJECT_VALUES = /* @__PURE__ */ new Set([
357
+ "ObjectExpression",
358
+ "ArrayExpression",
359
+ "NewExpression"
360
+ ]);
361
+ /** A `$state` call whose initial value or type argument is an object, array or class instance. */
362
+ function isObjectState(call) {
363
+ const arg = unwrapTs(call.arguments?.[0]);
364
+ if (isPrimitiveLiteral(arg)) return false;
365
+ if (OBJECT_VALUES.has(arg?.type)) return true;
366
+ if (arg?.type === "LogicalExpression" && arg.operator !== "&&" && !isPrimitiveLiteral(unwrapTs(arg.left)) && OBJECT_VALUES.has(unwrapTs(arg.right)?.type)) return true;
367
+ const type = (call.typeArguments ?? call.typeParameters)?.params?.[0];
368
+ if (type?.type === "TSTypeReference") return OBJECT_TYPE_NAMES.has(type.typeName?.name);
369
+ return OBJECT_TYPES.has(type?.type);
370
+ }
313
371
  function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
314
372
  const record = (name, kind) => {
315
373
  acc.add(name);
@@ -319,14 +377,38 @@ function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
319
377
  set.add(kind);
320
378
  }
321
379
  };
380
+ const primitiveStates = /* @__PURE__ */ new Set();
381
+ const objectStates = /* @__PURE__ */ new Set();
382
+ const heldObjects = /* @__PURE__ */ new Set();
383
+ walkEstree(root, (n) => {
384
+ const init = n?.type === "VariableDeclarator" ? unwrapTs(n.init) : void 0;
385
+ if (n?.id?.type !== "Identifier" || !isStateDeclaration(init)) return;
386
+ (isPrimitiveLiteral(init.arguments?.[0]) ? primitiveStates : objectStates).add(n.id.name);
387
+ if (localStates && isObjectState(init)) heldObjects.add(n.id.name);
388
+ });
389
+ for (const name of objectStates) primitiveStates.delete(name);
390
+ const aliasOf = /* @__PURE__ */ new Map();
391
+ const reassigned = /* @__PURE__ */ new Set();
392
+ if (localStates) walkEstree(root, (n) => {
393
+ const target = n?.type === "AssignmentExpression" ? n.left : n?.type === "UpdateExpression" ? n.argument : void 0;
394
+ if (target?.type === "Identifier") reassigned.add(target.name);
395
+ });
396
+ if (localStates) for (let pass = 0; pass < 2; pass++) walkEstree(root, (n) => {
397
+ const [id, from] = n?.type === "VariableDeclarator" && unwrapTs(n.init)?.type === "MemberExpression" ? [n.id, n.init] : n?.type === "ForOfStatement" && n.left?.type === "VariableDeclaration" ? [n.left.declarations?.[0]?.id, n.right] : [void 0, void 0];
398
+ const r = rootObjectName(unwrapTs(from));
399
+ const owner = r && (stateNames.has(r) ? r : aliasOf.get(r));
400
+ if (id?.type === "Identifier" && owner && !stateNames.has(id.name) && !reassigned.has(id.name)) aliasOf.set(id.name, owner);
401
+ });
322
402
  const visit = (stateNames) => (n, scope) => {
323
403
  const shadowed = (name) => name === void 0 || scope.has(name);
404
+ const mutated = (r) => {
405
+ if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
406
+ else if (r && stateNames.has(aliasOf.get(r))) record(aliasOf.get(r), "mutate");
407
+ };
324
408
  if (n?.type === "AssignmentExpression") {
325
409
  if (n.left?.type === "Identifier" && stateNames.has(n.left.name) && !shadowed(n.left.name)) record(n.left.name, "reassign");
326
- else if (n.left?.type === "MemberExpression") {
327
- const r = rootObjectName(n.left);
328
- if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
329
- } else if (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern") {
410
+ else if (n.left?.type === "MemberExpression") mutated(rootObjectName(n.left));
411
+ else if (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern") {
330
412
  const bound = /* @__PURE__ */ new Set();
331
413
  addBoundNames(n.left, bound);
332
414
  for (const name of bound) if (stateNames.has(name) && !shadowed(name)) record(name, "reassign");
@@ -334,24 +416,30 @@ function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
334
416
  } else if (n?.type === "UpdateExpression") {
335
417
  if (n.argument?.type === "Identifier") {
336
418
  if (stateNames.has(n.argument.name) && !shadowed(n.argument.name)) record(n.argument.name, "reassign");
337
- } else {
338
- const r = rootObjectName(n.argument);
339
- if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
340
- }
341
- } else if (n?.type === "UnaryExpression" && n.operator === "delete") {
342
- const r = rootObjectName(n.argument);
343
- if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
344
- } else if (n?.type === "CallExpression") {
345
- if (n.callee?.type === "MemberExpression") {
346
- const r = rootObjectName(n.callee);
347
- if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
348
- }
419
+ } else mutated(rootObjectName(n.argument));
420
+ } else if (n?.type === "UnaryExpression" && n.operator === "delete") mutated(rootObjectName(n.argument));
421
+ else if (n?.type === "CallExpression") {
422
+ if (n.callee?.type === "MemberExpression") mutated(rootObjectName(n.callee));
349
423
  for (const a of n.arguments ?? []) {
350
424
  const arg = a?.type === "SpreadElement" ? a.argument : a;
351
425
  const r = rootObjectName(arg);
352
426
  if (r && stateNames.has(r) && !shadowed(r)) record(r, "escape");
353
427
  }
354
- }
428
+ } else if (n?.type === "ReturnStatement") {
429
+ const out = unwrapTs(n.argument);
430
+ const r = out?.type === "Identifier" ? out.name : void 0;
431
+ if (r && stateNames.has(r) && !shadowed(r) && !primitiveStates.has(r)) record(r, "escape");
432
+ } else if (heldObjects.size > 0 && (n?.type === "ObjectExpression" || n?.type === "ArrayExpression")) {
433
+ const values = n.type === "ObjectExpression" ? (n.properties ?? []).map((p) => p?.value) : n.elements;
434
+ for (const v of values ?? []) {
435
+ const placed = unwrapTs(v);
436
+ const r = placed?.type === "Identifier" ? placed.name : void 0;
437
+ if (r && heldObjects.has(r) && stateNames.has(r) && !shadowed(r)) record(r, "escape");
438
+ }
439
+ } else if ((n?.type === "Property" || n?.type === "MethodDefinition") && n.kind === "get") walkEstree(n.value?.body, (m) => {
440
+ const r = m?.type === "ReturnStatement" ? rootObjectName(m.argument) : void 0;
441
+ if (r && stateNames.has(r) && !shadowed(r) && !primitiveStates.has(r)) record(r, "escape");
442
+ });
355
443
  };
356
444
  walkScoped(root, visit(stateNames));
357
445
  if (!localStates) return;
@@ -1739,6 +1827,7 @@ function isPropsCall(node) {
1739
1827
  }
1740
1828
  /** Whether a CallExpression is a `$bindable(...)` call (a destructured prop's default value). */
1741
1829
  function isBindableCall(node) {
1830
+ node = unwrapTs(node);
1742
1831
  return node?.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === "$bindable";
1743
1832
  }
1744
1833
  /**
@@ -1809,6 +1898,92 @@ const MUTATING_METHODS = /* @__PURE__ */ new Set([
1809
1898
  "delete",
1810
1899
  "clear"
1811
1900
  ]);
1901
+ /** Built-in types whose own methods include the mutating method names above. */
1902
+ const COLLECTION_TYPES = /* @__PURE__ */ new Set([
1903
+ "Array",
1904
+ "ReadonlyArray",
1905
+ "Set",
1906
+ "ReadonlySet",
1907
+ "Map",
1908
+ "ReadonlyMap",
1909
+ "WeakMap",
1910
+ "WeakSet",
1911
+ "SvelteSet",
1912
+ "SvelteMap",
1913
+ "URLSearchParams",
1914
+ "SvelteURLSearchParams",
1915
+ "FormData",
1916
+ "Headers",
1917
+ "Int8Array",
1918
+ "Uint8Array",
1919
+ "Uint8ClampedArray",
1920
+ "Int16Array",
1921
+ "Uint16Array",
1922
+ "Int32Array",
1923
+ "Uint32Array",
1924
+ "Float16Array",
1925
+ "Float32Array",
1926
+ "Float64Array",
1927
+ "BigInt64Array",
1928
+ "BigUint64Array"
1929
+ ]);
1930
+ const NON_COLLECTION_NODES = /* @__PURE__ */ new Set([
1931
+ "TSStringKeyword",
1932
+ "TSNumberKeyword",
1933
+ "TSBooleanKeyword",
1934
+ "TSBigIntKeyword",
1935
+ "TSNullKeyword",
1936
+ "TSUndefinedKeyword",
1937
+ "TSFunctionType",
1938
+ "TSTypeLiteral",
1939
+ "TSLiteralType"
1940
+ ]);
1941
+ /**
1942
+ * The props whose declared type, from the `$props()` annotation, is known not to be a collection: a
1943
+ * method called on one is its own API (`queryClient.clear()`), not an array, Set or Map mutation.
1944
+ * A named type counts unless it is a built-in collection or a same-file alias of one.
1945
+ */
1946
+ function nonCollectionProps(program) {
1947
+ const decls = /* @__PURE__ */ new Map();
1948
+ walkEstree(program, (n) => {
1949
+ if ((n.type === "TSInterfaceDeclaration" || n.type === "TSTypeAliasDeclaration") && n.id?.name) decls.set(n.id.name, n);
1950
+ });
1951
+ const unwrap = (t) => t?.type === "TSTypeAnnotation" ? t.typeAnnotation : t;
1952
+ const nonCollection = (t, depth) => {
1953
+ t = unwrap(t);
1954
+ if (!t || depth > 4) return false;
1955
+ if (NON_COLLECTION_NODES.has(t.type)) return true;
1956
+ if (t.type === "TSUnionType") return t.types.every((u) => nonCollection(u, depth + 1));
1957
+ if (t.type !== "TSTypeReference" || t.typeName?.type !== "Identifier") return false;
1958
+ const name = t.typeName.name;
1959
+ if (COLLECTION_TYPES.has(name)) return false;
1960
+ const decl = decls.get(name);
1961
+ if (decl?.type === "TSTypeAliasDeclaration") return nonCollection(decl.typeAnnotation, depth + 1);
1962
+ if (decl?.type === "TSInterfaceDeclaration") return !(decl.extends ?? []).some((h) => COLLECTION_TYPES.has(h.expression?.name));
1963
+ return !(t.typeArguments ?? t.typeParameters)?.params?.length;
1964
+ };
1965
+ const membersOf = (t, depth) => {
1966
+ t = unwrap(t);
1967
+ if (t?.type === "TSTypeLiteral") return t.members;
1968
+ if (t?.type !== "TSTypeReference" || depth > 4) return void 0;
1969
+ const decl = decls.get(t.typeName?.name);
1970
+ if (decl?.type === "TSInterfaceDeclaration") return decl.body?.body;
1971
+ return decl?.type === "TSTypeAliasDeclaration" ? membersOf(decl.typeAnnotation, depth + 1) : void 0;
1972
+ };
1973
+ const names = /* @__PURE__ */ new Set();
1974
+ walkEstree(program, (n) => {
1975
+ if (n.type !== "VariableDeclarator" || !n.init || !isPropsCall(unwrapTs(n.init))) return;
1976
+ if (n.id?.type !== "ObjectPattern") return;
1977
+ const types = /* @__PURE__ */ new Map();
1978
+ for (const m of membersOf(n.id.typeAnnotation, 0) ?? []) if (m.type === "TSPropertySignature" && m.key?.type === "Identifier") types.set(m.key.name, m.typeAnnotation);
1979
+ for (const p of n.id.properties ?? []) {
1980
+ if (p?.type !== "Property" || p.key?.type !== "Identifier" || !types.has(p.key.name)) continue;
1981
+ const local = p.value?.type === "AssignmentPattern" ? p.value.left : p.value;
1982
+ if (local?.type === "Identifier" && nonCollection(types.get(p.key.name), 0)) names.add(local.name);
1983
+ }
1984
+ });
1985
+ return names;
1986
+ }
1812
1987
  /**
1813
1988
  * Flag mutations of a non-`$bindable` prop (correctness/prop-mutation): a member-expression write
1814
1989
  * (`prop.x = …`, `prop.x += …`, `prop.x++`), `delete prop.x`, or a call to a mutating
@@ -1820,16 +1995,18 @@ const MUTATING_METHODS = /* @__PURE__ */ new Set([
1820
1995
  * A `legacy` (export let) prop is flagged only for `delete` and mutating method calls — the legacy
1821
1996
  * compiler turns a member write or update into an invalidating assignment (`prop(prop().x = …, true)`).
1822
1997
  */
1823
- function collectPropMutations(root, propNames, source, acc, legacy) {
1998
+ function collectPropMutations(root, propNames, source, acc, legacy, ownApi = /* @__PURE__ */ new Set()) {
1824
1999
  if (propNames.size === 0) return;
1825
2000
  const exemptCalls = /* @__PURE__ */ new Set();
2001
+ const returnedToProp = /* @__PURE__ */ new Set();
1826
2002
  if (legacy.size > 0) walkEstree(root, (fn) => {
1827
- if (!isDeferredBody(fn)) return;
2003
+ const body = isDeferredBody(fn) ? fn.body : fn.type === "LabeledStatement" && fn.label?.name === "$" ? fn.body : void 0;
2004
+ if (!body) return;
1828
2005
  const assigned = /* @__PURE__ */ new Set();
1829
- walkScoped(fn.body, (m, scope) => {
2006
+ walkScoped(body, (m, scope) => {
1830
2007
  if (m.type === "AssignmentExpression" && m.left?.type === "Identifier" && !scope.has(m.left.name)) assigned.add(m.left.name);
1831
2008
  });
1832
- walkEstree(fn.body, (m) => {
2009
+ walkEstree(body, (m) => {
1833
2010
  const r = m.type === "CallExpression" ? rootObjectName(m.callee?.object) : m.type === "UnaryExpression" && m.operator === "delete" ? rootObjectName(m.argument) : void 0;
1834
2011
  if (r && legacy.has(r) && assigned.has(r)) exemptCalls.add(m);
1835
2012
  });
@@ -1837,6 +2014,20 @@ function collectPropMutations(root, propNames, source, acc, legacy) {
1837
2014
  walkEstree(root, (m) => {
1838
2015
  const call = m.type === "AssignmentExpression" && m.left?.type === "Identifier" ? m.right : void 0;
1839
2016
  if (call?.type === "CallExpression" && rootObjectName(call.callee?.object) === m.left.name && call.arguments?.length === 1 && call.arguments[0]?.type === "ObjectExpression") exemptCalls.add(call);
2017
+ const awaited = m.type === "AwaitExpression" ? unwrapTs(m.argument) : void 0;
2018
+ if (awaited?.type === "CallExpression") exemptCalls.add(awaited);
2019
+ const chained = m.type === "CallExpression" && m.callee?.type === "MemberExpression" && !m.callee.computed && [
2020
+ "then",
2021
+ "catch",
2022
+ "finally"
2023
+ ].includes(m.callee.property?.name) ? unwrapTs(m.callee.object) : void 0;
2024
+ if (chained?.type === "CallExpression") exemptCalls.add(chained);
2025
+ if (m.type === "AssignmentExpression" && m.left?.type === "Identifier") {
2026
+ let back = unwrapTs(m.right);
2027
+ if (back?.type === "LogicalExpression") back = unwrapTs(back.left);
2028
+ if (back?.type === "ChainExpression") back = back.expression;
2029
+ if (back?.type === "CallExpression" && back.callee.type === "MemberExpression" && rootObjectName(back.callee.object) === m.left.name) returnedToProp.add(back);
2030
+ }
1840
2031
  });
1841
2032
  walkScoped(root, (n, scope) => {
1842
2033
  const flag = (r) => {
@@ -1855,6 +2046,8 @@ function collectPropMutations(root, propNames, source, acc, legacy) {
1855
2046
  } else if (n.type === "UnaryExpression" && n.operator === "delete") flag(rootObjectName(n.argument));
1856
2047
  else if (n.type === "CallExpression" && n.callee?.type === "MemberExpression") {
1857
2048
  const method = n.callee.property?.type === "Identifier" ? n.callee.property.name : void 0;
2049
+ const receiver = unwrapTs(n.callee.object);
2050
+ if (receiver?.type === "Identifier" && ownApi.has(receiver.name) && !returnedToProp.has(n)) return;
1858
2051
  if (method && MUTATING_METHODS.has(method)) flag(rootObjectName(n.callee.object));
1859
2052
  }
1860
2053
  });
@@ -1904,7 +2097,7 @@ function collectImportSources(program, source, acc) {
1904
2097
  }
1905
2098
  /** A specifier is "bare" (a node_modules package) when it is not relative/absolute/alias-local. */
1906
2099
  function isBareSpecifier(s) {
1907
- return !/^[./$#]/.test(s);
2100
+ return !/^([./$#]|@\/|~\/)/.test(s);
1908
2101
  }
1909
2102
  /**
1910
2103
  * Whether `name` is used anywhere in `roots` other than as the object of a static member access
@@ -1921,6 +2114,8 @@ function namespaceUsedDynamically(name, roots) {
1921
2114
  if (!n.computed) staticOrDeclaring.add(n.property);
1922
2115
  } else if ((n.type === "Property" && !n.shorthand || n.type === "TSPropertySignature" || n.type === "TSMethodSignature") && !n.computed) staticOrDeclaring.add(n.key);
1923
2116
  else if (n.type === "ImportNamespaceSpecifier") staticOrDeclaring.add(n.local);
2117
+ else if (n.type === "ImportSpecifier" && n.imported !== n.local) staticOrDeclaring.add(n.imported);
2118
+ else if (n.type === "ExportSpecifier" && n.exported !== n.local) staticOrDeclaring.add(n.exported);
1924
2119
  else if (n.type === "TSQualifiedName" || n.type === "TSTypeQuery") {
1925
2120
  const id = n.type === "TSQualifiedName" ? n.left : n.exprName;
1926
2121
  if (id?.type === "Identifier") staticOrDeclaring.add(id);
@@ -2312,8 +2507,9 @@ function parseComponentFacts(source, filename) {
2312
2507
  const legacyPropNames = collectLegacyPropNames(program);
2313
2508
  const nonBindableProps = /* @__PURE__ */ new Set([...collectPropNames(program, false), ...legacyPropNames]);
2314
2509
  const rawMutations = [];
2315
- collectPropMutations(program, nonBindableProps, source, rawMutations, legacyPropNames);
2316
- if (ast.fragment) collectPropMutations(ast.fragment, nonBindableProps, source, rawMutations, legacyPropNames);
2510
+ const ownApi = nonCollectionProps(program);
2511
+ collectPropMutations(program, nonBindableProps, source, rawMutations, legacyPropNames, ownApi);
2512
+ if (ast.fragment) collectPropMutations(ast.fragment, nonBindableProps, source, rawMutations, legacyPropNames, ownApi);
2317
2513
  const isLegacy = legacyPropNames.size > 0;
2318
2514
  for (const m of rawMutations) mutatedProps.push(legacyPropNames.has(m.name) ? {
2319
2515
  ...m,
@@ -2731,7 +2927,7 @@ async function collectKitModuleFacts(rt, cwd, aliases) {
2731
2927
  "src/hooks.server.{ts,js}"
2732
2928
  ].map((p) => rt.glob(p, cwd)));
2733
2929
  const files = [...new Set(lists.flat())];
2734
- return arbitrateServerStoreWrites(rt, cwd, await Promise.all(files.sort().map(async (rel) => {
2930
+ return decideLoadFactories(rt, cwd, await decideFlagGuards(rt, cwd, await dropClientWrites(rt, cwd, await arbitrateServerStoreWrites(rt, cwd, await Promise.all(files.sort().map(async (rel) => {
2735
2931
  const kind = kindOf(rel);
2736
2932
  let source;
2737
2933
  try {
@@ -2755,7 +2951,7 @@ async function collectKitModuleFacts(rt, cwd, aliases) {
2755
2951
  parseFailed: true
2756
2952
  };
2757
2953
  }
2758
- })));
2954
+ }))), aliases)));
2759
2955
  }
2760
2956
  /**
2761
2957
  * The files a resolved specifier may name, in resolution order. An extensionless path takes an
@@ -2773,20 +2969,58 @@ function moduleCandidates(repoPath) {
2773
2969
  `${repoPath}/index.js`
2774
2970
  ];
2775
2971
  }
2776
- /**
2777
- * Read the module a pending write targets and return the names it exports as an in-memory
2778
- * container. Returns an empty set when the file cannot be found or read: unresolvable means
2779
- * unarbitrated, which leaves the write exempt.
2780
- */
2781
- async function inMemoryExportsOf(rt, cwd, repoPath) {
2972
+ /** Read a module and parse it with `parse`; empty when it cannot be found or read. */
2973
+ async function exportsOf(rt, cwd, repoPath, parse) {
2782
2974
  for (const rel of moduleCandidates(repoPath)) try {
2783
2975
  if (!await rt.exists(rt.join(cwd, rel))) continue;
2784
- return parseInMemoryExports(await rt.readFile(rt.join(cwd, rel)), rel);
2976
+ return parse(await rt.readFile(rt.join(cwd, rel)), rel);
2785
2977
  } catch {
2786
2978
  return /* @__PURE__ */ new Set();
2787
2979
  }
2788
2980
  return /* @__PURE__ */ new Set();
2789
2981
  }
2982
+ /** Drop the handler `.set()`/`.update()` writes whose target module exports a persistence client under that name. */
2983
+ async function dropClientWrites(rt, cwd, facts, aliases) {
2984
+ const targets = [...new Set(facts.flatMap((f) => (f.storeWriteTargets ?? []).map((w) => w.resolved)))];
2985
+ if (targets.length === 0) return facts;
2986
+ const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, (src, file) => parseClientExports(src, file, aliases))])));
2987
+ return facts.map((f) => {
2988
+ const clients = (f.storeWriteTargets ?? []).filter((w) => byPath.get(w.resolved)?.has(w.imported));
2989
+ if (clients.length === 0) return f;
2990
+ const drop = (w) => w.via === "set-call" && clients.some((c) => c.name === w.name && c.line === w.line);
2991
+ return {
2992
+ ...f,
2993
+ importedStateWrites: f.importedStateWrites.filter((w) => !drop(w))
2994
+ };
2995
+ });
2996
+ }
2997
+ /** A `load` an imported factory builds never renders when the function the factory returns always exits. */
2998
+ async function decideLoadFactories(rt, cwd, facts) {
2999
+ const targets = [...new Set(facts.flatMap((f) => f.pendingLoadFactory ? [f.pendingLoadFactory.resolved] : []))];
3000
+ if (targets.length === 0) return facts;
3001
+ const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, parseExitFactoryExports)])));
3002
+ return facts.map((f) => {
3003
+ const g = f.pendingLoadFactory;
3004
+ return g && byPath.get(g.resolved)?.has(g.name) ? {
3005
+ ...f,
3006
+ loadNeverRenders: true
3007
+ } : f;
3008
+ });
3009
+ }
3010
+ /** A load whose leading guard exits on an imported flag that is the literal `false` never renders. */
3011
+ async function decideFlagGuards(rt, cwd, facts) {
3012
+ const targets = [...new Set(facts.flatMap((f) => f.pendingFlagGuard ? [f.pendingFlagGuard.resolved] : []))];
3013
+ if (targets.length === 0) return facts;
3014
+ const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, parseFalseExports)])));
3015
+ return facts.map((f) => {
3016
+ const g = f.pendingFlagGuard;
3017
+ const key = g && (g.member ? `${g.name}.${g.member}` : g.name);
3018
+ return g && byPath.get(g.resolved)?.has(key) ? {
3019
+ ...f,
3020
+ loadNeverRenders: true
3021
+ } : f;
3022
+ });
3023
+ }
2790
3024
  /**
2791
3025
  * Decide the `.set()`/`.update()` calls the parse could not: a call on a binding exported from
2792
3026
  * under the `$lib` server root is shared module state when that binding is an in-memory
@@ -2796,7 +3030,7 @@ async function inMemoryExportsOf(rt, cwd, repoPath) {
2796
3030
  async function arbitrateServerStoreWrites(rt, cwd, facts) {
2797
3031
  const targets = [...new Set(facts.flatMap((f) => f.pendingServerStoreWrites.map((w) => w.resolved)))];
2798
3032
  if (targets.length === 0) return facts;
2799
- const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await inMemoryExportsOf(rt, cwd, t)])));
3033
+ const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, parseInMemoryExports)])));
2800
3034
  return facts.map((f) => {
2801
3035
  const promoted = f.pendingServerStoreWrites.filter((w) => byPath.get(w.resolved)?.has(w.imported)).map((w) => ({
2802
3036
  name: w.name,
@@ -2926,28 +3160,51 @@ function findMinifyDisabled(source) {
2926
3160
  }
2927
3161
  //#endregion
2928
3162
  //#region src/svelte-config-parse.ts
2929
- /** Whether every branch of a `?:` / `||` / `&&` / `??` tree is the literal `''`. */
2930
- function alwaysEmptyString(expr) {
2931
- const e = unwrapTs(expr);
2932
- if (e.type === "Literal") return e.value === "";
2933
- if (e.type === "ConditionalExpression") return alwaysEmptyString(e.consequent) && alwaysEmptyString(e.alternate);
2934
- if (e.type === "LogicalExpression") return alwaysEmptyString(e.left) && alwaysEmptyString(e.right);
3163
+ function isServedBase(value) {
3164
+ return typeof value === "string" && value.startsWith("/") && !value.endsWith("/");
3165
+ }
3166
+ /** The top-level bindings declared `const`: a `let` may be reassigned on the way to the export. */
3167
+ function constBindings(program, bindings) {
3168
+ const names = /* @__PURE__ */ new Set();
3169
+ for (const stmt of program.body) {
3170
+ const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
3171
+ if (decl?.type !== "VariableDeclaration" || decl.kind !== "const") continue;
3172
+ for (const d of decl.declarations) if (d.id.type === "Identifier") names.add(d.id.name);
3173
+ }
3174
+ return new Map([...bindings].filter(([name]) => names.has(name)));
3175
+ }
3176
+ /** Whether no branch of a `?:` / `||` / `&&` / `??` tree is a literal base an app can be served under. */
3177
+ function neverABase(expr, consts) {
3178
+ const e = resolveBinding(expr, consts);
3179
+ if (e.type === "Literal") return !isServedBase(e.value);
3180
+ if (e.type === "ConditionalExpression") return neverABase(e.consequent, consts) && neverABase(e.alternate, consts);
3181
+ if (e.type === "LogicalExpression") return neverABase(e.left, consts) && neverABase(e.right, consts);
2935
3182
  return false;
2936
3183
  }
3184
+ /** `expr` with top-level `const` names followed to their initializers (`paths: { base }` naming `const base = ''`). */
3185
+ function resolveBinding(expr, consts) {
3186
+ let e = unwrapTs(expr);
3187
+ for (let i = 0; i < 4 && e.type === "Identifier"; i++) {
3188
+ const next = consts.get(e.name);
3189
+ if (!next) break;
3190
+ e = unwrapTs(next);
3191
+ }
3192
+ return e;
3193
+ }
2937
3194
  /**
2938
3195
  * `paths.base` off a resolved Kit-config object: `{ value }` for a non-empty string literal,
2939
3196
  * `{}` for any other expression (base exists, value unknowable — the `dev ? '' : '/repo'`
2940
- * deploy form), and undefined when absent or when every value it can take is the empty string.
3197
+ * deploy form), and undefined when absent or when no value it can take is a base SvelteKit serves under.
2941
3198
  */
2942
- function basePathOf(kitConfig, bindings) {
3199
+ function basePathOf(kitConfig, bindings, consts) {
2943
3200
  const paths = propOf(kitConfig, "paths");
2944
3201
  const pathsObj = paths ? unwrapToObjectExpression(paths.value, bindings) : void 0;
2945
3202
  if (!pathsObj) return void 0;
2946
3203
  const base = propOf(pathsObj, "base");
2947
3204
  if (!base) return void 0;
2948
- const value = unwrapTs(base.value);
2949
- if (value.type === "Literal") return typeof value.value === "string" && value.value !== "" ? { value: value.value } : void 0;
2950
- return alwaysEmptyString(value) ? void 0 : {};
3205
+ const value = resolveBinding(base.value, consts);
3206
+ if (value.type === "Literal") return isServedBase(value.value) ? { value: value.value } : void 0;
3207
+ return neverABase(value, consts) ? void 0 : {};
2951
3208
  }
2952
3209
  /** A property's key when it is a plain (non-computed) string or identifier key. */
2953
3210
  function keyNameOf(p) {
@@ -3173,7 +3430,33 @@ function findKitPathsBaseInSvelteConfig(source) {
3173
3430
  const bindings = collectTopLevelBindings(program);
3174
3431
  const kit = propOf(config, "kit");
3175
3432
  const kitObj = kit ? unwrapToObjectExpression(kit.value, bindings) : void 0;
3176
- return kitObj ? basePathOf(kitObj, bindings) : void 0;
3433
+ return kitObj ? basePathOf(kitObj, bindings, constBindings(program, bindings)) : void 0;
3434
+ }
3435
+ /**
3436
+ * `unwrapToObjectExpression`, but only where the literal is the value: a call passing more than one
3437
+ * argument (`mergeConfigs(a, b)`) or a spread could make it something else.
3438
+ */
3439
+ function knownObject(expr, bindings) {
3440
+ let current = expr;
3441
+ for (let i = 0; i < 4 && current; i++) {
3442
+ const e = unwrapTs(current);
3443
+ if (e.type === "ObjectExpression") return e.properties.some((p) => p.type === "SpreadElement") ? void 0 : e;
3444
+ if (e.type === "Identifier") current = bindings.get(e.name);
3445
+ else if (e.type === "CallExpression" && e.arguments.length === 1) current = e.arguments[0];
3446
+ else return void 0;
3447
+ }
3448
+ }
3449
+ /** Whether `kit.router.type` is the literal `'hash'` in a config whose value is known. */
3450
+ function findKitHashRouterInSvelteConfig(source) {
3451
+ const program = programOf(source, "svelte.config.js");
3452
+ if (!program) return false;
3453
+ const bindings = collectTopLevelBindings(program);
3454
+ const objectAt = (obj, key) => {
3455
+ const p = obj && propOf(obj, key);
3456
+ return p ? knownObject(p.value, bindings) : void 0;
3457
+ };
3458
+ const router = objectAt(objectAt(knownObject(findExportedExpression(program), bindings), "kit"), "router");
3459
+ return stringLiteralOf(router && propOf(router, "type")?.value) === "hash";
3177
3460
  }
3178
3461
  /**
3179
3462
  * Local names bound to `sveltekit` imported from '@sveltejs/kit/vite'. When no such import is
@@ -3206,7 +3489,7 @@ function findKitPathsBaseInViteConfig(source) {
3206
3489
  if (arg === void 0) return none;
3207
3490
  const kitConfig = unwrapToObjectExpression(arg, bindings);
3208
3491
  if (!kitConfig) return { kind: "unresolvable" };
3209
- const base = basePathOf(kitConfig, bindings);
3492
+ const base = basePathOf(kitConfig, bindings, constBindings(program, bindings));
3210
3493
  return base ? {
3211
3494
  kind: "resolved",
3212
3495
  base
@@ -3468,11 +3751,17 @@ const SITEMAP_SOURCE_PATHS = [
3468
3751
  "src/routes/sitemap.xml/+server.js"
3469
3752
  ];
3470
3753
  /** The `+server` endpoints above, wherever (group) directories nest them — filter with `servesRootFile`. */
3471
- const ROOT_FILE_ENDPOINT_GLOB = "src/routes/**/{robots.txt,sitemap.xml}/+server.{ts,js}";
3472
- /** Whether a `+server` endpoint serves `/<file>`: (group) directories add no URL segment. */
3754
+ const ROOT_FILE_ENDPOINT_GLOB = "src/routes/**/{robots.txt,sitemap*.xml}/+server.{ts,js}";
3755
+ /**
3756
+ * Whether a `+server` endpoint serves `/<file>`: (group) directories add no URL segment, and an optional
3757
+ * parameter in the name (`sitemap[[page]].xml`) still answers the bare name.
3758
+ */
3473
3759
  function servesRootFile(rel, file) {
3474
3760
  const segments = rel.split("/");
3475
- return segments[0] === "src" && segments[1] === "routes" && segments.at(-2) === file && /^\+server\.(ts|js)$/.test(segments.at(-1) ?? "") && segments.slice(2, -2).every((s) => /^\(.+\)$/.test(s));
3761
+ const dot = file.lastIndexOf(".");
3762
+ const optional = new RegExp(`^${file.slice(0, dot)}\\[\\[[^\\]]+\\]\\]\\${file.slice(dot)}$`);
3763
+ const dir = segments.at(-2) ?? "";
3764
+ return segments[0] === "src" && segments[1] === "routes" && (dir === file || optional.test(dir)) && /^\+server\.(ts|js)$/.test(segments.at(-1) ?? "") && segments.slice(2, -2).every((s) => /^\(.+\)$/.test(s));
3476
3765
  }
3477
3766
  /** Vite's own config resolution order — only the first existing file is the one Vite loads. */
3478
3767
  const VITE_CONFIG_FILES = [
@@ -3545,7 +3834,7 @@ async function runRules(rules, ctx) {
3545
3834
  }));
3546
3835
  const results = [];
3547
3836
  const failedRules = [];
3548
- for (const outcome of perRule) if (Array.isArray(outcome)) results.push(...outcome);
3837
+ for (const outcome of perRule) if (Array.isArray(outcome)) for (const r of outcome) results.push(r);
3549
3838
  else failedRules.push(outcome);
3550
3839
  return {
3551
3840
  results,
@@ -3717,7 +4006,7 @@ function formatConsoleReport(results, config, options = {}) {
3717
4006
  }
3718
4007
  lines.push("");
3719
4008
  }
3720
- if (options.byRoute) lines.push(...byRouteTree(p, results, config, options.verbose ?? false));
4009
+ if (options.byRoute) for (const line of byRouteTree(p, results, config, options.verbose ?? false)) lines.push(line);
3721
4010
  if (options.verbose && summary.dynamic > 0) lines.push(p.dim("↯ = set dynamically (verified at runtime)."));
3722
4011
  return lines.join("\n").replace(/\n+$/, "\n");
3723
4012
  }
@@ -3744,7 +4033,7 @@ function formatAgentReport(results, config) {
3744
4033
  if (!groups.has(key)) groups.set(key, []);
3745
4034
  groups.get(key).push(r);
3746
4035
  }
3747
- const groupSeverity = (rs) => Math.min(...rs.map((r) => SEVERITY_RANK[effectiveSeverity(r, config)]));
4036
+ const groupSeverity = (rs) => rs.reduce((min, r) => Math.min(min, SEVERITY_RANK[effectiveSeverity(r, config)]), Infinity);
3748
4037
  const orderedGroups = [...groups.entries()].sort((a, b) => groupSeverity(a[1]) - groupSeverity(b[1]) || a[0].localeCompare(b[0]));
3749
4038
  for (const [loc, rs] of orderedGroups) {
3750
4039
  rs.sort((x, y) => SEVERITY_RANK[effectiveSeverity(x, config)] - SEVERITY_RANK[effectiveSeverity(y, config)] || x.id.localeCompare(y.id));
@@ -4580,4 +4869,4 @@ function formatHtmlReport(results, config, meta) {
4580
4869
  return buildHtmlDocument(buildJsonReport(results, config, meta), meta);
4581
4870
  }
4582
4871
  //#endregion
4583
- export { ANCESTRY_DEPENDENT_TAGS, APP_SCRIPT, ASIDE_DEMOTING_TAGS, CHILD_NODE_KEYS, IDREF_ATTRS, NAMING_ATTRS, ROBOTS_SOURCE_PATHS, ROOT_FILE_ENDPOINT_GLOB, SEARCH_RESULT_RULES, SECTIONING_TAGS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, a11yAbbrTitle, a11yAccessibleName, a11yAriaHiddenFocus, a11yDeprecatedAria, a11yDeprecatedAttr, a11yDeprecatedElement, a11yDisallowedAriaProps, a11yDisallowedElement, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoAccesskey, a11yNoAutofocus, a11yNoDuplicateDt, a11yNoMissingIdRef, a11yPatternTitle, a11yPermittedContents, a11yPlaceholderLabelOption, a11yPositiveTabindex, a11yRequireDatetime, a11yRequiredAriaProps, a11yRequiredElement, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUnverifiedIdRef, a11yUseList, addFactsDirectives, allRules, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectConstantListExports, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessAutoplayMuted, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, declaredInstalledPackages, declaredLocalPackages, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, escapeHtml, explainRule, findAttr, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, isClassicScriptType, isPenalized, isPlainObject, isSvgSrc, isTopFragment, lineOf, noColorPalette, packageJsonWorkspaceGlobs, parseComponentFacts, parseModuleProgram, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceIframeLoading, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, pnpmWorkspaceGlobs, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveLandmark, resolveRepoLocalPath, resolveRuleOptions, routeNeverSsr, runAnalysis, runRules, safeHref, scoreColor, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, servesRootFile, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, validateRuleSetting, valueFromNodes, withFailedRulesOff, withIndexableOff, withInstalledPackages, withPackageImports, withReadLimit, withWorkspacePackages };
4872
+ export { ANCESTRY_DEPENDENT_TAGS, APP_SCRIPT, ASIDE_DEMOTING_TAGS, CHILD_NODE_KEYS, IDREF_ATTRS, NAMING_ATTRS, ROBOTS_SOURCE_PATHS, ROOT_FILE_ENDPOINT_GLOB, SEARCH_RESULT_RULES, SECTIONING_TAGS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, a11yAbbrTitle, a11yAccessibleName, a11yAriaHiddenFocus, a11yDeprecatedAria, a11yDeprecatedAttr, a11yDeprecatedElement, a11yDisallowedAriaProps, a11yDisallowedElement, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoAccesskey, a11yNoAutofocus, a11yNoDuplicateDt, a11yNoMissingIdRef, a11yPatternTitle, a11yPermittedContents, a11yPlaceholderLabelOption, a11yPositiveTabindex, a11yRequireDatetime, a11yRequiredAriaProps, a11yRequiredElement, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUnverifiedIdRef, a11yUseList, addFactsDirectives, allRules, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentConstantLists, collectComponentFacts, collectConstantListExports, collectFalseExports, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessAutoplayMuted, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, declaredInstalledPackages, declaredLocalPackages, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, escapeHtml, explainRule, findAttr, findKitHashRouterInSvelteConfig, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatJsonReportChunks, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, hasRel, isClassicScriptType, isPenalized, isPlainObject, isSvgSrc, isTopFragment, layoutNeverRenders, lineOf, noColorPalette, packageJsonWorkspaceGlobs, pageRendersOnlyWhenBuilding, parseComponentFacts, parseModuleProgram, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceIframeLoading, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, pnpmWorkspaceGlobs, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveLandmark, resolveRepoLocalPath, resolveRuleOptions, routeNeverSsr, runAnalysis, runRules, safeHref, scoreColor, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, servesRootFile, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, unwrapTs, validateRuleSetting, valueFromNodes, walkEstree, withFailedRulesOff, withIndexableOff, withInstalledPackages, withPackageImports, withReadLimit, withWorkspacePackages };