@jay-framework/seo-validator 0.20.0 → 0.22.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.
Files changed (2) hide show
  1. package/dist/index.js +37 -0
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { walkElements } from "@jay-framework/compiler-shared";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
2
4
  const validate = (ctx) => {
3
5
  const findings = [];
4
6
  let hasH1 = false;
@@ -123,6 +125,41 @@ const validate = (ctx) => {
123
125
  attribute: "fetchpriority"
124
126
  });
125
127
  }
128
+ const cssSources = [];
129
+ const styleBlocks = ctx.body.querySelectorAll?.("style") ?? [];
130
+ for (const styleEl of styleBlocks) {
131
+ const cssText = styleEl.textContent || "";
132
+ if (cssText)
133
+ cssSources.push({ css: cssText, source: "<style>" });
134
+ }
135
+ const linkedFiles = ctx.body.querySelectorAll?.('link[rel="stylesheet"]') ?? [];
136
+ for (const link of linkedFiles) {
137
+ const href = link.getAttribute?.("href");
138
+ if (href && !href.startsWith("http")) {
139
+ try {
140
+ const dir = path.dirname(path.resolve(ctx.projectRoot, ctx.filePath));
141
+ const cssPath = path.resolve(dir, href);
142
+ const cssText = fs.readFileSync(cssPath, "utf-8");
143
+ cssSources.push({ css: cssText, source: href });
144
+ } catch {
145
+ }
146
+ }
147
+ }
148
+ for (const { css, source } of cssSources) {
149
+ const importRegex = /@import\s+(?:url\(\s*['"]?([^'")]+)['"]?\s*\)|['"]([^'"]+)['"])/g;
150
+ let importMatch;
151
+ while ((importMatch = importRegex.exec(css)) !== null) {
152
+ const url = importMatch[1] || importMatch[2];
153
+ if (url.startsWith("https://") || url.startsWith("http://")) {
154
+ findings.push({
155
+ severity: "warning",
156
+ message: `CSS @import of external URL "${url}" creates a chained blocking request that delays page rendering`,
157
+ suggestion: 'Move this to a <link rel="stylesheet" href="..."> tag in the HTML <head> instead. This allows the browser preload scanner to discover both resources in parallel.',
158
+ element: source === "<style>" ? "<style>" : `<link href="${source}">`
159
+ });
160
+ }
161
+ }
162
+ }
126
163
  const componentHeadTags = new Set(
127
164
  ctx.headlessImports.flatMap((imp) => imp.providedHeadTags ?? [])
128
165
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/seo-validator",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "description": "SEO validation plugin for Jay Framework — checks jay-html templates for SEO best practices",
6
6
  "license": "Apache-2.0",
@@ -24,11 +24,11 @@
24
24
  "test": "vitest run"
25
25
  },
26
26
  "dependencies": {
27
- "@jay-framework/compiler-shared": "^0.20.0"
27
+ "@jay-framework/compiler-shared": "^0.22.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@jay-framework/dev-environment": "^0.20.0",
31
- "@jay-framework/jay-stack-cli": "^0.20.0",
30
+ "@jay-framework/dev-environment": "^0.22.0",
31
+ "@jay-framework/jay-stack-cli": "^0.22.0",
32
32
  "@types/node": "^22.15.21",
33
33
  "node-html-parser": "^6.1.0",
34
34
  "rimraf": "^5.0.5",