@svelte-vitals/core 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +187 -8
- package/dist/index.js +760 -54
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -857,10 +857,19 @@ function countLines(source) {
|
|
|
857
857
|
if (source.length === 0) return 0;
|
|
858
858
|
return source.split("\n").length - (source.endsWith("\n") ? 1 : 0);
|
|
859
859
|
}
|
|
860
|
+
function isTypeOnlyImport(n) {
|
|
861
|
+
if (n.importKind === "type") return true;
|
|
862
|
+
const specs = n.specifiers;
|
|
863
|
+
return Array.isArray(specs) && specs.length > 0 && specs.every((s) => s?.importKind === "type");
|
|
864
|
+
}
|
|
860
865
|
function collectImportSources(program, source, acc) {
|
|
861
866
|
walkEstree(program, (n) => {
|
|
862
867
|
if (n.type === "ImportDeclaration" && typeof n.source?.value === "string") {
|
|
863
|
-
acc.push({
|
|
868
|
+
acc.push({
|
|
869
|
+
source: n.source.value,
|
|
870
|
+
line: lineOf(source, n.start),
|
|
871
|
+
...isTypeOnlyImport(n) ? { type: true } : {}
|
|
872
|
+
});
|
|
864
873
|
}
|
|
865
874
|
});
|
|
866
875
|
}
|
|
@@ -1516,6 +1525,12 @@ async function collectComponentFacts(rt, cwd) {
|
|
|
1516
1525
|
);
|
|
1517
1526
|
}
|
|
1518
1527
|
|
|
1528
|
+
// src/source-files.ts
|
|
1529
|
+
async function collectSourceFiles(rt, cwd) {
|
|
1530
|
+
const files = await rt.glob("src/**/*", cwd);
|
|
1531
|
+
return files.slice().sort();
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1519
1534
|
// src/kit-module-parse.ts
|
|
1520
1535
|
var HANDLER_NAMES = /* @__PURE__ */ new Set([
|
|
1521
1536
|
"load",
|
|
@@ -1796,28 +1811,45 @@ function normalizePosix(path) {
|
|
|
1796
1811
|
}
|
|
1797
1812
|
return out.join("/");
|
|
1798
1813
|
}
|
|
1799
|
-
|
|
1814
|
+
var DEFAULT_KIT_ALIASES = [{ find: "$lib", replacement: "src/lib", match: "prefix" }];
|
|
1815
|
+
function aliasMatches(entry, spec) {
|
|
1816
|
+
if (entry.match === "exact") return spec === entry.find;
|
|
1817
|
+
if (spec.startsWith(`${entry.find}/`)) return true;
|
|
1818
|
+
return entry.match === "prefix" && spec === entry.find;
|
|
1819
|
+
}
|
|
1820
|
+
function resolveRepoLocalPath(spec, importerFile, aliases = DEFAULT_KIT_ALIASES) {
|
|
1800
1821
|
let path;
|
|
1801
|
-
if (spec.startsWith("
|
|
1802
|
-
else if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
1822
|
+
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
1803
1823
|
const dir = importerFile.split("/").slice(0, -1).join("/");
|
|
1804
1824
|
path = `${dir}/${spec}`;
|
|
1805
|
-
} else
|
|
1825
|
+
} else {
|
|
1826
|
+
const entry = aliases.find((a) => aliasMatches(a, spec));
|
|
1827
|
+
if (entry?.replacement == null) return void 0;
|
|
1828
|
+
if (entry.replacement.startsWith("/") || /^[A-Za-z]:\//.test(entry.replacement)) return void 0;
|
|
1829
|
+
path = entry.replacement + spec.slice(entry.find.length);
|
|
1830
|
+
}
|
|
1806
1831
|
return normalizePosix(path);
|
|
1807
1832
|
}
|
|
1808
|
-
function resolveRunesModuleSpecifier(spec, importerFile) {
|
|
1809
|
-
const path = resolveRepoLocalPath(spec, importerFile);
|
|
1833
|
+
function resolveRunesModuleSpecifier(spec, importerFile, aliases) {
|
|
1834
|
+
const path = resolveRepoLocalPath(spec, importerFile, aliases);
|
|
1810
1835
|
if (path === void 0) return void 0;
|
|
1811
1836
|
if (/\.svelte\.(ts|js)$/.test(path)) return path;
|
|
1812
1837
|
if (path.endsWith(".svelte")) return `${path}.ts`;
|
|
1813
1838
|
return void 0;
|
|
1814
1839
|
}
|
|
1815
|
-
function
|
|
1816
|
-
const
|
|
1840
|
+
function libServerRoot(aliases) {
|
|
1841
|
+
const lib = aliases?.find((a) => a.find === "$lib");
|
|
1842
|
+
if (lib && lib.replacement === null) return void 0;
|
|
1843
|
+
return `${lib?.replacement ?? "src/lib"}/server`;
|
|
1844
|
+
}
|
|
1845
|
+
function isLocalStateSpecifier(spec, importerFile, aliases) {
|
|
1846
|
+
const serverRoot = libServerRoot(aliases);
|
|
1847
|
+
if (serverRoot === void 0) return false;
|
|
1848
|
+
const path = resolveRepoLocalPath(spec, importerFile, aliases);
|
|
1817
1849
|
if (path === void 0) return false;
|
|
1818
|
-
return path !==
|
|
1850
|
+
return path !== serverRoot && !path.startsWith(`${serverRoot}/`);
|
|
1819
1851
|
}
|
|
1820
|
-
function parseKitModuleFacts(source, filename) {
|
|
1852
|
+
function parseKitModuleFacts(source, filename, aliases) {
|
|
1821
1853
|
const suppressions = collectSuppressions(source);
|
|
1822
1854
|
const { program, wrapped } = parseModuleProgram(source, filename);
|
|
1823
1855
|
const moduleStateReassignments = [];
|
|
@@ -1850,7 +1882,7 @@ function parseKitModuleFacts(source, filename) {
|
|
|
1850
1882
|
importedSpecifiers.set(s.local.name, spec);
|
|
1851
1883
|
}
|
|
1852
1884
|
if (names.length === 0) continue;
|
|
1853
|
-
const resolved = resolveRunesModuleSpecifier(spec, filename);
|
|
1885
|
+
const resolved = resolveRunesModuleSpecifier(spec, filename, aliases);
|
|
1854
1886
|
if (resolved) runesModuleImports.push({ source: spec, resolved, names, line: line(stmt.start) });
|
|
1855
1887
|
}
|
|
1856
1888
|
const moduleLets = /* @__PURE__ */ new Set();
|
|
@@ -1924,7 +1956,8 @@ function parseKitModuleFacts(source, filename) {
|
|
|
1924
1956
|
const method = n.callee.property?.type === "Identifier" ? n.callee.property.name : void 0;
|
|
1925
1957
|
if (method === "set" || method === "update") {
|
|
1926
1958
|
const r = importedRoot(n.callee.object);
|
|
1927
|
-
if (r && isLocalStateSpecifier(importedSpecifiers.get(r), filename))
|
|
1959
|
+
if (r && isLocalStateSpecifier(importedSpecifiers.get(r), filename, aliases))
|
|
1960
|
+
write = { name: r, via: "set-call" };
|
|
1928
1961
|
}
|
|
1929
1962
|
} else if (n.type === "AssignmentExpression" && (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern")) {
|
|
1930
1963
|
const scanPatternTargets = (pat) => {
|
|
@@ -2002,7 +2035,7 @@ function kindOf(file) {
|
|
|
2002
2035
|
const base = file.split("/").pop() ?? file;
|
|
2003
2036
|
return base.includes(".server.") || base.startsWith("+server.") ? "server" : "universal";
|
|
2004
2037
|
}
|
|
2005
|
-
async function collectKitModuleFacts(rt, cwd) {
|
|
2038
|
+
async function collectKitModuleFacts(rt, cwd, aliases) {
|
|
2006
2039
|
const patterns = [
|
|
2007
2040
|
"src/routes/**/+{page,layout}.server.{ts,js}",
|
|
2008
2041
|
"src/routes/**/+{page,layout}.{ts,js}",
|
|
@@ -2016,7 +2049,7 @@ async function collectKitModuleFacts(rt, cwd) {
|
|
|
2016
2049
|
const kind = kindOf(rel);
|
|
2017
2050
|
try {
|
|
2018
2051
|
const source = await rt.readFile(rt.join(cwd, rel));
|
|
2019
|
-
return { file: rel, kind, ...parseKitModuleFacts(source, rel) };
|
|
2052
|
+
return { file: rel, kind, ...parseKitModuleFacts(source, rel, aliases) };
|
|
2020
2053
|
} catch {
|
|
2021
2054
|
return emptyKitModuleFacts(rel, kind);
|
|
2022
2055
|
}
|
|
@@ -2114,6 +2147,79 @@ function basePathOf(kitConfig, bindings) {
|
|
|
2114
2147
|
}
|
|
2115
2148
|
return {};
|
|
2116
2149
|
}
|
|
2150
|
+
function keyNameOf(p) {
|
|
2151
|
+
if (p.computed) return void 0;
|
|
2152
|
+
if (p.key.type === "Identifier") return p.key.name;
|
|
2153
|
+
if (p.key.type === "Literal" && typeof p.key.value === "string") return p.key.value;
|
|
2154
|
+
return void 0;
|
|
2155
|
+
}
|
|
2156
|
+
function stringValueOf(p) {
|
|
2157
|
+
const v = unwrapTs(p.value);
|
|
2158
|
+
return v.type === "Literal" && typeof v.value === "string" ? v.value : void 0;
|
|
2159
|
+
}
|
|
2160
|
+
function aliasEntriesOf(kitConfig, bindings) {
|
|
2161
|
+
const alias = propOf(kitConfig, "alias");
|
|
2162
|
+
if (!alias) return [];
|
|
2163
|
+
const obj = unwrapToObjectExpression(alias.value, bindings);
|
|
2164
|
+
if (!obj) return void 0;
|
|
2165
|
+
const out = [];
|
|
2166
|
+
const at = /* @__PURE__ */ new Map();
|
|
2167
|
+
for (const p of obj.properties) {
|
|
2168
|
+
if (p.type !== "Property") return void 0;
|
|
2169
|
+
const key = keyNameOf(p);
|
|
2170
|
+
if (key === void 0) return void 0;
|
|
2171
|
+
const entry = { key, value: stringValueOf(p) ?? null };
|
|
2172
|
+
const seen = at.get(key);
|
|
2173
|
+
if (seen === void 0) {
|
|
2174
|
+
at.set(key, out.length);
|
|
2175
|
+
out.push(entry);
|
|
2176
|
+
} else out[seen] = entry;
|
|
2177
|
+
}
|
|
2178
|
+
return out;
|
|
2179
|
+
}
|
|
2180
|
+
function filesLibOf(kitConfig, bindings) {
|
|
2181
|
+
const files = propOf(kitConfig, "files");
|
|
2182
|
+
const obj = files ? unwrapToObjectExpression(files.value, bindings) : void 0;
|
|
2183
|
+
const lib = obj ? propOf(obj, "lib") : void 0;
|
|
2184
|
+
if (!lib) return void 0;
|
|
2185
|
+
return stringValueOf(lib) ?? null;
|
|
2186
|
+
}
|
|
2187
|
+
function findKitAliasesInSvelteConfig(source) {
|
|
2188
|
+
const program = programOf(source, "svelte.config.js");
|
|
2189
|
+
const config = program ? resolveConfigObject(program) : void 0;
|
|
2190
|
+
if (!program || !config) return { entries: [] };
|
|
2191
|
+
const bindings = collectTopLevelBindings(program);
|
|
2192
|
+
const kit = propOf(config, "kit");
|
|
2193
|
+
const kitObj = kit ? unwrapToObjectExpression(kit.value, bindings) : void 0;
|
|
2194
|
+
if (!kitObj) return { entries: [] };
|
|
2195
|
+
const filesLib = filesLibOf(kitObj, bindings);
|
|
2196
|
+
return { entries: aliasEntriesOf(kitObj, bindings), ...filesLib !== void 0 ? { filesLib } : {} };
|
|
2197
|
+
}
|
|
2198
|
+
function normalizeAliasValue(value) {
|
|
2199
|
+
const posix = value.replace(/\\/g, "/");
|
|
2200
|
+
const noStar = posix.endsWith("/*") ? posix.slice(0, -2) : posix;
|
|
2201
|
+
return noStar.replace(/\/+$/, "");
|
|
2202
|
+
}
|
|
2203
|
+
function compileKitAliases(raw) {
|
|
2204
|
+
const filesLib = raw.filesLib === null ? null : normalizeAliasValue(raw.filesLib ?? "src/lib");
|
|
2205
|
+
const out = [{ find: "$lib", replacement: filesLib, match: "prefix" }];
|
|
2206
|
+
const entries = raw.entries ?? [];
|
|
2207
|
+
const declared = new Set(entries.map((e) => e.key));
|
|
2208
|
+
for (const { key, value } of entries) {
|
|
2209
|
+
const star = key.endsWith("/*");
|
|
2210
|
+
out.push({
|
|
2211
|
+
find: star ? key.slice(0, -2) : key,
|
|
2212
|
+
replacement: value === null ? null : normalizeAliasValue(value),
|
|
2213
|
+
match: star ? "contents" : declared.has(`${key}/*`) ? "exact" : "prefix"
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
return out;
|
|
2217
|
+
}
|
|
2218
|
+
function resolveKitAliases(viteConfig, svelteConfig) {
|
|
2219
|
+
if (viteConfig && findKitPathsBaseInViteConfig(viteConfig.source).kind !== "no-plugin-config") return void 0;
|
|
2220
|
+
if (!svelteConfig) return void 0;
|
|
2221
|
+
return compileKitAliases(findKitAliasesInSvelteConfig(svelteConfig.source));
|
|
2222
|
+
}
|
|
2117
2223
|
function programOf(source, filename) {
|
|
2118
2224
|
try {
|
|
2119
2225
|
return parseModuleProgram(source, filename).program ?? void 0;
|
|
@@ -2263,7 +2369,7 @@ function detect(head, match) {
|
|
|
2263
2369
|
return tag ? { presence: tag.presence, value: tag.value } : { presence: "none", value: "absent" };
|
|
2264
2370
|
}
|
|
2265
2371
|
function headTagRule(opts) {
|
|
2266
|
-
const
|
|
2372
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
2267
2373
|
return {
|
|
2268
2374
|
id: opts.id,
|
|
2269
2375
|
title: opts.title,
|
|
@@ -2286,7 +2392,7 @@ function headTagRule(opts) {
|
|
|
2286
2392
|
location: head.file,
|
|
2287
2393
|
message,
|
|
2288
2394
|
recommendation: opts.recommendation,
|
|
2289
|
-
docsUrl:
|
|
2395
|
+
docsUrl: docsUrl11,
|
|
2290
2396
|
// Copy per finding: opts.fix is a rule-level template shared across all
|
|
2291
2397
|
// results this rule emits; a fresh object keeps findings independent.
|
|
2292
2398
|
...opts.fix ? { fix: { ...opts.fix } } : {}
|
|
@@ -2479,7 +2585,7 @@ var seoHtmlLang = {
|
|
|
2479
2585
|
|
|
2480
2586
|
// src/rules/perf/image-rule.ts
|
|
2481
2587
|
function imageRule(opts) {
|
|
2482
|
-
const
|
|
2588
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
2483
2589
|
const category = opts.category ?? "performance";
|
|
2484
2590
|
return {
|
|
2485
2591
|
id: opts.id,
|
|
@@ -2503,7 +2609,7 @@ function imageRule(opts) {
|
|
|
2503
2609
|
route: route.route,
|
|
2504
2610
|
message: opts.label,
|
|
2505
2611
|
recommendation: opts.recommendation,
|
|
2506
|
-
docsUrl:
|
|
2612
|
+
docsUrl: docsUrl11
|
|
2507
2613
|
});
|
|
2508
2614
|
continue;
|
|
2509
2615
|
}
|
|
@@ -2518,7 +2624,7 @@ function imageRule(opts) {
|
|
|
2518
2624
|
...img.line > 0 ? { line: img.line } : {},
|
|
2519
2625
|
message: `Missing ${opts.label}`,
|
|
2520
2626
|
recommendation: opts.recommendation,
|
|
2521
|
-
docsUrl:
|
|
2627
|
+
docsUrl: docsUrl11,
|
|
2522
2628
|
...opts.fix ? { fix: { ...opts.fix } } : {}
|
|
2523
2629
|
});
|
|
2524
2630
|
}
|
|
@@ -2578,7 +2684,7 @@ var performanceResponsiveImage = imageRule({
|
|
|
2578
2684
|
|
|
2579
2685
|
// src/rules/perf/link-rule.ts
|
|
2580
2686
|
function linkRule(opts) {
|
|
2581
|
-
const
|
|
2687
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
2582
2688
|
return {
|
|
2583
2689
|
id: opts.id,
|
|
2584
2690
|
title: opts.title,
|
|
@@ -2602,7 +2708,7 @@ function linkRule(opts) {
|
|
|
2602
2708
|
route: head.route,
|
|
2603
2709
|
message: opts.label,
|
|
2604
2710
|
recommendation: opts.recommendation,
|
|
2605
|
-
docsUrl:
|
|
2711
|
+
docsUrl: docsUrl11
|
|
2606
2712
|
});
|
|
2607
2713
|
continue;
|
|
2608
2714
|
}
|
|
@@ -2619,7 +2725,7 @@ function linkRule(opts) {
|
|
|
2619
2725
|
location: tag.file ?? head.file,
|
|
2620
2726
|
message: `Missing ${opts.label}`,
|
|
2621
2727
|
recommendation: opts.recommendation,
|
|
2622
|
-
docsUrl:
|
|
2728
|
+
docsUrl: docsUrl11,
|
|
2623
2729
|
...opts.fix ? { fix: { ...opts.fix } } : {}
|
|
2624
2730
|
});
|
|
2625
2731
|
}
|
|
@@ -2842,6 +2948,10 @@ function mapOption(options, key) {
|
|
|
2842
2948
|
const v = options[key];
|
|
2843
2949
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
|
|
2844
2950
|
}
|
|
2951
|
+
function isMentionedAnywhere(config, ruleId) {
|
|
2952
|
+
if (Object.hasOwn(config.rules, ruleId)) return true;
|
|
2953
|
+
return (config.overrides ?? []).some((entry) => entry.rules !== void 0 && Object.hasOwn(entry.rules, ruleId));
|
|
2954
|
+
}
|
|
2845
2955
|
function resolveRuleOptions(ruleId, spec, config, target, compiled) {
|
|
2846
2956
|
if (!spec) return {};
|
|
2847
2957
|
const out = defaultsOf(spec);
|
|
@@ -3047,7 +3157,7 @@ var seoIndexability = {
|
|
|
3047
3157
|
rationale: "A noindex directive removes the page from search results; an accidental noindex on a public route silently deindexes it.",
|
|
3048
3158
|
fix: FIX5,
|
|
3049
3159
|
async check(ctx) {
|
|
3050
|
-
const
|
|
3160
|
+
const docsUrl11 = docsUrlFor("seo/indexability");
|
|
3051
3161
|
const out = [];
|
|
3052
3162
|
for (const head of ctx.heads) {
|
|
3053
3163
|
const noindexed = head.tags.some((t) => t.kind === "meta" && t.name === "robots" && t.noindex === true);
|
|
@@ -3062,7 +3172,7 @@ var seoIndexability = {
|
|
|
3062
3172
|
location: head.file,
|
|
3063
3173
|
message: "Route is noindex \u2014 verify this is intentional",
|
|
3064
3174
|
recommendation: 'If this route should be indexed, remove noindex from its <meta name="robots">.',
|
|
3065
|
-
docsUrl:
|
|
3175
|
+
docsUrl: docsUrl11,
|
|
3066
3176
|
fix: { ...FIX5 }
|
|
3067
3177
|
});
|
|
3068
3178
|
}
|
|
@@ -3313,7 +3423,7 @@ function jsonldTags(head) {
|
|
|
3313
3423
|
return head.tags.filter((t) => t.kind === "jsonld" && typeof t.jsonld === "string");
|
|
3314
3424
|
}
|
|
3315
3425
|
function jsonldRule(opts) {
|
|
3316
|
-
const
|
|
3426
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
3317
3427
|
return {
|
|
3318
3428
|
id: opts.id,
|
|
3319
3429
|
title: opts.title,
|
|
@@ -3341,7 +3451,7 @@ function jsonldRule(opts) {
|
|
|
3341
3451
|
location: head.file,
|
|
3342
3452
|
message: problem,
|
|
3343
3453
|
recommendation: opts.recommendation,
|
|
3344
|
-
docsUrl:
|
|
3454
|
+
docsUrl: docsUrl11,
|
|
3345
3455
|
...opts.fix ? { fix: { ...opts.fix } } : {}
|
|
3346
3456
|
} : {
|
|
3347
3457
|
id: opts.id,
|
|
@@ -3351,7 +3461,7 @@ function jsonldRule(opts) {
|
|
|
3351
3461
|
route: head.route,
|
|
3352
3462
|
message: opts.label,
|
|
3353
3463
|
recommendation: opts.recommendation,
|
|
3354
|
-
docsUrl:
|
|
3464
|
+
docsUrl: docsUrl11
|
|
3355
3465
|
}
|
|
3356
3466
|
);
|
|
3357
3467
|
}
|
|
@@ -3375,7 +3485,7 @@ var seoJsonLdValidity = {
|
|
|
3375
3485
|
lang: "svelte"
|
|
3376
3486
|
},
|
|
3377
3487
|
async check(ctx) {
|
|
3378
|
-
const
|
|
3488
|
+
const docsUrl11 = docsUrlFor("seo/json-ld-validity");
|
|
3379
3489
|
const out = [];
|
|
3380
3490
|
for (const head of ctx.heads) {
|
|
3381
3491
|
for (const tag of jsonldTags(head)) {
|
|
@@ -3394,7 +3504,7 @@ var seoJsonLdValidity = {
|
|
|
3394
3504
|
location: head.file,
|
|
3395
3505
|
message: problem,
|
|
3396
3506
|
recommendation: "Make the JSON-LD valid JSON with both @context and @type.",
|
|
3397
|
-
docsUrl:
|
|
3507
|
+
docsUrl: docsUrl11,
|
|
3398
3508
|
fix: { ...seoJsonLdValidity.fix }
|
|
3399
3509
|
} : {
|
|
3400
3510
|
id: "seo/json-ld-validity",
|
|
@@ -3404,7 +3514,7 @@ var seoJsonLdValidity = {
|
|
|
3404
3514
|
route: head.route,
|
|
3405
3515
|
message: "JSON-LD validity",
|
|
3406
3516
|
recommendation: "Make the JSON-LD valid JSON with both @context and @type.",
|
|
3407
|
-
docsUrl:
|
|
3517
|
+
docsUrl: docsUrl11
|
|
3408
3518
|
}
|
|
3409
3519
|
);
|
|
3410
3520
|
}
|
|
@@ -3515,7 +3625,7 @@ function visibleLength(s) {
|
|
|
3515
3625
|
|
|
3516
3626
|
// src/rules/seo/length-rule.ts
|
|
3517
3627
|
function lengthRule(opts) {
|
|
3518
|
-
const
|
|
3628
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
3519
3629
|
const spec = {
|
|
3520
3630
|
min: { kind: "integer", default: opts.min, min: 0 },
|
|
3521
3631
|
max: { kind: "integer", default: opts.max, min: 1 }
|
|
@@ -3538,7 +3648,7 @@ function lengthRule(opts) {
|
|
|
3538
3648
|
const o = resolveRuleOptions(opts.id, spec, ctx.config, { route: head.route, file: location }, compiled);
|
|
3539
3649
|
const min = intOption(o, "min", opts.min);
|
|
3540
3650
|
const max = intOption(o, "max", opts.max);
|
|
3541
|
-
const
|
|
3651
|
+
const recommendation11 = typeof opts.recommendation === "function" ? opts.recommendation(o) : opts.recommendation;
|
|
3542
3652
|
const len = visibleLength(tag.text);
|
|
3543
3653
|
let problem;
|
|
3544
3654
|
if (len < min) problem = `${opts.noun} is too short (${len} chars; aim for ${min}\u2013${max})`;
|
|
@@ -3552,8 +3662,8 @@ function lengthRule(opts) {
|
|
|
3552
3662
|
route: head.route,
|
|
3553
3663
|
location,
|
|
3554
3664
|
message: problem,
|
|
3555
|
-
recommendation:
|
|
3556
|
-
docsUrl:
|
|
3665
|
+
recommendation: recommendation11,
|
|
3666
|
+
docsUrl: docsUrl11
|
|
3557
3667
|
} : {
|
|
3558
3668
|
id: opts.id,
|
|
3559
3669
|
category: "seo",
|
|
@@ -3561,8 +3671,8 @@ function lengthRule(opts) {
|
|
|
3561
3671
|
detection: PASS,
|
|
3562
3672
|
route: head.route,
|
|
3563
3673
|
message: opts.label,
|
|
3564
|
-
recommendation:
|
|
3565
|
-
docsUrl:
|
|
3674
|
+
recommendation: recommendation11,
|
|
3675
|
+
docsUrl: docsUrl11
|
|
3566
3676
|
}
|
|
3567
3677
|
);
|
|
3568
3678
|
}
|
|
@@ -3747,7 +3857,7 @@ var seoSingleH1 = {
|
|
|
3747
3857
|
|
|
3748
3858
|
// src/rules/seo/uniqueness-rule.ts
|
|
3749
3859
|
function uniquenessRule(opts) {
|
|
3750
|
-
const
|
|
3860
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
3751
3861
|
return {
|
|
3752
3862
|
id: opts.id,
|
|
3753
3863
|
title: opts.title,
|
|
@@ -3777,7 +3887,7 @@ function uniquenessRule(opts) {
|
|
|
3777
3887
|
location: e.file,
|
|
3778
3888
|
message: `${opts.noun} is duplicated across ${n} routes`,
|
|
3779
3889
|
recommendation: opts.recommendation,
|
|
3780
|
-
docsUrl:
|
|
3890
|
+
docsUrl: docsUrl11
|
|
3781
3891
|
} : {
|
|
3782
3892
|
id: opts.id,
|
|
3783
3893
|
category: "seo",
|
|
@@ -3786,7 +3896,7 @@ function uniquenessRule(opts) {
|
|
|
3786
3896
|
route: e.route,
|
|
3787
3897
|
message: opts.label,
|
|
3788
3898
|
recommendation: opts.recommendation,
|
|
3789
|
-
docsUrl:
|
|
3899
|
+
docsUrl: docsUrl11
|
|
3790
3900
|
};
|
|
3791
3901
|
});
|
|
3792
3902
|
}
|
|
@@ -3874,7 +3984,7 @@ function isSuppressed(m, ruleId, line) {
|
|
|
3874
3984
|
return (m.suppressions ?? []).some((s) => s.line === line && (!s.ruleIds || s.ruleIds.includes(ruleId)));
|
|
3875
3985
|
}
|
|
3876
3986
|
function kitModuleRule(opts) {
|
|
3877
|
-
const
|
|
3987
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
3878
3988
|
const severity = opts.severity ?? "warning";
|
|
3879
3989
|
return {
|
|
3880
3990
|
id: opts.id,
|
|
@@ -3898,7 +4008,7 @@ function kitModuleRule(opts) {
|
|
|
3898
4008
|
route: m.file,
|
|
3899
4009
|
message: opts.label,
|
|
3900
4010
|
recommendation: opts.recommendation,
|
|
3901
|
-
docsUrl:
|
|
4011
|
+
docsUrl: docsUrl11
|
|
3902
4012
|
});
|
|
3903
4013
|
continue;
|
|
3904
4014
|
}
|
|
@@ -3913,7 +4023,7 @@ function kitModuleRule(opts) {
|
|
|
3913
4023
|
...b.line > 0 ? { line: b.line } : {},
|
|
3914
4024
|
message: b.message,
|
|
3915
4025
|
recommendation: opts.recommendation,
|
|
3916
|
-
docsUrl:
|
|
4026
|
+
docsUrl: docsUrl11,
|
|
3917
4027
|
...opts.fix ? { fix: { ...opts.fix } } : {}
|
|
3918
4028
|
});
|
|
3919
4029
|
}
|
|
@@ -3949,7 +4059,7 @@ function isSuppressed2(c, ruleId, line) {
|
|
|
3949
4059
|
return (c.suppressions ?? []).some((s) => s.line === line && (!s.ruleIds || s.ruleIds.includes(ruleId)));
|
|
3950
4060
|
}
|
|
3951
4061
|
function componentRule(opts) {
|
|
3952
|
-
const
|
|
4062
|
+
const docsUrl11 = docsUrlFor(opts.id);
|
|
3953
4063
|
const severity = opts.severity ?? "warning";
|
|
3954
4064
|
return {
|
|
3955
4065
|
id: opts.id,
|
|
@@ -3965,9 +4075,9 @@ function componentRule(opts) {
|
|
|
3965
4075
|
const compiled = compileOverrides(ctx.config);
|
|
3966
4076
|
for (const c of ctx.components ?? []) {
|
|
3967
4077
|
const o = resolveRuleOptions(opts.id, opts.options, ctx.config, { route: c.file, file: c.file }, compiled);
|
|
3968
|
-
const
|
|
3969
|
-
if (!opts.applies(c, o)) continue;
|
|
3970
|
-
const bad = opts.bad(c, o).filter((b) => !(b.line > 0 && isSuppressed2(c, opts.id, b.line)));
|
|
4078
|
+
const recommendation11 = typeof opts.recommendation === "function" ? opts.recommendation(o) : opts.recommendation;
|
|
4079
|
+
if (!opts.applies(c, o, ctx)) continue;
|
|
4080
|
+
const bad = opts.bad(c, o, ctx).filter((b) => !(b.line > 0 && isSuppressed2(c, opts.id, b.line)));
|
|
3971
4081
|
if (bad.length === 0) {
|
|
3972
4082
|
out.push({
|
|
3973
4083
|
id: opts.id,
|
|
@@ -3976,8 +4086,8 @@ function componentRule(opts) {
|
|
|
3976
4086
|
detection: PASS3,
|
|
3977
4087
|
route: c.file,
|
|
3978
4088
|
message: opts.label,
|
|
3979
|
-
recommendation:
|
|
3980
|
-
docsUrl:
|
|
4089
|
+
recommendation: recommendation11,
|
|
4090
|
+
docsUrl: docsUrl11
|
|
3981
4091
|
});
|
|
3982
4092
|
continue;
|
|
3983
4093
|
}
|
|
@@ -3991,8 +4101,8 @@ function componentRule(opts) {
|
|
|
3991
4101
|
location: c.file,
|
|
3992
4102
|
...b.line > 0 ? { line: b.line } : {},
|
|
3993
4103
|
message: b.message,
|
|
3994
|
-
recommendation:
|
|
3995
|
-
docsUrl:
|
|
4104
|
+
recommendation: recommendation11,
|
|
4105
|
+
docsUrl: docsUrl11,
|
|
3996
4106
|
...opts.fix ? { fix: { ...opts.fix } } : {}
|
|
3997
4107
|
});
|
|
3998
4108
|
}
|
|
@@ -4597,7 +4707,7 @@ var architecturePrivateScopeImport = {
|
|
|
4597
4707
|
let sawScopedImport = false;
|
|
4598
4708
|
const violations = [];
|
|
4599
4709
|
for (const { source, line } of spans) {
|
|
4600
|
-
const target = resolveRepoLocalPath(source, c.file);
|
|
4710
|
+
const target = resolveRepoLocalPath(source, c.file, ctx.project.kitAliases);
|
|
4601
4711
|
if (target === void 0) continue;
|
|
4602
4712
|
const boundary = privateScopeOf(target, patterns);
|
|
4603
4713
|
if (boundary === void 0) continue;
|
|
@@ -4642,6 +4752,590 @@ var architecturePrivateScopeImport = {
|
|
|
4642
4752
|
}
|
|
4643
4753
|
};
|
|
4644
4754
|
|
|
4755
|
+
// src/rules/architecture/declarations.ts
|
|
4756
|
+
function ancestorDirs2(file) {
|
|
4757
|
+
const segments = file.split("/");
|
|
4758
|
+
const out = [];
|
|
4759
|
+
for (let i = 1; i < segments.length; i++) out.push(segments.slice(0, i).join("/"));
|
|
4760
|
+
return out;
|
|
4761
|
+
}
|
|
4762
|
+
function baseName(dir) {
|
|
4763
|
+
const cut = dir.lastIndexOf("/");
|
|
4764
|
+
return cut === -1 ? dir : dir.slice(cut + 1);
|
|
4765
|
+
}
|
|
4766
|
+
function childDirs(dirs) {
|
|
4767
|
+
const out = /* @__PURE__ */ new Map();
|
|
4768
|
+
for (const dir of dirs) {
|
|
4769
|
+
const cut = dir.lastIndexOf("/");
|
|
4770
|
+
if (cut === -1) continue;
|
|
4771
|
+
const parent = dir.slice(0, cut);
|
|
4772
|
+
let kids = out.get(parent);
|
|
4773
|
+
if (kids === void 0) out.set(parent, kids = []);
|
|
4774
|
+
kids.push(dir);
|
|
4775
|
+
}
|
|
4776
|
+
for (const kids of out.values()) kids.sort();
|
|
4777
|
+
return out;
|
|
4778
|
+
}
|
|
4779
|
+
function childFiles(files) {
|
|
4780
|
+
const out = /* @__PURE__ */ new Map();
|
|
4781
|
+
for (const file of files) {
|
|
4782
|
+
const cut = file.lastIndexOf("/");
|
|
4783
|
+
if (cut === -1) continue;
|
|
4784
|
+
const dir = file.slice(0, cut);
|
|
4785
|
+
let own = out.get(dir);
|
|
4786
|
+
if (own === void 0) out.set(dir, own = []);
|
|
4787
|
+
own.push(file.slice(cut + 1));
|
|
4788
|
+
}
|
|
4789
|
+
for (const own of out.values()) own.sort();
|
|
4790
|
+
return out;
|
|
4791
|
+
}
|
|
4792
|
+
function splitNames(value) {
|
|
4793
|
+
const out = [];
|
|
4794
|
+
for (const raw of value.split("|")) {
|
|
4795
|
+
const token = raw.trim();
|
|
4796
|
+
if (token.length > 0) out.push(token);
|
|
4797
|
+
}
|
|
4798
|
+
return out;
|
|
4799
|
+
}
|
|
4800
|
+
function keyShape(key) {
|
|
4801
|
+
const parts = key.split("/");
|
|
4802
|
+
let doubleStars = 0;
|
|
4803
|
+
for (const p of parts) if (p === "**") doubleStars++;
|
|
4804
|
+
return { segments: parts.length, doubleStars };
|
|
4805
|
+
}
|
|
4806
|
+
function createKeyCompiler() {
|
|
4807
|
+
const cache = /* @__PURE__ */ new Map();
|
|
4808
|
+
return (globs, bareGuard = false) => {
|
|
4809
|
+
const cacheKey = JSON.stringify([globs, bareGuard]);
|
|
4810
|
+
let entry = cache.get(cacheKey);
|
|
4811
|
+
if (entry === void 0) {
|
|
4812
|
+
entry = globs.map((key) => ({
|
|
4813
|
+
key,
|
|
4814
|
+
re: routeGlobToRegExp(key),
|
|
4815
|
+
...keyShape(key),
|
|
4816
|
+
...bareGuard && key.endsWith("/**") ? { barePrefixRe: routeGlobToRegExp(key.slice(0, -3)) } : {}
|
|
4817
|
+
}));
|
|
4818
|
+
cache.set(cacheKey, entry);
|
|
4819
|
+
}
|
|
4820
|
+
return entry;
|
|
4821
|
+
};
|
|
4822
|
+
}
|
|
4823
|
+
function matchKeys(dir, compiled) {
|
|
4824
|
+
const matched = [];
|
|
4825
|
+
let best;
|
|
4826
|
+
for (const entry of compiled) {
|
|
4827
|
+
if (entry.barePrefixRe?.test(dir)) continue;
|
|
4828
|
+
if (!entry.re.test(dir)) continue;
|
|
4829
|
+
matched.push(entry.key);
|
|
4830
|
+
if (best === void 0 || moreSpecificShaped(entry, best)) best = entry;
|
|
4831
|
+
}
|
|
4832
|
+
return best === void 0 ? { matched } : { matched, best: best.key };
|
|
4833
|
+
}
|
|
4834
|
+
function moreSpecificShaped(a, b) {
|
|
4835
|
+
if (a.segments !== b.segments) return a.segments > b.segments;
|
|
4836
|
+
if (a.doubleStars !== b.doubleStars) return a.doubleStars < b.doubleStars;
|
|
4837
|
+
if (a.key.length !== b.key.length) return a.key.length > b.key.length;
|
|
4838
|
+
return a.key < b.key;
|
|
4839
|
+
}
|
|
4840
|
+
function moreSpecificGlob(a, b) {
|
|
4841
|
+
return moreSpecificShaped({ key: a, ...keyShape(a) }, { key: b, ...keyShape(b) });
|
|
4842
|
+
}
|
|
4843
|
+
function reportAt(dir, files) {
|
|
4844
|
+
const prefix = `${dir}/`;
|
|
4845
|
+
const under = files.filter((f) => f.startsWith(prefix)).sort();
|
|
4846
|
+
return under.find((f) => !f.slice(prefix.length).includes("/")) ?? under[0];
|
|
4847
|
+
}
|
|
4848
|
+
function isExcluded(dir, ancestors, excluded) {
|
|
4849
|
+
return excluded.some(({ re }) => re.test(dir) || ancestors.some((a) => re.test(a)));
|
|
4850
|
+
}
|
|
4851
|
+
function keysMatchingAny(keys, dirs, compile) {
|
|
4852
|
+
const hit = /* @__PURE__ */ new Set();
|
|
4853
|
+
if (keys.length === 0 || dirs.length === 0) return hit;
|
|
4854
|
+
for (const { key, re, barePrefixRe } of compile(keys, true)) {
|
|
4855
|
+
if (dirs.some((d) => !barePrefixRe?.test(d) && re.test(d))) hit.add(key);
|
|
4856
|
+
}
|
|
4857
|
+
return hit;
|
|
4858
|
+
}
|
|
4859
|
+
function classifyUnusedKeys(unused, excludedDirs, compile) {
|
|
4860
|
+
const out = /* @__PURE__ */ new Map();
|
|
4861
|
+
if (unused.length === 0) return out;
|
|
4862
|
+
const shadowed = keysMatchingAny(unused, excludedDirs, compile);
|
|
4863
|
+
for (const key of unused) out.set(key, shadowed.has(key) ? "only-excluded" : "no-match");
|
|
4864
|
+
return out;
|
|
4865
|
+
}
|
|
4866
|
+
|
|
4867
|
+
// src/rules/architecture/unit-entry-file.ts
|
|
4868
|
+
var ID4 = "architecture/unit-entry-file";
|
|
4869
|
+
var docsUrl8 = docsUrlFor(ID4);
|
|
4870
|
+
var recommendation8 = "Give every declared unit directory a file named after it, or stop declaring that directory a unit.";
|
|
4871
|
+
var OPTIONS3 = {
|
|
4872
|
+
units: { kind: "string-map", default: {} },
|
|
4873
|
+
pascalCaseUnits: { kind: "string-map", default: {} },
|
|
4874
|
+
exclude: { kind: "string-list", default: [] }
|
|
4875
|
+
};
|
|
4876
|
+
function isPascalCase(name) {
|
|
4877
|
+
const c = name.charCodeAt(0);
|
|
4878
|
+
return c >= 65 && c <= 90;
|
|
4879
|
+
}
|
|
4880
|
+
var architectureUnitEntryFile = {
|
|
4881
|
+
id: ID4,
|
|
4882
|
+
title: "Unit entry file",
|
|
4883
|
+
category: "architecture",
|
|
4884
|
+
severity: "info",
|
|
4885
|
+
scope: "component",
|
|
4886
|
+
rationale: "A directory named after a unit but missing that unit's entry file is either an incomplete unit or a grouping wearing the wrong name; either way the tree no longer says what it means, and tooling that resolves by convention starts guessing.",
|
|
4887
|
+
fix: {
|
|
4888
|
+
description: "Make the directory and its entry file agree \u2014 add the entry file, or stop declaring this directory a unit."
|
|
4889
|
+
},
|
|
4890
|
+
options: OPTIONS3,
|
|
4891
|
+
async check(ctx) {
|
|
4892
|
+
const files = ctx.sourceFiles;
|
|
4893
|
+
if (files === void 0) return [];
|
|
4894
|
+
if (!isMentionedAnywhere(ctx.config, ID4)) return [];
|
|
4895
|
+
const compiledOverrides = compileOverrides(ctx.config);
|
|
4896
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
4897
|
+
for (const f of files) for (const d of ancestorDirs2(f)) dirs.add(d);
|
|
4898
|
+
const fileSet = new Set(files);
|
|
4899
|
+
const compile = createKeyCompiler();
|
|
4900
|
+
const out = [];
|
|
4901
|
+
const globalOptions = resolveRuleOptions(ID4, OPTIONS3, ctx.config);
|
|
4902
|
+
const globalKeys = /* @__PURE__ */ new Set([
|
|
4903
|
+
...Object.keys(mapOption(globalOptions, "units")),
|
|
4904
|
+
...Object.keys(mapOption(globalOptions, "pascalCaseUnits"))
|
|
4905
|
+
]);
|
|
4906
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
4907
|
+
const excludedDirs = [];
|
|
4908
|
+
const matchedSurviving = /* @__PURE__ */ new Set();
|
|
4909
|
+
for (const dir of [...dirs].sort()) {
|
|
4910
|
+
const o = resolveRuleOptions(ID4, OPTIONS3, ctx.config, { route: dir, file: dir }, compiledOverrides);
|
|
4911
|
+
const units = mapOption(o, "units");
|
|
4912
|
+
const pascalUnits = mapOption(o, "pascalCaseUnits");
|
|
4913
|
+
if (Object.keys(units).length === 0 && Object.keys(pascalUnits).length === 0) continue;
|
|
4914
|
+
const excluded = compile(listOption(o, "exclude"));
|
|
4915
|
+
const ancestors = ancestorDirs2(dir);
|
|
4916
|
+
if (isExcluded(dir, ancestors, excluded)) {
|
|
4917
|
+
excludedDirs.push(dir);
|
|
4918
|
+
continue;
|
|
4919
|
+
}
|
|
4920
|
+
const byPath = matchKeys(dir, compile(Object.keys(units), true));
|
|
4921
|
+
const byCasing = matchKeys(dir, compile(Object.keys(pascalUnits), true));
|
|
4922
|
+
for (const k of byPath.matched) if (globalKeys.has(k)) usedKeys.add(k);
|
|
4923
|
+
for (const k of byPath.matched) if (globalKeys.has(k)) matchedSurviving.add(k);
|
|
4924
|
+
for (const k of byCasing.matched) if (globalKeys.has(k)) matchedSurviving.add(k);
|
|
4925
|
+
if (isPascalCase(baseName(dir))) {
|
|
4926
|
+
for (const k of byCasing.matched) if (globalKeys.has(k)) usedKeys.add(k);
|
|
4927
|
+
}
|
|
4928
|
+
let ext = byPath.best === void 0 ? void 0 : units[byPath.best];
|
|
4929
|
+
const viaUnits = ext !== void 0;
|
|
4930
|
+
if (ext === void 0 && isPascalCase(baseName(dir))) {
|
|
4931
|
+
ext = byCasing.best === void 0 ? void 0 : pascalUnits[byCasing.best];
|
|
4932
|
+
}
|
|
4933
|
+
if (ext === void 0) continue;
|
|
4934
|
+
const expected = `${dir}/${baseName(dir)}${ext}`;
|
|
4935
|
+
if (fileSet.has(expected)) {
|
|
4936
|
+
out.push({
|
|
4937
|
+
id: ID4,
|
|
4938
|
+
category: "architecture",
|
|
4939
|
+
severity: "info",
|
|
4940
|
+
detection: { presence: "own", value: "static" },
|
|
4941
|
+
route: expected,
|
|
4942
|
+
message: "Unit entry file",
|
|
4943
|
+
recommendation: recommendation8,
|
|
4944
|
+
docsUrl: docsUrl8
|
|
4945
|
+
});
|
|
4946
|
+
continue;
|
|
4947
|
+
}
|
|
4948
|
+
const at = reportAt(dir, files);
|
|
4949
|
+
if (at === void 0) continue;
|
|
4950
|
+
out.push({
|
|
4951
|
+
id: ID4,
|
|
4952
|
+
category: "architecture",
|
|
4953
|
+
severity: "info",
|
|
4954
|
+
detection: { presence: "none", value: "absent" },
|
|
4955
|
+
route: dir,
|
|
4956
|
+
location: at,
|
|
4957
|
+
message: `${dir} declares a unit but has no ${expected}`,
|
|
4958
|
+
recommendation: recommendation8,
|
|
4959
|
+
docsUrl: docsUrl8,
|
|
4960
|
+
// Which declaration matched decides the wording: a `units` match like functions/getFoo/
|
|
4961
|
+
// is already camelCase, so telling its author to rename it would be nonsense.
|
|
4962
|
+
fix: {
|
|
4963
|
+
description: viaUnits ? `Add ${baseName(dir)}${ext} to this directory, or remove it from the units declaration.` : `Add the same-named entry file, or rename the directory to camelCase if it is a grouping.`
|
|
4964
|
+
}
|
|
4965
|
+
});
|
|
4966
|
+
}
|
|
4967
|
+
const inertKeys = [...globalKeys].filter((key) => !usedKeys.has(key)).sort();
|
|
4968
|
+
if (inertKeys.length > 0) {
|
|
4969
|
+
const shadowed = inertKeys.filter((k) => !matchedSurviving.has(k));
|
|
4970
|
+
const reasons = classifyUnusedKeys(shadowed, excludedDirs, compile);
|
|
4971
|
+
const why = (k) => reasons.get(k) === "only-excluded" ? "matched only excluded directories" : "matched no directory";
|
|
4972
|
+
const message = inertKeys.length === 1 ? `The declaration '${inertKeys[0]}' ${why(inertKeys[0])}, so it checks nothing.` : `These declarations check nothing: ${inertKeys.map((k) => `'${k}' (${why(k)})`).join(", ")}.`;
|
|
4973
|
+
out.push({
|
|
4974
|
+
id: ID4,
|
|
4975
|
+
category: "architecture",
|
|
4976
|
+
severity: "info",
|
|
4977
|
+
detection: { presence: "none", value: "absent" },
|
|
4978
|
+
message,
|
|
4979
|
+
recommendation: "Correct the glob, or remove the declaration.",
|
|
4980
|
+
docsUrl: docsUrl8
|
|
4981
|
+
});
|
|
4982
|
+
}
|
|
4983
|
+
return out;
|
|
4984
|
+
}
|
|
4985
|
+
};
|
|
4986
|
+
|
|
4987
|
+
// src/rules/architecture/casing.ts
|
|
4988
|
+
var CASINGS = {
|
|
4989
|
+
camelCase: /^[a-z][a-zA-Z0-9]*$/,
|
|
4990
|
+
PascalCase: /^[A-Z][a-zA-Z0-9]*$/,
|
|
4991
|
+
"kebab-case": /^[a-z0-9]+(-[a-z0-9]+)*$/,
|
|
4992
|
+
snake_case: /^[a-z0-9]+(_[a-z0-9]+)*$/
|
|
4993
|
+
};
|
|
4994
|
+
function parseCasings(value) {
|
|
4995
|
+
const known = [];
|
|
4996
|
+
const unknown = [];
|
|
4997
|
+
for (const name of splitNames(value)) {
|
|
4998
|
+
if (Object.hasOwn(CASINGS, name)) known.push(name);
|
|
4999
|
+
else unknown.push(name);
|
|
5000
|
+
}
|
|
5001
|
+
return { known, unknown };
|
|
5002
|
+
}
|
|
5003
|
+
function decodeSegment(name) {
|
|
5004
|
+
let inner = name;
|
|
5005
|
+
if (inner.length > 2 && inner.startsWith("(") && inner.endsWith(")")) inner = inner.slice(1, -1);
|
|
5006
|
+
else if (inner.length > 4 && inner.startsWith("[[") && inner.endsWith("]]")) inner = inner.slice(2, -2);
|
|
5007
|
+
else if (inner.length > 2 && inner.startsWith("[") && inner.endsWith("]")) inner = inner.slice(1, -1);
|
|
5008
|
+
if (inner.startsWith("...")) inner = inner.slice(3);
|
|
5009
|
+
const eq = inner.indexOf("=");
|
|
5010
|
+
if (eq !== -1) inner = inner.slice(0, eq);
|
|
5011
|
+
if (inner.length === 0 || /[[\]()]/.test(inner)) return void 0;
|
|
5012
|
+
return inner;
|
|
5013
|
+
}
|
|
5014
|
+
function satisfiesCasing(name, allowed) {
|
|
5015
|
+
if (!/[a-zA-Z]/.test(name)) return true;
|
|
5016
|
+
return allowed.some((c) => Object.hasOwn(CASINGS, c) && CASINGS[c].test(name));
|
|
5017
|
+
}
|
|
5018
|
+
|
|
5019
|
+
// src/rules/architecture/directory-naming.ts
|
|
5020
|
+
var ID5 = "architecture/directory-naming";
|
|
5021
|
+
var docsUrl9 = docsUrlFor(ID5);
|
|
5022
|
+
var recommendation9 = "Name each directory in the casing its location declares, or narrow the declaration.";
|
|
5023
|
+
var OPTIONS4 = {
|
|
5024
|
+
directories: { kind: "string-map", default: {} },
|
|
5025
|
+
exclude: { kind: "string-list", default: [] }
|
|
5026
|
+
};
|
|
5027
|
+
var architectureDirectoryNaming = {
|
|
5028
|
+
id: ID5,
|
|
5029
|
+
title: "Directory naming",
|
|
5030
|
+
category: "architecture",
|
|
5031
|
+
severity: "info",
|
|
5032
|
+
scope: "component",
|
|
5033
|
+
rationale: "A directory whose name breaks the convention its location declares stops carrying the meaning the convention gave it, and every reader \u2014 human or agent \u2014 has to open the directory to learn what it is.",
|
|
5034
|
+
fix: {
|
|
5035
|
+
description: "Rename the directory to the declared casing, or narrow the declaration that governs it."
|
|
5036
|
+
},
|
|
5037
|
+
options: OPTIONS4,
|
|
5038
|
+
async check(ctx) {
|
|
5039
|
+
const files = ctx.sourceFiles;
|
|
5040
|
+
if (files === void 0) return [];
|
|
5041
|
+
if (!isMentionedAnywhere(ctx.config, ID5)) return [];
|
|
5042
|
+
const compiledOverrides = compileOverrides(ctx.config);
|
|
5043
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
5044
|
+
for (const f of files) for (const d of ancestorDirs2(f)) dirs.add(d);
|
|
5045
|
+
const compile = createKeyCompiler();
|
|
5046
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
5047
|
+
const casingsOf = (value) => {
|
|
5048
|
+
let p = parsed.get(value);
|
|
5049
|
+
if (p === void 0) parsed.set(value, p = parseCasings(value));
|
|
5050
|
+
return p;
|
|
5051
|
+
};
|
|
5052
|
+
const out = [];
|
|
5053
|
+
const globalOptions = resolveRuleOptions(ID5, OPTIONS4, ctx.config);
|
|
5054
|
+
const globalMap = mapOption(globalOptions, "directories");
|
|
5055
|
+
const globalKeys = new Set(Object.keys(globalMap));
|
|
5056
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
5057
|
+
const excludedDirs = [];
|
|
5058
|
+
for (const dir of [...dirs].sort()) {
|
|
5059
|
+
const o = resolveRuleOptions(ID5, OPTIONS4, ctx.config, { route: dir, file: dir }, compiledOverrides);
|
|
5060
|
+
const declared = mapOption(o, "directories");
|
|
5061
|
+
if (Object.keys(declared).length === 0) continue;
|
|
5062
|
+
const excluded = compile(listOption(o, "exclude"));
|
|
5063
|
+
if (isExcluded(dir, ancestorDirs2(dir), excluded)) {
|
|
5064
|
+
excludedDirs.push(dir);
|
|
5065
|
+
continue;
|
|
5066
|
+
}
|
|
5067
|
+
const live = Object.keys(declared).filter((k) => casingsOf(declared[k]).known.length > 0);
|
|
5068
|
+
const m = matchKeys(dir, compile(live, true));
|
|
5069
|
+
for (const k of m.matched) if (globalKeys.has(k)) usedKeys.add(k);
|
|
5070
|
+
if (m.best === void 0) continue;
|
|
5071
|
+
const decoded = decodeSegment(baseName(dir));
|
|
5072
|
+
if (decoded === void 0) continue;
|
|
5073
|
+
const allowed = casingsOf(declared[m.best]).known;
|
|
5074
|
+
if (satisfiesCasing(decoded, allowed)) continue;
|
|
5075
|
+
const at = reportAt(dir, files);
|
|
5076
|
+
if (at === void 0) continue;
|
|
5077
|
+
out.push({
|
|
5078
|
+
id: ID5,
|
|
5079
|
+
category: "architecture",
|
|
5080
|
+
severity: "info",
|
|
5081
|
+
detection: { presence: "none", value: "absent" },
|
|
5082
|
+
route: dir,
|
|
5083
|
+
location: at,
|
|
5084
|
+
message: `${dir} must be ${allowed.join(" or ")}.`,
|
|
5085
|
+
recommendation: recommendation9,
|
|
5086
|
+
docsUrl: docsUrl9,
|
|
5087
|
+
fix: { description: "Rename the directory, or narrow the declaration that governs it." }
|
|
5088
|
+
});
|
|
5089
|
+
}
|
|
5090
|
+
const notes = /* @__PURE__ */ new Map();
|
|
5091
|
+
for (const key of globalKeys) {
|
|
5092
|
+
const { known, unknown } = casingsOf(globalMap[key]);
|
|
5093
|
+
if (known.length > 0 && unknown.length === 0) continue;
|
|
5094
|
+
const names = unknown.map((u) => `'${u}'`).join(", ");
|
|
5095
|
+
notes.set(
|
|
5096
|
+
key,
|
|
5097
|
+
known.length === 0 ? unknown.length === 0 ? "the value names no casing at all, so it checks nothing" : `unknown casing name ${names}, so it checks nothing` : `unknown casing name ${names}; the rest of the value still applies`
|
|
5098
|
+
);
|
|
5099
|
+
}
|
|
5100
|
+
const unclassified = [...globalKeys].filter(
|
|
5101
|
+
(key) => !notes.has(key) && !usedKeys.has(key) && casingsOf(globalMap[key]).known.length > 0
|
|
5102
|
+
);
|
|
5103
|
+
const reasons = classifyUnusedKeys(unclassified, excludedDirs, compile);
|
|
5104
|
+
for (const [key, reason] of reasons) {
|
|
5105
|
+
notes.set(key, reason === "only-excluded" ? "matched only excluded directories" : "matched no directory");
|
|
5106
|
+
}
|
|
5107
|
+
const reported = [...notes.keys()].sort();
|
|
5108
|
+
if (reported.length > 0) {
|
|
5109
|
+
const message = reported.length === 1 ? `The declaration '${reported[0]}' does not check what it says: ${notes.get(reported[0])}.` : `These declarations do not check what they say: ${reported.map((k) => `'${k}' (${notes.get(k)})`).join(", ")}.`;
|
|
5110
|
+
out.push({
|
|
5111
|
+
id: ID5,
|
|
5112
|
+
category: "architecture",
|
|
5113
|
+
severity: "info",
|
|
5114
|
+
detection: { presence: "none", value: "absent" },
|
|
5115
|
+
message,
|
|
5116
|
+
recommendation: "Correct the glob or the casing name, or remove the declaration.",
|
|
5117
|
+
docsUrl: docsUrl9
|
|
5118
|
+
});
|
|
5119
|
+
}
|
|
5120
|
+
return out;
|
|
5121
|
+
}
|
|
5122
|
+
};
|
|
5123
|
+
|
|
5124
|
+
// src/rules/architecture/reserved-directory-names.ts
|
|
5125
|
+
var ID6 = "architecture/reserved-directory-names";
|
|
5126
|
+
var docsUrl10 = docsUrlFor(ID6);
|
|
5127
|
+
var recommendation10 = "Use one of the names this location declares, or add the new name to the declaration.";
|
|
5128
|
+
var OPTIONS5 = {
|
|
5129
|
+
scopes: { kind: "string-map", default: {} },
|
|
5130
|
+
unitScopes: { kind: "string-map", default: {} },
|
|
5131
|
+
exclude: { kind: "string-list", default: [] }
|
|
5132
|
+
};
|
|
5133
|
+
function stem(file) {
|
|
5134
|
+
const dot = file.indexOf(".");
|
|
5135
|
+
return dot === -1 ? file : file.slice(0, dot);
|
|
5136
|
+
}
|
|
5137
|
+
function isUnitDir(dir, filesIn) {
|
|
5138
|
+
const name = baseName(dir);
|
|
5139
|
+
const first = name.charCodeAt(0);
|
|
5140
|
+
if (!(first >= 65 && first <= 90)) return false;
|
|
5141
|
+
const own = filesIn.get(dir);
|
|
5142
|
+
return own !== void 0 && own.some((f) => stem(f) === name);
|
|
5143
|
+
}
|
|
5144
|
+
var architectureReservedDirectoryNames = {
|
|
5145
|
+
id: ID6,
|
|
5146
|
+
title: "Reserved directory names",
|
|
5147
|
+
category: "architecture",
|
|
5148
|
+
severity: "info",
|
|
5149
|
+
scope: "component",
|
|
5150
|
+
rationale: "A closed set of directory names is only worth writing down if it stays closed: one directory outside it and the table stops describing the tree, so every reader has to open a directory to learn what it holds.",
|
|
5151
|
+
fix: {
|
|
5152
|
+
description: "Rename the directory to a declared name, move it under one of them, or add its name to the declaration."
|
|
5153
|
+
},
|
|
5154
|
+
options: OPTIONS5,
|
|
5155
|
+
async check(ctx) {
|
|
5156
|
+
const files = ctx.sourceFiles;
|
|
5157
|
+
if (files === void 0) return [];
|
|
5158
|
+
if (!isMentionedAnywhere(ctx.config, ID6)) return [];
|
|
5159
|
+
const compiledOverrides = compileOverrides(ctx.config);
|
|
5160
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
5161
|
+
for (const f of files) for (const d of ancestorDirs2(f)) dirs.add(d);
|
|
5162
|
+
const kids = childDirs(dirs);
|
|
5163
|
+
const filesIn = childFiles(files);
|
|
5164
|
+
const compile = createKeyCompiler();
|
|
5165
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
5166
|
+
const namesOf = (value) => {
|
|
5167
|
+
let n = parsed.get(value);
|
|
5168
|
+
if (n === void 0) parsed.set(value, n = splitNames(value));
|
|
5169
|
+
return n;
|
|
5170
|
+
};
|
|
5171
|
+
const out = [];
|
|
5172
|
+
const globalOptions = resolveRuleOptions(ID6, OPTIONS5, ctx.config);
|
|
5173
|
+
const globalScopes = mapOption(globalOptions, "scopes");
|
|
5174
|
+
const globalUnits = mapOption(globalOptions, "unitScopes");
|
|
5175
|
+
const globalKeys = /* @__PURE__ */ new Set([...Object.keys(globalScopes), ...Object.keys(globalUnits)]);
|
|
5176
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
5177
|
+
const excludedDirs = [];
|
|
5178
|
+
const nonUnitDirs = [];
|
|
5179
|
+
const collisions = /* @__PURE__ */ new Set();
|
|
5180
|
+
const noteCollisions = (scopesMap, unitMap) => {
|
|
5181
|
+
for (const key of Object.keys(scopesMap)) {
|
|
5182
|
+
if (!Object.hasOwn(unitMap, key)) continue;
|
|
5183
|
+
if (namesOf(scopesMap[key]).length === 0) continue;
|
|
5184
|
+
if (namesOf(unitMap[key]).length === 0) continue;
|
|
5185
|
+
collisions.add(key);
|
|
5186
|
+
}
|
|
5187
|
+
};
|
|
5188
|
+
noteCollisions(globalScopes, globalUnits);
|
|
5189
|
+
for (const dir of [...dirs].sort()) {
|
|
5190
|
+
const o = resolveRuleOptions(ID6, OPTIONS5, ctx.config, { route: dir, file: dir }, compiledOverrides);
|
|
5191
|
+
const scopes = mapOption(o, "scopes");
|
|
5192
|
+
const unitScopes = mapOption(o, "unitScopes");
|
|
5193
|
+
if (Object.keys(scopes).length === 0 && Object.keys(unitScopes).length === 0) continue;
|
|
5194
|
+
noteCollisions(scopes, unitScopes);
|
|
5195
|
+
const excluded = compile(listOption(o, "exclude"));
|
|
5196
|
+
if (isExcluded(dir, ancestorDirs2(dir), excluded)) {
|
|
5197
|
+
excludedDirs.push(dir);
|
|
5198
|
+
continue;
|
|
5199
|
+
}
|
|
5200
|
+
const liveScopes = Object.keys(scopes).filter((k) => namesOf(scopes[k]).length > 0);
|
|
5201
|
+
const isUnit = isUnitDir(dir, filesIn);
|
|
5202
|
+
const liveUnits = isUnit ? Object.keys(unitScopes).filter((k) => namesOf(unitScopes[k]).length > 0) : [];
|
|
5203
|
+
if (!isUnit) nonUnitDirs.push(dir);
|
|
5204
|
+
const byPosition = matchKeys(dir, compile(liveScopes, true));
|
|
5205
|
+
const byUnit = matchKeys(dir, compile(liveUnits, true));
|
|
5206
|
+
for (const k of byPosition.matched) if (globalKeys.has(k)) usedKeys.add(k);
|
|
5207
|
+
for (const k of byUnit.matched) if (globalKeys.has(k)) usedKeys.add(k);
|
|
5208
|
+
let governing;
|
|
5209
|
+
if (byPosition.best !== void 0 && byUnit.best !== void 0) {
|
|
5210
|
+
governing = moreSpecificGlob(byUnit.best, byPosition.best) ? namesOf(unitScopes[byUnit.best]) : namesOf(scopes[byPosition.best]);
|
|
5211
|
+
} else if (byPosition.best !== void 0) {
|
|
5212
|
+
governing = namesOf(scopes[byPosition.best]);
|
|
5213
|
+
} else if (byUnit.best !== void 0) {
|
|
5214
|
+
governing = namesOf(unitScopes[byUnit.best]);
|
|
5215
|
+
}
|
|
5216
|
+
if (governing === void 0) continue;
|
|
5217
|
+
const allowed = new Set(governing);
|
|
5218
|
+
for (const child of kids.get(dir) ?? []) {
|
|
5219
|
+
if (allowed.has(baseName(child))) continue;
|
|
5220
|
+
if (isExcluded(child, ancestorDirs2(child), excluded)) continue;
|
|
5221
|
+
const childOptions = resolveRuleOptions(
|
|
5222
|
+
ID6,
|
|
5223
|
+
OPTIONS5,
|
|
5224
|
+
ctx.config,
|
|
5225
|
+
{ route: child, file: child },
|
|
5226
|
+
compiledOverrides
|
|
5227
|
+
);
|
|
5228
|
+
if (isExcluded(child, ancestorDirs2(child), compile(listOption(childOptions, "exclude")))) continue;
|
|
5229
|
+
const at = reportAt(child, files);
|
|
5230
|
+
if (at === void 0) continue;
|
|
5231
|
+
out.push({
|
|
5232
|
+
id: ID6,
|
|
5233
|
+
category: "architecture",
|
|
5234
|
+
severity: "info",
|
|
5235
|
+
detection: { presence: "none", value: "absent" },
|
|
5236
|
+
route: child,
|
|
5237
|
+
location: at,
|
|
5238
|
+
message: `${child} is not one of the names declared here: ${governing.join(", ")}.`,
|
|
5239
|
+
recommendation: recommendation10,
|
|
5240
|
+
docsUrl: docsUrl10,
|
|
5241
|
+
fix: {
|
|
5242
|
+
description: "Rename it to a declared name, move it under one of them, or add its name to the declaration."
|
|
5243
|
+
}
|
|
5244
|
+
});
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
const notes = /* @__PURE__ */ new Map();
|
|
5248
|
+
for (const key of collisions) {
|
|
5249
|
+
notes.set(key, "declared in both scopes and unitScopes, so the scopes entry wins wherever both apply");
|
|
5250
|
+
}
|
|
5251
|
+
for (const key of globalKeys) {
|
|
5252
|
+
if (notes.has(key)) continue;
|
|
5253
|
+
const scopesEmpty = Object.hasOwn(globalScopes, key) && namesOf(globalScopes[key]).length === 0;
|
|
5254
|
+
const unitsEmpty = Object.hasOwn(globalUnits, key) && namesOf(globalUnits[key]).length === 0;
|
|
5255
|
+
if (scopesEmpty || unitsEmpty) {
|
|
5256
|
+
notes.set(key, "names no directory name at all");
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
const unused = [...globalKeys].filter((k) => !notes.has(k) && !usedKeys.has(k));
|
|
5260
|
+
const unitOnly = unused.filter((k) => Object.hasOwn(globalUnits, k) && !Object.hasOwn(globalScopes, k));
|
|
5261
|
+
for (const key of keysMatchingAny(unitOnly, nonUnitDirs, compile)) {
|
|
5262
|
+
notes.set(key, "matched directories but never a unit");
|
|
5263
|
+
}
|
|
5264
|
+
for (const [key, reason] of classifyUnusedKeys(
|
|
5265
|
+
unused.filter((k) => !notes.has(k)),
|
|
5266
|
+
excludedDirs,
|
|
5267
|
+
compile
|
|
5268
|
+
)) {
|
|
5269
|
+
notes.set(key, reason === "only-excluded" ? "matched only excluded directories" : "matched no directory");
|
|
5270
|
+
}
|
|
5271
|
+
const reported = [...notes.keys()].sort();
|
|
5272
|
+
if (reported.length > 0) {
|
|
5273
|
+
const message = reported.length === 1 ? `The declaration '${reported[0]}' does not check what it says: ${notes.get(reported[0])}.` : `These declarations do not check what they say: ${reported.map((k) => `'${k}' (${notes.get(k)})`).join(", ")}.`;
|
|
5274
|
+
out.push({
|
|
5275
|
+
id: ID6,
|
|
5276
|
+
category: "architecture",
|
|
5277
|
+
severity: "info",
|
|
5278
|
+
detection: { presence: "none", value: "absent" },
|
|
5279
|
+
message,
|
|
5280
|
+
recommendation: "Correct the glob or the names, or remove the declaration.",
|
|
5281
|
+
docsUrl: docsUrl10
|
|
5282
|
+
});
|
|
5283
|
+
}
|
|
5284
|
+
return out;
|
|
5285
|
+
}
|
|
5286
|
+
};
|
|
5287
|
+
|
|
5288
|
+
// src/rules/architecture/route-component-import.ts
|
|
5289
|
+
var ID7 = "architecture/route-component-import";
|
|
5290
|
+
var EXEMPT_IMPORTERS = ["**/*.stories.svelte", "**/*.test.svelte", "**/*.spec.svelte"];
|
|
5291
|
+
var ROUTES_DIR = "src/routes/";
|
|
5292
|
+
var ROUTE_ENTRY = /^\+(page|layout)(@.*)?\.svelte$/;
|
|
5293
|
+
function isRouteEntry(path) {
|
|
5294
|
+
if (!path.startsWith(ROUTES_DIR)) return false;
|
|
5295
|
+
const base = path.slice(path.lastIndexOf("/") + 1);
|
|
5296
|
+
return base === "+error.svelte" || ROUTE_ENTRY.test(base);
|
|
5297
|
+
}
|
|
5298
|
+
function routeEntryImports(c, ctx) {
|
|
5299
|
+
const out = [];
|
|
5300
|
+
for (const { source, line, type } of c.importSpans ?? []) {
|
|
5301
|
+
if (type) continue;
|
|
5302
|
+
const target = resolveRepoLocalPath(source, c.file, ctx.project.kitAliases);
|
|
5303
|
+
if (target !== void 0 && isRouteEntry(target)) out.push({ line, target });
|
|
5304
|
+
}
|
|
5305
|
+
return out;
|
|
5306
|
+
}
|
|
5307
|
+
var routeEntryImportsCache = /* @__PURE__ */ new WeakMap();
|
|
5308
|
+
function cachedRouteEntryImports(c, ctx) {
|
|
5309
|
+
const cached = routeEntryImportsCache.get(c);
|
|
5310
|
+
if (cached !== void 0 && cached.aliases === ctx.project.kitAliases) return cached.result;
|
|
5311
|
+
const result = routeEntryImports(c, ctx);
|
|
5312
|
+
routeEntryImportsCache.set(c, { aliases: ctx.project.kitAliases, result });
|
|
5313
|
+
return result;
|
|
5314
|
+
}
|
|
5315
|
+
var architectureRouteComponentImport = componentRule({
|
|
5316
|
+
id: ID7,
|
|
5317
|
+
title: "Route component import",
|
|
5318
|
+
category: "architecture",
|
|
5319
|
+
severity: "info",
|
|
5320
|
+
label: "Route component imports",
|
|
5321
|
+
options: { exemptImporters: { kind: "string-list", default: EXEMPT_IMPORTERS } },
|
|
5322
|
+
recommendation: "Extract the shared markup into a component under $lib and import that from both places, leaving the route entry to SvelteKit.",
|
|
5323
|
+
rationale: "A route entry is written on the assumption that SvelteKit renders it: Kit hands a page its data and params, and an error page its page.error and page.status. Imported from somewhere else it receives none of that and renders against nothing, or against the importing page data standing in for its own.",
|
|
5324
|
+
// Signal present = this file imports a route entry, exempt or not. An exempt file therefore
|
|
5325
|
+
// reaches `bad` and earns a PASS, rather than being called signal-free.
|
|
5326
|
+
// `_o`: this rule's option is read only in `bad`, but `ctx` is the third parameter, so the
|
|
5327
|
+
// second cannot simply be omitted the way every other component rule omits what it does not use.
|
|
5328
|
+
applies: (c, _o, ctx) => cachedRouteEntryImports(c, ctx).length > 0,
|
|
5329
|
+
bad: (c, o, ctx) => {
|
|
5330
|
+
const exempt = listOption(o, "exemptImporters").map(routeGlobToRegExp);
|
|
5331
|
+
if (exempt.some((re) => re.test(c.file))) return [];
|
|
5332
|
+
return cachedRouteEntryImports(c, ctx).map(({ line, target }) => ({
|
|
5333
|
+
line,
|
|
5334
|
+
message: `${target} is a SvelteKit route entry \u2014 imported here it renders without the data Kit would give it`
|
|
5335
|
+
}));
|
|
5336
|
+
}
|
|
5337
|
+
});
|
|
5338
|
+
|
|
4645
5339
|
// src/rules/perf/heavy-import.ts
|
|
4646
5340
|
var HEAVY_PACKAGES = {
|
|
4647
5341
|
lodash: "import a submodule (lodash/debounce) or use lodash-es for tree-shaking",
|
|
@@ -4665,8 +5359,8 @@ var performanceHeavyImport = componentRule({
|
|
|
4665
5359
|
const seen = /* @__PURE__ */ new Set();
|
|
4666
5360
|
const out = [];
|
|
4667
5361
|
const spans = c.importSpans ?? c.imports.map((source) => ({ source, line: 0 }));
|
|
4668
|
-
for (const { source: src, line } of spans) {
|
|
4669
|
-
if (!Object.hasOwn(packages, src) || seen.has(src)) continue;
|
|
5362
|
+
for (const { source: src, line, type } of spans) {
|
|
5363
|
+
if (type || !Object.hasOwn(packages, src) || seen.has(src)) continue;
|
|
4670
5364
|
seen.add(src);
|
|
4671
5365
|
out.push({ line, message: `Heavy import "${src}" \u2014 ${packages[src]}` });
|
|
4672
5366
|
}
|
|
@@ -4854,6 +5548,10 @@ var allRules = [
|
|
|
4854
5548
|
architectureComponentSize,
|
|
4855
5549
|
architecturePropCount,
|
|
4856
5550
|
architecturePrivateScopeImport,
|
|
5551
|
+
architectureUnitEntryFile,
|
|
5552
|
+
architectureDirectoryNaming,
|
|
5553
|
+
architectureReservedDirectoryNames,
|
|
5554
|
+
architectureRouteComponentImport,
|
|
4857
5555
|
performanceHeavyImport,
|
|
4858
5556
|
performanceNamespaceImport,
|
|
4859
5557
|
performanceMinifyDisabled,
|
|
@@ -6114,8 +6812,12 @@ export {
|
|
|
6114
6812
|
applyOverrides,
|
|
6115
6813
|
applyRuleSeverities,
|
|
6116
6814
|
architectureComponentSize,
|
|
6815
|
+
architectureDirectoryNaming,
|
|
6117
6816
|
architecturePrivateScopeImport,
|
|
6118
6817
|
architecturePropCount,
|
|
6818
|
+
architectureReservedDirectoryNames,
|
|
6819
|
+
architectureRouteComponentImport,
|
|
6820
|
+
architectureUnitEntryFile,
|
|
6119
6821
|
attrText,
|
|
6120
6822
|
attrTextOf,
|
|
6121
6823
|
attrValue,
|
|
@@ -6125,6 +6827,7 @@ export {
|
|
|
6125
6827
|
classify,
|
|
6126
6828
|
collectComponentFacts,
|
|
6127
6829
|
collectKitModuleFacts,
|
|
6830
|
+
collectSourceFiles,
|
|
6128
6831
|
compileOverrides,
|
|
6129
6832
|
computeHealth,
|
|
6130
6833
|
computeScore,
|
|
@@ -6152,6 +6855,7 @@ export {
|
|
|
6152
6855
|
escapeHtml,
|
|
6153
6856
|
explainRule,
|
|
6154
6857
|
findAttr,
|
|
6858
|
+
findKitAliasesInSvelteConfig,
|
|
6155
6859
|
findKitPathsBaseInSvelteConfig,
|
|
6156
6860
|
findKitPathsBaseInViteConfig,
|
|
6157
6861
|
findMinifyDisabled,
|
|
@@ -6166,6 +6870,7 @@ export {
|
|
|
6166
6870
|
headTagRule,
|
|
6167
6871
|
imageRule,
|
|
6168
6872
|
intOption,
|
|
6873
|
+
isMentionedAnywhere,
|
|
6169
6874
|
isPenalized,
|
|
6170
6875
|
lineOf,
|
|
6171
6876
|
linkRule,
|
|
@@ -6190,6 +6895,7 @@ export {
|
|
|
6190
6895
|
performanceSequentialAwaits,
|
|
6191
6896
|
performanceStateRaw,
|
|
6192
6897
|
renderAppShell,
|
|
6898
|
+
resolveKitAliases,
|
|
6193
6899
|
resolveKitPathsBase,
|
|
6194
6900
|
resolveRuleOptions,
|
|
6195
6901
|
resolveRunesModuleSpecifier,
|