@svelte-vitals/core 0.55.2 → 0.55.4
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -2
- package/dist/internal.d.ts +485 -259
- package/dist/internal.js +422 -138
- package/dist/{json-BQf9Qb2D.d.ts → json-CWGGrAda.d.ts} +88 -43
- package/dist/{markdown-DgHBAvYl.js → markdown-YuU0EwLR.js} +1040 -449
- package/package.json +6 -6
package/dist/internal.js
CHANGED
|
@@ -1,44 +1,10 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
|
|
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
|
|
35
|
-
//#region src/images.ts
|
|
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-YuU0EwLR.js";
|
|
2
|
+
// 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) {
|
|
38
5
|
return /^data:image\/svg\+xml[;,]/i.test(src) || /\.svg$/i.test(src.split(/[?#]/, 1)[0]);
|
|
39
6
|
}
|
|
40
|
-
|
|
41
|
-
//#region src/component-parse.ts
|
|
7
|
+
// src/component-parse.ts
|
|
42
8
|
const KNOWN_TAGS = new Set(Object.keys(HTML_SPEC.elements).map((k) => k.replace(/^svg:/, "").toLowerCase()));
|
|
43
9
|
/**
|
|
44
10
|
* Whether `expr` is a length-only list constructor: `Array(n)` / `new Array(n)` (single argument =
|
|
@@ -161,21 +127,46 @@ function collectConstantLists(programs, fragment, opts = {}) {
|
|
|
161
127
|
const stmt = opts.exported ? unwrapExport(top) : top;
|
|
162
128
|
if (stmt?.type !== "VariableDeclaration" || stmt.kind !== "const") continue;
|
|
163
129
|
for (const d of stmt.declarations ?? []) {
|
|
164
|
-
const init = unwrapTs(d?.init);
|
|
130
|
+
const init = opts.sets ? setLiteralList(unwrapTs(d?.init)) : unwrapTs(d?.init);
|
|
165
131
|
if (d?.id?.type === "Identifier" && init?.type === "ArrayExpression") {
|
|
166
132
|
if (!(init.elements ?? []).some((el) => el?.type === "SpreadElement")) candidates.set(d.id.name, d.id);
|
|
167
133
|
}
|
|
168
134
|
}
|
|
169
135
|
}
|
|
170
136
|
if (candidates.size === 0) return /* @__PURE__ */ new Set();
|
|
137
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
138
|
+
if (opts.exported) for (const program of programs) walkEstree(program, (n) => {
|
|
139
|
+
if (n.type !== "VariableDeclaration" || n.kind !== "const") return;
|
|
140
|
+
for (const d of n.declarations ?? []) {
|
|
141
|
+
const init = unwrapTs(d?.init);
|
|
142
|
+
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));
|
|
143
|
+
if (d?.id?.type !== "Identifier" || !list || candidates.has(d.id.name)) continue;
|
|
144
|
+
safe.add(list);
|
|
145
|
+
aliases.set(d.id.name, list.name);
|
|
146
|
+
candidates.set(d.id.name, d.id);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
171
149
|
for (const node of candidates.values()) safe.add(node);
|
|
172
150
|
const namespaces = new Set([...opts.imports ?? []].filter(([, b]) => b.imported === "*").map(([local]) => local));
|
|
173
151
|
const unsafe = /* @__PURE__ */ new Set();
|
|
174
152
|
const roots = [...programs, fragment];
|
|
175
153
|
const listOf = (n) => n?.type === "Identifier" && candidates.has(n.name) ? n.name : void 0;
|
|
154
|
+
const outerList = listOf;
|
|
176
155
|
const nsMember = (n) => n?.type === "MemberExpression" && !n.computed && namespaces.has(listOf(n.object)) ? `${n.object.name}.${n.property.name}` : void 0;
|
|
156
|
+
const outerMember = nsMember;
|
|
177
157
|
const members = /* @__PURE__ */ new Set();
|
|
178
|
-
|
|
158
|
+
const declared = /* @__PURE__ */ new Set();
|
|
159
|
+
for (const program of programs) {
|
|
160
|
+
const imported = /* @__PURE__ */ new Set();
|
|
161
|
+
for (const top of program.body ?? []) if (top?.type === "ImportDeclaration") for (const s of top.specifiers ?? []) imported.add(s?.local?.name);
|
|
162
|
+
for (const name of collectProgramBindings(program)) if (!imported.has(name)) declared.add(name);
|
|
163
|
+
}
|
|
164
|
+
for (const root of roots) walkScoped(root, (n, shadowed) => {
|
|
165
|
+
const listOf = (x) => {
|
|
166
|
+
const name = outerList(x);
|
|
167
|
+
return name && !shadowed.has(name) ? name : void 0;
|
|
168
|
+
};
|
|
169
|
+
const nsMember = (x) => x?.type === "MemberExpression" && listOf(x.object) ? outerMember(x) : void 0;
|
|
179
170
|
if (n.type === "EachBlock" && (listOf(n.expression) || nsMember(n.expression))) safe.add(n.expression);
|
|
180
171
|
if (n.type === "ForOfStatement" && (listOf(n.right) || nsMember(n.right))) safe.add(n.right);
|
|
181
172
|
if (n.type === "ArrayExpression") {
|
|
@@ -183,29 +174,46 @@ function collectConstantLists(programs, fragment, opts = {}) {
|
|
|
183
174
|
}
|
|
184
175
|
if (nsMember(n)) members.add(nsMember(n));
|
|
185
176
|
if (n.type === "MemberExpression") {
|
|
186
|
-
|
|
187
|
-
if (
|
|
177
|
+
const object = n.object ? unwrapTs(n.object) : n.object;
|
|
178
|
+
if (listOf(object) && !(n.computed && namespaces.has(object.name))) safe.add(object);
|
|
179
|
+
if (nsMember(object)) safe.add(object);
|
|
188
180
|
if (!n.computed) safe.add(n.property);
|
|
189
181
|
}
|
|
190
182
|
const target = n.type === "AssignmentExpression" ? n.left : n.type === "UpdateExpression" ? n.argument : void 0;
|
|
191
|
-
|
|
192
|
-
|
|
183
|
+
let root = target;
|
|
184
|
+
while (root && (root.type === "MemberExpression" || root.type.startsWith("TS"))) root = root.type === "MemberExpression" ? root.object : root.expression;
|
|
185
|
+
if (target?.type === "MemberExpression" && listOf(root)) unsafe.add(listOf(root));
|
|
186
|
+
const called = n.type === "CallExpression" && n.callee?.type === "MemberExpression" ? unwrapTs(n.callee.object) : void 0;
|
|
193
187
|
const calledList = listOf(called) ?? nsMember(called);
|
|
194
188
|
if (calledList) {
|
|
195
189
|
const { computed, property } = n.callee;
|
|
196
190
|
const method = !computed ? property?.name : property?.type === "Literal" && typeof property.value === "string" ? property.value : void 0;
|
|
197
191
|
if (computed && method === void 0 || method !== void 0 && MUTATING_METHODS.has(method)) unsafe.add(calledList);
|
|
198
192
|
}
|
|
193
|
+
const callee = n.type === "CallExpression" || n.type === "NewExpression" ? rootObjectName(n.callee) : void 0;
|
|
194
|
+
if (opts.exported && callee !== void 0 && !declared.has(callee) && !shadowed.has(callee)) {
|
|
195
|
+
for (const arg of n.arguments ?? []) if (listOf(unwrapTs(arg))) safe.add(unwrapTs(arg));
|
|
196
|
+
}
|
|
199
197
|
if (n.type === "BindDirective" && listOf(rootObjectNode(n.expression))) unsafe.add(listOf(rootObjectNode(n.expression)));
|
|
200
198
|
if (n.type === "Property" && !n.computed && !n.shorthand) safe.add(n.key);
|
|
201
199
|
});
|
|
202
|
-
for (const root of roots) walkEvalScope(root, (n) => {
|
|
200
|
+
for (const root of roots) walkEvalScope(root, (n, shadowed) => {
|
|
203
201
|
if (n.type.startsWith("TS") && !TS_VALUE_NODES.has(n.type)) return true;
|
|
204
202
|
const name = listOf(n) ?? nsMember(n);
|
|
205
|
-
if (name && !safe.has(n)) unsafe.add(name);
|
|
203
|
+
if (name && !safe.has(n) && !shadowed.has(name.split(".")[0])) unsafe.add(name);
|
|
206
204
|
return false;
|
|
207
205
|
}, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
|
|
208
|
-
|
|
206
|
+
for (const [alias, list] of aliases) if (unsafe.has(alias)) unsafe.add(list);
|
|
207
|
+
return new Set([...candidates.keys(), ...members].filter((key) => !aliases.has(key) && !unsafe.has(key) && !unsafe.has(key.split(".")[0])));
|
|
208
|
+
}
|
|
209
|
+
/** A component's script `const` lists that nothing in its scripts or template can change. */
|
|
210
|
+
function collectComponentConstantLists(ast, opts = {}) {
|
|
211
|
+
return collectConstantLists([ast.module?.content, ast.instance?.content].filter(Boolean), ast.fragment ?? ast, opts);
|
|
212
|
+
}
|
|
213
|
+
/** The array literal of `new Set([…])`, or the node itself. */
|
|
214
|
+
function setLiteralList(n) {
|
|
215
|
+
const arg = n?.type === "NewExpression" && n.callee?.type === "Identifier" && n.callee.name === "Set" ? n.arguments : void 0;
|
|
216
|
+
return arg?.length === 1 && unwrapTs(arg[0])?.type === "ArrayExpression" ? unwrapTs(arg[0]) : n;
|
|
209
217
|
}
|
|
210
218
|
/**
|
|
211
219
|
* Export names of a module's constant lists — `export const X = [ … ]`, or a local one exported by
|
|
@@ -288,6 +296,14 @@ function isEffectRootCall(node) {
|
|
|
288
296
|
* `$state.raw(...)`, or `$state.frozen(...)` — but NOT readers like
|
|
289
297
|
* `$state.snapshot(...)`, which would otherwise pollute the state-name set (correctness/effect-as-derived).
|
|
290
298
|
*/
|
|
299
|
+
/** An absent argument or a literal no reader can write through: `0`, `'x'`, `null`, `-1`, `` `x` ``. */
|
|
300
|
+
function isPrimitiveLiteral(node) {
|
|
301
|
+
if (node === void 0) return true;
|
|
302
|
+
if (node.type === "Literal") return !("regex" in node);
|
|
303
|
+
if (node.type === "TemplateLiteral") return node.expressions.length === 0;
|
|
304
|
+
if (node.type === "UnaryExpression") return node.argument?.type === "Literal";
|
|
305
|
+
return node.type === "Identifier" && node.name === "undefined";
|
|
306
|
+
}
|
|
291
307
|
function isStateDeclaration(node) {
|
|
292
308
|
const c = node?.callee;
|
|
293
309
|
if (c?.type === "Identifier") return c.name === "$state";
|
|
@@ -296,7 +312,18 @@ function isStateDeclaration(node) {
|
|
|
296
312
|
}
|
|
297
313
|
/** True when a function's body does nothing but assign to `$state` identifiers (correctness/effect-as-derived). */
|
|
298
314
|
function bodyOnlyAssignsState(fn, stateNames) {
|
|
299
|
-
const
|
|
315
|
+
const readsItself = (expr) => {
|
|
316
|
+
let reads = false;
|
|
317
|
+
const names = /* @__PURE__ */ new Set();
|
|
318
|
+
walkEstree(expr.right, (n) => {
|
|
319
|
+
if (n?.type === "MemberExpression" && !n.computed) names.add(n.property);
|
|
320
|
+
if (n?.type === "Property" && !n.computed && !n.shorthand) names.add(n.key);
|
|
321
|
+
if (n?.type === "TSTypeQuery") names.add(n.exprName);
|
|
322
|
+
reads ||= n?.type === "Identifier" && n.name === expr.left.name && !names.has(n);
|
|
323
|
+
});
|
|
324
|
+
return reads;
|
|
325
|
+
};
|
|
326
|
+
const isStateAssign = (expr) => expr?.type === "AssignmentExpression" && expr.operator === "=" && expr.left?.type === "Identifier" && stateNames.has(expr.left.name) && !readsItself(expr);
|
|
300
327
|
const body = fn?.body;
|
|
301
328
|
if (!body) return false;
|
|
302
329
|
if (body.type !== "BlockStatement") return isStateAssign(body);
|
|
@@ -310,6 +337,36 @@ function isDerivedDeclaration(node) {
|
|
|
310
337
|
if (c?.type === "MemberExpression" && c.object?.type === "Identifier" && c.object.name === "$derived") return c.property?.type === "Identifier" && c.property.name === "by";
|
|
311
338
|
return false;
|
|
312
339
|
}
|
|
340
|
+
const OBJECT_TYPES = /* @__PURE__ */ new Set([
|
|
341
|
+
"TSArrayType",
|
|
342
|
+
"TSTupleType",
|
|
343
|
+
"TSTypeLiteral"
|
|
344
|
+
]);
|
|
345
|
+
const OBJECT_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
346
|
+
"Array",
|
|
347
|
+
"ReadonlyArray",
|
|
348
|
+
"Record",
|
|
349
|
+
"Map",
|
|
350
|
+
"Set",
|
|
351
|
+
"WeakMap",
|
|
352
|
+
"WeakSet",
|
|
353
|
+
"Date"
|
|
354
|
+
]);
|
|
355
|
+
const OBJECT_VALUES = /* @__PURE__ */ new Set([
|
|
356
|
+
"ObjectExpression",
|
|
357
|
+
"ArrayExpression",
|
|
358
|
+
"NewExpression"
|
|
359
|
+
]);
|
|
360
|
+
/** A `$state` call whose initial value or type argument is an object, array or class instance. */
|
|
361
|
+
function isObjectState(call) {
|
|
362
|
+
const arg = unwrapTs(call.arguments?.[0]);
|
|
363
|
+
if (isPrimitiveLiteral(arg)) return false;
|
|
364
|
+
if (OBJECT_VALUES.has(arg?.type)) return true;
|
|
365
|
+
if (arg?.type === "LogicalExpression" && arg.operator !== "&&" && !isPrimitiveLiteral(unwrapTs(arg.left)) && OBJECT_VALUES.has(unwrapTs(arg.right)?.type)) return true;
|
|
366
|
+
const type = (call.typeArguments ?? call.typeParameters)?.params?.[0];
|
|
367
|
+
if (type?.type === "TSTypeReference") return OBJECT_TYPE_NAMES.has(type.typeName?.name);
|
|
368
|
+
return OBJECT_TYPES.has(type?.type);
|
|
369
|
+
}
|
|
313
370
|
function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
|
|
314
371
|
const record = (name, kind) => {
|
|
315
372
|
acc.add(name);
|
|
@@ -319,14 +376,38 @@ function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
|
|
|
319
376
|
set.add(kind);
|
|
320
377
|
}
|
|
321
378
|
};
|
|
379
|
+
const primitiveStates = /* @__PURE__ */ new Set();
|
|
380
|
+
const objectStates = /* @__PURE__ */ new Set();
|
|
381
|
+
const heldObjects = /* @__PURE__ */ new Set();
|
|
382
|
+
walkEstree(root, (n) => {
|
|
383
|
+
const init = n?.type === "VariableDeclarator" ? unwrapTs(n.init) : void 0;
|
|
384
|
+
if (n?.id?.type !== "Identifier" || !isStateDeclaration(init)) return;
|
|
385
|
+
(isPrimitiveLiteral(init.arguments?.[0]) ? primitiveStates : objectStates).add(n.id.name);
|
|
386
|
+
if (localStates && isObjectState(init)) heldObjects.add(n.id.name);
|
|
387
|
+
});
|
|
388
|
+
for (const name of objectStates) primitiveStates.delete(name);
|
|
389
|
+
const aliasOf = /* @__PURE__ */ new Map();
|
|
390
|
+
const reassigned = /* @__PURE__ */ new Set();
|
|
391
|
+
if (localStates) walkEstree(root, (n) => {
|
|
392
|
+
const target = n?.type === "AssignmentExpression" ? n.left : n?.type === "UpdateExpression" ? n.argument : void 0;
|
|
393
|
+
if (target?.type === "Identifier") reassigned.add(target.name);
|
|
394
|
+
});
|
|
395
|
+
if (localStates) for (let pass = 0; pass < 2; pass++) walkEstree(root, (n) => {
|
|
396
|
+
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];
|
|
397
|
+
const r = rootObjectName(unwrapTs(from));
|
|
398
|
+
const owner = r && (stateNames.has(r) ? r : aliasOf.get(r));
|
|
399
|
+
if (id?.type === "Identifier" && owner && !stateNames.has(id.name) && !reassigned.has(id.name)) aliasOf.set(id.name, owner);
|
|
400
|
+
});
|
|
322
401
|
const visit = (stateNames) => (n, scope) => {
|
|
323
402
|
const shadowed = (name) => name === void 0 || scope.has(name);
|
|
403
|
+
const mutated = (r) => {
|
|
404
|
+
if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
|
|
405
|
+
else if (r && stateNames.has(aliasOf.get(r))) record(aliasOf.get(r), "mutate");
|
|
406
|
+
};
|
|
324
407
|
if (n?.type === "AssignmentExpression") {
|
|
325
408
|
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
|
-
|
|
328
|
-
if (r && stateNames.has(r) && !shadowed(r)) record(r, "mutate");
|
|
329
|
-
} else if (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern") {
|
|
409
|
+
else if (n.left?.type === "MemberExpression") mutated(rootObjectName(n.left));
|
|
410
|
+
else if (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern") {
|
|
330
411
|
const bound = /* @__PURE__ */ new Set();
|
|
331
412
|
addBoundNames(n.left, bound);
|
|
332
413
|
for (const name of bound) if (stateNames.has(name) && !shadowed(name)) record(name, "reassign");
|
|
@@ -334,24 +415,30 @@ function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
|
|
|
334
415
|
} else if (n?.type === "UpdateExpression") {
|
|
335
416
|
if (n.argument?.type === "Identifier") {
|
|
336
417
|
if (stateNames.has(n.argument.name) && !shadowed(n.argument.name)) record(n.argument.name, "reassign");
|
|
337
|
-
} else
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
}
|
|
418
|
+
} else mutated(rootObjectName(n.argument));
|
|
419
|
+
} else if (n?.type === "UnaryExpression" && n.operator === "delete") mutated(rootObjectName(n.argument));
|
|
420
|
+
else if (n?.type === "CallExpression") {
|
|
421
|
+
if (n.callee?.type === "MemberExpression") mutated(rootObjectName(n.callee));
|
|
349
422
|
for (const a of n.arguments ?? []) {
|
|
350
423
|
const arg = a?.type === "SpreadElement" ? a.argument : a;
|
|
351
424
|
const r = rootObjectName(arg);
|
|
352
425
|
if (r && stateNames.has(r) && !shadowed(r)) record(r, "escape");
|
|
353
426
|
}
|
|
354
|
-
}
|
|
427
|
+
} else if (n?.type === "ReturnStatement") {
|
|
428
|
+
const out = unwrapTs(n.argument);
|
|
429
|
+
const r = out?.type === "Identifier" ? out.name : void 0;
|
|
430
|
+
if (r && stateNames.has(r) && !shadowed(r) && !primitiveStates.has(r)) record(r, "escape");
|
|
431
|
+
} else if (heldObjects.size > 0 && (n?.type === "ObjectExpression" || n?.type === "ArrayExpression")) {
|
|
432
|
+
const values = n.type === "ObjectExpression" ? (n.properties ?? []).map((p) => p?.value) : n.elements;
|
|
433
|
+
for (const v of values ?? []) {
|
|
434
|
+
const placed = unwrapTs(v);
|
|
435
|
+
const r = placed?.type === "Identifier" ? placed.name : void 0;
|
|
436
|
+
if (r && heldObjects.has(r) && stateNames.has(r) && !shadowed(r)) record(r, "escape");
|
|
437
|
+
}
|
|
438
|
+
} else if ((n?.type === "Property" || n?.type === "MethodDefinition") && n.kind === "get") walkEstree(n.value?.body, (m) => {
|
|
439
|
+
const r = m?.type === "ReturnStatement" ? rootObjectName(m.argument) : void 0;
|
|
440
|
+
if (r && stateNames.has(r) && !shadowed(r) && !primitiveStates.has(r)) record(r, "escape");
|
|
441
|
+
});
|
|
355
442
|
};
|
|
356
443
|
walkScoped(root, visit(stateNames));
|
|
357
444
|
if (!localStates) return;
|
|
@@ -1028,12 +1115,25 @@ function selectNativeRole(attributes) {
|
|
|
1028
1115
|
if (attrValueOf(size) !== "static") return void 0;
|
|
1029
1116
|
return Number(attrTextOf(size)) > 1 ? "listbox" : "combobox";
|
|
1030
1117
|
}
|
|
1031
|
-
|
|
1118
|
+
/** svelte-dnd-action's zone actions, which set `role="listitem"` on each child element at runtime. */
|
|
1119
|
+
const DND_ZONE_ACTIONS = /* @__PURE__ */ new Set(["dndzone", "dragHandleZone"]);
|
|
1120
|
+
/** A zone action unless its options' final say on `autoAriaDisabled` is a literal `true`, which turns the item roles off. */
|
|
1121
|
+
function isDndZone(a) {
|
|
1122
|
+
if (a?.type !== "UseDirective" || !DND_ZONE_ACTIONS.has(a.name)) return false;
|
|
1123
|
+
const opts = unwrapTs(a.expression);
|
|
1124
|
+
if (opts?.type !== "ObjectExpression") return true;
|
|
1125
|
+
const keyOf = (p) => p.computed ? p.key?.type === "Literal" ? String(p.key.value) : void 0 : String(p.key?.name ?? p.key?.value);
|
|
1126
|
+
const last = opts.properties.filter((p) => p.type === "SpreadElement" || p.type === "Property" && (keyOf(p) === void 0 || keyOf(p) === "autoAriaDisabled")).at(-1);
|
|
1127
|
+
return !(last?.type === "Property" && keyOf(last) === "autoAriaDisabled" && last.value?.type === "Literal" && last.value.value === true);
|
|
1128
|
+
}
|
|
1129
|
+
function collectAriaElements(node, source, acc, parentIsDndZone = false) {
|
|
1032
1130
|
if (Array.isArray(node)) {
|
|
1033
|
-
for (const child of node) collectAriaElements(child, source, acc);
|
|
1131
|
+
for (const child of node) collectAriaElements(child, source, acc, parentIsDndZone);
|
|
1034
1132
|
return;
|
|
1035
1133
|
}
|
|
1036
1134
|
if (!node || typeof node !== "object") return;
|
|
1135
|
+
let inDndZone = parentIsDndZone;
|
|
1136
|
+
if (typeof node.type === "string" && (node.type.endsWith("Element") || node.type === "Component" || node.type.startsWith("Svelte")) && node.type !== "SvelteBoundary") inDndZone = Array.isArray(node.attributes) && node.attributes.some(isDndZone);
|
|
1037
1137
|
if (node.type === "RegularElement" && Array.isArray(node.attributes)) {
|
|
1038
1138
|
const roleAttr = findAttr(node.attributes, "role");
|
|
1039
1139
|
const ariaAttrs = node.attributes.filter((a) => a?.type === "Attribute" && typeof a.name === "string" && a.name.toLowerCase().startsWith("aria-"));
|
|
@@ -1043,7 +1143,7 @@ function collectAriaElements(node, source, acc) {
|
|
|
1043
1143
|
const hasList = tag === "input" && findAttr(node.attributes, "list") !== void 0;
|
|
1044
1144
|
const selectKind = tag === "select" ? selectNativeRole(node.attributes) : void 0;
|
|
1045
1145
|
const hasSpread = node.attributes.some((a) => a?.type === "SpreadAttribute");
|
|
1046
|
-
const hasAction = node.attributes.some((a) => a?.type === "UseDirective");
|
|
1146
|
+
const hasAction = parentIsDndZone || node.attributes.some((a) => a?.type === "UseDirective");
|
|
1047
1147
|
acc.push({
|
|
1048
1148
|
tag,
|
|
1049
1149
|
line: lineOf(source, node.start),
|
|
@@ -1061,7 +1161,7 @@ function collectAriaElements(node, source, acc) {
|
|
|
1061
1161
|
});
|
|
1062
1162
|
}
|
|
1063
1163
|
}
|
|
1064
|
-
for (const key of CHILD_NODE_KEYS) if (key in node) collectAriaElements(node[key], source, acc);
|
|
1164
|
+
for (const key of CHILD_NODE_KEYS) if (key in node) collectAriaElements(node[key], source, acc, inDndZone);
|
|
1065
1165
|
}
|
|
1066
1166
|
/** This element's `Attribute` nodes (directives/spreads excluded) as `isInteractiveElement`'s
|
|
1067
1167
|
* input shape. */
|
|
@@ -1739,6 +1839,7 @@ function isPropsCall(node) {
|
|
|
1739
1839
|
}
|
|
1740
1840
|
/** Whether a CallExpression is a `$bindable(...)` call (a destructured prop's default value). */
|
|
1741
1841
|
function isBindableCall(node) {
|
|
1842
|
+
node = unwrapTs(node);
|
|
1742
1843
|
return node?.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === "$bindable";
|
|
1743
1844
|
}
|
|
1744
1845
|
/**
|
|
@@ -1809,6 +1910,92 @@ const MUTATING_METHODS = /* @__PURE__ */ new Set([
|
|
|
1809
1910
|
"delete",
|
|
1810
1911
|
"clear"
|
|
1811
1912
|
]);
|
|
1913
|
+
/** Built-in types whose own methods include the mutating method names above. */
|
|
1914
|
+
const COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
1915
|
+
"Array",
|
|
1916
|
+
"ReadonlyArray",
|
|
1917
|
+
"Set",
|
|
1918
|
+
"ReadonlySet",
|
|
1919
|
+
"Map",
|
|
1920
|
+
"ReadonlyMap",
|
|
1921
|
+
"WeakMap",
|
|
1922
|
+
"WeakSet",
|
|
1923
|
+
"SvelteSet",
|
|
1924
|
+
"SvelteMap",
|
|
1925
|
+
"URLSearchParams",
|
|
1926
|
+
"SvelteURLSearchParams",
|
|
1927
|
+
"FormData",
|
|
1928
|
+
"Headers",
|
|
1929
|
+
"Int8Array",
|
|
1930
|
+
"Uint8Array",
|
|
1931
|
+
"Uint8ClampedArray",
|
|
1932
|
+
"Int16Array",
|
|
1933
|
+
"Uint16Array",
|
|
1934
|
+
"Int32Array",
|
|
1935
|
+
"Uint32Array",
|
|
1936
|
+
"Float16Array",
|
|
1937
|
+
"Float32Array",
|
|
1938
|
+
"Float64Array",
|
|
1939
|
+
"BigInt64Array",
|
|
1940
|
+
"BigUint64Array"
|
|
1941
|
+
]);
|
|
1942
|
+
const NON_COLLECTION_NODES = /* @__PURE__ */ new Set([
|
|
1943
|
+
"TSStringKeyword",
|
|
1944
|
+
"TSNumberKeyword",
|
|
1945
|
+
"TSBooleanKeyword",
|
|
1946
|
+
"TSBigIntKeyword",
|
|
1947
|
+
"TSNullKeyword",
|
|
1948
|
+
"TSUndefinedKeyword",
|
|
1949
|
+
"TSFunctionType",
|
|
1950
|
+
"TSTypeLiteral",
|
|
1951
|
+
"TSLiteralType"
|
|
1952
|
+
]);
|
|
1953
|
+
/**
|
|
1954
|
+
* The props whose declared type, from the `$props()` annotation, is known not to be a collection: a
|
|
1955
|
+
* method called on one is its own API (`queryClient.clear()`), not an array, Set or Map mutation.
|
|
1956
|
+
* A named type counts unless it is a built-in collection or a same-file alias of one.
|
|
1957
|
+
*/
|
|
1958
|
+
function nonCollectionProps(program) {
|
|
1959
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1960
|
+
walkEstree(program, (n) => {
|
|
1961
|
+
if ((n.type === "TSInterfaceDeclaration" || n.type === "TSTypeAliasDeclaration") && n.id?.name) decls.set(n.id.name, n);
|
|
1962
|
+
});
|
|
1963
|
+
const unwrap = (t) => t?.type === "TSTypeAnnotation" ? t.typeAnnotation : t;
|
|
1964
|
+
const nonCollection = (t, depth) => {
|
|
1965
|
+
t = unwrap(t);
|
|
1966
|
+
if (!t || depth > 4) return false;
|
|
1967
|
+
if (NON_COLLECTION_NODES.has(t.type)) return true;
|
|
1968
|
+
if (t.type === "TSUnionType") return t.types.every((u) => nonCollection(u, depth + 1));
|
|
1969
|
+
if (t.type !== "TSTypeReference" || t.typeName?.type !== "Identifier") return false;
|
|
1970
|
+
const name = t.typeName.name;
|
|
1971
|
+
if (COLLECTION_TYPES.has(name)) return false;
|
|
1972
|
+
const decl = decls.get(name);
|
|
1973
|
+
if (decl?.type === "TSTypeAliasDeclaration") return nonCollection(decl.typeAnnotation, depth + 1);
|
|
1974
|
+
if (decl?.type === "TSInterfaceDeclaration") return !(decl.extends ?? []).some((h) => COLLECTION_TYPES.has(h.expression?.name));
|
|
1975
|
+
return !(t.typeArguments ?? t.typeParameters)?.params?.length;
|
|
1976
|
+
};
|
|
1977
|
+
const membersOf = (t, depth) => {
|
|
1978
|
+
t = unwrap(t);
|
|
1979
|
+
if (t?.type === "TSTypeLiteral") return t.members;
|
|
1980
|
+
if (t?.type !== "TSTypeReference" || depth > 4) return void 0;
|
|
1981
|
+
const decl = decls.get(t.typeName?.name);
|
|
1982
|
+
if (decl?.type === "TSInterfaceDeclaration") return decl.body?.body;
|
|
1983
|
+
return decl?.type === "TSTypeAliasDeclaration" ? membersOf(decl.typeAnnotation, depth + 1) : void 0;
|
|
1984
|
+
};
|
|
1985
|
+
const names = /* @__PURE__ */ new Set();
|
|
1986
|
+
walkEstree(program, (n) => {
|
|
1987
|
+
if (n.type !== "VariableDeclarator" || !n.init || !isPropsCall(unwrapTs(n.init))) return;
|
|
1988
|
+
if (n.id?.type !== "ObjectPattern") return;
|
|
1989
|
+
const types = /* @__PURE__ */ new Map();
|
|
1990
|
+
for (const m of membersOf(n.id.typeAnnotation, 0) ?? []) if (m.type === "TSPropertySignature" && m.key?.type === "Identifier") types.set(m.key.name, m.typeAnnotation);
|
|
1991
|
+
for (const p of n.id.properties ?? []) {
|
|
1992
|
+
if (p?.type !== "Property" || p.key?.type !== "Identifier" || !types.has(p.key.name)) continue;
|
|
1993
|
+
const local = p.value?.type === "AssignmentPattern" ? p.value.left : p.value;
|
|
1994
|
+
if (local?.type === "Identifier" && nonCollection(types.get(p.key.name), 0)) names.add(local.name);
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
return names;
|
|
1998
|
+
}
|
|
1812
1999
|
/**
|
|
1813
2000
|
* Flag mutations of a non-`$bindable` prop (correctness/prop-mutation): a member-expression write
|
|
1814
2001
|
* (`prop.x = …`, `prop.x += …`, `prop.x++`), `delete prop.x`, or a call to a mutating
|
|
@@ -1820,16 +2007,18 @@ const MUTATING_METHODS = /* @__PURE__ */ new Set([
|
|
|
1820
2007
|
* A `legacy` (export let) prop is flagged only for `delete` and mutating method calls — the legacy
|
|
1821
2008
|
* compiler turns a member write or update into an invalidating assignment (`prop(prop().x = …, true)`).
|
|
1822
2009
|
*/
|
|
1823
|
-
function collectPropMutations(root, propNames, source, acc, legacy) {
|
|
2010
|
+
function collectPropMutations(root, propNames, source, acc, legacy, ownApi = /* @__PURE__ */ new Set()) {
|
|
1824
2011
|
if (propNames.size === 0) return;
|
|
1825
2012
|
const exemptCalls = /* @__PURE__ */ new Set();
|
|
2013
|
+
const returnedToProp = /* @__PURE__ */ new Set();
|
|
1826
2014
|
if (legacy.size > 0) walkEstree(root, (fn) => {
|
|
1827
|
-
|
|
2015
|
+
const body = isDeferredBody(fn) ? fn.body : fn.type === "LabeledStatement" && fn.label?.name === "$" ? fn.body : void 0;
|
|
2016
|
+
if (!body) return;
|
|
1828
2017
|
const assigned = /* @__PURE__ */ new Set();
|
|
1829
|
-
walkScoped(
|
|
2018
|
+
walkScoped(body, (m, scope) => {
|
|
1830
2019
|
if (m.type === "AssignmentExpression" && m.left?.type === "Identifier" && !scope.has(m.left.name)) assigned.add(m.left.name);
|
|
1831
2020
|
});
|
|
1832
|
-
walkEstree(
|
|
2021
|
+
walkEstree(body, (m) => {
|
|
1833
2022
|
const r = m.type === "CallExpression" ? rootObjectName(m.callee?.object) : m.type === "UnaryExpression" && m.operator === "delete" ? rootObjectName(m.argument) : void 0;
|
|
1834
2023
|
if (r && legacy.has(r) && assigned.has(r)) exemptCalls.add(m);
|
|
1835
2024
|
});
|
|
@@ -1837,6 +2026,20 @@ function collectPropMutations(root, propNames, source, acc, legacy) {
|
|
|
1837
2026
|
walkEstree(root, (m) => {
|
|
1838
2027
|
const call = m.type === "AssignmentExpression" && m.left?.type === "Identifier" ? m.right : void 0;
|
|
1839
2028
|
if (call?.type === "CallExpression" && rootObjectName(call.callee?.object) === m.left.name && call.arguments?.length === 1 && call.arguments[0]?.type === "ObjectExpression") exemptCalls.add(call);
|
|
2029
|
+
const awaited = m.type === "AwaitExpression" ? unwrapTs(m.argument) : void 0;
|
|
2030
|
+
if (awaited?.type === "CallExpression") exemptCalls.add(awaited);
|
|
2031
|
+
const chained = m.type === "CallExpression" && m.callee?.type === "MemberExpression" && !m.callee.computed && [
|
|
2032
|
+
"then",
|
|
2033
|
+
"catch",
|
|
2034
|
+
"finally"
|
|
2035
|
+
].includes(m.callee.property?.name) ? unwrapTs(m.callee.object) : void 0;
|
|
2036
|
+
if (chained?.type === "CallExpression") exemptCalls.add(chained);
|
|
2037
|
+
if (m.type === "AssignmentExpression" && m.left?.type === "Identifier") {
|
|
2038
|
+
let back = unwrapTs(m.right);
|
|
2039
|
+
if (back?.type === "LogicalExpression") back = unwrapTs(back.left);
|
|
2040
|
+
if (back?.type === "ChainExpression") back = back.expression;
|
|
2041
|
+
if (back?.type === "CallExpression" && back.callee.type === "MemberExpression" && rootObjectName(back.callee.object) === m.left.name) returnedToProp.add(back);
|
|
2042
|
+
}
|
|
1840
2043
|
});
|
|
1841
2044
|
walkScoped(root, (n, scope) => {
|
|
1842
2045
|
const flag = (r) => {
|
|
@@ -1855,6 +2058,8 @@ function collectPropMutations(root, propNames, source, acc, legacy) {
|
|
|
1855
2058
|
} else if (n.type === "UnaryExpression" && n.operator === "delete") flag(rootObjectName(n.argument));
|
|
1856
2059
|
else if (n.type === "CallExpression" && n.callee?.type === "MemberExpression") {
|
|
1857
2060
|
const method = n.callee.property?.type === "Identifier" ? n.callee.property.name : void 0;
|
|
2061
|
+
const receiver = unwrapTs(n.callee.object);
|
|
2062
|
+
if (receiver?.type === "Identifier" && ownApi.has(receiver.name) && !returnedToProp.has(n)) return;
|
|
1858
2063
|
if (method && MUTATING_METHODS.has(method)) flag(rootObjectName(n.callee.object));
|
|
1859
2064
|
}
|
|
1860
2065
|
});
|
|
@@ -1904,7 +2109,7 @@ function collectImportSources(program, source, acc) {
|
|
|
1904
2109
|
}
|
|
1905
2110
|
/** A specifier is "bare" (a node_modules package) when it is not relative/absolute/alias-local. */
|
|
1906
2111
|
function isBareSpecifier(s) {
|
|
1907
|
-
return !/^[./$#]/.test(s);
|
|
2112
|
+
return !/^([./$#]|@\/|~\/)/.test(s);
|
|
1908
2113
|
}
|
|
1909
2114
|
/**
|
|
1910
2115
|
* Whether `name` is used anywhere in `roots` other than as the object of a static member access
|
|
@@ -1921,6 +2126,8 @@ function namespaceUsedDynamically(name, roots) {
|
|
|
1921
2126
|
if (!n.computed) staticOrDeclaring.add(n.property);
|
|
1922
2127
|
} else if ((n.type === "Property" && !n.shorthand || n.type === "TSPropertySignature" || n.type === "TSMethodSignature") && !n.computed) staticOrDeclaring.add(n.key);
|
|
1923
2128
|
else if (n.type === "ImportNamespaceSpecifier") staticOrDeclaring.add(n.local);
|
|
2129
|
+
else if (n.type === "ImportSpecifier" && n.imported !== n.local) staticOrDeclaring.add(n.imported);
|
|
2130
|
+
else if (n.type === "ExportSpecifier" && n.exported !== n.local) staticOrDeclaring.add(n.exported);
|
|
1924
2131
|
else if (n.type === "TSQualifiedName" || n.type === "TSTypeQuery") {
|
|
1925
2132
|
const id = n.type === "TSQualifiedName" ? n.left : n.exprName;
|
|
1926
2133
|
if (id?.type === "Identifier") staticOrDeclaring.add(id);
|
|
@@ -2312,8 +2519,9 @@ function parseComponentFacts(source, filename) {
|
|
|
2312
2519
|
const legacyPropNames = collectLegacyPropNames(program);
|
|
2313
2520
|
const nonBindableProps = /* @__PURE__ */ new Set([...collectPropNames(program, false), ...legacyPropNames]);
|
|
2314
2521
|
const rawMutations = [];
|
|
2315
|
-
|
|
2316
|
-
|
|
2522
|
+
const ownApi = nonCollectionProps(program);
|
|
2523
|
+
collectPropMutations(program, nonBindableProps, source, rawMutations, legacyPropNames, ownApi);
|
|
2524
|
+
if (ast.fragment) collectPropMutations(ast.fragment, nonBindableProps, source, rawMutations, legacyPropNames, ownApi);
|
|
2317
2525
|
const isLegacy = legacyPropNames.size > 0;
|
|
2318
2526
|
for (const m of rawMutations) mutatedProps.push(legacyPropNames.has(m.name) ? {
|
|
2319
2527
|
...m,
|
|
@@ -2494,8 +2702,7 @@ function parseComponentFacts(source, filename) {
|
|
|
2494
2702
|
duplicateDts
|
|
2495
2703
|
};
|
|
2496
2704
|
}
|
|
2497
|
-
|
|
2498
|
-
//#region src/inline-directives.ts
|
|
2705
|
+
// src/inline-directives.ts
|
|
2499
2706
|
/**
|
|
2500
2707
|
* Enter every fact-carrying file into a directive index — components, Kit modules, and the Vite
|
|
2501
2708
|
* config when its minify flag was read. The one home for the union both pipelines apply, so a new
|
|
@@ -2578,8 +2785,7 @@ function unknownDirectiveIds(index, rules) {
|
|
|
2578
2785
|
for (const [file, directives] of index) for (const d of directives) for (const id of d.ruleIds ?? []) if (!known.has(id)) seen.add(`${file}:${d.line - 1} disables unknown rule "${id}" — run \`svelte-vitals explain --list\` for the ids.`);
|
|
2579
2786
|
return [...seen].sort();
|
|
2580
2787
|
}
|
|
2581
|
-
|
|
2582
|
-
//#region src/component.ts
|
|
2788
|
+
// src/component.ts
|
|
2583
2789
|
/**
|
|
2584
2790
|
* Component-body facts for the Correctness category — the source-analysis boundary
|
|
2585
2791
|
* (mirrors images.ts / headings.ts). Collected by the static (CLI) provider only;
|
|
@@ -2672,8 +2878,7 @@ async function collectComponentFacts(rt, cwd) {
|
|
|
2672
2878
|
}
|
|
2673
2879
|
}));
|
|
2674
2880
|
}
|
|
2675
|
-
|
|
2676
|
-
//#region src/source-files.ts
|
|
2881
|
+
// src/source-files.ts
|
|
2677
2882
|
/**
|
|
2678
2883
|
* Every file under `src/`, as project-relative paths, sorted. Paths only — nothing is
|
|
2679
2884
|
* read, so this is the cheaper of the two passes over `src/` (the component collector
|
|
@@ -2690,8 +2895,7 @@ async function collectComponentFacts(rt, cwd) {
|
|
|
2690
2895
|
async function collectSourceFiles(rt, cwd) {
|
|
2691
2896
|
return (await rt.glob("src/**/*", cwd)).slice().sort();
|
|
2692
2897
|
}
|
|
2693
|
-
|
|
2694
|
-
//#region src/kit-module-collect.ts
|
|
2898
|
+
// src/kit-module-collect.ts
|
|
2695
2899
|
/** Fallback facts for a Kit file that fails to read or parse (dev tooling must never throw). */
|
|
2696
2900
|
function emptyKitModuleFacts(file, kind) {
|
|
2697
2901
|
return {
|
|
@@ -2731,7 +2935,7 @@ async function collectKitModuleFacts(rt, cwd, aliases) {
|
|
|
2731
2935
|
"src/hooks.server.{ts,js}"
|
|
2732
2936
|
].map((p) => rt.glob(p, cwd)));
|
|
2733
2937
|
const files = [...new Set(lists.flat())];
|
|
2734
|
-
return arbitrateServerStoreWrites(rt, cwd, await Promise.all(files.sort().map(async (rel) => {
|
|
2938
|
+
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
2939
|
const kind = kindOf(rel);
|
|
2736
2940
|
let source;
|
|
2737
2941
|
try {
|
|
@@ -2755,7 +2959,7 @@ async function collectKitModuleFacts(rt, cwd, aliases) {
|
|
|
2755
2959
|
parseFailed: true
|
|
2756
2960
|
};
|
|
2757
2961
|
}
|
|
2758
|
-
})));
|
|
2962
|
+
}))), aliases)));
|
|
2759
2963
|
}
|
|
2760
2964
|
/**
|
|
2761
2965
|
* The files a resolved specifier may name, in resolution order. An extensionless path takes an
|
|
@@ -2773,20 +2977,58 @@ function moduleCandidates(repoPath) {
|
|
|
2773
2977
|
`${repoPath}/index.js`
|
|
2774
2978
|
];
|
|
2775
2979
|
}
|
|
2776
|
-
/**
|
|
2777
|
-
|
|
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) {
|
|
2980
|
+
/** Read a module and parse it with `parse`; empty when it cannot be found or read. */
|
|
2981
|
+
async function exportsOf(rt, cwd, repoPath, parse) {
|
|
2782
2982
|
for (const rel of moduleCandidates(repoPath)) try {
|
|
2783
2983
|
if (!await rt.exists(rt.join(cwd, rel))) continue;
|
|
2784
|
-
return
|
|
2984
|
+
return parse(await rt.readFile(rt.join(cwd, rel)), rel);
|
|
2785
2985
|
} catch {
|
|
2786
2986
|
return /* @__PURE__ */ new Set();
|
|
2787
2987
|
}
|
|
2788
2988
|
return /* @__PURE__ */ new Set();
|
|
2789
2989
|
}
|
|
2990
|
+
/** Drop the handler `.set()`/`.update()` writes whose target module exports a persistence client under that name. */
|
|
2991
|
+
async function dropClientWrites(rt, cwd, facts, aliases) {
|
|
2992
|
+
const targets = [...new Set(facts.flatMap((f) => (f.storeWriteTargets ?? []).map((w) => w.resolved)))];
|
|
2993
|
+
if (targets.length === 0) return facts;
|
|
2994
|
+
const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, (src, file) => parseClientExports(src, file, aliases))])));
|
|
2995
|
+
return facts.map((f) => {
|
|
2996
|
+
const clients = (f.storeWriteTargets ?? []).filter((w) => byPath.get(w.resolved)?.has(w.imported));
|
|
2997
|
+
if (clients.length === 0) return f;
|
|
2998
|
+
const drop = (w) => w.via === "set-call" && clients.some((c) => c.name === w.name && c.line === w.line);
|
|
2999
|
+
return {
|
|
3000
|
+
...f,
|
|
3001
|
+
importedStateWrites: f.importedStateWrites.filter((w) => !drop(w))
|
|
3002
|
+
};
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
/** A `load` an imported factory builds never renders when the function the factory returns always exits. */
|
|
3006
|
+
async function decideLoadFactories(rt, cwd, facts) {
|
|
3007
|
+
const targets = [...new Set(facts.flatMap((f) => f.pendingLoadFactory ? [f.pendingLoadFactory.resolved] : []))];
|
|
3008
|
+
if (targets.length === 0) return facts;
|
|
3009
|
+
const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, parseExitFactoryExports)])));
|
|
3010
|
+
return facts.map((f) => {
|
|
3011
|
+
const g = f.pendingLoadFactory;
|
|
3012
|
+
return g && byPath.get(g.resolved)?.has(g.name) ? {
|
|
3013
|
+
...f,
|
|
3014
|
+
loadNeverRenders: true
|
|
3015
|
+
} : f;
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
/** A load whose leading guard exits on an imported flag that is the literal `false` never renders. */
|
|
3019
|
+
async function decideFlagGuards(rt, cwd, facts) {
|
|
3020
|
+
const targets = [...new Set(facts.flatMap((f) => f.pendingFlagGuard ? [f.pendingFlagGuard.resolved] : []))];
|
|
3021
|
+
if (targets.length === 0) return facts;
|
|
3022
|
+
const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, parseFalseExports)])));
|
|
3023
|
+
return facts.map((f) => {
|
|
3024
|
+
const g = f.pendingFlagGuard;
|
|
3025
|
+
const key = g && (g.member ? `${g.name}.${g.member}` : g.name);
|
|
3026
|
+
return g && byPath.get(g.resolved)?.has(key) ? {
|
|
3027
|
+
...f,
|
|
3028
|
+
loadNeverRenders: true
|
|
3029
|
+
} : f;
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
2790
3032
|
/**
|
|
2791
3033
|
* Decide the `.set()`/`.update()` calls the parse could not: a call on a binding exported from
|
|
2792
3034
|
* under the `$lib` server root is shared module state when that binding is an in-memory
|
|
@@ -2796,7 +3038,7 @@ async function inMemoryExportsOf(rt, cwd, repoPath) {
|
|
|
2796
3038
|
async function arbitrateServerStoreWrites(rt, cwd, facts) {
|
|
2797
3039
|
const targets = [...new Set(facts.flatMap((f) => f.pendingServerStoreWrites.map((w) => w.resolved)))];
|
|
2798
3040
|
if (targets.length === 0) return facts;
|
|
2799
|
-
const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await
|
|
3041
|
+
const byPath = new Map(await Promise.all(targets.map(async (t) => [t, await exportsOf(rt, cwd, t, parseInMemoryExports)])));
|
|
2800
3042
|
return facts.map((f) => {
|
|
2801
3043
|
const promoted = f.pendingServerStoreWrites.filter((w) => byPath.get(w.resolved)?.has(w.imported)).map((w) => ({
|
|
2802
3044
|
name: w.name,
|
|
@@ -2810,8 +3052,7 @@ async function arbitrateServerStoreWrites(rt, cwd, facts) {
|
|
|
2810
3052
|
};
|
|
2811
3053
|
});
|
|
2812
3054
|
}
|
|
2813
|
-
|
|
2814
|
-
//#region src/config-object.ts
|
|
3055
|
+
// src/config-object.ts
|
|
2815
3056
|
/**
|
|
2816
3057
|
* Non-computed property of an object literal, by key name (`build` or `'build'`).
|
|
2817
3058
|
* Returns the LAST matching property, honoring JavaScript object semantics where
|
|
@@ -2898,8 +3139,7 @@ function resolveConfigObject(program) {
|
|
|
2898
3139
|
if (!exported) return void 0;
|
|
2899
3140
|
return unwrapToObjectExpression(exported, collectTopLevelBindings(program));
|
|
2900
3141
|
}
|
|
2901
|
-
|
|
2902
|
-
//#region src/vite-config-parse.ts
|
|
3142
|
+
// src/vite-config-parse.ts
|
|
2903
3143
|
/**
|
|
2904
3144
|
* The `build: { minify: false }` override, when present as a literal: returns the
|
|
2905
3145
|
* `minify` property's 1-based line in the ORIGINAL source. Undefined for clean,
|
|
@@ -2924,30 +3164,52 @@ function findMinifyDisabled(source) {
|
|
|
2924
3164
|
if (!minify || minifyValue?.type !== "Literal" || minifyValue.value !== false) return void 0;
|
|
2925
3165
|
return { line: Math.max(0, lineOf(wrapped, minify.start) - 1) };
|
|
2926
3166
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
3167
|
+
// src/svelte-config-parse.ts
|
|
3168
|
+
function isServedBase(value) {
|
|
3169
|
+
return typeof value === "string" && value.startsWith("/") && !value.endsWith("/");
|
|
3170
|
+
}
|
|
3171
|
+
/** The top-level bindings declared `const`: a `let` may be reassigned on the way to the export. */
|
|
3172
|
+
function constBindings(program, bindings) {
|
|
3173
|
+
const names = /* @__PURE__ */ new Set();
|
|
3174
|
+
for (const stmt of program.body) {
|
|
3175
|
+
const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
|
|
3176
|
+
if (decl?.type !== "VariableDeclaration" || decl.kind !== "const") continue;
|
|
3177
|
+
for (const d of decl.declarations) if (d.id.type === "Identifier") names.add(d.id.name);
|
|
3178
|
+
}
|
|
3179
|
+
return new Map([...bindings].filter(([name]) => names.has(name)));
|
|
3180
|
+
}
|
|
3181
|
+
/** Whether no branch of a `?:` / `||` / `&&` / `??` tree is a literal base an app can be served under. */
|
|
3182
|
+
function neverABase(expr, consts) {
|
|
3183
|
+
const e = resolveBinding(expr, consts);
|
|
3184
|
+
if (e.type === "Literal") return !isServedBase(e.value);
|
|
3185
|
+
if (e.type === "ConditionalExpression") return neverABase(e.consequent, consts) && neverABase(e.alternate, consts);
|
|
3186
|
+
if (e.type === "LogicalExpression") return neverABase(e.left, consts) && neverABase(e.right, consts);
|
|
2935
3187
|
return false;
|
|
2936
3188
|
}
|
|
3189
|
+
/** `expr` with top-level `const` names followed to their initializers (`paths: { base }` naming `const base = ''`). */
|
|
3190
|
+
function resolveBinding(expr, consts) {
|
|
3191
|
+
let e = unwrapTs(expr);
|
|
3192
|
+
for (let i = 0; i < 4 && e.type === "Identifier"; i++) {
|
|
3193
|
+
const next = consts.get(e.name);
|
|
3194
|
+
if (!next) break;
|
|
3195
|
+
e = unwrapTs(next);
|
|
3196
|
+
}
|
|
3197
|
+
return e;
|
|
3198
|
+
}
|
|
2937
3199
|
/**
|
|
2938
3200
|
* `paths.base` off a resolved Kit-config object: `{ value }` for a non-empty string literal,
|
|
2939
3201
|
* `{}` for any other expression (base exists, value unknowable — the `dev ? '' : '/repo'`
|
|
2940
|
-
* deploy form), and undefined when absent or when
|
|
3202
|
+
* deploy form), and undefined when absent or when no value it can take is a base SvelteKit serves under.
|
|
2941
3203
|
*/
|
|
2942
|
-
function basePathOf(kitConfig, bindings) {
|
|
3204
|
+
function basePathOf(kitConfig, bindings, consts) {
|
|
2943
3205
|
const paths = propOf(kitConfig, "paths");
|
|
2944
3206
|
const pathsObj = paths ? unwrapToObjectExpression(paths.value, bindings) : void 0;
|
|
2945
3207
|
if (!pathsObj) return void 0;
|
|
2946
3208
|
const base = propOf(pathsObj, "base");
|
|
2947
3209
|
if (!base) return void 0;
|
|
2948
|
-
const value =
|
|
2949
|
-
if (value.type === "Literal") return
|
|
2950
|
-
return
|
|
3210
|
+
const value = resolveBinding(base.value, consts);
|
|
3211
|
+
if (value.type === "Literal") return isServedBase(value.value) ? { value: value.value } : void 0;
|
|
3212
|
+
return neverABase(value, consts) ? void 0 : {};
|
|
2951
3213
|
}
|
|
2952
3214
|
/** A property's key when it is a plain (non-computed) string or identifier key. */
|
|
2953
3215
|
function keyNameOf(p) {
|
|
@@ -3173,7 +3435,33 @@ function findKitPathsBaseInSvelteConfig(source) {
|
|
|
3173
3435
|
const bindings = collectTopLevelBindings(program);
|
|
3174
3436
|
const kit = propOf(config, "kit");
|
|
3175
3437
|
const kitObj = kit ? unwrapToObjectExpression(kit.value, bindings) : void 0;
|
|
3176
|
-
return kitObj ? basePathOf(kitObj, bindings) : void 0;
|
|
3438
|
+
return kitObj ? basePathOf(kitObj, bindings, constBindings(program, bindings)) : void 0;
|
|
3439
|
+
}
|
|
3440
|
+
/**
|
|
3441
|
+
* `unwrapToObjectExpression`, but only where the literal is the value: a call passing more than one
|
|
3442
|
+
* argument (`mergeConfigs(a, b)`) or a spread could make it something else.
|
|
3443
|
+
*/
|
|
3444
|
+
function knownObject(expr, bindings) {
|
|
3445
|
+
let current = expr;
|
|
3446
|
+
for (let i = 0; i < 4 && current; i++) {
|
|
3447
|
+
const e = unwrapTs(current);
|
|
3448
|
+
if (e.type === "ObjectExpression") return e.properties.some((p) => p.type === "SpreadElement") ? void 0 : e;
|
|
3449
|
+
if (e.type === "Identifier") current = bindings.get(e.name);
|
|
3450
|
+
else if (e.type === "CallExpression" && e.arguments.length === 1) current = e.arguments[0];
|
|
3451
|
+
else return void 0;
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
/** Whether `kit.router.type` is the literal `'hash'` in a config whose value is known. */
|
|
3455
|
+
function findKitHashRouterInSvelteConfig(source) {
|
|
3456
|
+
const program = programOf(source, "svelte.config.js");
|
|
3457
|
+
if (!program) return false;
|
|
3458
|
+
const bindings = collectTopLevelBindings(program);
|
|
3459
|
+
const objectAt = (obj, key) => {
|
|
3460
|
+
const p = obj && propOf(obj, key);
|
|
3461
|
+
return p ? knownObject(p.value, bindings) : void 0;
|
|
3462
|
+
};
|
|
3463
|
+
const router = objectAt(objectAt(knownObject(findExportedExpression(program), bindings), "kit"), "router");
|
|
3464
|
+
return stringLiteralOf(router && propOf(router, "type")?.value) === "hash";
|
|
3177
3465
|
}
|
|
3178
3466
|
/**
|
|
3179
3467
|
* Local names bound to `sveltekit` imported from '@sveltejs/kit/vite'. When no such import is
|
|
@@ -3206,7 +3494,7 @@ function findKitPathsBaseInViteConfig(source) {
|
|
|
3206
3494
|
if (arg === void 0) return none;
|
|
3207
3495
|
const kitConfig = unwrapToObjectExpression(arg, bindings);
|
|
3208
3496
|
if (!kitConfig) return { kind: "unresolvable" };
|
|
3209
|
-
const base = basePathOf(kitConfig, bindings);
|
|
3497
|
+
const base = basePathOf(kitConfig, bindings, constBindings(program, bindings));
|
|
3210
3498
|
return base ? {
|
|
3211
3499
|
kind: "resolved",
|
|
3212
3500
|
base
|
|
@@ -3235,8 +3523,7 @@ function resolveKitPathsBase(viteConfig, svelteConfig) {
|
|
|
3235
3523
|
file: svelteConfig.file
|
|
3236
3524
|
} : void 0;
|
|
3237
3525
|
}
|
|
3238
|
-
|
|
3239
|
-
//#region src/workspace-packages.ts
|
|
3526
|
+
// src/workspace-packages.ts
|
|
3240
3527
|
const DEPENDENCY_FIELDS = [
|
|
3241
3528
|
"dependencies",
|
|
3242
3529
|
"devDependencies",
|
|
@@ -3448,8 +3735,7 @@ function withInstalledPackages(aliases, packages) {
|
|
|
3448
3735
|
const svelte = packages.filter((p) => isSveltePackage(p.manifest));
|
|
3449
3736
|
return svelte.length === 0 ? aliases : [...aliases ?? DEFAULT_KIT_ALIASES, ...svelte.flatMap(entriesOf)];
|
|
3450
3737
|
}
|
|
3451
|
-
|
|
3452
|
-
//#region src/project-paths.ts
|
|
3738
|
+
// src/project-paths.ts
|
|
3453
3739
|
/**
|
|
3454
3740
|
* Source-file locations that satisfy the project-scope rules, shared by every
|
|
3455
3741
|
* mode so the static (CLI) and rendered (plugin) collectors never drift. This
|
|
@@ -3468,11 +3754,17 @@ const SITEMAP_SOURCE_PATHS = [
|
|
|
3468
3754
|
"src/routes/sitemap.xml/+server.js"
|
|
3469
3755
|
];
|
|
3470
3756
|
/** The `+server` endpoints above, wherever (group) directories nest them — filter with `servesRootFile`. */
|
|
3471
|
-
const ROOT_FILE_ENDPOINT_GLOB = "src/routes/**/{robots.txt,sitemap
|
|
3472
|
-
/**
|
|
3757
|
+
const ROOT_FILE_ENDPOINT_GLOB = "src/routes/**/{robots.txt,sitemap*.xml}/+server.{ts,js}";
|
|
3758
|
+
/**
|
|
3759
|
+
* Whether a `+server` endpoint serves `/<file>`: (group) directories add no URL segment, and an optional
|
|
3760
|
+
* parameter in the name (`sitemap[[page]].xml`) still answers the bare name.
|
|
3761
|
+
*/
|
|
3473
3762
|
function servesRootFile(rel, file) {
|
|
3474
3763
|
const segments = rel.split("/");
|
|
3475
|
-
|
|
3764
|
+
const dot = file.lastIndexOf(".");
|
|
3765
|
+
const optional = new RegExp(`^${file.slice(0, dot)}\\[\\[[^\\]]+\\]\\]\\${file.slice(dot)}$`);
|
|
3766
|
+
const dir = segments.at(-2) ?? "";
|
|
3767
|
+
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
3768
|
}
|
|
3477
3769
|
/** Vite's own config resolution order — only the first existing file is the one Vite loads. */
|
|
3478
3770
|
const VITE_CONFIG_FILES = [
|
|
@@ -3485,8 +3777,7 @@ const VITE_CONFIG_FILES = [
|
|
|
3485
3777
|
];
|
|
3486
3778
|
/** SvelteKit's config resolution order (`@sveltejs/kit` checks js before ts). */
|
|
3487
3779
|
const SVELTE_CONFIG_FILES = ["svelte.config.js", "svelte.config.ts"];
|
|
3488
|
-
|
|
3489
|
-
//#region src/runtime.ts
|
|
3780
|
+
// src/runtime.ts
|
|
3490
3781
|
/**
|
|
3491
3782
|
* How many file reads may be in flight at once. Analysis reads every `.svelte` file in a project
|
|
3492
3783
|
* in parallel, which on a large project opens more descriptors than the process is allowed: at
|
|
@@ -3519,8 +3810,7 @@ function withReadLimit(readFile, limit = READ_CONCURRENCY) {
|
|
|
3519
3810
|
}
|
|
3520
3811
|
};
|
|
3521
3812
|
}
|
|
3522
|
-
|
|
3523
|
-
//#region src/engine.ts
|
|
3813
|
+
// src/engine.ts
|
|
3524
3814
|
/**
|
|
3525
3815
|
* Run a set of rules against a shared context and collect their findings.
|
|
3526
3816
|
* Rules are independent, so they run concurrently; results are flattened in
|
|
@@ -3545,7 +3835,7 @@ async function runRules(rules, ctx) {
|
|
|
3545
3835
|
}));
|
|
3546
3836
|
const results = [];
|
|
3547
3837
|
const failedRules = [];
|
|
3548
|
-
for (const outcome of perRule) if (Array.isArray(outcome)) results.push(
|
|
3838
|
+
for (const outcome of perRule) if (Array.isArray(outcome)) for (const r of outcome) results.push(r);
|
|
3549
3839
|
else failedRules.push(outcome);
|
|
3550
3840
|
return {
|
|
3551
3841
|
results,
|
|
@@ -3573,8 +3863,7 @@ async function runAnalysis(rules, ctx, directives) {
|
|
|
3573
3863
|
scoringConfig: withFailedRulesOff(ctx.config, failedRuleIds)
|
|
3574
3864
|
};
|
|
3575
3865
|
}
|
|
3576
|
-
|
|
3577
|
-
//#region src/reporter/palette.ts
|
|
3866
|
+
// src/reporter/palette.ts
|
|
3578
3867
|
/** Default: no decoration (identity) — output is byte-identical to plain text. */
|
|
3579
3868
|
const noColorPalette = {
|
|
3580
3869
|
bold: (s) => s,
|
|
@@ -3590,8 +3879,7 @@ function scoreColor(p, score) {
|
|
|
3590
3879
|
if (score >= 70) return p.yellow;
|
|
3591
3880
|
return p.red;
|
|
3592
3881
|
}
|
|
3593
|
-
|
|
3594
|
-
//#region src/reporter/console.ts
|
|
3882
|
+
// src/reporter/console.ts
|
|
3595
3883
|
const RULE = "────────────────────────";
|
|
3596
3884
|
const SEVERITY_TITLE = {
|
|
3597
3885
|
critical: "Critical",
|
|
@@ -3717,12 +4005,11 @@ function formatConsoleReport(results, config, options = {}) {
|
|
|
3717
4005
|
}
|
|
3718
4006
|
lines.push("");
|
|
3719
4007
|
}
|
|
3720
|
-
if (options.byRoute)
|
|
4008
|
+
if (options.byRoute) for (const line of byRouteTree(p, results, config, options.verbose ?? false)) lines.push(line);
|
|
3721
4009
|
if (options.verbose && summary.dynamic > 0) lines.push(p.dim("↯ = set dynamically (verified at runtime)."));
|
|
3722
4010
|
return lines.join("\n").replace(/\n+$/, "\n");
|
|
3723
4011
|
}
|
|
3724
|
-
|
|
3725
|
-
//#region src/reporter/agent.ts
|
|
4012
|
+
// src/reporter/agent.ts
|
|
3726
4013
|
/** Render failing findings as an agent-actionable Markdown remediation document (issue #18). */
|
|
3727
4014
|
function formatAgentReport(results, config) {
|
|
3728
4015
|
const failing = results.filter((r) => classify(r, config) === "fail");
|
|
@@ -3744,7 +4031,7 @@ function formatAgentReport(results, config) {
|
|
|
3744
4031
|
if (!groups.has(key)) groups.set(key, []);
|
|
3745
4032
|
groups.get(key).push(r);
|
|
3746
4033
|
}
|
|
3747
|
-
const groupSeverity = (rs) =>
|
|
4034
|
+
const groupSeverity = (rs) => rs.reduce((min, r) => Math.min(min, SEVERITY_RANK[effectiveSeverity(r, config)]), Infinity);
|
|
3748
4035
|
const orderedGroups = [...groups.entries()].sort((a, b) => groupSeverity(a[1]) - groupSeverity(b[1]) || a[0].localeCompare(b[0]));
|
|
3749
4036
|
for (const [loc, rs] of orderedGroups) {
|
|
3750
4037
|
rs.sort((x, y) => SEVERITY_RANK[effectiveSeverity(x, config)] - SEVERITY_RANK[effectiveSeverity(y, config)] || x.id.localeCompare(y.id));
|
|
@@ -3763,8 +4050,7 @@ function formatAgentReport(results, config) {
|
|
|
3763
4050
|
}
|
|
3764
4051
|
return lines.join("\n").replace(/\n+$/, "\n");
|
|
3765
4052
|
}
|
|
3766
|
-
|
|
3767
|
-
//#region src/reporter/sarif.ts
|
|
4053
|
+
// src/reporter/sarif.ts
|
|
3768
4054
|
/**
|
|
3769
4055
|
* SARIF `artifactLocation.uri` is a URI reference: `#`, `?`, `%`, spaces and non-ASCII in a raw
|
|
3770
4056
|
* path would be read as fragment/query/escape/invalid and mis-attribute (or drop) the alert in
|
|
@@ -3820,8 +4106,7 @@ function formatSarifReport(results, config, meta) {
|
|
|
3820
4106
|
};
|
|
3821
4107
|
return JSON.stringify(log, null, 2);
|
|
3822
4108
|
}
|
|
3823
|
-
|
|
3824
|
-
//#region src/reporter/app-shell.ts
|
|
4109
|
+
// src/reporter/app-shell.ts
|
|
3825
4110
|
function escapeHtml(s) {
|
|
3826
4111
|
return s.replace(/[&<>"']/g, (c) => c === "&" ? "&" : c === "<" ? "<" : c === ">" ? ">" : c === "\"" ? """ : "'");
|
|
3827
4112
|
}
|
|
@@ -4579,5 +4864,4 @@ function buildHtmlDocument(report, meta) {
|
|
|
4579
4864
|
function formatHtmlReport(results, config, meta) {
|
|
4580
4865
|
return buildHtmlDocument(buildJsonReport(results, config, meta), meta);
|
|
4581
4866
|
}
|
|
4582
|
-
|
|
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 };
|
|
4867
|
+
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 };
|