nuxt-link-checker 4.3.9 → 5.0.1
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/LICENSE.md +1 -1
- package/README.md +8 -2
- package/dist/eslint.d.mts +5 -0
- package/dist/eslint.d.ts +5 -0
- package/dist/eslint.mjs +356 -0
- package/dist/module.d.mts +13 -0
- package/dist/module.d.ts +123 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +166 -118
- package/dist/runtime/app/plugins/ui.client.d.ts +3 -2
- package/dist/runtime/app/plugins/ui.client.js +2 -1
- package/dist/runtime/app/plugins/view/Squiggle.d.vue.ts +5 -1
- package/dist/runtime/app/plugins/view/Squiggle.vue.d.ts +5 -1
- package/dist/runtime/app/plugins/view/client.js +14 -9
- package/dist/runtime/server/providers/content-v2.d.ts +4 -1
- package/dist/runtime/server/providers/content-v3.d.ts +3 -3
- package/dist/runtime/server/routes/__link-checker__/inspect.js +4 -4
- package/dist/runtime/shared/crawl.d.ts +2 -7
- package/dist/runtime/shared/diff.d.ts +6 -21
- package/dist/runtime/shared/diff.js +2 -1
- package/dist/runtime/shared/inspections/absolute-site-urls.d.ts +2 -1
- package/dist/runtime/shared/inspections/link-text.d.ts +2 -1
- package/dist/runtime/shared/inspections/missing-hash.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-baseless.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-double-slashes.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-double-slashes.js +5 -3
- package/dist/runtime/shared/inspections/no-duplicate-query-params.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-error-response.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-javascript.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-missing-href.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-non-ascii-chars.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-non-ascii-chars.js +2 -1
- package/dist/runtime/shared/inspections/no-underscores.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-uppercase-chars.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-uppercase-chars.js +2 -1
- package/dist/runtime/shared/inspections/no-whitespace.d.ts +2 -1
- package/dist/runtime/shared/inspections/no-whitespace.js +2 -1
- package/dist/runtime/shared/inspections/trailing-slash.d.ts +2 -1
- package/dist/runtime/shared/redirects.d.ts +2 -1
- package/dist/runtime/shared/sharedUtils.d.ts +8 -4
- package/dist/runtime/shared/sharedUtils.js +10 -26
- package/package.json +44 -33
- package/dist/client/200.html +0 -1
- package/dist/client/404.html +0 -1
- package/dist/client/_nuxt/C8aYPslm.js +0 -1
- package/dist/client/_nuxt/CSiVy0Dp.js +0 -160
- package/dist/client/_nuxt/CVO1_9PV.js +0 -1
- package/dist/client/_nuxt/Cp-IABpG.js +0 -1
- package/dist/client/_nuxt/D0r3Knsf.js +0 -1
- package/dist/client/_nuxt/DDvKHl5l.js +0 -1
- package/dist/client/_nuxt/OpE9gtc9.js +0 -1
- package/dist/client/_nuxt/builds/latest.json +0 -1
- package/dist/client/_nuxt/builds/meta/d73fb88d-9131-40ff-9538-e066a6e3577e.json +0 -1
- package/dist/client/_nuxt/entry.vDvfjTuE.css +0 -1
- package/dist/client/_nuxt/error-404.DO_nRKko.css +0 -1
- package/dist/client/_nuxt/error-500.DwrmseFw.css +0 -1
- package/dist/client/_nuxt/uApKdN3K.js +0 -1
- package/dist/client/_nuxt/wDzz0qaB.js +0 -1
- package/dist/client/index.html +0 -1
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 Harlan Wilton
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
6
|
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ need this module.
|
|
|
27
27
|
- ✅ 13 SEO focused link inspections
|
|
28
28
|
- ✨ See live inspections right in your Nuxt App
|
|
29
29
|
- 🧙 Magically fix them in Nuxt Dev Tools
|
|
30
|
-
- 🚩 Generate reports on build (
|
|
30
|
+
- 🚩 Generate reports on build (HTML, markdown)
|
|
31
31
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
@@ -37,6 +37,12 @@ Install `nuxt-link-checker` dependency to your project:
|
|
|
37
37
|
npx nuxi@latest module add link-checker
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
> [!TIP]
|
|
41
|
+
> Generate an Agent Skill for this package using [skilld](https://github.com/harlan-zw/skilld):
|
|
42
|
+
> ```bash
|
|
43
|
+
> npx skilld add nuxt-link-checker
|
|
44
|
+
> ```
|
|
45
|
+
|
|
40
46
|
## Documentation
|
|
41
47
|
|
|
42
48
|
[📖 Read the full documentation](https://nuxtseo.com/link-checker/getting-started/installation) for more information.
|
|
@@ -49,7 +55,7 @@ npx nuxi@latest module add link-checker
|
|
|
49
55
|
|
|
50
56
|
<p align="center">
|
|
51
57
|
<a href="https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg">
|
|
52
|
-
<img src='https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg'/>
|
|
58
|
+
<img src='https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg' alt="Sponsors"/>
|
|
53
59
|
</a>
|
|
54
60
|
</p>
|
|
55
61
|
|
package/dist/eslint.d.ts
ADDED
package/dist/eslint.mjs
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { statSync, readFileSync } from 'node:fs';
|
|
2
|
+
import Fuse from 'fuse.js';
|
|
3
|
+
import { join } from 'pathe';
|
|
4
|
+
import { createRouter } from 'radix3';
|
|
5
|
+
|
|
6
|
+
const MD_LINK_RE = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
7
|
+
const BACKSLASH_RE = /\\/g;
|
|
8
|
+
const SINGLE_QUOTE_RE = /'/g;
|
|
9
|
+
function extractMarkdownLinks(text) {
|
|
10
|
+
const links = [];
|
|
11
|
+
const lines = text.split("\n");
|
|
12
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13
|
+
const line = lines[i];
|
|
14
|
+
MD_LINK_RE.lastIndex = 0;
|
|
15
|
+
let match = MD_LINK_RE.exec(line);
|
|
16
|
+
while (match !== null) {
|
|
17
|
+
const linkText = match[1] ?? "";
|
|
18
|
+
const url = match[2] ?? "";
|
|
19
|
+
const urlStart = match.index + linkText.length + 2;
|
|
20
|
+
links.push({
|
|
21
|
+
url,
|
|
22
|
+
line: i + 1,
|
|
23
|
+
column: urlStart + 1
|
|
24
|
+
});
|
|
25
|
+
match = MD_LINK_RE.exec(line);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return links;
|
|
29
|
+
}
|
|
30
|
+
const lineMapStack = [];
|
|
31
|
+
const markdownProcessor = {
|
|
32
|
+
meta: {
|
|
33
|
+
name: "link-checker/markdown",
|
|
34
|
+
version: "1.0.0"
|
|
35
|
+
},
|
|
36
|
+
preprocess(text) {
|
|
37
|
+
const links = extractMarkdownLinks(text);
|
|
38
|
+
if (!links.length) {
|
|
39
|
+
lineMapStack.push(/* @__PURE__ */ new Map());
|
|
40
|
+
return [{ text, filename: "0.md" }];
|
|
41
|
+
}
|
|
42
|
+
const virtualLines = [];
|
|
43
|
+
const lineMap = /* @__PURE__ */ new Map();
|
|
44
|
+
for (let i = 0; i < links.length; i++) {
|
|
45
|
+
const link = links[i];
|
|
46
|
+
const escaped = link.url.replace(BACKSLASH_RE, "\\\\").replace(SINGLE_QUOTE_RE, "\\'");
|
|
47
|
+
virtualLines.push(`navigateTo('${escaped}')`);
|
|
48
|
+
lineMap.set(i + 1, link);
|
|
49
|
+
}
|
|
50
|
+
lineMapStack.push(lineMap);
|
|
51
|
+
return [
|
|
52
|
+
{ text: virtualLines.join("\n"), filename: "0.js" }
|
|
53
|
+
];
|
|
54
|
+
},
|
|
55
|
+
postprocess(messages) {
|
|
56
|
+
const lineMap = lineMapStack.pop();
|
|
57
|
+
if (!lineMap || !lineMap.size)
|
|
58
|
+
return messages.flat();
|
|
59
|
+
return messages.flat().map((msg) => {
|
|
60
|
+
const link = lineMap.get(msg.line);
|
|
61
|
+
if (link) {
|
|
62
|
+
return {
|
|
63
|
+
...msg,
|
|
64
|
+
line: link.line,
|
|
65
|
+
column: link.column
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return msg;
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
supportsAutofix: false
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let cachedRoutes;
|
|
75
|
+
let cachedMtime;
|
|
76
|
+
let cachedPath;
|
|
77
|
+
function loadRoutes(options) {
|
|
78
|
+
const routesFile = options?.routesFile ?? join(options?.rootDir ?? process.cwd(), ".nuxt/link-checker/routes.json");
|
|
79
|
+
let mtime;
|
|
80
|
+
try {
|
|
81
|
+
mtime = statSync(routesFile).mtimeMs;
|
|
82
|
+
} catch {
|
|
83
|
+
return { staticRoutes: [], dynamicRoutes: [] };
|
|
84
|
+
}
|
|
85
|
+
if (cachedRoutes && cachedPath === routesFile && cachedMtime === mtime)
|
|
86
|
+
return cachedRoutes;
|
|
87
|
+
try {
|
|
88
|
+
const data = JSON.parse(readFileSync(routesFile, "utf-8"));
|
|
89
|
+
cachedRoutes = data;
|
|
90
|
+
cachedMtime = mtime;
|
|
91
|
+
cachedPath = routesFile;
|
|
92
|
+
return data;
|
|
93
|
+
} catch {
|
|
94
|
+
return { staticRoutes: [], dynamicRoutes: [] };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function createSuggester(routes) {
|
|
98
|
+
const fuse = new Fuse(routes, { threshold: 0.5 });
|
|
99
|
+
return (link) => {
|
|
100
|
+
const result = fuse.search(link)?.[0]?.item;
|
|
101
|
+
return result !== link ? result : void 0;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function createRouteMatcher(dynamicRoutes) {
|
|
105
|
+
const router = createRouter();
|
|
106
|
+
for (const route of dynamicRoutes) {
|
|
107
|
+
router.insert(route, { pattern: route });
|
|
108
|
+
}
|
|
109
|
+
return (path) => {
|
|
110
|
+
const match = router.lookup(path);
|
|
111
|
+
return match ? match.pattern : null;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const LINK_ELEMENTS = /* @__PURE__ */ new Set(["a", "NuxtLink", "nuxt-link", "RouterLink", "router-link"]);
|
|
116
|
+
const LINK_ATTRS = /* @__PURE__ */ new Set(["to", "href"]);
|
|
117
|
+
const SKIP_PREFIXES = ["http://", "https://", "//", "mailto:", "tel:", "javascript:", "blob:", "data:", "ftp:"];
|
|
118
|
+
const WHITESPACE_RE = /\s+/;
|
|
119
|
+
const STATIC_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
120
|
+
".pdf",
|
|
121
|
+
".png",
|
|
122
|
+
".jpg",
|
|
123
|
+
".jpeg",
|
|
124
|
+
".gif",
|
|
125
|
+
".svg",
|
|
126
|
+
".ico",
|
|
127
|
+
".webp",
|
|
128
|
+
".avif",
|
|
129
|
+
".mp3",
|
|
130
|
+
".mp4",
|
|
131
|
+
".webm",
|
|
132
|
+
".ogg",
|
|
133
|
+
".wav",
|
|
134
|
+
".zip",
|
|
135
|
+
".gz",
|
|
136
|
+
".tar",
|
|
137
|
+
".rar",
|
|
138
|
+
".xml",
|
|
139
|
+
".txt",
|
|
140
|
+
".json",
|
|
141
|
+
".csv",
|
|
142
|
+
".rss",
|
|
143
|
+
".atom",
|
|
144
|
+
".woff",
|
|
145
|
+
".woff2",
|
|
146
|
+
".ttf",
|
|
147
|
+
".eot",
|
|
148
|
+
".js",
|
|
149
|
+
".css",
|
|
150
|
+
".map"
|
|
151
|
+
]);
|
|
152
|
+
function shouldSkipLink(link) {
|
|
153
|
+
if (!link || link === "#" || link.startsWith("#"))
|
|
154
|
+
return true;
|
|
155
|
+
if (SKIP_PREFIXES.some((prefix) => link.startsWith(prefix)))
|
|
156
|
+
return true;
|
|
157
|
+
const pathname = (link.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
158
|
+
const lastDot = pathname.lastIndexOf(".");
|
|
159
|
+
if (lastDot !== -1 && STATIC_FILE_EXTENSIONS.has(pathname.slice(lastDot).toLowerCase()))
|
|
160
|
+
return true;
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
function stripQueryAndHash(link) {
|
|
164
|
+
const hashIndex = link.indexOf("#");
|
|
165
|
+
const queryIndex = link.indexOf("?");
|
|
166
|
+
const endIndex = Math.min(
|
|
167
|
+
hashIndex === -1 ? link.length : hashIndex,
|
|
168
|
+
queryIndex === -1 ? link.length : queryIndex
|
|
169
|
+
);
|
|
170
|
+
return link.slice(0, endIndex);
|
|
171
|
+
}
|
|
172
|
+
function hasRelNofollow(element) {
|
|
173
|
+
const attrs = element.startTag?.attributes ?? element.attributes ?? [];
|
|
174
|
+
return attrs.some(
|
|
175
|
+
(attr) => !attr.directive && attr.key?.name === "rel" && typeof attr.value?.value === "string" && attr.value.value.split(WHITESPACE_RE).includes("nofollow")
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
function createTemplateVisitors(report) {
|
|
179
|
+
return {
|
|
180
|
+
"VAttribute[directive=false]": function(node) {
|
|
181
|
+
if (!LINK_ATTRS.has(node.key?.name))
|
|
182
|
+
return;
|
|
183
|
+
const parent = node.parent?.parent;
|
|
184
|
+
if (!parent || !LINK_ELEMENTS.has(parent.rawName))
|
|
185
|
+
return;
|
|
186
|
+
if (hasRelNofollow(parent))
|
|
187
|
+
return;
|
|
188
|
+
const value = node.value?.value;
|
|
189
|
+
if (typeof value === "string" && !shouldSkipLink(value))
|
|
190
|
+
report(value, node);
|
|
191
|
+
},
|
|
192
|
+
'VAttribute[directive=true][key.name.name="bind"]': function(node) {
|
|
193
|
+
if (!LINK_ATTRS.has(node.key?.argument?.name))
|
|
194
|
+
return;
|
|
195
|
+
const parent = node.parent?.parent;
|
|
196
|
+
if (!parent || !LINK_ELEMENTS.has(parent.rawName))
|
|
197
|
+
return;
|
|
198
|
+
if (hasRelNofollow(parent))
|
|
199
|
+
return;
|
|
200
|
+
const expr = node.value?.expression;
|
|
201
|
+
if (expr?.type === "Literal" && typeof expr.value === "string" && !shouldSkipLink(expr.value))
|
|
202
|
+
report(expr.value, expr);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function createScriptVisitors(report) {
|
|
207
|
+
return {
|
|
208
|
+
CallExpression(node) {
|
|
209
|
+
const callee = node.callee;
|
|
210
|
+
if (!callee)
|
|
211
|
+
return;
|
|
212
|
+
let isNavCall = false;
|
|
213
|
+
if (callee.type === "Identifier" && callee.name === "navigateTo")
|
|
214
|
+
isNavCall = true;
|
|
215
|
+
if (callee.type === "MemberExpression" && callee.object?.type === "Identifier" && callee.object.name === "router" && callee.property?.type === "Identifier" && (callee.property.name === "push" || callee.property.name === "replace")) {
|
|
216
|
+
isNavCall = true;
|
|
217
|
+
}
|
|
218
|
+
if (!isNavCall)
|
|
219
|
+
return;
|
|
220
|
+
const arg = node.arguments?.[0];
|
|
221
|
+
if (arg?.type === "Literal" && typeof arg.value === "string" && !shouldSkipLink(arg.value))
|
|
222
|
+
report(arg.value, arg);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function createCombinedVisitors(context, check) {
|
|
227
|
+
const isVue = context.filename.endsWith(".vue");
|
|
228
|
+
const scriptVisitors = createScriptVisitors(check);
|
|
229
|
+
if (isVue) {
|
|
230
|
+
const parserServices = context.sourceCode.parserServices;
|
|
231
|
+
if (parserServices?.defineTemplateBodyVisitor) {
|
|
232
|
+
return parserServices.defineTemplateBodyVisitor(
|
|
233
|
+
createTemplateVisitors(check),
|
|
234
|
+
scriptVisitors
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return scriptVisitors;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const rule$1 = {
|
|
242
|
+
meta: {
|
|
243
|
+
type: "problem",
|
|
244
|
+
docs: {
|
|
245
|
+
description: "Validate that relative URLs match a known vue-router route pattern"
|
|
246
|
+
},
|
|
247
|
+
schema: [{
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
routesFile: { type: "string" },
|
|
251
|
+
rootDir: { type: "string" }
|
|
252
|
+
},
|
|
253
|
+
additionalProperties: false
|
|
254
|
+
}],
|
|
255
|
+
messages: {
|
|
256
|
+
invalidRoute: 'Link "{{link}}" does not match any known route.{{suggestion}}'
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
create(context) {
|
|
260
|
+
const options = context.options[0];
|
|
261
|
+
const routes = loadRoutes({ routesFile: options?.routesFile, rootDir: options?.rootDir });
|
|
262
|
+
if (!routes.staticRoutes.length && !routes.dynamicRoutes.length)
|
|
263
|
+
return {};
|
|
264
|
+
const allStaticSet = new Set(routes.staticRoutes);
|
|
265
|
+
const matchDynamic = createRouteMatcher(routes.dynamicRoutes);
|
|
266
|
+
const suggest = createSuggester(routes.staticRoutes);
|
|
267
|
+
const check = (link, node) => {
|
|
268
|
+
if (!link.startsWith("/"))
|
|
269
|
+
return;
|
|
270
|
+
const pathname = stripQueryAndHash(link);
|
|
271
|
+
if (allStaticSet.has(pathname))
|
|
272
|
+
return;
|
|
273
|
+
if (matchDynamic(pathname))
|
|
274
|
+
return;
|
|
275
|
+
const suggested = suggest(pathname);
|
|
276
|
+
const suggestion = suggested ? ` Did you mean "${suggested}"?` : "";
|
|
277
|
+
context.report({
|
|
278
|
+
node,
|
|
279
|
+
messageId: "invalidRoute",
|
|
280
|
+
data: { link: pathname, suggestion }
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
return createCombinedVisitors(context, check);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const rule = {
|
|
288
|
+
meta: {
|
|
289
|
+
type: "suggestion",
|
|
290
|
+
docs: {
|
|
291
|
+
description: "Validate that relative URLs exist in the sitemap"
|
|
292
|
+
},
|
|
293
|
+
schema: [{
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
routesFile: { type: "string" },
|
|
297
|
+
rootDir: { type: "string" }
|
|
298
|
+
},
|
|
299
|
+
additionalProperties: false
|
|
300
|
+
}],
|
|
301
|
+
messages: {
|
|
302
|
+
notInSitemap: 'Link "{{link}}" is not in the sitemap.{{suggestion}}'
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
create(context) {
|
|
306
|
+
const options = context.options[0];
|
|
307
|
+
const routes = loadRoutes({ routesFile: options?.routesFile, rootDir: options?.rootDir });
|
|
308
|
+
if (!routes.staticRoutes.length)
|
|
309
|
+
return {};
|
|
310
|
+
const staticSet = new Set(routes.staticRoutes);
|
|
311
|
+
const matchDynamic = createRouteMatcher(routes.dynamicRoutes);
|
|
312
|
+
const suggest = createSuggester(routes.staticRoutes);
|
|
313
|
+
const patternsWithSitemapEntries = /* @__PURE__ */ new Set();
|
|
314
|
+
for (const url of routes.staticRoutes) {
|
|
315
|
+
const pattern = matchDynamic(url);
|
|
316
|
+
if (pattern)
|
|
317
|
+
patternsWithSitemapEntries.add(pattern);
|
|
318
|
+
}
|
|
319
|
+
const check = (link, node) => {
|
|
320
|
+
if (!link.startsWith("/"))
|
|
321
|
+
return;
|
|
322
|
+
const pathname = stripQueryAndHash(link);
|
|
323
|
+
if (staticSet.has(pathname))
|
|
324
|
+
return;
|
|
325
|
+
const matchedPattern = matchDynamic(pathname);
|
|
326
|
+
if (matchedPattern) {
|
|
327
|
+
if (!patternsWithSitemapEntries.has(matchedPattern))
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const suggested = suggest(pathname);
|
|
331
|
+
const suggestion = suggested ? ` Did you mean "${suggested}"?` : "";
|
|
332
|
+
context.report({
|
|
333
|
+
node,
|
|
334
|
+
messageId: "notInSitemap",
|
|
335
|
+
data: { link: pathname, suggestion }
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
return createCombinedVisitors(context, check);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const plugin = {
|
|
343
|
+
meta: {
|
|
344
|
+
name: "nuxt-link-checker",
|
|
345
|
+
version: "1.0.0"
|
|
346
|
+
},
|
|
347
|
+
rules: {
|
|
348
|
+
"valid-route": rule$1,
|
|
349
|
+
"valid-sitemap-link": rule
|
|
350
|
+
},
|
|
351
|
+
processors: {
|
|
352
|
+
markdown: markdownProcessor
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
export { plugin as default };
|
package/dist/module.d.mts
CHANGED
|
@@ -95,6 +95,19 @@ interface ModuleOptions {
|
|
|
95
95
|
* @default true
|
|
96
96
|
*/
|
|
97
97
|
enabled: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Pages to exclude from link checking entirely.
|
|
100
|
+
*
|
|
101
|
+
* When a page matches, none of its links will be inspected.
|
|
102
|
+
*
|
|
103
|
+
* Supports the same pattern types as `excludeLinks`:
|
|
104
|
+
* - Exact matches: `/about`
|
|
105
|
+
* - Wildcards: `/admin/**`
|
|
106
|
+
* - RegExp patterns: `/^\/blog\/\d+$/`
|
|
107
|
+
*
|
|
108
|
+
* @default []
|
|
109
|
+
*/
|
|
110
|
+
excludePages: (string | RegExp)[];
|
|
98
111
|
/**
|
|
99
112
|
* Display debug information.
|
|
100
113
|
*
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { CreateStorageOptions } from 'unstorage';
|
|
3
|
+
|
|
4
|
+
interface ModuleOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the build should fail when a 404 is encountered.
|
|
7
|
+
*/
|
|
8
|
+
failOnError: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Skip specific inspections from running.
|
|
11
|
+
*/
|
|
12
|
+
skipInspections: string[];
|
|
13
|
+
/**
|
|
14
|
+
* The timeout for fetching a URL.
|
|
15
|
+
*
|
|
16
|
+
* @default 5000
|
|
17
|
+
*/
|
|
18
|
+
fetchTimeout: number;
|
|
19
|
+
/**
|
|
20
|
+
* Links to ignore when running inspections.
|
|
21
|
+
*
|
|
22
|
+
* Supports:
|
|
23
|
+
* - Exact matches: `/about`
|
|
24
|
+
* - Wildcards: `/admin/**`, `/api/*`
|
|
25
|
+
* - RegExp patterns: `/^\\/blog\\/\\d+$/`
|
|
26
|
+
*
|
|
27
|
+
* Uses radix3 route matching for string patterns.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* excludeLinks: [
|
|
32
|
+
* '/admin/**', // Exclude all admin routes
|
|
33
|
+
* '/api/*', // Exclude direct /api children
|
|
34
|
+
* /^\/blog\/\d+$/, // Exclude blog posts using regex
|
|
35
|
+
* 'https://example.com/**' // Exclude external domain
|
|
36
|
+
* ]
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
excludeLinks: (string | RegExp)[];
|
|
40
|
+
/**
|
|
41
|
+
* Generate a report when using nuxt build` or `nuxt generate`.
|
|
42
|
+
*/
|
|
43
|
+
report?: {
|
|
44
|
+
/**
|
|
45
|
+
* Whether to output a HTML report.
|
|
46
|
+
*/
|
|
47
|
+
html?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Whether to output a JSON report.
|
|
50
|
+
*/
|
|
51
|
+
markdown?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether to output a JSON report.
|
|
54
|
+
*/
|
|
55
|
+
json?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Where to store the files.
|
|
58
|
+
*
|
|
59
|
+
* Either provide a path relative to the nuxt root or an object with options for `unstorage`.
|
|
60
|
+
*
|
|
61
|
+
* By default, they'll be in your .output directory.
|
|
62
|
+
*/
|
|
63
|
+
storage?: string | CreateStorageOptions;
|
|
64
|
+
/**
|
|
65
|
+
* Whether to publish the reports with the build.
|
|
66
|
+
*
|
|
67
|
+
* By default, will output files at `.output/public/__link-checker__/link-checker-report.<format>`.
|
|
68
|
+
*/
|
|
69
|
+
publish?: boolean;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Whether to show live inspections in your Nuxt app.
|
|
73
|
+
*/
|
|
74
|
+
showLiveInspections: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Whether to run the module on `nuxt build` or `nuxt generate`.
|
|
77
|
+
*/
|
|
78
|
+
runOnBuild: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Should remote URLs be fetched.
|
|
81
|
+
*
|
|
82
|
+
* @default false
|
|
83
|
+
*/
|
|
84
|
+
fetchRemoteUrls: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Enable when your nuxt/content files match your pages. This will automatically detect link sources
|
|
87
|
+
* for the current page.
|
|
88
|
+
*
|
|
89
|
+
* This is the same behavior to using `nuxt/content` with `documentDriven: true`.
|
|
90
|
+
*/
|
|
91
|
+
strictNuxtContentPaths: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Whether the module is enabled.
|
|
94
|
+
*
|
|
95
|
+
* @default true
|
|
96
|
+
*/
|
|
97
|
+
enabled: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Pages to exclude from link checking entirely.
|
|
100
|
+
*
|
|
101
|
+
* When a page matches, none of its links will be inspected.
|
|
102
|
+
*
|
|
103
|
+
* Supports the same pattern types as `excludeLinks`:
|
|
104
|
+
* - Exact matches: `/about`
|
|
105
|
+
* - Wildcards: `/admin/**`
|
|
106
|
+
* - RegExp patterns: `/^\/blog\/\d+$/`
|
|
107
|
+
*
|
|
108
|
+
* @default []
|
|
109
|
+
*/
|
|
110
|
+
excludePages: (string | RegExp)[];
|
|
111
|
+
/**
|
|
112
|
+
* Display debug information.
|
|
113
|
+
*
|
|
114
|
+
* @default false
|
|
115
|
+
*/
|
|
116
|
+
debug: boolean;
|
|
117
|
+
}
|
|
118
|
+
interface ModuleHooks {
|
|
119
|
+
}
|
|
120
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
121
|
+
|
|
122
|
+
export { _default as default };
|
|
123
|
+
export type { ModuleHooks, ModuleOptions };
|