@svelte-vitals/core 0.55.1 → 0.55.2
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +80 -18
- package/dist/internal.js +638 -50
- package/dist/{json-TIGneC1p.d.ts → json-BQf9Qb2D.d.ts} +156 -95
- package/dist/{markdown-BOH3FPQl.js → markdown-DgHBAvYl.js} +636 -307
- package/package.json +3 -3
package/dist/internal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
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
2
|
//#region src/head.ts
|
|
3
3
|
const JS_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
4
4
|
"application/ecmascript",
|
|
@@ -32,20 +32,42 @@ function isClassicScriptType(type) {
|
|
|
32
32
|
return JS_MIME_TYPES.has(type.replace(ASCII_WHITESPACE_EDGES, "").toLowerCase());
|
|
33
33
|
}
|
|
34
34
|
//#endregion
|
|
35
|
+
//#region src/images.ts
|
|
36
|
+
/** A `.svg` path (query and fragment ignored) or an inline `data:image/svg+xml` URI. */
|
|
37
|
+
function isSvgSrc(src) {
|
|
38
|
+
return /^data:image\/svg\+xml[;,]/i.test(src) || /\.svg$/i.test(src.split(/[?#]/, 1)[0]);
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
35
41
|
//#region src/component-parse.ts
|
|
36
42
|
const KNOWN_TAGS = new Set(Object.keys(HTML_SPEC.elements).map((k) => k.replace(/^svg:/, "").toLowerCase()));
|
|
37
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Whether `expr` is a length-only list constructor: `Array(n)` / `new Array(n)` (single argument =
|
|
45
|
+
* length semantics), `Array.from({ length: n }, …)`, or `Array.from(<any of these>, …)`, optionally
|
|
46
|
+
* followed by `.fill(x)` (every item the same value) or `.keys()` (the indices themselves).
|
|
47
|
+
*/
|
|
38
48
|
function isLengthOnlyArrayCall(expr) {
|
|
39
49
|
const e = unwrapTs(expr);
|
|
40
50
|
if (!e) return false;
|
|
51
|
+
if (e.type === "CallExpression" && e.callee?.type === "MemberExpression" && !e.callee.computed && e.callee.object.type !== "Super" && e.callee.property.type === "Identifier" && (e.callee.property.name === "fill" || e.callee.property.name === "keys")) {
|
|
52
|
+
const receiver = unwrapTs(e.callee.object);
|
|
53
|
+
if (receiver?.type === "ArrayExpression") return receiver.elements.every((el) => el?.type !== "SpreadElement" || isLengthOnlyArrayCall(el.argument));
|
|
54
|
+
return isLengthOnlyArrayCall(e.callee.object);
|
|
55
|
+
}
|
|
41
56
|
if ((e.type === "CallExpression" || e.type === "NewExpression") && e.callee?.type === "Identifier" && e.callee.name === "Array") return (e.arguments?.length ?? 0) === 1;
|
|
42
|
-
if (e.type === "CallExpression" && e.callee?.type === "MemberExpression" && !e.callee.computed && e.callee.object?.type === "Identifier" && e.callee.object.name === "Array" && e.callee.property.type === "Identifier" && e.callee.property.name === "from"
|
|
57
|
+
if (e.type === "CallExpression" && e.callee?.type === "MemberExpression" && !e.callee.computed && e.callee.object?.type === "Identifier" && e.callee.object.name === "Array" && e.callee.property.type === "Identifier" && e.callee.property.name === "from") {
|
|
58
|
+
const source = e.arguments?.[0];
|
|
59
|
+
if (source?.type === "ObjectExpression") return (source.properties ?? []).some(isLengthProperty);
|
|
60
|
+
return source !== void 0 && source.type !== "SpreadElement" && isLengthOnlyArrayCall(source);
|
|
61
|
+
}
|
|
43
62
|
return false;
|
|
44
63
|
}
|
|
64
|
+
function isLengthProperty(p) {
|
|
65
|
+
return p?.type === "Property" && !p.computed && (p.key?.name === "length" || p.key?.value === "length");
|
|
66
|
+
}
|
|
45
67
|
/**
|
|
46
68
|
* Whether the each expression yields no item identity to key on: a constant
|
|
47
69
|
* inline array literal (fixed length, never reorders), a length-only list
|
|
48
|
-
* (`Array(n)`, `new Array(n)`, `[...Array(n)]`, `Array.from({ length: n })` —
|
|
70
|
+
* (`Array(n)`, `new Array(n)`, `[...Array(n)]`, `Array.from({ length: n })`, `Array(n).fill(x)`, `{ length: n }` —
|
|
49
71
|
* placeholder/skeleton lists), or a spread array whose every element spreads a
|
|
50
72
|
* length-only list. Such blocks are skipped entirely — neither each-key nor
|
|
51
73
|
* each-index-key can give useful advice on them.
|
|
@@ -53,6 +75,7 @@ function isLengthOnlyArrayCall(expr) {
|
|
|
53
75
|
function isIdentityFreeEach(node) {
|
|
54
76
|
const expr = unwrapTs(node.expression);
|
|
55
77
|
if (expr.type === "ArrayExpression" && Array.isArray(expr.elements)) return expr.elements.every((el) => el?.type !== "SpreadElement" || isLengthOnlyArrayCall(el.argument));
|
|
78
|
+
if (expr.type === "ObjectExpression") return expr.properties.length === 1 && isLengthProperty(expr.properties[0]);
|
|
56
79
|
return isLengthOnlyArrayCall(expr);
|
|
57
80
|
}
|
|
58
81
|
/**
|
|
@@ -97,19 +120,152 @@ function isIndexKey(each) {
|
|
|
97
120
|
if (typeof each.index !== "string" || each.key == null) return false;
|
|
98
121
|
return isIndexExpression(each.key, each.index);
|
|
99
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* TS nodes that wrap value-space code; every other `TS*` node is a type position. Enums,
|
|
125
|
+
* namespaces and parameter properties are left out: Svelte's own TS support rejects them.
|
|
126
|
+
*/
|
|
127
|
+
const TS_VALUE_NODES = /* @__PURE__ */ new Set([
|
|
128
|
+
"TSAsExpression",
|
|
129
|
+
"TSSatisfiesExpression",
|
|
130
|
+
"TSNonNullExpression",
|
|
131
|
+
"TSInstantiationExpression",
|
|
132
|
+
"TSTypeAssertion"
|
|
133
|
+
]);
|
|
134
|
+
/**
|
|
135
|
+
* Names of `const X = [ … ]` array literals (script top level) that nothing can reorder: every
|
|
136
|
+
* other mention of `X` in the file is a member read, an `{#each X …}`, a `for (… of X)` or a copy into an array literal
|
|
137
|
+
* (`[...X]`). Such a list is the inline
|
|
138
|
+
* literal the each rules already skip, just given a name. Anything else — a mutating call, an
|
|
139
|
+
* assignment through it, passing or spreading `X` — keeps the list keyed. A component's value
|
|
140
|
+
* imports are held to the same uses (`imports`), since the module behind one may be such a list;
|
|
141
|
+
* a module's `export const` is a candidate only with `exported`, because a component's `export`
|
|
142
|
+
* is written by its parent.
|
|
143
|
+
*/
|
|
144
|
+
function collectConstantLists(programs, fragment, opts = {}) {
|
|
145
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
146
|
+
const safe = /* @__PURE__ */ new Set();
|
|
147
|
+
const iterated = opts.imports ? eachListRoots(fragment) : void 0;
|
|
148
|
+
for (const program of programs) for (const top of program.body ?? []) {
|
|
149
|
+
if (iterated?.size && top?.type === "ImportDeclaration" && top.importKind !== "type") for (const s of top.specifiers ?? []) {
|
|
150
|
+
if (s?.importKind === "type" || s?.type === "ImportDefaultSpecifier" || !iterated.has(s?.local?.name)) continue;
|
|
151
|
+
const imported = s.type === "ImportNamespaceSpecifier" ? "*" : s.imported?.name ?? s.imported?.value;
|
|
152
|
+
if (typeof imported !== "string") continue;
|
|
153
|
+
opts.imports.set(s.local.name, {
|
|
154
|
+
source: String(top.source.value),
|
|
155
|
+
imported
|
|
156
|
+
});
|
|
157
|
+
candidates.set(s.local.name, s.local);
|
|
158
|
+
safe.add(s.imported);
|
|
159
|
+
}
|
|
160
|
+
if (opts.exported && top?.type === "ExportNamedDeclaration" && !top.source) for (const s of top.specifiers ?? []) safe.add(s.local).add(s.exported);
|
|
161
|
+
const stmt = opts.exported ? unwrapExport(top) : top;
|
|
162
|
+
if (stmt?.type !== "VariableDeclaration" || stmt.kind !== "const") continue;
|
|
163
|
+
for (const d of stmt.declarations ?? []) {
|
|
164
|
+
const init = unwrapTs(d?.init);
|
|
165
|
+
if (d?.id?.type === "Identifier" && init?.type === "ArrayExpression") {
|
|
166
|
+
if (!(init.elements ?? []).some((el) => el?.type === "SpreadElement")) candidates.set(d.id.name, d.id);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (candidates.size === 0) return /* @__PURE__ */ new Set();
|
|
171
|
+
for (const node of candidates.values()) safe.add(node);
|
|
172
|
+
const namespaces = new Set([...opts.imports ?? []].filter(([, b]) => b.imported === "*").map(([local]) => local));
|
|
173
|
+
const unsafe = /* @__PURE__ */ new Set();
|
|
174
|
+
const roots = [...programs, fragment];
|
|
175
|
+
const listOf = (n) => n?.type === "Identifier" && candidates.has(n.name) ? n.name : void 0;
|
|
176
|
+
const nsMember = (n) => n?.type === "MemberExpression" && !n.computed && namespaces.has(listOf(n.object)) ? `${n.object.name}.${n.property.name}` : void 0;
|
|
177
|
+
const members = /* @__PURE__ */ new Set();
|
|
178
|
+
for (const root of roots) walkEstree(root, (n) => {
|
|
179
|
+
if (n.type === "EachBlock" && (listOf(n.expression) || nsMember(n.expression))) safe.add(n.expression);
|
|
180
|
+
if (n.type === "ForOfStatement" && (listOf(n.right) || nsMember(n.right))) safe.add(n.right);
|
|
181
|
+
if (n.type === "ArrayExpression") {
|
|
182
|
+
for (const el of n.elements ?? []) if (el?.type === "SpreadElement" && (listOf(el.argument) || nsMember(el.argument))) safe.add(el.argument);
|
|
183
|
+
}
|
|
184
|
+
if (nsMember(n)) members.add(nsMember(n));
|
|
185
|
+
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);
|
|
188
|
+
if (!n.computed) safe.add(n.property);
|
|
189
|
+
}
|
|
190
|
+
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;
|
|
193
|
+
const calledList = listOf(called) ?? nsMember(called);
|
|
194
|
+
if (calledList) {
|
|
195
|
+
const { computed, property } = n.callee;
|
|
196
|
+
const method = !computed ? property?.name : property?.type === "Literal" && typeof property.value === "string" ? property.value : void 0;
|
|
197
|
+
if (computed && method === void 0 || method !== void 0 && MUTATING_METHODS.has(method)) unsafe.add(calledList);
|
|
198
|
+
}
|
|
199
|
+
if (n.type === "BindDirective" && listOf(rootObjectNode(n.expression))) unsafe.add(listOf(rootObjectNode(n.expression)));
|
|
200
|
+
if (n.type === "Property" && !n.computed && !n.shorthand) safe.add(n.key);
|
|
201
|
+
});
|
|
202
|
+
for (const root of roots) walkEvalScope(root, (n) => {
|
|
203
|
+
if (n.type.startsWith("TS") && !TS_VALUE_NODES.has(n.type)) return true;
|
|
204
|
+
const name = listOf(n) ?? nsMember(n);
|
|
205
|
+
if (name && !safe.has(n)) unsafe.add(name);
|
|
206
|
+
return false;
|
|
207
|
+
}, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
|
|
208
|
+
return new Set([...candidates.keys(), ...members].filter((key) => !unsafe.has(key) && !unsafe.has(key.split(".")[0])));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Export names of a module's constant lists — `export const X = [ … ]`, or a local one exported by
|
|
212
|
+
* name — that the module itself never writes (correctness/each-key, correctness/each-index-key).
|
|
213
|
+
*/
|
|
214
|
+
function collectConstantListExports(program) {
|
|
215
|
+
const lists = collectConstantLists([program], void 0, { exported: true });
|
|
216
|
+
const out = /* @__PURE__ */ new Set();
|
|
217
|
+
for (const top of program?.body ?? []) {
|
|
218
|
+
if (top?.type !== "ExportNamedDeclaration" || top.exportKind === "type") continue;
|
|
219
|
+
for (const d of top.declaration?.declarations ?? []) if (d?.id?.type === "Identifier" && lists.has(d.id.name)) out.add(d.id.name);
|
|
220
|
+
for (const s of top.source ? [] : top.specifiers ?? []) if (lists.has(s.local.name)) out.add(s.exported.name ?? s.exported.value);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
/** The root names `{#each}` blocks iterate: `xs` for both `{#each xs …}` and `{#each xs.ys …}`. */
|
|
225
|
+
function eachListRoots(node, acc = /* @__PURE__ */ new Set()) {
|
|
226
|
+
if (Array.isArray(node)) {
|
|
227
|
+
for (const child of node) eachListRoots(child, acc);
|
|
228
|
+
return acc;
|
|
229
|
+
}
|
|
230
|
+
if (!node || typeof node !== "object") return acc;
|
|
231
|
+
if (node.type === "EachBlock") {
|
|
232
|
+
const expr = node.expression?.type === "MemberExpression" ? node.expression.object : node.expression;
|
|
233
|
+
if (expr?.type === "Identifier") acc.add(expr.name);
|
|
234
|
+
}
|
|
235
|
+
for (const key of CHILD_NODE_KEYS) if (key in node) eachListRoots(node[key], acc);
|
|
236
|
+
return acc;
|
|
237
|
+
}
|
|
238
|
+
/** The innermost object of a member chain (`a` for `a.b[c].d`), or the node itself. */
|
|
239
|
+
function rootObjectNode(n) {
|
|
240
|
+
let cur = n;
|
|
241
|
+
while (cur?.type === "MemberExpression") cur = cur.object;
|
|
242
|
+
return cur;
|
|
243
|
+
}
|
|
100
244
|
/** Recursively collect every `{#each}` block in the template (correctness/each-key). */
|
|
101
|
-
function collectEachBlocks(node, source, acc) {
|
|
245
|
+
function collectEachBlocks(node, source, acc, lists, imports) {
|
|
102
246
|
if (Array.isArray(node)) {
|
|
103
|
-
for (const child of node) collectEachBlocks(child, source, acc);
|
|
247
|
+
for (const child of node) collectEachBlocks(child, source, acc, lists, imports);
|
|
104
248
|
return;
|
|
105
249
|
}
|
|
106
250
|
if (!node || typeof node !== "object") return;
|
|
107
|
-
if (node.type === "EachBlock" && node.context != null && !isIdentityFreeEach(node))
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
251
|
+
if (node.type === "EachBlock" && node.context != null && !isIdentityFreeEach(node)) {
|
|
252
|
+
const expr = node.expression;
|
|
253
|
+
const list = expr?.type === "Identifier" ? expr.name : expr?.type === "MemberExpression" && !expr.computed && expr.object?.type === "Identifier" ? `${expr.object.name}.${expr.property.name}` : void 0;
|
|
254
|
+
const constant = list !== void 0 && lists.has(list);
|
|
255
|
+
const [local, member] = list?.split(".") ?? [];
|
|
256
|
+
const binding = constant ? imports.get(local) : void 0;
|
|
257
|
+
const indexKey = isIndexKey(node);
|
|
258
|
+
if (!constant || binding) acc.push({
|
|
259
|
+
hasKey: node.key != null,
|
|
260
|
+
line: lineOf(source, node.start),
|
|
261
|
+
...indexKey ? { indexKey: true } : {},
|
|
262
|
+
...binding && (indexKey || node.key == null) ? { importedList: member ? {
|
|
263
|
+
source: binding.source,
|
|
264
|
+
imported: member
|
|
265
|
+
} : binding } : {}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
for (const key of CHILD_NODE_KEYS) if (key in node) collectEachBlocks(node[key], source, acc, lists, imports);
|
|
113
269
|
}
|
|
114
270
|
/**
|
|
115
271
|
* Whether a CallExpression *creates an effect*: `$effect(...)` or `$effect.pre(...)`.
|
|
@@ -154,7 +310,7 @@ function isDerivedDeclaration(node) {
|
|
|
154
310
|
if (c?.type === "MemberExpression" && c.object?.type === "Identifier" && c.object.name === "$derived") return c.property?.type === "Identifier" && c.property.name === "by";
|
|
155
311
|
return false;
|
|
156
312
|
}
|
|
157
|
-
function collectStateWrites(root, stateNames, acc, kinds) {
|
|
313
|
+
function collectStateWrites(root, stateNames, acc, kinds, localStates = false) {
|
|
158
314
|
const record = (name, kind) => {
|
|
159
315
|
acc.add(name);
|
|
160
316
|
if (kinds) {
|
|
@@ -163,7 +319,7 @@ function collectStateWrites(root, stateNames, acc, kinds) {
|
|
|
163
319
|
set.add(kind);
|
|
164
320
|
}
|
|
165
321
|
};
|
|
166
|
-
|
|
322
|
+
const visit = (stateNames) => (n, scope) => {
|
|
167
323
|
const shadowed = (name) => name === void 0 || scope.has(name);
|
|
168
324
|
if (n?.type === "AssignmentExpression") {
|
|
169
325
|
if (n.left?.type === "Identifier" && stateNames.has(n.left.name) && !shadowed(n.left.name)) record(n.left.name, "reassign");
|
|
@@ -196,6 +352,17 @@ function collectStateWrites(root, stateNames, acc, kinds) {
|
|
|
196
352
|
if (r && stateNames.has(r) && !shadowed(r)) record(r, "escape");
|
|
197
353
|
}
|
|
198
354
|
}
|
|
355
|
+
};
|
|
356
|
+
walkScoped(root, visit(stateNames));
|
|
357
|
+
if (!localStates) return;
|
|
358
|
+
walkEstree(root, (n) => {
|
|
359
|
+
if (n.type !== "BlockStatement") return;
|
|
360
|
+
const own = /* @__PURE__ */ new Set();
|
|
361
|
+
for (const stmt of n.body ?? []) {
|
|
362
|
+
if (stmt?.type !== "VariableDeclaration") continue;
|
|
363
|
+
for (const d of stmt.declarations ?? []) if (d?.id?.type === "Identifier" && stateNames.has(d.id.name) && isStateDeclaration(unwrapTs(d.init))) own.add(d.id.name);
|
|
364
|
+
}
|
|
365
|
+
if (own.size > 0) walkScoped(n.body, visit(own));
|
|
199
366
|
});
|
|
200
367
|
}
|
|
201
368
|
/** Function-shaped nodes whose bodies defer evaluation — prop reads inside them stay reactive (compiled to call-time reads). */
|
|
@@ -421,16 +588,17 @@ function collectFragmentAliasRefs(node, names, acc, shadowed = /* @__PURE__ */ n
|
|
|
421
588
|
}
|
|
422
589
|
}
|
|
423
590
|
/**
|
|
424
|
-
* Each-context taint (performance/state-raw condition 5): for `{#each candidate as item}`
|
|
591
|
+
* Each-context taint (performance/state-raw condition 5, correctness/unmutated-state): for `{#each candidate as item}`
|
|
425
592
|
* or `{#each candidate.path as item}`, any mutate/escape of the context binding (or index)
|
|
426
593
|
* inside the block — member writes, method calls, call arguments, `bind:`, component props —
|
|
427
594
|
* disqualifies the candidate over the candidate or a member path of it
|
|
428
595
|
* (`{#each obj.items as item}`): item-level edits stop being reactive under $state.raw.
|
|
429
596
|
* Pure reassignments of the context name are ignored (they don't touch the list's contents).
|
|
597
|
+
* `includeIndex: false` ignores the index binding, a number that cannot carry a write back to the list.
|
|
430
598
|
*/
|
|
431
|
-
function collectEachContextTaint(node, names, acc, shadowed = /* @__PURE__ */ new Set()) {
|
|
599
|
+
function collectEachContextTaint(node, names, acc, shadowed = /* @__PURE__ */ new Set(), includeIndex = true) {
|
|
432
600
|
if (Array.isArray(node)) {
|
|
433
|
-
for (const child of node) collectEachContextTaint(child, names, acc, shadowed);
|
|
601
|
+
for (const child of node) collectEachContextTaint(child, names, acc, shadowed, includeIndex);
|
|
434
602
|
return;
|
|
435
603
|
}
|
|
436
604
|
if (!node || typeof node !== "object" || typeof node.type !== "string") return;
|
|
@@ -442,7 +610,7 @@ function collectEachContextTaint(node, names, acc, shadowed = /* @__PURE__ */ ne
|
|
|
442
610
|
if (target !== void 0 && names.has(target) && !shadowed.has(target)) {
|
|
443
611
|
const ctxNames = /* @__PURE__ */ new Set();
|
|
444
612
|
addBoundNames(node.context, ctxNames);
|
|
445
|
-
if (typeof node.index === "string") ctxNames.add(node.index);
|
|
613
|
+
if (includeIndex && typeof node.index === "string") ctxNames.add(node.index);
|
|
446
614
|
if (ctxNames.size > 0) {
|
|
447
615
|
const union = /* @__PURE__ */ new Set();
|
|
448
616
|
const kinds = /* @__PURE__ */ new Map();
|
|
@@ -457,7 +625,7 @@ function collectEachContextTaint(node, names, acc, shadowed = /* @__PURE__ */ ne
|
|
|
457
625
|
}
|
|
458
626
|
for (const key of Object.keys(node)) {
|
|
459
627
|
if (WALK_IGNORED_KEYS.has(key)) continue;
|
|
460
|
-
collectEachContextTaint(node[key], names, acc, scope);
|
|
628
|
+
collectEachContextTaint(node[key], names, acc, scope, includeIndex);
|
|
461
629
|
}
|
|
462
630
|
}
|
|
463
631
|
/**
|
|
@@ -656,6 +824,30 @@ function collectNewExprLocalNames(program, acc) {
|
|
|
656
824
|
});
|
|
657
825
|
}
|
|
658
826
|
/**
|
|
827
|
+
* Top-level locals declared as a plain alias of an imported binding or a `new …()` local
|
|
828
|
+
* (`const p = presenter`, `const p = presenter as Presenter`), to a fixed point so alias chains resolve in any order
|
|
829
|
+
* (correctness/effect-as-onmount). Top level only: names match by text, not scope, so a
|
|
830
|
+
* function-local `const id = presenter` would otherwise mark every `id` in the component reactive.
|
|
831
|
+
* Runs before rune names are added: an alias of a `$state` primitive is a non-reactive snapshot.
|
|
832
|
+
*/
|
|
833
|
+
function collectAliasLocalNames(program, acc) {
|
|
834
|
+
const declarators = (program?.body ?? []).flatMap((stmt) => {
|
|
835
|
+
const decl = unwrapExport(stmt);
|
|
836
|
+
return decl?.type === "VariableDeclaration" ? decl.declarations : [];
|
|
837
|
+
});
|
|
838
|
+
for (let grew = true; grew;) {
|
|
839
|
+
grew = false;
|
|
840
|
+
for (const d of declarators) {
|
|
841
|
+
if (d.id?.type !== "Identifier" || !d.init || acc.has(d.id.name)) continue;
|
|
842
|
+
const init = unwrapTs(d.init);
|
|
843
|
+
if (init.type === "Identifier" && acc.has(init.name)) {
|
|
844
|
+
acc.add(d.id.name);
|
|
845
|
+
grew = true;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
659
851
|
* Whether an $effect callback body reads a reactive value (correctness/effect-as-onmount, conservative):
|
|
660
852
|
* a reactive name (rune declarator, imported binding, or a local declared with a `new …()`
|
|
661
853
|
* initializer — see `collectImportedLocalNames`/`collectNewExprLocalNames`), a `$`-prefixed
|
|
@@ -851,10 +1043,12 @@ function collectAriaElements(node, source, acc) {
|
|
|
851
1043
|
const hasList = tag === "input" && findAttr(node.attributes, "list") !== void 0;
|
|
852
1044
|
const selectKind = tag === "select" ? selectNativeRole(node.attributes) : void 0;
|
|
853
1045
|
const hasSpread = node.attributes.some((a) => a?.type === "SpreadAttribute");
|
|
1046
|
+
const hasAction = node.attributes.some((a) => a?.type === "UseDirective");
|
|
854
1047
|
acc.push({
|
|
855
1048
|
tag,
|
|
856
1049
|
line: lineOf(source, node.start),
|
|
857
1050
|
...roleAttr ? { role: classifyAttrValue(roleAttr.value) } : {},
|
|
1051
|
+
...hasAction ? { hasAction: true } : {},
|
|
858
1052
|
...inputType !== void 0 ? { inputType: inputType.toLowerCase() } : {},
|
|
859
1053
|
...hasList ? { hasList: true } : {},
|
|
860
1054
|
...selectKind ? { selectKind } : {},
|
|
@@ -989,10 +1183,13 @@ function hasNamingAttr(attributes) {
|
|
|
989
1183
|
"title"
|
|
990
1184
|
].some((name) => hasNamingValue(attributes, name));
|
|
991
1185
|
}
|
|
992
|
-
/** A
|
|
993
|
-
*
|
|
1186
|
+
/** A custom element may be form-associated and its shadow root may supply content; a namespaced tag
|
|
1187
|
+
* (`<enhanced:img>`) is rewritten by a preprocessor into markup not visible here. Treated as
|
|
1188
|
+
* unknowable by both a11y scanners. */
|
|
994
1189
|
function isCustomElement(node) {
|
|
995
|
-
|
|
1190
|
+
if (node.type !== "RegularElement" || typeof node.name !== "string") return false;
|
|
1191
|
+
const tag = node.name.toLowerCase();
|
|
1192
|
+
return tag.includes("-") || !KNOWN_TAGS.has(tag);
|
|
996
1193
|
}
|
|
997
1194
|
function scanAccessibleNameSubtree(node, skip) {
|
|
998
1195
|
if (Array.isArray(node)) {
|
|
@@ -1591,6 +1788,7 @@ function collectLegacyPropNames(program) {
|
|
|
1591
1788
|
const names = /* @__PURE__ */ new Set();
|
|
1592
1789
|
for (const stmt of program.body ?? []) {
|
|
1593
1790
|
if (stmt?.type !== "ExportNamedDeclaration" || stmt.declaration?.type !== "VariableDeclaration") continue;
|
|
1791
|
+
if (stmt.declaration.kind === "const") continue;
|
|
1594
1792
|
for (const d of stmt.declaration.declarations ?? []) if (d?.id?.type === "Identifier") names.add(d.id.name);
|
|
1595
1793
|
}
|
|
1596
1794
|
return names;
|
|
@@ -1619,9 +1817,27 @@ const MUTATING_METHODS = /* @__PURE__ */ new Set([
|
|
|
1619
1817
|
* reassignment for ephemeral state; only mutation is prohibited. Run over the instance
|
|
1620
1818
|
* program AND the template fragment (inline handlers can mutate props in the template).
|
|
1621
1819
|
* Scope-aware (issue #140): a local that shadows the prop's name is not flagged.
|
|
1820
|
+
* A `legacy` (export let) prop is flagged only for `delete` and mutating method calls — the legacy
|
|
1821
|
+
* compiler turns a member write or update into an invalidating assignment (`prop(prop().x = …, true)`).
|
|
1622
1822
|
*/
|
|
1623
|
-
function collectPropMutations(root, propNames, source, acc) {
|
|
1823
|
+
function collectPropMutations(root, propNames, source, acc, legacy) {
|
|
1624
1824
|
if (propNames.size === 0) return;
|
|
1825
|
+
const exemptCalls = /* @__PURE__ */ new Set();
|
|
1826
|
+
if (legacy.size > 0) walkEstree(root, (fn) => {
|
|
1827
|
+
if (!isDeferredBody(fn)) return;
|
|
1828
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
1829
|
+
walkScoped(fn.body, (m, scope) => {
|
|
1830
|
+
if (m.type === "AssignmentExpression" && m.left?.type === "Identifier" && !scope.has(m.left.name)) assigned.add(m.left.name);
|
|
1831
|
+
});
|
|
1832
|
+
walkEstree(fn.body, (m) => {
|
|
1833
|
+
const r = m.type === "CallExpression" ? rootObjectName(m.callee?.object) : m.type === "UnaryExpression" && m.operator === "delete" ? rootObjectName(m.argument) : void 0;
|
|
1834
|
+
if (r && legacy.has(r) && assigned.has(r)) exemptCalls.add(m);
|
|
1835
|
+
});
|
|
1836
|
+
});
|
|
1837
|
+
walkEstree(root, (m) => {
|
|
1838
|
+
const call = m.type === "AssignmentExpression" && m.left?.type === "Identifier" ? m.right : void 0;
|
|
1839
|
+
if (call?.type === "CallExpression" && rootObjectName(call.callee?.object) === m.left.name && call.arguments?.length === 1 && call.arguments[0]?.type === "ObjectExpression") exemptCalls.add(call);
|
|
1840
|
+
});
|
|
1625
1841
|
walkScoped(root, (n, scope) => {
|
|
1626
1842
|
const flag = (r) => {
|
|
1627
1843
|
if (r && propNames.has(r) && !scope.has(r)) acc.push({
|
|
@@ -1629,9 +1845,14 @@ function collectPropMutations(root, propNames, source, acc) {
|
|
|
1629
1845
|
line: lineOf(source, n.start)
|
|
1630
1846
|
});
|
|
1631
1847
|
};
|
|
1632
|
-
if (
|
|
1633
|
-
|
|
1634
|
-
|
|
1848
|
+
if (exemptCalls.has(n)) return;
|
|
1849
|
+
if (n.type === "AssignmentExpression" && n.left?.type === "MemberExpression") {
|
|
1850
|
+
const r = rootObjectName(n.left);
|
|
1851
|
+
if (r && !legacy.has(r)) flag(r);
|
|
1852
|
+
} else if (n.type === "UpdateExpression" && n.argument?.type === "MemberExpression") {
|
|
1853
|
+
const r = rootObjectName(n.argument);
|
|
1854
|
+
if (r && !legacy.has(r)) flag(r);
|
|
1855
|
+
} else if (n.type === "UnaryExpression" && n.operator === "delete") flag(rootObjectName(n.argument));
|
|
1635
1856
|
else if (n.type === "CallExpression" && n.callee?.type === "MemberExpression") {
|
|
1636
1857
|
const method = n.callee.property?.type === "Identifier" ? n.callee.property.name : void 0;
|
|
1637
1858
|
if (method && MUTATING_METHODS.has(method)) flag(rootObjectName(n.callee.object));
|
|
@@ -1666,13 +1887,18 @@ function isTypeOnlyImport(n) {
|
|
|
1666
1887
|
const specs = n.specifiers;
|
|
1667
1888
|
return Array.isArray(specs) && specs.length > 0 && specs.every((s) => s?.importKind === "type");
|
|
1668
1889
|
}
|
|
1890
|
+
/** Whether an import declaration binds only named exports — no default, `default as`, or namespace (see `ComponentFacts.importSpans`). */
|
|
1891
|
+
function isNamedOnlyImport(n) {
|
|
1892
|
+
const specs = n.specifiers;
|
|
1893
|
+
return Array.isArray(specs) && specs.length > 0 && specs.every((s) => s?.type === "ImportSpecifier" && (s.imported?.name ?? s.imported?.value) !== "default");
|
|
1894
|
+
}
|
|
1669
1895
|
/** Module specifiers of every `import`, each with its source line (see `ComponentFacts.importSpans`). */
|
|
1670
1896
|
function collectImportSources(program, source, acc) {
|
|
1671
1897
|
walkEstree(program, (n) => {
|
|
1672
1898
|
if (n.type === "ImportDeclaration" && typeof n.source?.value === "string") acc.push({
|
|
1673
1899
|
source: n.source.value,
|
|
1674
1900
|
line: lineOf(source, n.start),
|
|
1675
|
-
...isTypeOnlyImport(n) ? { type: true } : {}
|
|
1901
|
+
...isTypeOnlyImport(n) ? { type: true } : isNamedOnlyImport(n) ? { named: true } : {}
|
|
1676
1902
|
});
|
|
1677
1903
|
});
|
|
1678
1904
|
}
|
|
@@ -1680,13 +1906,45 @@ function collectImportSources(program, source, acc) {
|
|
|
1680
1906
|
function isBareSpecifier(s) {
|
|
1681
1907
|
return !/^[./$#]/.test(s);
|
|
1682
1908
|
}
|
|
1683
|
-
/**
|
|
1684
|
-
|
|
1909
|
+
/**
|
|
1910
|
+
* Whether `name` is used anywhere in `roots` other than as the object of a static member access
|
|
1911
|
+
* (`X.foo`, `X['foo']`) — passing `X` on, spreading it or indexing it by a variable is what makes
|
|
1912
|
+
* a bundler keep the whole module. `<X.Foo />` in markup is a component name, not an identifier,
|
|
1913
|
+
* so it counts as static. A same-named local (`scopeIntroducedNames`) shadows the namespace.
|
|
1914
|
+
*/
|
|
1915
|
+
function namespaceUsedDynamically(name, roots) {
|
|
1916
|
+
const staticOrDeclaring = /* @__PURE__ */ new Set();
|
|
1917
|
+
for (const root of roots) walkEstree(root, (n) => {
|
|
1918
|
+
if (n.type === "MemberExpression") {
|
|
1919
|
+
const literalKey = n.computed && n.property?.type === "Literal" && typeof n.property.value === "string";
|
|
1920
|
+
if (n.object?.type === "Identifier" && n.object.name === name && (!n.computed || literalKey)) staticOrDeclaring.add(n.object);
|
|
1921
|
+
if (!n.computed) staticOrDeclaring.add(n.property);
|
|
1922
|
+
} else if ((n.type === "Property" && !n.shorthand || n.type === "TSPropertySignature" || n.type === "TSMethodSignature") && !n.computed) staticOrDeclaring.add(n.key);
|
|
1923
|
+
else if (n.type === "ImportNamespaceSpecifier") staticOrDeclaring.add(n.local);
|
|
1924
|
+
else if (n.type === "TSQualifiedName" || n.type === "TSTypeQuery") {
|
|
1925
|
+
const id = n.type === "TSQualifiedName" ? n.left : n.exprName;
|
|
1926
|
+
if (id?.type === "Identifier") staticOrDeclaring.add(id);
|
|
1927
|
+
if (n.type === "TSQualifiedName") staticOrDeclaring.add(n.right);
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
let dynamic = false;
|
|
1931
|
+
for (const root of roots) walkScoped(root, (n, shadowed) => {
|
|
1932
|
+
if (n.type === "Identifier" && n.name === name && !staticOrDeclaring.has(n) && !shadowed.has(name)) dynamic = true;
|
|
1933
|
+
});
|
|
1934
|
+
return dynamic;
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Value `import * as X from '<bare pkg>'` namespace imports (type-only excluded) that `roots` (every
|
|
1938
|
+
* program and template of the file) use dynamically — performance/namespace-import. Static member
|
|
1939
|
+
* accesses tree-shake like named imports, so a namespace used only that way is not reported.
|
|
1940
|
+
*/
|
|
1941
|
+
function collectNamespaceImports(program, source, acc, roots) {
|
|
1685
1942
|
walkEstree(program, (n) => {
|
|
1686
1943
|
if (n.type !== "ImportDeclaration" || n.importKind === "type") return;
|
|
1687
1944
|
const spec = n.source?.value;
|
|
1688
1945
|
if (typeof spec !== "string" || !isBareSpecifier(spec)) return;
|
|
1689
|
-
|
|
1946
|
+
const ns = (n.specifiers ?? []).find((s) => s?.type === "ImportNamespaceSpecifier");
|
|
1947
|
+
if (ns?.local?.type === "Identifier" && namespaceUsedDynamically(ns.local.name, roots)) acc.push({
|
|
1690
1948
|
source: spec,
|
|
1691
1949
|
line: lineOf(source, n.start)
|
|
1692
1950
|
});
|
|
@@ -1931,7 +2189,7 @@ function parseModuleFacts(source, filename) {
|
|
|
1931
2189
|
line: shift(s.line)
|
|
1932
2190
|
});
|
|
1933
2191
|
const rawNamespaceImports = [];
|
|
1934
|
-
collectNamespaceImports(program, wrapped, rawNamespaceImports);
|
|
2192
|
+
collectNamespaceImports(program, wrapped, rawNamespaceImports, [program]);
|
|
1935
2193
|
for (const n of rawNamespaceImports) namespaceImports.push({
|
|
1936
2194
|
...n,
|
|
1937
2195
|
line: shift(n.line)
|
|
@@ -1971,7 +2229,10 @@ function parseComponentFacts(source, filename) {
|
|
|
1971
2229
|
if (MODULE_FILE_RE.test(filename)) return parseModuleFacts(source, filename);
|
|
1972
2230
|
const ast = parseSvelte(source, filename);
|
|
1973
2231
|
const eachBlocks = [];
|
|
1974
|
-
|
|
2232
|
+
const scriptPrograms = [ast.module?.content, ast.instance?.content].filter(Boolean);
|
|
2233
|
+
const listImports = /* @__PURE__ */ new Map();
|
|
2234
|
+
const lists = collectConstantLists(scriptPrograms, ast.fragment ?? ast, { imports: listImports });
|
|
2235
|
+
collectEachBlocks(ast.fragment ?? ast, source, eachBlocks, lists, listImports);
|
|
1975
2236
|
const htmlTags = [];
|
|
1976
2237
|
const javascriptUrls = [];
|
|
1977
2238
|
collectSecurityFacts(ast.fragment ?? ast, source, htmlTags, javascriptUrls);
|
|
@@ -2016,17 +2277,26 @@ function parseComponentFacts(source, filename) {
|
|
|
2016
2277
|
const moduleProgram = ast.module?.content;
|
|
2017
2278
|
const importSpans = [];
|
|
2018
2279
|
const namespaceImports = [];
|
|
2280
|
+
const usageRoots = [
|
|
2281
|
+
moduleProgram,
|
|
2282
|
+
ast.instance?.content,
|
|
2283
|
+
ast.fragment
|
|
2284
|
+
].filter(Boolean);
|
|
2019
2285
|
if (moduleProgram) {
|
|
2020
2286
|
collectImportSources(moduleProgram, source, importSpans);
|
|
2021
|
-
collectNamespaceImports(moduleProgram, source, namespaceImports);
|
|
2287
|
+
collectNamespaceImports(moduleProgram, source, namespaceImports, usageRoots);
|
|
2022
2288
|
}
|
|
2023
2289
|
const orphanEffects = moduleProgram ? collectOrphanEffects(moduleProgram, source) : [];
|
|
2024
2290
|
const orphanLifecycleCalls = moduleProgram ? collectOrphanLifecycleCalls(moduleProgram, source) : [];
|
|
2025
2291
|
const browserGlobalRefs = [];
|
|
2026
|
-
if (moduleProgram)
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2292
|
+
if (moduleProgram) {
|
|
2293
|
+
const hoisted = /* @__PURE__ */ new Set();
|
|
2294
|
+
collectImportedLocalNames(ast.instance?.content, hoisted);
|
|
2295
|
+
for (const r of collectBrowserGlobalRefs(moduleProgram, source, { bound: hoisted })) browserGlobalRefs.push({
|
|
2296
|
+
...r,
|
|
2297
|
+
context: "module"
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2030
2300
|
const effects = [];
|
|
2031
2301
|
const constableStates = [];
|
|
2032
2302
|
const mutatedProps = [];
|
|
@@ -2037,13 +2307,14 @@ function parseComponentFacts(source, filename) {
|
|
|
2037
2307
|
const program = ast.instance?.content;
|
|
2038
2308
|
if (program) {
|
|
2039
2309
|
collectImportSources(program, source, importSpans);
|
|
2040
|
-
collectNamespaceImports(program, source, namespaceImports);
|
|
2310
|
+
collectNamespaceImports(program, source, namespaceImports, usageRoots);
|
|
2041
2311
|
propCount = countProps(program);
|
|
2042
2312
|
const legacyPropNames = collectLegacyPropNames(program);
|
|
2043
2313
|
const nonBindableProps = /* @__PURE__ */ new Set([...collectPropNames(program, false), ...legacyPropNames]);
|
|
2044
2314
|
const rawMutations = [];
|
|
2045
|
-
collectPropMutations(program, nonBindableProps, source, rawMutations);
|
|
2046
|
-
if (ast.fragment) collectPropMutations(ast.fragment, nonBindableProps, source, rawMutations);
|
|
2315
|
+
collectPropMutations(program, nonBindableProps, source, rawMutations, legacyPropNames);
|
|
2316
|
+
if (ast.fragment) collectPropMutations(ast.fragment, nonBindableProps, source, rawMutations, legacyPropNames);
|
|
2317
|
+
const isLegacy = legacyPropNames.size > 0;
|
|
2047
2318
|
for (const m of rawMutations) mutatedProps.push(legacyPropNames.has(m.name) ? {
|
|
2048
2319
|
...m,
|
|
2049
2320
|
legacy: true
|
|
@@ -2061,7 +2332,6 @@ function parseComponentFacts(source, filename) {
|
|
|
2061
2332
|
}
|
|
2062
2333
|
const referenced = /* @__PURE__ */ new Set();
|
|
2063
2334
|
if (ast.fragment) collectFragmentRefs(ast.fragment, candidateNames, referenced);
|
|
2064
|
-
const isLegacy = legacyPropNames.size > 0;
|
|
2065
2335
|
for (const c of candidates) if (!disqualified.has(c.name) && referenced.has(c.name)) stalePropDerivations.push(isLegacy ? {
|
|
2066
2336
|
...c,
|
|
2067
2337
|
legacy: true
|
|
@@ -2075,7 +2345,9 @@ function parseComponentFacts(source, filename) {
|
|
|
2075
2345
|
if (moduleProgram) {
|
|
2076
2346
|
collectImportedLocalNames(moduleProgram, reactiveNames);
|
|
2077
2347
|
collectNewExprLocalNames(moduleProgram, reactiveNames);
|
|
2348
|
+
collectAliasLocalNames(moduleProgram, reactiveNames);
|
|
2078
2349
|
}
|
|
2350
|
+
collectAliasLocalNames(program, reactiveNames);
|
|
2079
2351
|
const stateDecls = [];
|
|
2080
2352
|
walkEstree(program, (n) => {
|
|
2081
2353
|
if (n.type !== "VariableDeclarator" || !n.init) return;
|
|
@@ -2100,11 +2372,12 @@ function parseComponentFacts(source, filename) {
|
|
|
2100
2372
|
});
|
|
2101
2373
|
});
|
|
2102
2374
|
const writtenOrEscaped = /* @__PURE__ */ new Set();
|
|
2103
|
-
collectStateWrites(program, stateNames, writtenOrEscaped);
|
|
2375
|
+
collectStateWrites(program, stateNames, writtenOrEscaped, void 0, true);
|
|
2104
2376
|
if (ast.fragment) {
|
|
2105
|
-
collectStateWrites(ast.fragment, stateNames, writtenOrEscaped);
|
|
2377
|
+
collectStateWrites(ast.fragment, stateNames, writtenOrEscaped, void 0, true);
|
|
2106
2378
|
collectTemplateEscapes(ast.fragment, stateNames, writtenOrEscaped);
|
|
2107
2379
|
collectDirectiveEscapes(ast.fragment, stateNames, writtenOrEscaped);
|
|
2380
|
+
collectEachContextTaint(ast.fragment, stateNames, writtenOrEscaped, /* @__PURE__ */ new Set(), false);
|
|
2108
2381
|
}
|
|
2109
2382
|
for (const d of stateDecls) if (!writtenOrEscaped.has(d.name)) constableStates.push(d);
|
|
2110
2383
|
const rawableCandidates = [];
|
|
@@ -2653,10 +2926,18 @@ function findMinifyDisabled(source) {
|
|
|
2653
2926
|
}
|
|
2654
2927
|
//#endregion
|
|
2655
2928
|
//#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);
|
|
2935
|
+
return false;
|
|
2936
|
+
}
|
|
2656
2937
|
/**
|
|
2657
2938
|
* `paths.base` off a resolved Kit-config object: `{ value }` for a non-empty string literal,
|
|
2658
2939
|
* `{}` for any other expression (base exists, value unknowable — the `dev ? '' : '/repo'`
|
|
2659
|
-
* deploy form), and undefined when absent or
|
|
2940
|
+
* deploy form), and undefined when absent or when every value it can take is the empty string.
|
|
2660
2941
|
*/
|
|
2661
2942
|
function basePathOf(kitConfig, bindings) {
|
|
2662
2943
|
const paths = propOf(kitConfig, "paths");
|
|
@@ -2666,7 +2947,7 @@ function basePathOf(kitConfig, bindings) {
|
|
|
2666
2947
|
if (!base) return void 0;
|
|
2667
2948
|
const value = unwrapTs(base.value);
|
|
2668
2949
|
if (value.type === "Literal") return typeof value.value === "string" && value.value !== "" ? { value: value.value } : void 0;
|
|
2669
|
-
return {};
|
|
2950
|
+
return alwaysEmptyString(value) ? void 0 : {};
|
|
2670
2951
|
}
|
|
2671
2952
|
/** A property's key when it is a plain (non-computed) string or identifier key. */
|
|
2672
2953
|
function keyNameOf(p) {
|
|
@@ -2674,10 +2955,46 @@ function keyNameOf(p) {
|
|
|
2674
2955
|
if (p.key.type === "Identifier") return p.key.name;
|
|
2675
2956
|
if (p.key.type === "Literal" && typeof p.key.value === "string") return p.key.value;
|
|
2676
2957
|
}
|
|
2677
|
-
|
|
2958
|
+
function stringLiteralOf(expr) {
|
|
2959
|
+
return expr?.type === "Literal" && typeof expr.value === "string" ? expr.value : void 0;
|
|
2960
|
+
}
|
|
2961
|
+
/** `__dirname`, `import.meta.dirname` or `process.cwd()`: the config's directory, which is the project root. */
|
|
2962
|
+
function isRootDir(expr) {
|
|
2963
|
+
if (expr.type === "Identifier") return expr.name === "__dirname";
|
|
2964
|
+
if (expr.type === "MemberExpression" && expr.property.type === "Identifier") return expr.object.type === "MetaProperty" && expr.property.name === "dirname";
|
|
2965
|
+
return expr.type === "CallExpression" && expr.callee.type === "MemberExpression" && expr.callee.object.type === "Identifier" && expr.callee.object.name === "process" && expr.callee.property.type === "Identifier" && expr.callee.property.name === "cwd";
|
|
2966
|
+
}
|
|
2967
|
+
/**
|
|
2968
|
+
* A config path value, relative to the project root: a string literal, `resolve(…)`/`join(…)`
|
|
2969
|
+
* (bare or `path.`-qualified) of string literals led by an optional root directory, or
|
|
2970
|
+
* `fileURLToPath(new URL('<literal>', import.meta.url))`. Anything else is unreadable.
|
|
2971
|
+
*/
|
|
2972
|
+
function pathValueOf(value) {
|
|
2973
|
+
const v = unwrapTs(value);
|
|
2974
|
+
if (v.type !== "CallExpression") return stringLiteralOf(v);
|
|
2975
|
+
const callee = v.callee.type === "MemberExpression" && !v.callee.computed ? v.callee.property : v.callee;
|
|
2976
|
+
const name = callee.type === "Identifier" ? callee.name : void 0;
|
|
2977
|
+
const args = v.arguments;
|
|
2978
|
+
if (name === "fileURLToPath") {
|
|
2979
|
+
const url = args[0];
|
|
2980
|
+
if (url?.type !== "NewExpression") return void 0;
|
|
2981
|
+
const [href, base] = url.arguments;
|
|
2982
|
+
return base?.type === "MemberExpression" && base.object.type === "MetaProperty" && base.property.type === "Identifier" && base.property.name === "url" ? stringLiteralOf(href) : void 0;
|
|
2983
|
+
}
|
|
2984
|
+
if (name !== "resolve" && name !== "join") return void 0;
|
|
2985
|
+
const parts = [];
|
|
2986
|
+
for (const [i, arg] of args.entries()) {
|
|
2987
|
+
if (i === 0 && isRootDir(arg)) continue;
|
|
2988
|
+
const part = stringLiteralOf(arg);
|
|
2989
|
+
if (part === void 0) return void 0;
|
|
2990
|
+
if (name === "resolve" && part.startsWith("/")) parts.length = 0;
|
|
2991
|
+
parts.push(part);
|
|
2992
|
+
}
|
|
2993
|
+
return parts.join("/");
|
|
2994
|
+
}
|
|
2995
|
+
/** A property's value as a config path (see `pathValueOf`), else undefined. */
|
|
2678
2996
|
function stringValueOf(p) {
|
|
2679
|
-
|
|
2680
|
-
return v.type === "Literal" && typeof v.value === "string" ? v.value : void 0;
|
|
2997
|
+
return pathValueOf(p.value);
|
|
2681
2998
|
}
|
|
2682
2999
|
/**
|
|
2683
3000
|
* `kit.alias`'s entries in source order. Undefined when the key set cannot be known (see
|
|
@@ -2745,7 +3062,7 @@ function findKitAliasesInSvelteConfig(source) {
|
|
|
2745
3062
|
* same irregularities available to it.
|
|
2746
3063
|
*/
|
|
2747
3064
|
function normalizeAliasValue(value) {
|
|
2748
|
-
const posix = value.replace(/\\/g, "/");
|
|
3065
|
+
const posix = value.replace(/\\/g, "/").replace(/^(\.\/)+/, "");
|
|
2749
3066
|
return (posix.endsWith("/*") ? posix.slice(0, -2) : posix).replace(/\/+$/, "");
|
|
2750
3067
|
}
|
|
2751
3068
|
/**
|
|
@@ -2788,6 +3105,57 @@ function resolveKitAliases(viteConfig, svelteConfig) {
|
|
|
2788
3105
|
if (!svelteConfig) return void 0;
|
|
2789
3106
|
return compileKitAliases(findKitAliasesInSvelteConfig(svelteConfig.source));
|
|
2790
3107
|
}
|
|
3108
|
+
/** Conditions every Vite build of a Svelte app matches; `types`/`require` never apply to an ESM import. */
|
|
3109
|
+
const MATCHED_CONDITIONS = /* @__PURE__ */ new Set([
|
|
3110
|
+
"svelte",
|
|
3111
|
+
"import",
|
|
3112
|
+
"module",
|
|
3113
|
+
"default"
|
|
3114
|
+
]);
|
|
3115
|
+
const IGNORED_CONDITIONS = /* @__PURE__ */ new Set(["types", "require"]);
|
|
3116
|
+
/**
|
|
3117
|
+
* The target a package `imports` value resolves to, walking conditions in declaration order as Node
|
|
3118
|
+
* does. A condition that depends on the environment (`node`, `browser`, `development`, …) makes the
|
|
3119
|
+
* answer unknowable here, so it resolves to nothing rather than to a branch that may not apply.
|
|
3120
|
+
*/
|
|
3121
|
+
function importTarget(value) {
|
|
3122
|
+
if (typeof value === "string") return value;
|
|
3123
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3124
|
+
for (const [condition, branch] of Object.entries(value)) {
|
|
3125
|
+
if (IGNORED_CONDITIONS.has(condition)) continue;
|
|
3126
|
+
return MATCHED_CONDITIONS.has(condition) ? importTarget(branch) : void 0;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
/**
|
|
3130
|
+
* `aliases` followed by the project's package.json `imports` entries (`"#lib/*": "./src/lib/*"`),
|
|
3131
|
+
* after Kit's own: Vite's alias plugin resolves before Node-style subpath imports. The entries are
|
|
3132
|
+
* ordered by Node's precedence — exact keys, then patterns by longest prefix — because the alias
|
|
3133
|
+
* resolver takes the first match. Only a project-relative target counts; anything else is skipped,
|
|
3134
|
+
* which leaves the specifier unresolved as before.
|
|
3135
|
+
*/
|
|
3136
|
+
function withPackageImports(aliases, packageJsonSource) {
|
|
3137
|
+
let imports;
|
|
3138
|
+
try {
|
|
3139
|
+
imports = JSON.parse(packageJsonSource ?? "").imports;
|
|
3140
|
+
} catch {
|
|
3141
|
+
return aliases;
|
|
3142
|
+
}
|
|
3143
|
+
if (!imports || typeof imports !== "object") return aliases;
|
|
3144
|
+
const entries = [];
|
|
3145
|
+
for (const [key, raw] of Object.entries(imports)) {
|
|
3146
|
+
const target = importTarget(raw);
|
|
3147
|
+
if (target === void 0 || !target.startsWith("./")) continue;
|
|
3148
|
+
const star = key.endsWith("/*");
|
|
3149
|
+
if (star !== target.endsWith("/*") || key.slice(0, star ? -2 : void 0).includes("*")) continue;
|
|
3150
|
+
entries.push({
|
|
3151
|
+
find: star ? key.slice(0, -2) : key,
|
|
3152
|
+
replacement: normalizeAliasValue(target.slice(2)),
|
|
3153
|
+
match: star ? "contents" : "exact"
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
entries.sort((a, b) => a.match === b.match ? a.match === "exact" ? 0 : b.find.length - a.find.length : a.match === "exact" ? -1 : 1);
|
|
3157
|
+
return entries.length > 0 ? [...aliases ?? DEFAULT_KIT_ALIASES, ...entries] : aliases;
|
|
3158
|
+
}
|
|
2791
3159
|
/** Parse a config source to a program, or undefined when it cannot be parsed. */
|
|
2792
3160
|
function programOf(source, filename) {
|
|
2793
3161
|
try {
|
|
@@ -2868,6 +3236,219 @@ function resolveKitPathsBase(viteConfig, svelteConfig) {
|
|
|
2868
3236
|
} : void 0;
|
|
2869
3237
|
}
|
|
2870
3238
|
//#endregion
|
|
3239
|
+
//#region src/workspace-packages.ts
|
|
3240
|
+
const DEPENDENCY_FIELDS = [
|
|
3241
|
+
"dependencies",
|
|
3242
|
+
"devDependencies",
|
|
3243
|
+
"peerDependencies"
|
|
3244
|
+
];
|
|
3245
|
+
/**
|
|
3246
|
+
* The app's dependencies that link a package of the same repository, name → range: a `workspace:`
|
|
3247
|
+
* version range, or a `link:`/`file:` path. A `workspace:` range that renames (`workspace:other@*`)
|
|
3248
|
+
* or names a path is left out, since the specifier would not be the package's own name.
|
|
3249
|
+
*/
|
|
3250
|
+
function declaredLocalPackages(packageJsonSource) {
|
|
3251
|
+
const out = /* @__PURE__ */ new Map();
|
|
3252
|
+
let pkg;
|
|
3253
|
+
try {
|
|
3254
|
+
pkg = JSON.parse(packageJsonSource ?? "");
|
|
3255
|
+
} catch {
|
|
3256
|
+
return out;
|
|
3257
|
+
}
|
|
3258
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
3259
|
+
const deps = pkg?.[field];
|
|
3260
|
+
if (!deps || typeof deps !== "object") continue;
|
|
3261
|
+
for (const [name, range] of Object.entries(deps)) if (typeof range === "string" && /^(?:workspace:(?:[\^~*\d]|$)|link:|file:)/.test(range)) out.set(name, range);
|
|
3262
|
+
}
|
|
3263
|
+
return out;
|
|
3264
|
+
}
|
|
3265
|
+
/** The `packages:` globs of a `pnpm-workspace.yaml`, in block (`- 'apps/*'`) or flow (`[a, b]`) form. */
|
|
3266
|
+
function pnpmWorkspaceGlobs(yaml) {
|
|
3267
|
+
const out = [];
|
|
3268
|
+
const item = (raw) => {
|
|
3269
|
+
const value = raw.trim().replace(/^(['"])(.*)\1$/, "$2");
|
|
3270
|
+
if (value) out.push(value);
|
|
3271
|
+
};
|
|
3272
|
+
let inPackages = false;
|
|
3273
|
+
for (const line of yaml.split(/\r?\n/)) {
|
|
3274
|
+
if (/^\s*(#|$)/.test(line)) continue;
|
|
3275
|
+
const key = /^packages\s*:\s*(.*)$/.exec(line);
|
|
3276
|
+
if (key) {
|
|
3277
|
+
const flow = /^\[(.*)\]/.exec(key[1].replace(/\s+#.*$/, ""));
|
|
3278
|
+
if (flow) flow[1].split(",").forEach(item);
|
|
3279
|
+
inPackages = !flow;
|
|
3280
|
+
continue;
|
|
3281
|
+
}
|
|
3282
|
+
if (!inPackages) continue;
|
|
3283
|
+
if (/^[^\s-]/.test(line)) break;
|
|
3284
|
+
const entry = /^\s*-\s*(.*?)\s*(?:\s#.*)?$/.exec(line);
|
|
3285
|
+
if (entry) item(entry[1]);
|
|
3286
|
+
}
|
|
3287
|
+
return out;
|
|
3288
|
+
}
|
|
3289
|
+
/** A root `package.json`'s `workspaces` globs (an array, or `{ packages }`); undefined when it declares none. */
|
|
3290
|
+
function packageJsonWorkspaceGlobs(source) {
|
|
3291
|
+
let workspaces;
|
|
3292
|
+
try {
|
|
3293
|
+
workspaces = JSON.parse(source ?? "").workspaces;
|
|
3294
|
+
} catch {
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
const list = Array.isArray(workspaces) ? workspaces : workspaces?.packages;
|
|
3298
|
+
return Array.isArray(list) ? list.filter((g) => typeof g === "string") : void 0;
|
|
3299
|
+
}
|
|
3300
|
+
/** A package-relative `svelte` field (`./dist/index.js`, `index.js`); undefined when absent or leaving the package. */
|
|
3301
|
+
function svelteField(manifest) {
|
|
3302
|
+
let field;
|
|
3303
|
+
try {
|
|
3304
|
+
field = JSON.parse(manifest).svelte;
|
|
3305
|
+
} catch {
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3308
|
+
if (typeof field !== "string" || field.startsWith("/") || /^[A-Za-z]:/.test(field)) return void 0;
|
|
3309
|
+
const rel = field.replace(/^\.\//, "");
|
|
3310
|
+
return rel && !rel.split("/").includes("..") ? rel : void 0;
|
|
3311
|
+
}
|
|
3312
|
+
/** `dir/rel`, or `dir` itself for an empty `rel`. */
|
|
3313
|
+
function inDir(dir, rel) {
|
|
3314
|
+
const tail = normalizeAliasValue(rel);
|
|
3315
|
+
return tail ? `${dir}/${tail}` : dir;
|
|
3316
|
+
}
|
|
3317
|
+
/**
|
|
3318
|
+
* One package's entries, as Node resolves a bare specifier against its `exports`: a key without
|
|
3319
|
+
* `*` matches exactly, a `./x/*` key matches the directory's contents (its target may add a suffix
|
|
3320
|
+
* after `*`: `./src/*.js`); exact keys come first, then
|
|
3321
|
+
* patterns by longest prefix. The condition walk is `importTarget`'s, in declaration order over
|
|
3322
|
+
* `svelte`/`import`/`module`/`default`. A target this cannot follow — an environment condition,
|
|
3323
|
+
* a `null` exclusion, a `*` mid-pattern or mid-segment, a path leaving the package — stays as an opaque entry so
|
|
3324
|
+
* a shorter pattern cannot answer in its place. Without `exports`, the bare name resolves through
|
|
3325
|
+
* the `svelte` field and subpaths through the package's file layout.
|
|
3326
|
+
*/
|
|
3327
|
+
function entriesOf({ name, dir, manifest }) {
|
|
3328
|
+
let exports;
|
|
3329
|
+
try {
|
|
3330
|
+
exports = JSON.parse(manifest).exports;
|
|
3331
|
+
} catch {
|
|
3332
|
+
return [];
|
|
3333
|
+
}
|
|
3334
|
+
if (exports === void 0) {
|
|
3335
|
+
const field = svelteField(manifest);
|
|
3336
|
+
return [...field === void 0 ? [] : [{
|
|
3337
|
+
find: name,
|
|
3338
|
+
replacement: inDir(dir, field),
|
|
3339
|
+
match: "exact",
|
|
3340
|
+
root: dir
|
|
3341
|
+
}], {
|
|
3342
|
+
find: name,
|
|
3343
|
+
replacement: dir,
|
|
3344
|
+
match: "prefix",
|
|
3345
|
+
root: dir
|
|
3346
|
+
}];
|
|
3347
|
+
}
|
|
3348
|
+
const keys = exports && typeof exports === "object" && !Array.isArray(exports) ? Object.keys(exports) : [];
|
|
3349
|
+
const dotted = keys.filter((k) => k.startsWith(".")).length;
|
|
3350
|
+
if (dotted > 0 && dotted < keys.length) return [{
|
|
3351
|
+
find: name,
|
|
3352
|
+
replacement: null,
|
|
3353
|
+
match: "prefix",
|
|
3354
|
+
root: dir
|
|
3355
|
+
}];
|
|
3356
|
+
const subpaths = dotted > 0 ? exports : { ".": exports };
|
|
3357
|
+
const out = [];
|
|
3358
|
+
for (const [key, value] of Object.entries(subpaths)) {
|
|
3359
|
+
const star = key.indexOf("*");
|
|
3360
|
+
const target = importTarget(value);
|
|
3361
|
+
const local = target?.startsWith("./") && !target.split("/").includes("..") ? target.slice(2) : void 0;
|
|
3362
|
+
if (star < 0) {
|
|
3363
|
+
const replacement = local === void 0 || local.includes("*") ? null : inDir(dir, local);
|
|
3364
|
+
out.push({
|
|
3365
|
+
find: name + key.slice(1),
|
|
3366
|
+
replacement,
|
|
3367
|
+
match: "exact",
|
|
3368
|
+
root: dir
|
|
3369
|
+
});
|
|
3370
|
+
continue;
|
|
3371
|
+
}
|
|
3372
|
+
const head = key.slice(1, star);
|
|
3373
|
+
const trailing = star === key.length - 1 && head.endsWith("/");
|
|
3374
|
+
const at = local?.indexOf("*") ?? -1;
|
|
3375
|
+
const follows = trailing && at > 0 && local[at - 1] === "/" && !local.includes("*", at + 1);
|
|
3376
|
+
const suffix = follows ? local.slice(at + 1) : "";
|
|
3377
|
+
out.push({
|
|
3378
|
+
find: name + head.slice(0, head.lastIndexOf("/")),
|
|
3379
|
+
replacement: follows ? inDir(dir, local.slice(0, at - 1)) : null,
|
|
3380
|
+
match: "contents",
|
|
3381
|
+
root: dir,
|
|
3382
|
+
...suffix ? { suffix } : {}
|
|
3383
|
+
});
|
|
3384
|
+
}
|
|
3385
|
+
return out.sort((a, b) => a.match === b.match ? a.match === "exact" ? 0 : b.find.length - a.find.length : a.match === "exact" ? -1 : 1);
|
|
3386
|
+
}
|
|
3387
|
+
/**
|
|
3388
|
+
* `aliases` widened with the app's workspace packages, the way the bundler sees them: an alias
|
|
3389
|
+
* whose value points into `node_modules/<package>` names the package's own directory (the link a
|
|
3390
|
+
* workspace install creates, readable whether or not it was installed), and every package's
|
|
3391
|
+
* `exports` follows the aliases, as Vite resolves a bare specifier only after its alias plugin.
|
|
3392
|
+
*/
|
|
3393
|
+
function withWorkspacePackages(aliases, packages) {
|
|
3394
|
+
if (packages.length === 0) return aliases;
|
|
3395
|
+
const linked = (entry) => {
|
|
3396
|
+
for (const { name, dir } of packages) {
|
|
3397
|
+
const from = `node_modules/${name}`;
|
|
3398
|
+
const r = entry.replacement;
|
|
3399
|
+
if (r === from || r?.startsWith(`${from}/`)) return {
|
|
3400
|
+
...entry,
|
|
3401
|
+
replacement: dir + r.slice(from.length),
|
|
3402
|
+
root: dir
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
return entry;
|
|
3406
|
+
};
|
|
3407
|
+
return [...(aliases ?? DEFAULT_KIT_ALIASES).map(linked), ...packages.flatMap(entriesOf)];
|
|
3408
|
+
}
|
|
3409
|
+
/**
|
|
3410
|
+
* The app's dependencies that an install puts in `node_modules`: every declared name whose range is
|
|
3411
|
+
* not a local protocol (`workspace:`, `link:`, `file:`, `portal:`). Those are the workspace
|
|
3412
|
+
* resolver's, whose bounds must not be sidestepped through their `node_modules` link.
|
|
3413
|
+
*/
|
|
3414
|
+
function declaredInstalledPackages(packageJsonSource) {
|
|
3415
|
+
let pkg;
|
|
3416
|
+
try {
|
|
3417
|
+
pkg = JSON.parse(packageJsonSource ?? "");
|
|
3418
|
+
} catch {
|
|
3419
|
+
return [];
|
|
3420
|
+
}
|
|
3421
|
+
const out = /* @__PURE__ */ new Set();
|
|
3422
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
3423
|
+
const deps = pkg?.[field];
|
|
3424
|
+
if (!deps || typeof deps !== "object") continue;
|
|
3425
|
+
for (const [name, range] of Object.entries(deps)) if (typeof range === "string" && !/^(?:workspace|link|file|portal):/.test(range)) out.add(name);
|
|
3426
|
+
}
|
|
3427
|
+
return [...out];
|
|
3428
|
+
}
|
|
3429
|
+
function hasSvelteCondition(value) {
|
|
3430
|
+
if (!value || typeof value !== "object") return false;
|
|
3431
|
+
return Object.entries(value).some(([key, branch]) => key === "svelte" || hasSvelteCondition(branch));
|
|
3432
|
+
}
|
|
3433
|
+
/** Whether a manifest publishes Svelte components: a `svelte` field, or a `svelte` condition in `exports`. */
|
|
3434
|
+
function isSveltePackage(manifest) {
|
|
3435
|
+
try {
|
|
3436
|
+
const { svelte, exports } = JSON.parse(manifest);
|
|
3437
|
+
return typeof svelte === "string" || hasSvelteCondition(exports);
|
|
3438
|
+
} catch {
|
|
3439
|
+
return false;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
/**
|
|
3443
|
+
* `aliases` widened with the Svelte packages installed in `node_modules` (`dir` is the textual
|
|
3444
|
+
* `node_modules/<name>` path, relative to the app), compiled like workspace packages. Packages that
|
|
3445
|
+
* publish no Svelte marker get no entries: nothing they export is a component.
|
|
3446
|
+
*/
|
|
3447
|
+
function withInstalledPackages(aliases, packages) {
|
|
3448
|
+
const svelte = packages.filter((p) => isSveltePackage(p.manifest));
|
|
3449
|
+
return svelte.length === 0 ? aliases : [...aliases ?? DEFAULT_KIT_ALIASES, ...svelte.flatMap(entriesOf)];
|
|
3450
|
+
}
|
|
3451
|
+
//#endregion
|
|
2871
3452
|
//#region src/project-paths.ts
|
|
2872
3453
|
/**
|
|
2873
3454
|
* Source-file locations that satisfy the project-scope rules, shared by every
|
|
@@ -2886,6 +3467,13 @@ const SITEMAP_SOURCE_PATHS = [
|
|
|
2886
3467
|
"src/routes/sitemap.xml/+server.ts",
|
|
2887
3468
|
"src/routes/sitemap.xml/+server.js"
|
|
2888
3469
|
];
|
|
3470
|
+
/** 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. */
|
|
3473
|
+
function servesRootFile(rel, file) {
|
|
3474
|
+
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));
|
|
3476
|
+
}
|
|
2889
3477
|
/** Vite's own config resolution order — only the first existing file is the one Vite loads. */
|
|
2890
3478
|
const VITE_CONFIG_FILES = [
|
|
2891
3479
|
"vite.config.js",
|
|
@@ -3992,4 +4580,4 @@ function formatHtmlReport(results, config, meta) {
|
|
|
3992
4580
|
return buildHtmlDocument(buildJsonReport(results, config, meta), meta);
|
|
3993
4581
|
}
|
|
3994
4582
|
//#endregion
|
|
3995
|
-
export { ANCESTRY_DEPENDENT_TAGS, APP_SCRIPT, ASIDE_DEMOTING_TAGS, CHILD_NODE_KEYS, IDREF_ATTRS, NAMING_ATTRS, ROBOTS_SOURCE_PATHS, 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, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessAutoplayMuted, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, escapeHtml, explainRule, findAttr, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, isClassicScriptType, isPenalized, isPlainObject, isTopFragment, lineOf, noColorPalette, parseComponentFacts, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceIframeLoading, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveLandmark, resolveRepoLocalPath, resolveRuleOptions, 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, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, validateRuleSetting, valueFromNodes, withFailedRulesOff, withIndexableOff, withReadLimit };
|
|
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 };
|