nuxt-link-checker 3.1.2 → 3.2.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.
- package/LICENSE.md +9 -0
- package/README.md +26 -17
- package/dist/client/200.html +90 -85
- package/dist/client/404.html +90 -85
- package/dist/client/_nuxt/{CE0v8NN9.js → 0qbUsQ6_.js} +1 -1
- package/dist/client/_nuxt/2aygx1xg.js +1 -0
- package/dist/client/_nuxt/{DBxrVw_D.js → 5Vw9JATd.js} +1 -1
- package/dist/client/_nuxt/6RUVFG9U.js +1 -0
- package/dist/client/_nuxt/B1Z2YEe0.js +1 -0
- package/dist/client/_nuxt/{OiuHoWY5.js → B1rsb5QC.js} +1 -1
- package/dist/client/_nuxt/B2uSYIFn.js +1 -0
- package/dist/client/_nuxt/BA1ndEPy.js +1 -0
- package/dist/client/_nuxt/BCUAspaU.js +41 -0
- package/dist/client/_nuxt/{p-youoOz.js → BCawSwWi.js} +1 -1
- package/dist/client/_nuxt/{rAPcFIiw.js → BYqZ7etr.js} +1 -1
- package/dist/client/_nuxt/{BjE2mIY4.js → BbalPOsb.js} +1 -1
- package/dist/client/_nuxt/{DwFwdhDE.js → Bcw97oti.js} +1 -1
- package/dist/client/_nuxt/{aPBkmDv4.js → BfjtVDDH.js} +1 -1
- package/dist/client/_nuxt/{CiXlrwC7.js → BhC5s0V7.js} +1 -1
- package/dist/client/_nuxt/{BVLG6Y-w.js → Bmb0A568.js} +1 -1
- package/dist/client/_nuxt/{DpVQgTom.js → BoZcIFWd.js} +1 -1
- package/dist/client/_nuxt/BqtQrzRp.js +1 -0
- package/dist/client/_nuxt/BrbOHqWv.js +1 -0
- package/dist/client/_nuxt/{BWgb8Pxw.js → Bv_sPlCv.js} +1 -1
- package/dist/client/_nuxt/BwpQXVS2.js +1 -0
- package/dist/client/_nuxt/BzaEo2Du.js +1 -0
- package/dist/client/_nuxt/C-_shW-Y.js +1 -0
- package/dist/client/_nuxt/{nkNqggyT.js → CBzZD_Xh.js} +1 -1
- package/dist/client/_nuxt/CD_QflpE.js +1 -0
- package/dist/client/_nuxt/{Bpo8LMpD.js → CI15r38m.js} +1 -1
- package/dist/client/_nuxt/{JbneklYT.js → CK7S-5jS.js} +1 -1
- package/dist/client/_nuxt/{B9hcNGex.js → CKfqYE_r.js} +1 -1
- package/dist/client/_nuxt/CLXkKSjS.js +1 -0
- package/dist/client/_nuxt/CM4fc1WH.js +1 -0
- package/dist/client/_nuxt/CVESyXxo.js +1 -0
- package/dist/client/_nuxt/{vFtNXPBc.js → CWJejSyX.js} +1 -1
- package/dist/client/_nuxt/{Bt50SWsh.js → CY2R9RuK.js} +1 -1
- package/dist/client/_nuxt/{Ckz-YMxz.js → CeZwWGti.js} +1 -1
- package/dist/client/_nuxt/CjDtw9vr.js +1 -0
- package/dist/client/_nuxt/{BhLNiG9c.js → ClV7KpCc.js} +1 -1
- package/dist/client/_nuxt/{D7z58M2W.js → Cm25um3E.js} +1 -1
- package/dist/client/_nuxt/CmCqftbK.js +1 -0
- package/dist/client/_nuxt/Cmhojp3q.js +1 -0
- package/dist/client/_nuxt/{BwojPDJW.js → CnsmiicP.js} +1 -1
- package/dist/client/_nuxt/{DCRAZVCM.js → CodIOJDM.js} +1 -1
- package/dist/client/_nuxt/{0e7srHXh.js → Csvi84BS.js} +1 -1
- package/dist/client/_nuxt/{CHZeBJpW.js → CuIYtGag.js} +1 -1
- package/dist/client/_nuxt/{cVkK16_y.js → Cuk6v7N8.js} +1 -1
- package/dist/client/_nuxt/{I1uqSxRv.js → Cv3cJnDV.js} +1 -1
- package/dist/client/_nuxt/Cv7ZOjGq.js +1 -0
- package/dist/client/_nuxt/{Da_8ueAw.js → CyEc264g.js} +1 -1
- package/dist/client/_nuxt/{DTlJ9n0s.js → CyIlscnE.js} +1 -1
- package/dist/client/_nuxt/{Bx0W9Nc2.js → D0xWnrgR.js} +1 -1
- package/dist/client/_nuxt/{CRR3mLlZ.js → D2O4HAMr.js} +1 -1
- package/dist/client/_nuxt/{B7-uoU3n.js → D7oLnXFd.js} +1 -1
- package/dist/client/_nuxt/{Bexp5DGn.js → DFQ4Xr-4.js} +1 -1
- package/dist/client/_nuxt/{DLSxEyHP.js → DH5Ifo-i.js} +1 -1
- package/dist/client/_nuxt/DMFdBl0X.js +1 -0
- package/dist/client/_nuxt/DNgb01dh.js +1 -0
- package/dist/client/_nuxt/DRW-0cLl.js +1 -0
- package/dist/client/_nuxt/{Xse1gjxk.js → DRhUEtVu.js} +1 -1
- package/dist/client/_nuxt/{BGdveAQA.js → DUdzsfxe.js} +1 -1
- package/dist/client/_nuxt/{CYyBA3kJ.js → Db-JQHi3.js} +1 -1
- package/dist/client/_nuxt/DepYB8Ml.js +1 -0
- package/dist/client/_nuxt/{ChEme8PX.js → DhG9BCpW.js} +1 -1
- package/dist/client/_nuxt/{BxJadH_W.js → DhqJCFIX.js} +1 -1
- package/dist/client/_nuxt/DqpEK-75.js +1 -0
- package/dist/client/_nuxt/Ds-gbosJ.js +1 -0
- package/dist/client/_nuxt/{D4O64OTH.js → DvA-6Bhw.js} +1 -1
- package/dist/client/_nuxt/{CV1i-Amj.js → E3gJ1_iC.js} +1 -1
- package/dist/client/_nuxt/{B66eQrDP.js → FMvz1E70.js} +1 -1
- package/dist/client/_nuxt/GBQ2dnAY.js +1 -0
- package/dist/client/_nuxt/{CJrM-UL-.js → JJNjxVar.js} +1 -1
- package/dist/client/_nuxt/{D_QQB6Yv.js → KUYRHWK4.js} +1 -1
- package/dist/client/_nuxt/LGGdnPYs.js +1 -0
- package/dist/client/_nuxt/Lp17StYA.js +1 -0
- package/dist/client/_nuxt/RXHOmnpW.js +1 -0
- package/dist/client/_nuxt/{4NJYcRSJ.js → W2lx4xae.js} +1 -1
- package/dist/client/_nuxt/YurBl9Qv.js +1 -0
- package/dist/client/_nuxt/{ZBuUynGX.js → aDoct50J.js} +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/2259ca4d-d009-41be-acfd-fea8f1d19525.json +1 -0
- package/dist/client/_nuxt/{D0ybgVpa.js → dwbxrGt_.js} +1 -1
- package/dist/client/_nuxt/{D3CL8VSI.js → eACALr6E.js} +1 -1
- package/dist/client/_nuxt/entry.CzbW4W9L.css +1 -0
- package/dist/client/_nuxt/error-404.fx_5-qn-.css +1 -0
- package/dist/client/_nuxt/error-500.dWWWhQqD.css +1 -0
- package/dist/client/_nuxt/{B8E_5cfo.js → gOa3Yz9U.js} +1 -1
- package/dist/client/_nuxt/{BbzkbZ9e.js → i22b9gbM.js} +1 -1
- package/dist/client/_nuxt/{CE38B3OF.js → mDBYJdpO.js} +1 -1
- package/dist/client/_nuxt/{g_GSZ1hr.js → u4bm_LMz.js} +1 -1
- package/dist/client/_nuxt/{A13Lx_ye.js → vocl-2a6.js} +1 -1
- package/dist/client/_nuxt/{CN6rGqjK.js → xfgw03QH.js} +1 -1
- package/dist/client/_nuxt/zchoCBdH.js +1 -0
- package/dist/client/index.html +90 -85
- package/dist/module.json +1 -1
- package/dist/module.mjs +163 -159
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/Main.vue +1 -1
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/client.js +5 -4
- package/dist/runtime/{nitro → server}/routes/__link-checker__/inspect.js +4 -4
- package/dist/runtime/{pure → shared}/index.d.ts +3 -3
- package/dist/runtime/{pure → shared}/index.js +5 -5
- package/dist/runtime/{pure → shared}/inspect.js +28 -17
- package/dist/runtime/{pure/inspections/descriptive-link-text.js → shared/inspections/link-text.js} +2 -2
- package/dist/runtime/shared/inspections/no-baseless.d.ts +1 -0
- package/dist/runtime/{pure → shared}/inspections/no-baseless.js +3 -2
- package/dist/runtime/shared/inspections/no-double-slashes.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-double-slashes.js +25 -0
- package/dist/runtime/shared/inspections/no-duplicate-query-params.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-duplicate-query-params.js +28 -0
- package/dist/runtime/shared/inspections/no-missing-href.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-missing-href.js +17 -0
- package/dist/runtime/shared/inspections/no-non-ascii-chars.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-non-ascii-chars.js +18 -0
- package/dist/runtime/shared/inspections/no-underscores.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-underscores.js +17 -0
- package/dist/runtime/shared/inspections/no-uppercase-chars.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-uppercase-chars.js +17 -0
- package/dist/runtime/shared/inspections/no-whitespace.d.ts +1 -0
- package/dist/runtime/shared/inspections/no-whitespace.js +25 -0
- package/dist/runtime/types.d.ts +4 -3
- package/package.json +24 -24
- package/dist/client/_nuxt/35JD59R_.js +0 -1
- package/dist/client/_nuxt/9xzcv9xf.js +0 -1
- package/dist/client/_nuxt/B-GI89LT.js +0 -1
- package/dist/client/_nuxt/BGXXrhfN.js +0 -40
- package/dist/client/_nuxt/BStEqDGx.js +0 -1
- package/dist/client/_nuxt/BT8b6B_-.js +0 -1
- package/dist/client/_nuxt/BTo9ix6S.js +0 -1
- package/dist/client/_nuxt/BeSffZBD.js +0 -1
- package/dist/client/_nuxt/Begb3drz.js +0 -1
- package/dist/client/_nuxt/C3t61SNw.js +0 -1
- package/dist/client/_nuxt/C8UtRc_0.js +0 -1
- package/dist/client/_nuxt/COtMjVi7.js +0 -1
- package/dist/client/_nuxt/C_OO3DVV.js +0 -1
- package/dist/client/_nuxt/D5gt1ch3.js +0 -1
- package/dist/client/_nuxt/D6HQHXJE.js +0 -1
- package/dist/client/_nuxt/DIvA8ll9.js +0 -1
- package/dist/client/_nuxt/DQJjjPvk.js +0 -1
- package/dist/client/_nuxt/DWt6wkI-.js +0 -1
- package/dist/client/_nuxt/DanlQboj.js +0 -1
- package/dist/client/_nuxt/DateCQAz.js +0 -1
- package/dist/client/_nuxt/Dt1_zKdO.js +0 -1
- package/dist/client/_nuxt/Jwd9j1ay.js +0 -1
- package/dist/client/_nuxt/NfFGm2lL.js +0 -1
- package/dist/client/_nuxt/S_yhxdK1.js +0 -1
- package/dist/client/_nuxt/builds/meta/cd536571-8901-45d4-b5c2-149b2eb2ef0c.json +0 -1
- package/dist/client/_nuxt/eCoydtrh.js +0 -1
- package/dist/client/_nuxt/entry.BPVwhKGg.css +0 -1
- package/dist/client/_nuxt/error-404.CcgDkxt2.css +0 -1
- package/dist/client/_nuxt/error-500.CcBgumSp.css +0 -1
- package/dist/client/_nuxt/keK8bHwS.js +0 -1
- package/dist/runtime/pure/inspections/no-baseless.d.ts +0 -1
- package/dist/runtime/{nuxt/plugin → app/plugins}/ui.client.d.ts +0 -0
- package/dist/runtime/{nuxt/plugin → app/plugins}/ui.client.js +0 -0
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/Squiggle.vue +0 -0
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/client.d.ts +1 -1
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/state.d.ts +0 -0
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/state.js +0 -0
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/utils.d.ts +0 -0
- package/dist/runtime/{nuxt/plugin → app/plugins}/view/utils.js +0 -0
- package/dist/runtime/{nitro → server}/composables/content-mock.d.ts +0 -0
- package/dist/runtime/{nitro → server}/composables/content-mock.js +0 -0
- package/dist/runtime/{nitro → server}/routes/__link-checker__/debug.d.ts +0 -0
- package/dist/runtime/{nitro → server}/routes/__link-checker__/debug.js +1 -1
- package/dist/runtime/{nitro → server}/routes/__link-checker__/inspect.d.ts +0 -0
- package/dist/runtime/{nitro → server}/routes/__link-checker__/links.d.ts +0 -0
- package/dist/runtime/{nitro → server}/routes/__link-checker__/links.js +1 -1
- /package/dist/runtime/{nitro → server}/tsconfig.json +0 -0
- /package/dist/runtime/{pure → shared}/crawl.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/crawl.js +0 -0
- /package/dist/runtime/{pure → shared}/diff.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/diff.js +0 -0
- /package/dist/runtime/{pure → shared}/inspect.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/inspections/absolute-site-urls.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/inspections/absolute-site-urls.js +0 -0
- /package/dist/runtime/{pure/inspections/descriptive-link-text.d.ts → shared/inspections/link-text.d.ts} +0 -0
- /package/dist/runtime/{pure → shared}/inspections/missing-hash.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/inspections/missing-hash.js +0 -0
- /package/dist/runtime/{pure/inspections/no-error-response-status.d.ts → shared/inspections/no-error-response.d.ts} +0 -0
- /package/dist/runtime/{pure/inspections/no-error-response-status.js → shared/inspections/no-error-response.js} +0 -0
- /package/dist/runtime/{pure → shared}/inspections/no-javascript.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/inspections/no-javascript.js +0 -0
- /package/dist/runtime/{pure → shared}/inspections/trailing-slash.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/inspections/trailing-slash.js +0 -0
- /package/dist/runtime/{pure → shared}/inspections/util.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/inspections/util.js +0 -0
- /package/dist/runtime/{pure → shared}/redirects.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/redirects.js +0 -0
- /package/dist/runtime/{pure → shared}/sharedUtils.d.ts +0 -0
- /package/dist/runtime/{pure → shared}/sharedUtils.js +0 -0
package/dist/module.mjs
CHANGED
|
@@ -2,32 +2,174 @@ import { useNuxt, extendPages, defineNuxtModule, createResolver, useLogger, addP
|
|
|
2
2
|
import { useSiteConfig, installNuxtSiteConfig } from 'nuxt-site-config-kit';
|
|
3
3
|
import { readPackageJSON } from 'pkg-types';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
7
|
+
import { diffArrays } from 'diff';
|
|
7
8
|
import { resolve, relative } from 'pathe';
|
|
9
|
+
import { generateLinkDiff } from '../dist/runtime/shared/diff.js';
|
|
10
|
+
import { joinURL, withoutLeadingSlash } from 'ufo';
|
|
11
|
+
import chalk from 'chalk';
|
|
8
12
|
import { load } from 'cheerio';
|
|
9
|
-
import
|
|
13
|
+
import Fuse from 'fuse.js';
|
|
10
14
|
import { createStorage } from 'unstorage';
|
|
11
15
|
import fsDriver from 'unstorage/drivers/fs';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
import { setLinkResponse, getLinkResponse, crawlFetch } from '../dist/runtime/shared/crawl.js';
|
|
17
|
+
import { inspect } from '../dist/runtime/shared/inspect.js';
|
|
18
|
+
import { createFilter } from '../dist/runtime/shared/sharedUtils.js';
|
|
19
|
+
|
|
20
|
+
function convertNuxtPagesToPaths(pages) {
|
|
21
|
+
return pages.map((page) => {
|
|
22
|
+
return page.children?.length ? page.children.map((child) => {
|
|
23
|
+
return {
|
|
24
|
+
path: joinURL(page.path, child.path),
|
|
25
|
+
page: child
|
|
26
|
+
};
|
|
27
|
+
}) : { page, path: page.path };
|
|
28
|
+
}).flat().filter((p) => !p.path.includes(":")).map((p) => ({
|
|
29
|
+
title: p.page?.meta?.title || "",
|
|
30
|
+
link: p.path
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function useViteWebSocket(nuxt = useNuxt()) {
|
|
34
|
+
return new Promise((_resolve) => {
|
|
35
|
+
nuxt.hooks.hook("vite:serverCreated", (viteServer) => {
|
|
36
|
+
_resolve(viteServer.ws);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEVTOOLS_UI_ROUTE = "/__nuxt-link-checker";
|
|
42
|
+
const DEVTOOLS_UI_LOCAL_PORT = 3030;
|
|
43
|
+
const RPC_NAMESPACE = "nuxt-link-checker-rpc";
|
|
44
|
+
function setupDevToolsUI(options, moduleResolve, nuxt = useNuxt()) {
|
|
45
|
+
const clientPath = moduleResolve("./client");
|
|
46
|
+
const isProductionBuild = existsSync(clientPath);
|
|
47
|
+
if (isProductionBuild) {
|
|
48
|
+
nuxt.hook("vite:serverCreated", async (server) => {
|
|
49
|
+
const sirv = await import('sirv').then((r) => r.default || r);
|
|
50
|
+
server.middlewares.use(
|
|
51
|
+
DEVTOOLS_UI_ROUTE,
|
|
52
|
+
sirv(clientPath, { dev: true, single: true })
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
57
|
+
config.server = config.server || {};
|
|
58
|
+
config.server.proxy = config.server.proxy || {};
|
|
59
|
+
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
60
|
+
target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
|
|
61
|
+
changeOrigin: true,
|
|
62
|
+
followRedirects: true,
|
|
63
|
+
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
68
|
+
tabs.push({
|
|
69
|
+
// unique identifier
|
|
70
|
+
name: "nuxt-link-checker",
|
|
71
|
+
// title to display in the tab
|
|
72
|
+
title: "Link Checker",
|
|
73
|
+
// any icon from Iconify, or a URL to an image
|
|
74
|
+
icon: "carbon:cloud-satellite-link",
|
|
75
|
+
// iframe view
|
|
76
|
+
view: {
|
|
77
|
+
type: "iframe",
|
|
78
|
+
src: DEVTOOLS_UI_ROUTE
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
let isConnected = false;
|
|
83
|
+
const viteServerWs = useViteWebSocket();
|
|
84
|
+
const rpc = new Promise((promiseResolve) => {
|
|
85
|
+
onDevToolsInitialized(async () => {
|
|
86
|
+
const rpc2 = extendServerRpc(RPC_NAMESPACE, {
|
|
87
|
+
getOptions() {
|
|
88
|
+
return options;
|
|
89
|
+
},
|
|
90
|
+
async reset() {
|
|
91
|
+
const ws = await viteServerWs;
|
|
92
|
+
ws.send("nuxt-link-checker:reset");
|
|
93
|
+
},
|
|
94
|
+
async scrollToLink(link) {
|
|
95
|
+
const ws = await viteServerWs;
|
|
96
|
+
ws.send("nuxt-link-checker:scroll-to-link", link);
|
|
97
|
+
},
|
|
98
|
+
async applyLinkFixes(diff, original, replacement) {
|
|
99
|
+
for (const { filepath } of diff) {
|
|
100
|
+
const rootDirFolderName = nuxt.options.rootDir.split("/").pop();
|
|
101
|
+
const filepathWithoutRoot = filepath.replace(new RegExp(`^${nuxt.options.rootDir}/`), "").replace(new RegExp(`^${rootDirFolderName}/`), "");
|
|
102
|
+
const fp = resolve(nuxt.options.rootDir, filepathWithoutRoot);
|
|
103
|
+
const contents = await readFile(fp, "utf8");
|
|
104
|
+
const diff2 = generateLinkDiff(contents, original, replacement);
|
|
105
|
+
await writeFile(fp, diff2.code, "utf8");
|
|
106
|
+
}
|
|
107
|
+
const ws = await viteServerWs;
|
|
108
|
+
ws.send("nuxt-link-checker:reset");
|
|
109
|
+
},
|
|
110
|
+
async toggleLiveInspections(enabled) {
|
|
111
|
+
const ws = await viteServerWs;
|
|
112
|
+
ws.send("nuxt-link-checker:live-inspections", { enabled });
|
|
113
|
+
},
|
|
114
|
+
async connected() {
|
|
115
|
+
const ws = await viteServerWs;
|
|
116
|
+
ws.send("nuxt-link-checker:connected");
|
|
117
|
+
isConnected = true;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
promiseResolve(rpc2);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
viteServerWs.then((ws) => {
|
|
124
|
+
ws.on("nuxt-link-checker:queueWorking", async (payload) => {
|
|
125
|
+
if (isConnected) {
|
|
126
|
+
const _rpc = await rpc;
|
|
127
|
+
_rpc.broadcast.queueWorking(payload).catch(() => {
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
ws.on("nuxt-link-checker:updated", async () => {
|
|
132
|
+
if (isConnected) {
|
|
133
|
+
const _rpc = await rpc;
|
|
134
|
+
_rpc.broadcast.updated().catch(() => {
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
ws.on("nuxt-link-checker:filter", async (payload) => {
|
|
139
|
+
if (isConnected) {
|
|
140
|
+
const _rpc = await rpc;
|
|
141
|
+
_rpc.broadcast.filter(payload).catch(() => {
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
let lastRoutes = [];
|
|
146
|
+
extendPages(async (pages) => {
|
|
147
|
+
const routes = convertNuxtPagesToPaths(pages);
|
|
148
|
+
if (lastRoutes.length) {
|
|
149
|
+
const routeDiff = diffArrays(lastRoutes, routes);
|
|
150
|
+
if (routeDiff.some((diff) => diff.added || diff.removed))
|
|
151
|
+
ws.send("nuxt-link-checker:reset");
|
|
152
|
+
}
|
|
153
|
+
lastRoutes = routes;
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
19
157
|
|
|
20
158
|
const linkMap = {};
|
|
21
|
-
function extractPayload(html) {
|
|
159
|
+
function extractPayload(html, rootNodeId = "#__nuxt") {
|
|
160
|
+
if (String(rootNodeId).length) {
|
|
161
|
+
rootNodeId = rootNodeId[0] === "#" ? rootNodeId : `#${rootNodeId}`;
|
|
162
|
+
}
|
|
22
163
|
const $ = load(html);
|
|
23
|
-
const ids = $(
|
|
164
|
+
const ids = $(`${rootNodeId} [id]`.trim()).map((i, el) => $(el).attr("id")).get();
|
|
24
165
|
const title = $("title").text();
|
|
25
|
-
const links = $(
|
|
166
|
+
const links = $(`${rootNodeId} a`.trim()).map((i, el) => {
|
|
26
167
|
return {
|
|
168
|
+
role: $(el).attr("role") || "",
|
|
27
169
|
link: $(el).attr("href") || "",
|
|
28
170
|
textContent: ($(el).attr("aria-label") || $(el).attr("title") || $(el).text()).trim() || ""
|
|
29
171
|
};
|
|
30
|
-
}).get()
|
|
172
|
+
}).get();
|
|
31
173
|
return { title, ids, links };
|
|
32
174
|
}
|
|
33
175
|
function isNuxtGenerate(nuxt = useNuxt()) {
|
|
@@ -42,7 +184,7 @@ function prerender(config, nuxt = useNuxt()) {
|
|
|
42
184
|
nitro.hooks.hook("prerender:generate", async (ctx) => {
|
|
43
185
|
const route = decodeURI(ctx.route);
|
|
44
186
|
if (ctx.contents && !ctx.error && ctx.fileName?.endsWith(".html") && !route.endsWith(".html") && urlFilter(route))
|
|
45
|
-
linkMap[route] = extractPayload(ctx.contents);
|
|
187
|
+
linkMap[route] = extractPayload(ctx.contents, nuxt.options.app.rootAttrs?.id || "");
|
|
46
188
|
setLinkResponse(route, Promise.resolve({ status: Number(ctx.error?.statusCode) || 200, statusText: ctx.error?.statusMessage || "", headers: {} }));
|
|
47
189
|
});
|
|
48
190
|
nitro.hooks.hook("prerender:done", async () => {
|
|
@@ -230,144 +372,6 @@ function prerender(config, nuxt = useNuxt()) {
|
|
|
230
372
|
});
|
|
231
373
|
}
|
|
232
374
|
|
|
233
|
-
function convertNuxtPagesToPaths(pages) {
|
|
234
|
-
return pages.map((page) => {
|
|
235
|
-
return page.children?.length ? page.children.map((child) => {
|
|
236
|
-
return {
|
|
237
|
-
path: joinURL(page.path, child.path),
|
|
238
|
-
page: child
|
|
239
|
-
};
|
|
240
|
-
}) : { page, path: page.path };
|
|
241
|
-
}).flat().filter((p) => !p.path.includes(":")).map((p) => ({
|
|
242
|
-
title: p.page?.meta?.title || "",
|
|
243
|
-
link: p.path
|
|
244
|
-
}));
|
|
245
|
-
}
|
|
246
|
-
function useViteWebSocket(nuxt = useNuxt()) {
|
|
247
|
-
return new Promise((_resolve) => {
|
|
248
|
-
nuxt.hooks.hook("vite:serverCreated", (viteServer) => {
|
|
249
|
-
_resolve(viteServer.ws);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const DEVTOOLS_UI_ROUTE = "/__nuxt-link-checker";
|
|
255
|
-
const DEVTOOLS_UI_LOCAL_PORT = 3030;
|
|
256
|
-
const RPC_NAMESPACE = "nuxt-link-checker-rpc";
|
|
257
|
-
function setupDevToolsUI(options, moduleResolve, nuxt = useNuxt()) {
|
|
258
|
-
const clientPath = moduleResolve("./client");
|
|
259
|
-
const isProductionBuild = existsSync(clientPath);
|
|
260
|
-
if (isProductionBuild) {
|
|
261
|
-
nuxt.hook("vite:serverCreated", async (server) => {
|
|
262
|
-
const sirv = await import('sirv').then((r) => r.default || r);
|
|
263
|
-
server.middlewares.use(
|
|
264
|
-
DEVTOOLS_UI_ROUTE,
|
|
265
|
-
sirv(clientPath, { dev: true, single: true })
|
|
266
|
-
);
|
|
267
|
-
});
|
|
268
|
-
} else {
|
|
269
|
-
nuxt.hook("vite:extendConfig", (config) => {
|
|
270
|
-
config.server = config.server || {};
|
|
271
|
-
config.server.proxy = config.server.proxy || {};
|
|
272
|
-
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
273
|
-
target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
|
|
274
|
-
changeOrigin: true,
|
|
275
|
-
followRedirects: true,
|
|
276
|
-
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
|
|
277
|
-
};
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
281
|
-
tabs.push({
|
|
282
|
-
// unique identifier
|
|
283
|
-
name: "nuxt-link-checker",
|
|
284
|
-
// title to display in the tab
|
|
285
|
-
title: "Link Checker",
|
|
286
|
-
// any icon from Iconify, or a URL to an image
|
|
287
|
-
icon: "carbon:cloud-satellite-link",
|
|
288
|
-
// iframe view
|
|
289
|
-
view: {
|
|
290
|
-
type: "iframe",
|
|
291
|
-
src: DEVTOOLS_UI_ROUTE
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
let isConnected = false;
|
|
296
|
-
const viteServerWs = useViteWebSocket();
|
|
297
|
-
const rpc = new Promise((promiseResolve) => {
|
|
298
|
-
onDevToolsInitialized(async () => {
|
|
299
|
-
const rpc2 = extendServerRpc(RPC_NAMESPACE, {
|
|
300
|
-
getOptions() {
|
|
301
|
-
return options;
|
|
302
|
-
},
|
|
303
|
-
async reset() {
|
|
304
|
-
const ws = await viteServerWs;
|
|
305
|
-
ws.send("nuxt-link-checker:reset");
|
|
306
|
-
},
|
|
307
|
-
async scrollToLink(link) {
|
|
308
|
-
const ws = await viteServerWs;
|
|
309
|
-
ws.send("nuxt-link-checker:scroll-to-link", link);
|
|
310
|
-
},
|
|
311
|
-
async applyLinkFixes(diff, original, replacement) {
|
|
312
|
-
for (const { filepath } of diff) {
|
|
313
|
-
const rootDirFolderName = nuxt.options.rootDir.split("/").pop();
|
|
314
|
-
const filepathWithoutRoot = filepath.replace(new RegExp(`^${nuxt.options.rootDir}/`), "").replace(new RegExp(`^${rootDirFolderName}/`), "");
|
|
315
|
-
const fp = resolve(nuxt.options.rootDir, filepathWithoutRoot);
|
|
316
|
-
const contents = await readFile(fp, "utf8");
|
|
317
|
-
const diff2 = generateLinkDiff(contents, original, replacement);
|
|
318
|
-
await writeFile(fp, diff2.code, "utf8");
|
|
319
|
-
}
|
|
320
|
-
const ws = await viteServerWs;
|
|
321
|
-
ws.send("nuxt-link-checker:reset");
|
|
322
|
-
},
|
|
323
|
-
async toggleLiveInspections(enabled) {
|
|
324
|
-
const ws = await viteServerWs;
|
|
325
|
-
ws.send("nuxt-link-checker:live-inspections", { enabled });
|
|
326
|
-
},
|
|
327
|
-
async connected() {
|
|
328
|
-
const ws = await viteServerWs;
|
|
329
|
-
ws.send("nuxt-link-checker:connected");
|
|
330
|
-
isConnected = true;
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
promiseResolve(rpc2);
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
viteServerWs.then((ws) => {
|
|
337
|
-
ws.on("nuxt-link-checker:queueWorking", async (payload) => {
|
|
338
|
-
if (isConnected) {
|
|
339
|
-
const _rpc = await rpc;
|
|
340
|
-
_rpc.broadcast.queueWorking(payload).catch(() => {
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
ws.on("nuxt-link-checker:updated", async () => {
|
|
345
|
-
if (isConnected) {
|
|
346
|
-
const _rpc = await rpc;
|
|
347
|
-
_rpc.broadcast.updated().catch(() => {
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
ws.on("nuxt-link-checker:filter", async (payload) => {
|
|
352
|
-
if (isConnected) {
|
|
353
|
-
const _rpc = await rpc;
|
|
354
|
-
_rpc.broadcast.filter(payload).catch(() => {
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
let lastRoutes = [];
|
|
359
|
-
extendPages(async (pages) => {
|
|
360
|
-
const routes = convertNuxtPagesToPaths(pages);
|
|
361
|
-
if (lastRoutes.length) {
|
|
362
|
-
const routeDiff = diffArrays(lastRoutes, routes);
|
|
363
|
-
if (routeDiff.some((diff) => diff.added || diff.removed))
|
|
364
|
-
ws.send("nuxt-link-checker:reset");
|
|
365
|
-
}
|
|
366
|
-
lastRoutes = routes;
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
|
|
371
375
|
const module = defineNuxtModule({
|
|
372
376
|
meta: {
|
|
373
377
|
name: "nuxt-link-checker",
|
|
@@ -408,20 +412,20 @@ const module = defineNuxtModule({
|
|
|
408
412
|
const isDevToolsEnabled = typeof nuxt.options.devtools === "boolean" ? nuxt.options.devtools : nuxt.options.devtools.enabled;
|
|
409
413
|
if (nuxt.options.dev && isDevToolsEnabled) {
|
|
410
414
|
addPlugin({
|
|
411
|
-
src: resolve("./runtime/
|
|
415
|
+
src: resolve("./runtime/app/plugins/ui.client"),
|
|
412
416
|
mode: "client"
|
|
413
417
|
});
|
|
414
418
|
addServerHandler({
|
|
415
419
|
route: "/__link-checker__/inspect",
|
|
416
|
-
handler: resolve("./runtime/
|
|
420
|
+
handler: resolve("./runtime/server/routes/__link-checker__/inspect")
|
|
417
421
|
});
|
|
418
422
|
addServerHandler({
|
|
419
423
|
route: "/__link-checker__/links",
|
|
420
|
-
handler: resolve("./runtime/
|
|
424
|
+
handler: resolve("./runtime/server/routes/__link-checker__/links")
|
|
421
425
|
});
|
|
422
426
|
addServerHandler({
|
|
423
427
|
route: "/__link-checker__/debug.json",
|
|
424
|
-
handler: resolve("./runtime/
|
|
428
|
+
handler: resolve("./runtime/server/routes/__link-checker__/debug")
|
|
425
429
|
});
|
|
426
430
|
const pagePromise = new Promise((_resolve) => {
|
|
427
431
|
extendPages((pages) => {
|
|
@@ -437,9 +441,9 @@ const module = defineNuxtModule({
|
|
|
437
441
|
const hasSitemapModule = (hasNuxtModule("@nuxtjs/sitemap") || hasNuxtModule("nuxt-simple-sitemap") && await hasNuxtModuleCompatibility("nuxt-simple-sitemap", ">=4")) && nuxt.options.sitemap?.enabled !== false;
|
|
438
442
|
nuxt.options.nitro.alias = nuxt.options.nitro.alias || {};
|
|
439
443
|
if (!hasNuxtModule("@nuxt/content")) {
|
|
440
|
-
nuxt.options.nitro.alias["#content/server"] = resolve("./runtime/
|
|
444
|
+
nuxt.options.nitro.alias["#content/server"] = resolve("./runtime/server/composables/content-mock");
|
|
441
445
|
}
|
|
442
|
-
nuxt.options.nitro.alias["#link-checker/
|
|
446
|
+
nuxt.options.nitro.alias["#link-checker/shared"] = resolve("./runtime/shared");
|
|
443
447
|
nuxt.options.runtimeConfig.public["nuxt-link-checker"] = {
|
|
444
448
|
version,
|
|
445
449
|
hasSitemapModule,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { type Ref, computed, ref } from 'vue'
|
|
3
2
|
import type { NuxtLinkCheckerClient } from '../../../types'
|
|
3
|
+
import { computed, type Ref, ref } from 'vue'
|
|
4
4
|
import Squiggle from './Squiggle.vue'
|
|
5
5
|
import { useEventListener } from './utils'
|
|
6
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
2
|
import { useLocalStorage } from "@vueuse/core";
|
|
3
|
-
import {
|
|
3
|
+
import { computed, createApp, h, ref, shallowReactive, unref } from "vue";
|
|
4
|
+
import { createFilter } from "../../../shared/sharedUtils.js";
|
|
4
5
|
import Main from "./Main.vue";
|
|
5
6
|
import { linkDb } from "./state.js";
|
|
6
|
-
import { useRuntimeConfig } from "#imports";
|
|
7
7
|
function resolveDevtoolsIframe() {
|
|
8
8
|
const iframe = document.querySelector("#nuxt-devtools-iframe");
|
|
9
9
|
if (!iframe)
|
|
@@ -47,7 +47,7 @@ export async function setupLinkCheckerClient({ nuxt, route }) {
|
|
|
47
47
|
elMap = {};
|
|
48
48
|
visibleLinks.clear();
|
|
49
49
|
lastIds = [...new Set([...document.querySelectorAll("#__nuxt [id]")].map((el) => el.id))];
|
|
50
|
-
[...document.querySelectorAll("#__nuxt a
|
|
50
|
+
[...document.querySelectorAll("#__nuxt a")].map((el) => ({ el, link: el.getAttribute("href") })).forEach(({ el, link }) => {
|
|
51
51
|
if (!link)
|
|
52
52
|
return;
|
|
53
53
|
if (!filter(link))
|
|
@@ -65,6 +65,7 @@ export async function setupLinkCheckerClient({ nuxt, route }) {
|
|
|
65
65
|
}
|
|
66
66
|
queue.push({
|
|
67
67
|
link,
|
|
68
|
+
role: el.getAttribute("role") || "",
|
|
68
69
|
textContent: (el.textContent || el.getAttribute("aria-label") || el.getAttribute("title") || "").trim(),
|
|
69
70
|
paths
|
|
70
71
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { useNitroOrigin, useRuntimeConfig, useSiteConfig } from "#imports";
|
|
2
|
+
import Fuse from "fuse.js";
|
|
1
3
|
import { defineEventHandler, getHeader, readBody } from "h3";
|
|
4
|
+
import { resolve } from "pathe";
|
|
2
5
|
import { fixSlashes } from "site-config-stack/urls";
|
|
3
6
|
import { parseURL } from "ufo";
|
|
4
|
-
import {
|
|
5
|
-
import Fuse from "fuse.js";
|
|
6
|
-
import { generateFileLinkDiff, generateFileLinkPreviews, getLinkResponse, inspect, isNonFetchableLink, lruFsCache } from "#link-checker/pure";
|
|
7
|
-
import { useNitroOrigin, useRuntimeConfig, useSiteConfig } from "#imports";
|
|
7
|
+
import { generateFileLinkDiff, generateFileLinkPreviews, getLinkResponse, inspect, isNonFetchableLink, lruFsCache } from "#link-checker/shared";
|
|
8
8
|
import { serverQueryContent } from "#content/server";
|
|
9
9
|
function isInternalRoute(path) {
|
|
10
10
|
const lastSegment = path.split("/").pop() || path;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { inspect } from './inspect.js';
|
|
2
|
-
import { generateFileLinkDiff, generateFileLinkPreviews, lruFsCache } from './diff.js';
|
|
3
1
|
import { getLinkResponse } from './crawl.js';
|
|
2
|
+
import { generateFileLinkDiff, generateFileLinkPreviews, lruFsCache } from './diff.js';
|
|
3
|
+
import { inspect } from './inspect.js';
|
|
4
4
|
import { isNonFetchableLink } from './inspections/util.js';
|
|
5
|
-
export {
|
|
5
|
+
export { generateFileLinkDiff, generateFileLinkPreviews, getLinkResponse, inspect, isNonFetchableLink, lruFsCache, };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { inspect } from "./inspect.js";
|
|
2
|
-
import { generateFileLinkDiff, generateFileLinkPreviews, lruFsCache } from "./diff.js";
|
|
3
1
|
import { getLinkResponse } from "./crawl.js";
|
|
2
|
+
import { generateFileLinkDiff, generateFileLinkPreviews, lruFsCache } from "./diff.js";
|
|
3
|
+
import { inspect } from "./inspect.js";
|
|
4
4
|
import { isNonFetchableLink } from "./inspections/util.js";
|
|
5
5
|
export {
|
|
6
|
-
inspect,
|
|
7
6
|
generateFileLinkDiff,
|
|
8
7
|
generateFileLinkPreviews,
|
|
9
|
-
lruFsCache,
|
|
10
8
|
getLinkResponse,
|
|
11
|
-
|
|
9
|
+
inspect,
|
|
10
|
+
isNonFetchableLink,
|
|
11
|
+
lruFsCache
|
|
12
12
|
};
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { parseURL } from "ufo";
|
|
2
|
-
import
|
|
2
|
+
import RuleAbsoluteSiteUrls from "./inspections/absolute-site-urls.js";
|
|
3
|
+
import RuleDescriptiveLinkText from "./inspections/link-text.js";
|
|
3
4
|
import RuleMissingHash from "./inspections/missing-hash.js";
|
|
4
|
-
import
|
|
5
|
+
import RuleNoDocumentRelative from "./inspections/no-baseless.js";
|
|
6
|
+
import RuleNoDoubleSlashes from "./inspections/no-double-slashes.js";
|
|
7
|
+
import RuleNoDuplicateQueryParams from "./inspections/no-duplicate-query-params.js";
|
|
8
|
+
import RuleNoErrorResponse from "./inspections/no-error-response.js";
|
|
5
9
|
import RuleNoJavascript from "./inspections/no-javascript.js";
|
|
6
|
-
import
|
|
10
|
+
import RuleNoMissingHref from "./inspections/no-missing-href.js";
|
|
11
|
+
import RuleNoNonAsciiChars from "./inspections/no-non-ascii-chars.js";
|
|
12
|
+
import RuleNoUnderscores from "./inspections/no-underscores.js";
|
|
13
|
+
import RuleNoUppercaseChars from "./inspections/no-uppercase-chars.js";
|
|
14
|
+
import RuleNoWhitespace from "./inspections/no-whitespace.js";
|
|
15
|
+
import RuleTrailingSlash from "./inspections/trailing-slash.js";
|
|
7
16
|
import RuleRedirects from "./redirects.js";
|
|
8
|
-
import RuleNoErrorResponse from "./inspections/no-error-response-status.js";
|
|
9
|
-
import RuleDescriptiveLinkText from "./inspections/descriptive-link-text.js";
|
|
10
|
-
import { isNonFetchableLink } from "./inspections/util.js";
|
|
11
17
|
export const AllInspections = [
|
|
18
|
+
RuleNoMissingHref(),
|
|
19
|
+
RuleNoDuplicateQueryParams(),
|
|
20
|
+
RuleNoNonAsciiChars(),
|
|
12
21
|
RuleMissingHash(),
|
|
22
|
+
RuleNoUnderscores(),
|
|
23
|
+
RuleNoWhitespace(),
|
|
24
|
+
RuleNoDoubleSlashes(),
|
|
13
25
|
RuleNoErrorResponse(),
|
|
14
|
-
|
|
26
|
+
RuleNoDocumentRelative(),
|
|
15
27
|
RuleNoJavascript(),
|
|
16
28
|
RuleTrailingSlash(),
|
|
29
|
+
RuleNoUppercaseChars(),
|
|
17
30
|
RuleAbsoluteSiteUrls(),
|
|
18
31
|
RuleRedirects(),
|
|
19
32
|
RuleDescriptiveLinkText()
|
|
@@ -23,26 +36,24 @@ export function inspect(ctx, rules) {
|
|
|
23
36
|
const res = { error: [], warning: [], fix: ctx.link, link: ctx.link };
|
|
24
37
|
let link = ctx.link;
|
|
25
38
|
const url = parseURL(link);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
name: "invalid-url",
|
|
29
|
-
scope: "error",
|
|
30
|
-
message: `Invalid URL: ${link}`
|
|
31
|
-
});
|
|
32
|
-
return res;
|
|
33
|
-
}
|
|
34
|
-
const validInspections = Object.entries(rules).filter(([name]) => !ctx.skipInspections || !ctx.skipInspections.includes(name)).map(([, rule]) => rule);
|
|
39
|
+
const validInspections = rules.filter(({ id }) => !(ctx.skipInspections || []).includes(id));
|
|
40
|
+
let processing = true;
|
|
35
41
|
for (const rule of validInspections) {
|
|
36
42
|
rule.test({
|
|
37
43
|
...ctx,
|
|
38
44
|
link,
|
|
39
45
|
url,
|
|
40
|
-
report(obj) {
|
|
46
|
+
report(obj, stop) {
|
|
47
|
+
if (stop) {
|
|
48
|
+
processing = false;
|
|
49
|
+
}
|
|
41
50
|
res[obj.scope].push(obj);
|
|
42
51
|
if (obj.fix)
|
|
43
52
|
link = obj.fix;
|
|
44
53
|
}
|
|
45
54
|
});
|
|
55
|
+
if (!processing)
|
|
56
|
+
break;
|
|
46
57
|
}
|
|
47
58
|
res.passes = !res.error?.length && !res.warning?.length;
|
|
48
59
|
res.fix = link;
|
package/dist/runtime/{pure/inspections/descriptive-link-text.js → shared/inspections/link-text.js}
RENAMED
|
@@ -9,7 +9,7 @@ export default function RuleDescriptiveLinkText() {
|
|
|
9
9
|
report({
|
|
10
10
|
name: "link-text",
|
|
11
11
|
scope: "warning",
|
|
12
|
-
message: "
|
|
12
|
+
message: "Missing link textContent, title or aria-label.",
|
|
13
13
|
tip: "Links with descriptive text are easier to understand for screen readers and search engines."
|
|
14
14
|
});
|
|
15
15
|
}
|
|
@@ -29,7 +29,7 @@ export default function RuleDescriptiveLinkText() {
|
|
|
29
29
|
report({
|
|
30
30
|
name: "link-text",
|
|
31
31
|
scope: "warning",
|
|
32
|
-
message: "
|
|
32
|
+
message: `Link text "${textContent}" should be more descriptive.`,
|
|
33
33
|
tip: `The ${textContent} descriptive text does not provide any context to the link.`
|
|
34
34
|
});
|
|
35
35
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function RuleNoDocumentRelative(): import("../../types").Rule;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { joinURL } from "ufo";
|
|
2
2
|
import { defineRule, isNonFetchableLink } from "./util.js";
|
|
3
|
-
export default function
|
|
3
|
+
export default function RuleNoDocumentRelative() {
|
|
4
4
|
return defineRule({
|
|
5
5
|
id: "no-baseless",
|
|
6
|
+
// TODO rename to no-document-relative
|
|
6
7
|
test({ link, fromPath, report }) {
|
|
7
8
|
if (link.startsWith("/") || link.startsWith("http") || isNonFetchableLink(link))
|
|
8
9
|
return;
|
|
9
10
|
report({
|
|
10
11
|
name: "no-baseless",
|
|
11
12
|
scope: "warning",
|
|
12
|
-
message: "
|
|
13
|
+
message: "Links should be root relative.",
|
|
13
14
|
fix: `${joinURL(fromPath, link)}`,
|
|
14
15
|
fixDescription: `Add base ${fromPath}.`
|
|
15
16
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function RuleNoDoubleSlashes(): import("../../types").Rule;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineRule } from "./util.js";
|
|
2
|
+
export default function RuleNoDoubleSlashes() {
|
|
3
|
+
return defineRule({
|
|
4
|
+
id: "no-double-slashes",
|
|
5
|
+
test({ url, link, report }) {
|
|
6
|
+
if (link.startsWith("//") && !link.includes(".")) {
|
|
7
|
+
report({
|
|
8
|
+
name: "no-double-slashes",
|
|
9
|
+
scope: "warning",
|
|
10
|
+
message: "Links should not contain double slashes.",
|
|
11
|
+
fix: link.replaceAll(/(^\/{2,}|\/{2,})/g, "/"),
|
|
12
|
+
fixDescription: "Remove double slashes."
|
|
13
|
+
});
|
|
14
|
+
} else if (url.pathname.match(/(^\/{2,}|\/{2,})/)) {
|
|
15
|
+
report({
|
|
16
|
+
name: "no-double-slashes",
|
|
17
|
+
scope: "warning",
|
|
18
|
+
message: "Links should not contain double slashes.",
|
|
19
|
+
fix: link.replace(url.pathname, url.pathname.replaceAll(/(^\/{2,}|\/{2,})/g, "/")),
|
|
20
|
+
fixDescription: "Remove double slashes."
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function RuleNoDuplicateQueryParams(): import("../../types").Rule;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineRule } from "./util.js";
|
|
2
|
+
export default function RuleNoDuplicateQueryParams() {
|
|
3
|
+
return defineRule({
|
|
4
|
+
id: "no-duplicate-query-params",
|
|
5
|
+
test({ report, link, url }) {
|
|
6
|
+
if (!url.search)
|
|
7
|
+
return;
|
|
8
|
+
const search = url.search.slice(1);
|
|
9
|
+
const searchParams = search.split("&").map((param) => param.split("=")[0]);
|
|
10
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
11
|
+
for (const param of searchParams) {
|
|
12
|
+
if (duplicates.has(param)) {
|
|
13
|
+
const fix = link.replace(new RegExp(`([?&])${param}=[^&]*&?`), "$1");
|
|
14
|
+
report({
|
|
15
|
+
name: "no-duplicate-query-params",
|
|
16
|
+
scope: "warning",
|
|
17
|
+
message: "Links should not contain duplicated query parameters.",
|
|
18
|
+
fix,
|
|
19
|
+
tip: "Duplicate query parameters can cause canonical URL issues.",
|
|
20
|
+
fixDescription: "Remove duplicate query parameter."
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
duplicates.add(param);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function RuleNoMissingHref(): import("../../types").Rule;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineRule } from "./util.js";
|
|
2
|
+
export default function RuleNoMissingHref() {
|
|
3
|
+
return defineRule({
|
|
4
|
+
id: "no-missing-href",
|
|
5
|
+
test({ report, link, role }) {
|
|
6
|
+
if (link.trim().length > 0 || role === "button") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
report({
|
|
10
|
+
name: "no-missing-href",
|
|
11
|
+
scope: "warning",
|
|
12
|
+
message: "For accessibility and UX anchor tags require a href attribute.",
|
|
13
|
+
tip: 'Use a button element with type="button" instead if the link is not navigational.'
|
|
14
|
+
}, true);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function RuleNoNonAsciiChars(): import("../../types").Rule;
|