@vesk/adapter 0.2.50-canary.6 → 0.2.50-canary.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.
@@ -1 +1 @@
1
- {"version":3,"file":"seo-audit.d.ts","sourceRoot":"","sources":["../src/seo-audit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,cAAc,EAAoB,MAAM,yBAAyB,CAAC;AAgI/F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CA8C9F"}
1
+ {"version":3,"file":"seo-audit.d.ts","sourceRoot":"","sources":["../src/seo-audit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,cAAc,EAAoB,MAAM,yBAAyB,CAAC;AAqK/F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAgD9F"}
package/dist/seo-audit.js CHANGED
@@ -40,6 +40,16 @@ function collectCombinedSource(appDir) {
40
40
  }
41
41
  return combined;
42
42
  }
43
+ /**
44
+ * Does this source declare its head as data?
45
+ *
46
+ * `export const metadata = defineMetadata({...})` is a complete head on its own,
47
+ * so every check that asks "is there a title / description / og tag?" has to
48
+ * consider it — otherwise the audit fails the exact style the docs recommend.
49
+ */
50
+ function hasMetadataDeclaration(src) {
51
+ return /\bdefineMetadata\s*\(|export\s+const\s+metadata\b/.test(src);
52
+ }
43
53
  const SEO_CHECKS = {
44
54
  h1: (src) => {
45
55
  const count = (src.match(/<h1[^>]*>/gi) || []).length;
@@ -76,6 +86,16 @@ const SEO_CHECKS = {
76
86
  return issues;
77
87
  },
78
88
  metaDescription: (src) => {
89
+ if (hasMetadataDeclaration(src)) {
90
+ // `export const metadata = defineMetadata({ description: … })` is the
91
+ // declared source of the description; the audit reads source text, so it
92
+ // can see the declaration but not evaluate it (and must not flag a page
93
+ // for using the documented API).
94
+ if (!/\bdescription\s*:/.test(src)) {
95
+ return [{ severity: SEVERITY.WARN, message: 'No `description` in the metadata declaration' }];
96
+ }
97
+ return [];
98
+ }
79
99
  if (!/name=["']description["']/.test(src)) {
80
100
  return [{ severity: SEVERITY.WARN, message: 'Missing meta description' }];
81
101
  }
@@ -83,6 +103,12 @@ const SEO_CHECKS = {
83
103
  },
84
104
  ogTags: (src) => {
85
105
  const issues = [];
106
+ if (hasMetadataDeclaration(src)) {
107
+ // Whether a page wants Open Graph tags is the author's call, and a
108
+ // metadata declaration is a complete head by itself — so do not invent
109
+ // three warnings about tags the audit cannot see.
110
+ return issues;
111
+ }
86
112
  const required = ['og:title', 'og:description', 'og:image'];
87
113
  for (const tag of required) {
88
114
  const pattern = new RegExp(`property=["']${tag}["']`);
@@ -99,8 +125,18 @@ const SEO_CHECKS = {
99
125
  return [];
100
126
  },
101
127
  title: (src) => {
128
+ // A static `export const metadata = defineMetadata({ title: … })` is the
129
+ // documented way to declare a title now, and it produces the same <title>.
130
+ // Without this, the page style the docs recommend failed `vesk seo
131
+ // --strict` with "missing <title>".
132
+ if (hasMetadataDeclaration(src)) {
133
+ if (!/\btitle\s*:/.test(src)) {
134
+ return [{ severity: SEVERITY.WARN, message: 'No `title` in the metadata declaration — it will render no <title>' }];
135
+ }
136
+ return [];
137
+ }
102
138
  if (!/<title>/i.test(src) && !/<Head>/i.test(src) && !/title>/i.test(src)) {
103
- return [{ severity: SEVERITY.ERROR, message: 'Missing <title> or <Head> — page title is critical for SEO' }];
139
+ return [{ severity: SEVERITY.ERROR, message: 'Missing <title>, <Head>, or `export const metadata` — page title is critical for SEO' }];
104
140
  }
105
141
  return [];
106
142
  },
@@ -125,10 +161,11 @@ export function runSeoAudit(appDir, _options) {
125
161
  const combined = collectCombinedSource(appDir);
126
162
  if (combined.length === 0) {
127
163
  console.error('vesk seo-audit: no page.vsk files found');
128
- return { passed: 0, errors: 0, warnings: 0 };
164
+ return { passed: 0, errors: 0, warnings: 0, issues: [] };
129
165
  }
130
166
  let errors = 0;
131
167
  let warnings = 0;
168
+ const allIssues = [];
132
169
  for (const { path, src } of combined) {
133
170
  const relPath = path.replace(appDir, '').replace(/^[\\/]/, '');
134
171
  const routeDir = relPath === 'page.vsk' ? '' : relPath.replace(/[\\/]page\.vsk$/, '');
@@ -140,6 +177,7 @@ export function runSeoAudit(appDir, _options) {
140
177
  for (const check of Object.values(SEO_CHECKS)) {
141
178
  const issues = check(src);
142
179
  for (const issue of issues) {
180
+ allIssues.push({ ...issue, page: relPath || 'page.vsk' });
143
181
  if (issue.severity === SEVERITY.ERROR) {
144
182
  fileErrors++;
145
183
  }
@@ -165,5 +203,5 @@ export function runSeoAudit(appDir, _options) {
165
203
  const total = combined.length;
166
204
  const status = errors > 0 ? 'FAIL' : (warnings > 0 ? 'PASS_WARN' : 'PASS');
167
205
  console.error(`vesk seo-audit: ${total} pages — ${errors} errors, ${warnings} warnings [${status}]`);
168
- return { passed: total, errors, warnings };
206
+ return { passed: total, errors, warnings, issues: allIssues };
169
207
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/adapter",
3
- "version": "0.2.50-canary.6",
3
+ "version": "0.2.50-canary.7",
4
4
  "type": "module",
5
5
  "description": "Vesk adapter — builds deployable .vesk/ output for Deno and Node serverless",
6
6
  "main": "./dist/index.js",
@@ -23,9 +23,9 @@
23
23
  "sharp": "^0.35.3"
24
24
  },
25
25
  "dependencies": {
26
- "@vesk/compiler": "^0.2.50-canary.6",
27
- "@vesk/runtime": "^0.2.50-canary.6",
28
- "@vesk/types": "^0.2.50-canary.6"
26
+ "@vesk/compiler": "^0.2.50-canary.7",
27
+ "@vesk/runtime": "^0.2.50-canary.7",
28
+ "@vesk/types": "^0.2.50-canary.7"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.18.1",