@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.
- package/dist/seo-audit.d.ts.map +1 -1
- package/dist/seo-audit.js +41 -3
- package/package.json +4 -4
package/dist/seo-audit.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
|
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.
|
|
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.
|
|
27
|
-
"@vesk/runtime": "^0.2.50-canary.
|
|
28
|
-
"@vesk/types": "^0.2.50-canary.
|
|
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",
|