@svelte-vitals/vite 0.36.0 → 0.36.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.
@@ -1,6 +1,6 @@
1
- import { i as parseHtmlHead, r as toOccurrenceMap, t as isLoopbackOrigin } from "../loopback-dIwCgCTG.js";
1
+ import { a as parseHtmlHead, r as toOccurrenceMap, t as isLoopbackOrigin } from "../loopback-OZjFnUGp.js";
2
2
  import { defineConfig } from "@svelte-vitals/core";
3
- import { allRules, applyRuleSeverities, defaultProject, effectiveSeverity, formatFailedRuleWarning, isPenalized, runRules, selectRules, terminalSafe } from "@svelte-vitals/core/internal";
3
+ import { allRules, defaultProject, effectiveSeverity, formatFailedRuleWarning, isPenalized, runAnalysis, selectRules, terminalSafe } from "@svelte-vitals/core/internal";
4
4
  import { DEV } from "esm-env";
5
5
  //#region src/hooks/handle.ts
6
6
  /** Stable signature of a route's penalized findings, so ingest is skipped when a repeat visit finds nothing new. */
@@ -66,16 +66,14 @@ async function analyzeAndIngest(html, route, origin, rules, config, lastSignatur
66
66
  idCandidates: [...new Set(ids)],
67
67
  fullyResolved: true
68
68
  }];
69
- const { results: ruleResults, failedRules } = await runRules(rules, {
69
+ const { results, failedRules, failedRuleIds } = await runAnalysis(rules, {
70
70
  heads: [head],
71
71
  headings,
72
72
  images,
73
73
  a11y,
74
74
  project: defaultProject,
75
75
  config
76
- });
77
- const results = applyRuleSeverities(ruleResults, config);
78
- const failedRuleIds = failedRules.map((f) => f.id);
76
+ }, /* @__PURE__ */ new Map());
79
77
  if (failedRules.length > 0 && globalThis.process?.env?.SVELTE_VITALS_DEBUG) for (const f of failedRules) warn(formatFailedRuleWarning(f));
80
78
  const signature = `${findingSignature(results, config)}|failed:${[...failedRuleIds].sort().join(",")}`;
81
79
  if (lastSignature.get(route) === signature) return;
package/dist/index.js CHANGED
@@ -1,11 +1,10 @@
1
- import { n as collectRenderedHeads, t as isLoopbackOrigin } from "./loopback-dIwCgCTG.js";
1
+ import { i as globFiles, n as collectRenderedHeads, t as isLoopbackOrigin } from "./loopback-OZjFnUGp.js";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
5
5
  import { CATEGORIES, defineConfig } from "@svelte-vitals/core";
6
- import { ROBOTS_SOURCE_PATHS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, allRules, applyInlineDirectives, applyOverrides, applyRuleSeverities, buildJsonReport, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, computeScore, defaultConfig, findMinifyDisabled, formatConsoleReport, formatFailedRuleWarning, formatJsonReport, hasFailureAtOrAbove, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRuleOptions, runRules, safeHref, selectRules, shouldSkipRangeCheck, skippedFileWarnings, summarize, terminalSafe, unknownDirectiveIds, validateRuleSetting, withFailedRulesOff, withReadLimit } from "@svelte-vitals/core/internal";
6
+ import { ROBOTS_SOURCE_PATHS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, addFactsDirectives, allRules, buildJsonReport, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, computeScore, defaultConfig, findMinifyDisabled, formatConsoleReport, formatFailedRuleWarning, formatJsonReport, hasFailureAtOrAbove, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRuleOptions, runAnalysis, safeHref, selectRules, shouldSkipRangeCheck, skippedFileWarnings, summarize, terminalSafe, unknownDirectiveIds, validateRuleSetting, withFailedRulesOff, withReadLimit } from "@svelte-vitals/core/internal";
7
7
  import { CONFIG_FILENAMES, analyzeProject, findUnknownRuleIds, knownRuleIds, loadConfigFile, registryTag, ruleOptionsSpec } from "svelte-vitals";
8
- import { glob } from "tinyglobby";
9
8
  //#region src/providers/rendered/project.ts
10
9
  async function exists(path) {
11
10
  try {
@@ -78,10 +77,7 @@ const nodeRuntime = {
78
77
  return false;
79
78
  }
80
79
  },
81
- glob: (pattern, cwd) => glob(pattern, {
82
- cwd,
83
- dot: false
84
- }),
80
+ glob: (pattern, cwd) => globFiles(pattern, cwd),
85
81
  join: (...parts) => join(...parts)
86
82
  };
87
83
  /**
@@ -185,7 +181,13 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
185
181
  const kitModules = await collectKitModuleFacts$1(cwd, project.kitAliases);
186
182
  const selected = selectRules(allRules, config);
187
183
  if (selected.some((r) => r.id === "a11y/unverified-id-ref")) warnings.push("a11y/unverified-id-ref has no effect in rendered mode — the prerendered document is always fully resolved.");
188
- const { results: rawResults, examined, failedRules } = await runRules(selected, {
184
+ const directives = /* @__PURE__ */ new Map();
185
+ addFactsDirectives(directives, {
186
+ components,
187
+ kitModules,
188
+ viteMinifyDisabled: project.viteMinifyDisabled
189
+ });
190
+ const { results, examined, failedRules, scoringConfig } = await runAnalysis(selected, {
189
191
  heads,
190
192
  headings,
191
193
  images,
@@ -195,16 +197,10 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
195
197
  config,
196
198
  kitModules,
197
199
  sourceFiles
198
- });
199
- const directives = /* @__PURE__ */ new Map();
200
- for (const c of components) directives.set(c.file, c.suppressions ?? []);
201
- for (const m of kitModules) directives.set(m.file, m.suppressions ?? []);
202
- if (project.viteMinifyDisabled?.file) directives.set(project.viteMinifyDisabled.file, project.viteMinifyDisabled.suppressions ?? []);
203
- const results = applyInlineDirectives(applyOverrides(applyRuleSeverities(rawResults, config), config), directives, selected, config);
200
+ }, directives);
204
201
  warnings.push(...skippedFileWarnings([...components, ...kitModules]));
205
202
  warnings.push(...unknownDirectiveIds(directives, allRules));
206
203
  for (const f of failedRules) warnings.push(formatFailedRuleWarning(f));
207
- const scoringConfig = withFailedRulesOff(config, failedRules.map((f) => f.id));
208
204
  const { score } = computeScore(results, scoringConfig);
209
205
  const summary = summarize(results, scoringConfig);
210
206
  const failed = hasFailureAtOrAbove(summary, scoringConfig.failOn);
@@ -1,48 +1,11 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { IDREF_ATTRS, LANDMARK_ROLES, decodeFragmentId, isTopFragment, splitTokens, stripTextDirective } from "@svelte-vitals/core/internal";
4
- import { glob } from "tinyglobby";
1
+ import { glob, readFile, stat } from "node:fs/promises";
2
+ import { join, relative, sep } from "node:path";
3
+ import { ASIDE_DEMOTING_TAGS, IDREF_ATTRS, NAMING_ATTRS, SECTIONING_TAGS, decodeFragmentId, isClassicScriptType, isTopFragment, resolveLandmark, splitTokens, stripTextDirective } from "@svelte-vitals/core/internal";
5
4
  import { parse } from "node-html-parser";
6
5
  //#region src/providers/rendered/parse-html.ts
7
6
  function attrValue(v) {
8
7
  return v !== void 0 && v.trim().length > 0 ? "static" : "absent";
9
8
  }
10
- const JS_MIME_TYPES = /* @__PURE__ */ new Set([
11
- "application/ecmascript",
12
- "application/javascript",
13
- "application/x-ecmascript",
14
- "application/x-javascript",
15
- "text/ecmascript",
16
- "text/javascript",
17
- "text/javascript1.0",
18
- "text/javascript1.1",
19
- "text/javascript1.2",
20
- "text/javascript1.3",
21
- "text/javascript1.4",
22
- "text/javascript1.5",
23
- "text/jscript",
24
- "text/livescript",
25
- "text/x-ecmascript",
26
- "text/x-javascript"
27
- ]);
28
- function isClassicScriptType(type) {
29
- if (type === void 0) return true;
30
- const normalized = type.trim().toLowerCase();
31
- return normalized === "" || JS_MIME_TYPES.has(normalized);
32
- }
33
- const SECTIONING_TAGS = /* @__PURE__ */ new Set([
34
- "article",
35
- "aside",
36
- "main",
37
- "nav",
38
- "section"
39
- ]);
40
- const ASIDE_DEMOTING_TAGS = /* @__PURE__ */ new Set([
41
- "article",
42
- "aside",
43
- "nav",
44
- "section"
45
- ]);
46
9
  /** Whole-document scan (not body-scoped: app.html shell ids/refs are real occurrences too). */
47
10
  function collectA11y(root) {
48
11
  const landmarks = [];
@@ -54,16 +17,13 @@ function collectA11y(root) {
54
17
  const tag = el.rawTagName?.toLowerCase();
55
18
  if (tag && ctx.inBody) elementTags.add(tag);
56
19
  const roleAttr = el.getAttribute("role");
57
- let landmark;
58
- if (roleAttr !== void 0) {
59
- const role = splitTokens(roleAttr)[0];
60
- landmark = role && LANDMARK_ROLES.has(role) ? role : void 0;
61
- } else if (tag === "main") landmark = "main";
62
- else if ((tag === "header" || tag === "footer") && ctx.sectioning === 0) landmark = tag === "header" ? "banner" : "contentinfo";
63
- else if (tag === "aside") {
64
- const named = ["aria-label", "aria-labelledby"].some((a) => (el.getAttribute(a) ?? "").trim().length > 0);
65
- landmark = ctx.asideDemoting === 0 || named ? "complementary" : void 0;
66
- }
20
+ const landmark = resolveLandmark({
21
+ tag,
22
+ roleTokens: roleAttr !== void 0 ? splitTokens(roleAttr) : void 0,
23
+ named: tag === "aside" && NAMING_ATTRS.some((a) => (el.getAttribute(a) ?? "").trim().length > 0),
24
+ insideSectioning: ctx.sectioning > 0,
25
+ insideAsideDemoting: ctx.asideDemoting > 0
26
+ });
67
27
  if (landmark) {
68
28
  landmarks.push(landmark);
69
29
  const within = ctx.landmarks.at(-1);
@@ -221,6 +181,32 @@ function parseHtmlHead(html) {
221
181
  };
222
182
  }
223
183
  //#endregion
184
+ //#region src/glob.ts
185
+ /**
186
+ * `node:fs` glob constrained to the contract callers rely on: files only (fs.glob also matches
187
+ * directories), cwd-relative POSIX-separated paths on every platform (fs.glob returns platform
188
+ * separators), dotfiles excluded (fs.glob's default). A symlink's Dirent never reports isFile(),
189
+ * so symlinks are stat'd and kept when they point at a file; symlinked *directories* are not
190
+ * traversed — fs.glob never descends into them. Duplicated in the CLI package — core purity
191
+ * (no `node:` imports in core) rules out the shared home.
192
+ */
193
+ async function globFiles(pattern, cwd) {
194
+ const out = [];
195
+ for await (const entry of glob(pattern, {
196
+ cwd,
197
+ withFileTypes: true
198
+ })) {
199
+ const path = join(entry.parentPath, entry.name);
200
+ if (!entry.isFile()) {
201
+ if (!entry.isSymbolicLink()) continue;
202
+ if (!(await stat(path).catch(() => null))?.isFile()) continue;
203
+ }
204
+ const rel = relative(cwd, path);
205
+ out.push(sep === "/" ? rel : rel.split(sep).join("/"));
206
+ }
207
+ return out;
208
+ }
209
+ //#endregion
224
210
  //#region src/providers/rendered/collect.ts
225
211
  /** Group raw occurrence keys (one entry per hit, in document order) by key, `file` attached, `line: 0` (rendered mode does not track source lines). */
226
212
  function toOccurrenceMap(keys, file) {
@@ -240,7 +226,7 @@ function deriveRouteFromHtmlPath(relPath) {
240
226
  }
241
227
  /** Read every prerendered HTML page under `prerenderPagesDir` into ResolvedHead[]. */
242
228
  async function collectRenderedHeads(prerenderPagesDir) {
243
- const files = (await glob("**/*.html", { cwd: prerenderPagesDir })).sort();
229
+ const files = (await globFiles("**/*.html", prerenderPagesDir)).sort();
244
230
  const parsedFiles = await Promise.all(files.map(async (rel) => ({
245
231
  rel,
246
232
  parsed: parseHtmlHead(await readFile(join(prerenderPagesDir, rel), "utf8"))
@@ -318,4 +304,4 @@ function isLoopbackOrigin(origin) {
318
304
  }
319
305
  }
320
306
  //#endregion
321
- export { parseHtmlHead as i, collectRenderedHeads as n, toOccurrenceMap as r, isLoopbackOrigin as t };
307
+ export { parseHtmlHead as a, globFiles as i, collectRenderedHeads as n, toOccurrenceMap as r, isLoopbackOrigin as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-vitals/vite",
3
- "version": "0.36.0",
3
+ "version": "0.36.2",
4
4
  "description": "Vite/SvelteKit plugin for svelte-vitals — analyzes prerendered HTML during vite build, plus a live dev dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,13 +40,12 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "esm-env": "^1.2.2",
43
- "node-html-parser": "^9.0.1",
44
- "tinyglobby": "^0.2.17"
43
+ "node-html-parser": "^9.0.2"
45
44
  },
46
45
  "peerDependencies": {
47
- "@svelte-vitals/core": "^0.50.0",
46
+ "@svelte-vitals/core": "^0.51.0",
48
47
  "@sveltejs/kit": "^2.70.3",
49
- "svelte-vitals": "^0.54.0",
48
+ "svelte-vitals": "^0.54.2",
50
49
  "vite": "^8.2.2"
51
50
  },
52
51
  "peerDependenciesMeta": {
@@ -55,12 +54,12 @@
55
54
  }
56
55
  },
57
56
  "devDependencies": {
58
- "@svelte-vitals/core": "0.50.0",
57
+ "@svelte-vitals/core": "0.51.0",
59
58
  "@sveltejs/kit": "^2.70.3",
60
59
  "@types/node": "^24.13.3",
61
- "happy-dom": "^20.11.6",
62
- "svelte": "^5.56.10",
63
- "svelte-vitals": "0.54.0",
60
+ "happy-dom": "^20.11.15",
61
+ "svelte": "^5.57.0",
62
+ "svelte-vitals": "0.54.2",
64
63
  "vite": "^8.2.2"
65
64
  },
66
65
  "scripts": {