@jay-framework/seo-validator 0.19.5 → 0.19.7

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 +20 -12
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -123,8 +123,11 @@ const validate = (ctx) => {
123
123
  attribute: "fetchpriority"
124
124
  });
125
125
  }
126
+ const componentHeadTags = new Set(
127
+ ctx.headlessImports.flatMap((imp) => imp.providedHeadTags ?? [])
128
+ );
126
129
  if (ctx.head) {
127
- if (!ctx.head.title) {
130
+ if (!ctx.head.title && !componentHeadTags.has("title")) {
128
131
  findings.push({
129
132
  severity: "warning",
130
133
  message: "Page has no <title> element",
@@ -133,7 +136,7 @@ const validate = (ctx) => {
133
136
  });
134
137
  }
135
138
  const hasDescription = ctx.head.meta.some((m) => m.name?.toLowerCase() === "description");
136
- if (!hasDescription) {
139
+ if (!hasDescription && !componentHeadTags.has("meta:description")) {
137
140
  findings.push({
138
141
  severity: "warning",
139
142
  message: 'Page has no <meta name="description">',
@@ -142,18 +145,23 @@ const validate = (ctx) => {
142
145
  attribute: "name"
143
146
  });
144
147
  }
145
- const hasCanonical = ctx.head.links.some((l) => l.rel === "canonical");
146
- if (!hasCanonical) {
147
- findings.push({
148
- severity: "warning",
149
- message: 'Page has no <link rel="canonical">',
150
- suggestion: 'Add <link rel="canonical" href="..."> in <head> to specify the preferred URL. This prevents duplicate content issues in search engines.',
151
- element: "<link>",
152
- attribute: "rel"
153
- });
148
+ const canonical = ctx.head.links.find((l) => l.rel === "canonical");
149
+ if (canonical) {
150
+ const hasBinding = canonical.href.some((p) => p.kind === "binding");
151
+ const hrefStr = canonical.href.map((p) => p.value).join("");
152
+ if (!hrefStr.startsWith("http://") && !hrefStr.startsWith("https://") && !hasBinding) {
153
+ findings.push({
154
+ severity: "warning",
155
+ message: "Canonical URL should be absolute",
156
+ suggestion: "Change the canonical href to an absolute URL (e.g., https://example.com/page). Relative canonicals may not be interpreted correctly by all search engines.",
157
+ element: "<link>",
158
+ attribute: "href"
159
+ });
160
+ }
154
161
  }
155
162
  const robotsMeta = ctx.head.meta.find((m) => m.name?.toLowerCase() === "robots");
156
- if (robotsMeta && /noindex/i.test(robotsMeta.content)) {
163
+ const robotsContent = robotsMeta?.content.map((p) => p.value).join("");
164
+ if (robotsContent && /noindex/i.test(robotsContent)) {
157
165
  findings.push({
158
166
  severity: "warning",
159
167
  message: 'Page has <meta name="robots" content="noindex"> — it will not appear in search results',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/seo-validator",
3
- "version": "0.19.5",
3
+ "version": "0.19.7",
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.19.5"
27
+ "@jay-framework/compiler-shared": "^0.19.7"
28
28
  },
29
29
  "devDependencies": {
30
- "@jay-framework/dev-environment": "^0.19.5",
31
- "@jay-framework/jay-stack-cli": "^0.19.5",
30
+ "@jay-framework/dev-environment": "^0.19.7",
31
+ "@jay-framework/jay-stack-cli": "^0.19.7",
32
32
  "@types/node": "^22.15.21",
33
33
  "node-html-parser": "^6.1.0",
34
34
  "rimraf": "^5.0.5",