nuxt-link-checker 2.1.1 → 2.1.2

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
- import{_ as a,u as n,o as r,c as l,a as e,t as s,b as c,w as d,d as p,e as f,p as x,f as h}from"./entry.48231557.js";const m=t=>(x("data-v-ac41e552"),t=t(),h(),t),u={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},g=m(()=>e("div",{class:"fixed left-0 right-0 spotlight z-10"},null,-1)),_={class:"max-w-520px text-center z-20"},b=["textContent"],w=["textContent"],y={class:"w-full flex items-center justify-center"},S={__name:"error-404",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:404},statusMessage:{type:String,default:"Not Found"},description:{type:String,default:"Sorry, the page you are looking for could not be found."},backHome:{type:String,default:"Go back home"}},setup(t){const o=t;return n({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}a{color:inherit;text-decoration:inherit}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(k,v)=>{const i=f;return r(),l("div",u,[g,e("div",_,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,b),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,w),e("div",y,[c(i,{to:"/",class:"gradient-border text-md sm:text-xl py-2 px-4 sm:py-3 sm:px-6 cursor-pointer"},{default:d(()=>[p(s(t.backHome),1)]),_:1})])])])}}},z=a(S,[["__scopeId","data-v-ac41e552"]]);export{z as default};
1
+ import{_ as a,u as n,o as r,c as l,a as e,t as s,b as c,w as d,d as p,e as f,p as x,f as h}from"./entry.5e22743d.js";const m=t=>(x("data-v-ac41e552"),t=t(),h(),t),u={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},g=m(()=>e("div",{class:"fixed left-0 right-0 spotlight z-10"},null,-1)),_={class:"max-w-520px text-center z-20"},b=["textContent"],w=["textContent"],y={class:"w-full flex items-center justify-center"},S={__name:"error-404",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:404},statusMessage:{type:String,default:"Not Found"},description:{type:String,default:"Sorry, the page you are looking for could not be found."},backHome:{type:String,default:"Go back home"}},setup(t){const o=t;return n({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}a{color:inherit;text-decoration:inherit}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(k,v)=>{const i=f;return r(),l("div",u,[g,e("div",_,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,b),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,w),e("div",y,[c(i,{to:"/",class:"gradient-border text-md sm:text-xl py-2 px-4 sm:py-3 sm:px-6 cursor-pointer"},{default:d(()=>[p(s(t.backHome),1)]),_:1})])])])}}},z=a(S,[["__scopeId","data-v-ac41e552"]]);export{z as default};
@@ -1 +1 @@
1
- import{_ as i,u as a,o as r,c as n,a as e,t as s,p as l,f as d}from"./entry.48231557.js";const c=t=>(l("data-v-35000bea"),t=t(),d(),t),p={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},h=c(()=>e("div",{class:"fixed -bottom-1/2 left-0 right-0 h-1/2 spotlight"},null,-1)),f={class:"max-w-520px text-center"},g=["textContent"],m=["textContent"],b={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:500},statusMessage:{type:String,default:"Server error"},description:{type:String,default:"This page is temporarily unavailable."}},setup(t){const o=t;return a({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(x,u)=>(r(),n("div",p,[h,e("div",f,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,g),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,m)])]))}},w=i(b,[["__scopeId","data-v-35000bea"]]);export{w as default};
1
+ import{_ as i,u as a,o as r,c as n,a as e,t as s,p as l,f as d}from"./entry.5e22743d.js";const c=t=>(l("data-v-35000bea"),t=t(),d(),t),p={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},h=c(()=>e("div",{class:"fixed -bottom-1/2 left-0 right-0 h-1/2 spotlight"},null,-1)),f={class:"max-w-520px text-center"},g=["textContent"],m=["textContent"],b={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:500},statusMessage:{type:String,default:"Server error"},description:{type:String,default:"This page is temporarily unavailable."}},setup(t){const o=t;return a({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(x,u)=>(r(),n("div",p,[h,e("div",f,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,g),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,m)])]))}},w=i(b,[["__scopeId","data-v-35000bea"]]);export{w as default};
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html >
3
3
  <head><meta charset="utf-8">
4
- <meta name="viewport" content="width=device-width, initial-scale=1"><link rel="modulepreload" as="script" crossorigin href="/__nuxt-link-checker/_nuxt/entry.48231557.js"><link rel="preload" as="style" href="/__nuxt-link-checker/_nuxt/entry.5fe95c60.css"><link rel="prefetch" as="style" href="/__nuxt-link-checker/_nuxt/error-404.d7252154.css"><link rel="prefetch" as="script" crossorigin href="/__nuxt-link-checker/_nuxt/error-404.821eb1e0.js"><link rel="prefetch" as="style" href="/__nuxt-link-checker/_nuxt/error-500.06915589.css"><link rel="prefetch" as="script" crossorigin href="/__nuxt-link-checker/_nuxt/error-500.eb30c601.js"><link rel="stylesheet" href="/__nuxt-link-checker/_nuxt/entry.5fe95c60.css"><script>"use strict";(()=>{const a=window,e=document.documentElement,m=["dark","light"],c=window.localStorage.getItem("nuxt-color-mode")||"system";let n=c==="system"?f():c;const l=e.getAttribute("data-color-mode-forced");l&&(n=l),i(n),a["__NUXT_COLOR_MODE__"]={preference:c,value:n,getColorScheme:f,addColorScheme:i,removeColorScheme:d};function i(o){const t=""+o+"",s="";e.classList?e.classList.add(t):e.className+=" "+t,s&&e.setAttribute("data-"+s,o)}function d(o){const t=""+o+"",s="";e.classList?e.classList.remove(t):e.className=e.className.replace(new RegExp(t,"g"),""),s&&e.removeAttribute("data-"+s)}function r(o){return a.matchMedia("(prefers-color-scheme"+o+")")}function f(){if(a.matchMedia&&r("").media!=="not all"){for(const o of m)if(r(":"+o).matches)return o}return"light"}})();
4
+ <meta name="viewport" content="width=device-width, initial-scale=1"><link rel="modulepreload" as="script" crossorigin href="/__nuxt-link-checker/_nuxt/entry.5e22743d.js"><link rel="preload" as="style" href="/__nuxt-link-checker/_nuxt/entry.5606e9a3.css"><link rel="prefetch" as="style" href="/__nuxt-link-checker/_nuxt/error-404.d7252154.css"><link rel="prefetch" as="script" crossorigin href="/__nuxt-link-checker/_nuxt/error-404.609b100b.js"><link rel="prefetch" as="style" href="/__nuxt-link-checker/_nuxt/error-500.06915589.css"><link rel="prefetch" as="script" crossorigin href="/__nuxt-link-checker/_nuxt/error-500.5402d037.js"><link rel="stylesheet" href="/__nuxt-link-checker/_nuxt/entry.5606e9a3.css"><script>"use strict";(()=>{const a=window,e=document.documentElement,m=["dark","light"],c=window.localStorage.getItem("nuxt-color-mode")||"system";let n=c==="system"?f():c;const l=e.getAttribute("data-color-mode-forced");l&&(n=l),i(n),a["__NUXT_COLOR_MODE__"]={preference:c,value:n,getColorScheme:f,addColorScheme:i,removeColorScheme:d};function i(o){const t=""+o+"",s="";e.classList?e.classList.add(t):e.className+=" "+t,s&&e.setAttribute("data-"+s,o)}function d(o){const t=""+o+"",s="";e.classList?e.classList.remove(t):e.className=e.className.replace(new RegExp(t,"g"),""),s&&e.removeAttribute("data-"+s)}function r(o){return a.matchMedia("(prefers-color-scheme"+o+")")}function f(){if(a.matchMedia&&r("").media!=="not all"){for(const o of m)if(r(":"+o).matches)return o}return"light"}})();
5
5
  </script></head>
6
- <body ><div id="__nuxt"><svg class="nuxt-spa-loading" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 25" fill="none" width="80"><path d="M24.236 22.006h10.742L25.563 5.822l-8.979 14.31a4 4 0 0 1-3.388 1.874H2.978l11.631-20 5.897 10.567"></path></svg><style>.nuxt-spa-loading{position:fixed;top:50%;left:50%;transform:translate(-50%, -50%)}.nuxt-spa-loading>path{fill:none;stroke:#00DC82;stroke-width:4px;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:128;stroke-dashoffset:128;animation:nuxt-spa-loading-move 3s linear infinite}@keyframes nuxt-spa-loading-move{100%{stroke-dashoffset:-128}}</style></div><script type="application/json" id="__NUXT_DATA__" data-ssr="false">[{"_errors":1,"serverRendered":2,"data":3,"state":4},{},false,{},{}]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-link-checker",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="module" src="/__nuxt-link-checker/_nuxt/entry.48231557.js" crossorigin></script></body>
6
+ <body ><div id="__nuxt"><svg class="nuxt-spa-loading" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 25" fill="none" width="80"><path d="M24.236 22.006h10.742L25.563 5.822l-8.979 14.31a4 4 0 0 1-3.388 1.874H2.978l11.631-20 5.897 10.567"></path></svg><style>.nuxt-spa-loading{position:fixed;top:50%;left:50%;transform:translate(-50%, -50%)}.nuxt-spa-loading>path{fill:none;stroke:#00DC82;stroke-width:4px;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:128;stroke-dashoffset:128;animation:nuxt-spa-loading-move 3s linear infinite}@keyframes nuxt-spa-loading-move{100%{stroke-dashoffset:-128}}</style></div><script type="application/json" id="__NUXT_DATA__" data-ssr="false">[{"_errors":1,"serverRendered":2,"data":3,"state":4},{},false,{},{}]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-link-checker",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="module" src="/__nuxt-link-checker/_nuxt/entry.5e22743d.js" crossorigin></script></body>
7
7
  </html>
package/dist/module.d.ts CHANGED
@@ -78,6 +78,12 @@ interface ModuleOptions {
78
78
  * Whether to run the module on `nuxt build` or `nuxt generate`.
79
79
  */
80
80
  runOnBuild: boolean;
81
+ /**
82
+ * Should remote URLs be fetched.
83
+ *
84
+ * @default true (disabled in stackblitz)
85
+ */
86
+ fetchRemoteUrls: boolean;
81
87
  /**
82
88
  * Whether the module is enabled.
83
89
  *
package/dist/module.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "bridge": false
6
6
  },
7
7
  "configKey": "linkChecker",
8
- "version": "2.1.1"
8
+ "version": "2.1.2"
9
9
  }
package/dist/module.mjs CHANGED
@@ -1,13 +1,15 @@
1
1
  import { useNuxt, extendPages, defineNuxtModule, useLogger, createResolver, addPlugin, addServerHandler, addServerPlugin, hasNuxtModule } from '@nuxt/kit';
2
2
  import { useSiteConfig, installNuxtSiteConfig, updateSiteConfig } from 'nuxt-site-config-kit';
3
+ import { provider } from 'std-env';
3
4
  import fs, { readFile, writeFile } from 'node:fs/promises';
4
5
  import chalk from 'chalk';
5
6
  import Fuse from 'fuse.js';
6
- import { resolve } from 'pathe';
7
+ import { resolve, relative } from 'pathe';
7
8
  import { load } from 'cheerio';
8
9
  import { parseURL, joinURL } from 'ufo';
9
10
  import { fixSlashes } from 'site-config-stack';
10
11
  import { toRouteMatcher, createRouter } from 'radix3';
12
+ import { $fetch } from 'ofetch';
11
13
  import { existsSync } from 'node:fs';
12
14
  import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
13
15
  import { diffLines, diffArrays } from 'diff';
@@ -238,17 +240,21 @@ function createFilter(options = {}) {
238
240
  };
239
241
  }
240
242
  async function crawlFetch(link, options = {}) {
241
- const fetch = options.fetch || globalThis.fetch;
243
+ const $ = options.fetch || $fetch;
242
244
  const timeout = options.timeout || 5e3;
243
245
  const timeoutController = new AbortController();
244
246
  const abortRequestTimeout = setTimeout(() => timeoutController.abort(), timeout);
245
- return await fetch(link, {
247
+ return await $(link, {
246
248
  method: "HEAD",
247
249
  signal: timeoutController.signal,
248
250
  headers: {
249
251
  "user-agent": "Nuxt Link Checker"
250
252
  }
251
- }).catch(() => ({ status: 404, statusText: "Not Found", headers: {} })).finally(() => clearTimeout(abortRequestTimeout));
253
+ }).catch((error) => {
254
+ if (error.name === "AbortError")
255
+ return { status: 408, statusText: "Request Timeout", headers: {} };
256
+ return { status: 404, statusText: "Not Found", headers: {} };
257
+ }).finally(() => clearTimeout(abortRequestTimeout));
252
258
  }
253
259
 
254
260
  const responses = {};
@@ -259,10 +265,14 @@ function extractPayload(html) {
259
265
  const links = $("#__nuxt a[href]").map((i, el) => $(el).attr("href")).get();
260
266
  return { ids, links };
261
267
  }
262
- async function getLinkResponse(link, timeout) {
268
+ async function getLinkResponse(link, timeout, fetchRemoteUrls) {
263
269
  const response = responses[link];
264
270
  if (!response) {
265
- responses[link] = crawlFetch(link, { timeout });
271
+ if (isInvalidLinkProtocol(link) || link.startsWith("#") || link.startsWith("http") && fetchRemoteUrls) {
272
+ responses[link] = Promise.resolve({ status: 200, statusText: "OK", headers: {} });
273
+ } else {
274
+ responses[link] = crawlFetch(link, { timeout });
275
+ }
266
276
  }
267
277
  return responses[link];
268
278
  }
@@ -276,7 +286,7 @@ function prerender(config, nuxt = useNuxt()) {
276
286
  if (ctx.contents && !ctx.error && ctx.fileName?.endsWith(".html") && !ctx.route.endsWith(".html") && urlFilter(ctx.route)) {
277
287
  linkMap[ctx.route] = extractPayload(ctx.contents);
278
288
  linkMap[ctx.route].links.forEach((link) => {
279
- getLinkResponse(link, config.fetchTimeout);
289
+ getLinkResponse(link, config.fetchTimeout, config.fetchRemoteUrls);
280
290
  });
281
291
  }
282
292
  responses[ctx.route] = Promise.resolve({ status: Number(ctx.error?.statusCode) || 200, statusText: ctx.error?.statusMessage || "" });
@@ -294,7 +304,7 @@ function prerender(config, nuxt = useNuxt()) {
294
304
  let errorCount = 0;
295
305
  await Promise.all(payloads.map(async ([route, payload]) => {
296
306
  const reports = await Promise.all(payload.links.map(async (link) => {
297
- const response = await getLinkResponse(link);
307
+ const response = await getLinkResponse(link, config.fetchTimeout, config.fetchRemoteUrls);
298
308
  return inspect({
299
309
  ids: linkMap[route].ids,
300
310
  fromPath: route,
@@ -368,7 +378,7 @@ function prerender(config, nuxt = useNuxt()) {
368
378
  </html>
369
379
  `;
370
380
  await fs.writeFile(resolve(nitro.options.output.dir, "link-checker-report.html"), html);
371
- nitro.logger.info(`Nuxt Link Checker Report written to ${resolve(nitro.options.output.dir, "link-checker-report.html")}`);
381
+ nitro.logger.info(`Nuxt Link Checker HTML report written to ${relative(nuxt.options.rootDir, resolve(nitro.options.output.dir, "link-checker-report.html"))}`);
372
382
  }
373
383
  }
374
384
  if (config.report?.markdown) {
@@ -376,27 +386,26 @@ function prerender(config, nuxt = useNuxt()) {
376
386
  const reportMarkdown = reports.map((r) => {
377
387
  const errors2 = r.error?.map((error) => {
378
388
  return `| ${r.link} | ${error.message} (${error.name}) |`;
379
- }).join("");
389
+ });
380
390
  const warnings2 = r.warning?.map((warning) => {
381
391
  return `| ${r.link} | ${warning.message} (${warning.name}) |`;
382
- }).join("");
383
- return `${errors2}${warnings2}`;
384
- }).join("");
392
+ });
393
+ return [errors2, warnings2];
394
+ }).flat().filter(Boolean).join("\n");
385
395
  const markdown = [
386
396
  "# Link Checker Report",
387
397
  "",
388
398
  "| Link | Message |",
389
- "| --- | --- |",
390
- reportMarkdown
391
- ].join("\n");
399
+ "| --- | --- |"
400
+ ].join("\n") + reportMarkdown;
392
401
  await fs.writeFile(resolve(nitro.options.output.dir, "link-checker-report.md"), markdown);
393
- nitro.logger.info(`Nuxt Link Checker Report written to ${resolve(nitro.options.output.dir, "link-checker-report.md")}`);
402
+ nitro.logger.info(`Nuxt Link Checker Markdown report written to ${relative(nuxt.options.rootDir, resolve(nitro.options.output.dir, "link-checker-report.md"))}`);
394
403
  }
395
404
  }
396
405
  }));
397
406
  if (errorCount > 0 && config.failOnError) {
398
407
  nitro.logger.error(`Nuxt Link Checker found ${errorCount} errors, failing build.`);
399
- nitro.logger.log(chalk.gray('You can disable this by setting "linkChecker: { failOn404: false }" in your nuxt.config.ts.'));
408
+ nitro.logger.log(chalk.gray('You can disable this by setting "linkChecker: { failOnError: false }" in your nuxt.config.'));
400
409
  process.exit(1);
401
410
  }
402
411
  });
@@ -568,6 +577,7 @@ const module = defineNuxtModule({
568
577
  configKey: "linkChecker"
569
578
  },
570
579
  defaults: {
580
+ fetchRemoteUrls: provider !== "stackblitz",
571
581
  runOnBuild: true,
572
582
  debug: false,
573
583
  showLiveInspections: true,
@@ -593,6 +603,11 @@ const module = defineNuxtModule({
593
603
  host: config.host
594
604
  });
595
605
  }
606
+ if (config.fetchRemoteUrls) {
607
+ config.fetchRemoteUrls = (await crawlFetch("https://google.com")).status === 200;
608
+ if (!config.fetchRemoteUrls)
609
+ logger.warn("Remote URL fetching is disabled because https://google.com could not be fetched.");
610
+ }
596
611
  const isDevToolsEnabled = typeof nuxt.options.devtools === "boolean" ? nuxt.options.devtools : nuxt.options.devtools.enabled;
597
612
  if (nuxt.options.dev && isDevToolsEnabled) {
598
613
  addPlugin({
@@ -624,7 +639,8 @@ const module = defineNuxtModule({
624
639
  excludeLinks: config.excludeLinks,
625
640
  skipInspections: config.skipInspections,
626
641
  fetchTimeout: config.fetchTimeout,
627
- showLiveInspections: config.showLiveInspections
642
+ showLiveInspections: config.showLiveInspections,
643
+ fetchRemoteUrls: config.fetchRemoteUrls
628
644
  };
629
645
  setupDevToolsUI(config, resolve);
630
646
  }
@@ -1,4 +1,5 @@
1
1
  import { computed, createApp, h, ref, shallowReactive, unref } from "vue";
2
+ import { useLocalStorage } from "@vueuse/core";
2
3
  import { createFilter } from "../../sharedUtils.mjs";
3
4
  import Main from "./Main.vue";
4
5
  import { linkDb } from "./state.mjs";
@@ -28,7 +29,7 @@ export async function setupLinkCheckerClient({ nuxt }) {
28
29
  const route = useRoute();
29
30
  let startQueueIdleId;
30
31
  let startQueueTimeoutId;
31
- const showInspections = ref(false);
32
+ const showInspections = useLocalStorage("nuxt-link-checker:show-inspections", true);
32
33
  const runtimeConfig = useRuntimeConfig().public["nuxt-link-checker"];
33
34
  const filter = createFilter({
34
35
  exclude: runtimeConfig.excludeLinks
@@ -130,7 +131,7 @@ export async function setupLinkCheckerClient({ nuxt }) {
130
131
  devtoolsButton.dispatchEvent(new MouseEvent("click", { bubbles: true }));
131
132
  const interval = setInterval(() => {
132
133
  devtoolsClient = resolveDevtoolsIframe();
133
- if (devtoolsClient && devtoolsClient.host.getIframe()) {
134
+ if (devtoolsClient && devtoolsClient.host?.getIframe()) {
134
135
  devtoolsClient.host.getIframe().src = `/__nuxt_devtools__/client/modules/custom-nuxt-link-checker?link=${encodeURIComponent(link)}`;
135
136
  isOpeningDevtools = false;
136
137
  clearInterval(interval);
@@ -138,7 +139,11 @@ export async function setupLinkCheckerClient({ nuxt }) {
138
139
  }, 250);
139
140
  }
140
141
  } else {
141
- devtoolsClient.host.open();
142
+ if (typeof devtoolsClient.host.open === "function") {
143
+ devtoolsClient.host.open();
144
+ } else {
145
+ devtoolsClient.host.devtools.open();
146
+ }
142
147
  const srcPath = new URL(devtoolsClient.host.getIframe().src).pathname;
143
148
  if (!srcPath.startsWith("/__nuxt_devtools__/client/modules/custom-nuxt-link-checker")) {
144
149
  devtoolsClient.host.getIframe().src = `/__nuxt_devtools__/client/modules/custom-nuxt-link-checker?link=${encodeURIComponent(link)}`;
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<Partial<import("../../types").LinkInspectionResult>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Partial<import("../../types").LinkInspectionResult>>>;
2
2
  export default _default;
@@ -19,7 +19,7 @@ export default defineEventHandler(async (e) => {
19
19
  };
20
20
  const runtimeConfig = useRuntimeConfig().public["nuxt-link-checker"];
21
21
  let response;
22
- if (isInvalidLinkProtocol(link) || link.startsWith("#")) {
22
+ if (isInvalidLinkProtocol(link) || link.startsWith("#") || link.startsWith("http") && runtimeConfig.fetchRemoteUrls) {
23
23
  response = { status: 200, statusText: "OK", headers: {} };
24
24
  } else {
25
25
  response = await crawlFetch(link, {
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<any[]>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any[]>>;
2
2
  export default _default;
@@ -6,8 +6,4 @@ export declare function createFilter(options?: CreateFilterOptions): (path: stri
6
6
  export declare function crawlFetch(link: string, options?: {
7
7
  fetch?: typeof globalThis.fetch;
8
8
  timeout?: number;
9
- }): Promise<Response | {
10
- status: number;
11
- statusText: string;
12
- headers: {};
13
- }>;
9
+ }): Promise<any>;
@@ -1,4 +1,5 @@
1
1
  import { createRouter, toRouteMatcher } from "radix3";
2
+ import { $fetch } from "ofetch";
2
3
  export function createFilter(options = {}) {
3
4
  const include = options.include || [];
4
5
  const exclude = options.exclude || [];
@@ -26,15 +27,19 @@ export function createFilter(options = {}) {
26
27
  };
27
28
  }
28
29
  export async function crawlFetch(link, options = {}) {
29
- const fetch = options.fetch || globalThis.fetch;
30
+ const $ = options.fetch || $fetch;
30
31
  const timeout = options.timeout || 5e3;
31
32
  const timeoutController = new AbortController();
32
33
  const abortRequestTimeout = setTimeout(() => timeoutController.abort(), timeout);
33
- return await fetch(link, {
34
+ return await $(link, {
34
35
  method: "HEAD",
35
36
  signal: timeoutController.signal,
36
37
  headers: {
37
38
  "user-agent": "Nuxt Link Checker"
38
39
  }
39
- }).catch(() => ({ status: 404, statusText: "Not Found", headers: {} })).finally(() => clearTimeout(abortRequestTimeout));
40
+ }).catch((error) => {
41
+ if (error.name === "AbortError")
42
+ return { status: 408, statusText: "Request Timeout", headers: {} };
43
+ return { status: 404, statusText: "Not Found", headers: {} };
44
+ }).finally(() => clearTimeout(abortRequestTimeout));
40
45
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-link-checker",
3
3
  "type": "module",
4
- "version": "2.1.1",
4
+ "version": "2.1.2",
5
5
  "packageManager": "pnpm@8.6.12",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/harlan-zw",
@@ -22,7 +22,8 @@
22
22
  },
23
23
  "build": {
24
24
  "externals": [
25
- "ofetch"
25
+ "ofetch",
26
+ "std-env"
26
27
  ]
27
28
  },
28
29
  "main": "./dist/module.cjs",
@@ -31,35 +32,35 @@
31
32
  "dist"
32
33
  ],
33
34
  "dependencies": {
34
- "@nuxt/devtools-kit": "^0.7.5",
35
- "@nuxt/devtools-ui-kit": "^0.7.5",
35
+ "@nuxt/devtools-kit": "^0.8.0",
36
+ "@nuxt/devtools-ui-kit": "^0.8.0",
36
37
  "@nuxt/kit": "^3.6.5",
37
38
  "@vueuse/core": "^10.3.0",
38
39
  "chalk": "^5.3.0",
39
40
  "cheerio": "1.0.0-rc.12",
40
41
  "diff": "^5.1.0",
41
42
  "fuse.js": "^6.6.2",
42
- "magic-string": "^0.30.2",
43
+ "magic-string": "^0.30.3",
43
44
  "nuxt-site-config": "1.0.10",
44
45
  "nuxt-site-config-kit": "1.0.10",
45
46
  "pathe": "^1.1.1",
46
- "radix3": "^1.0.1",
47
+ "radix3": "^1.1.0",
47
48
  "shiki-es": "^0.14.0",
48
49
  "sirv": "^2.0.3",
49
50
  "site-config-stack": "^1.0.10",
50
51
  "ufo": "^1.2.0"
51
52
  },
52
53
  "devDependencies": {
53
- "@antfu/eslint-config": "^0.40.2",
54
+ "@antfu/eslint-config": "^0.41.0",
54
55
  "@nuxt/module-builder": "^0.4.0",
55
56
  "@nuxt/test-utils": "^3.6.5",
56
57
  "@nuxtjs/eslint-config-typescript": "^12.0.0",
57
58
  "@types/diff": "^5.0.3",
58
- "bumpp": "^9.1.1",
59
- "eslint": "8.46.0",
60
- "execa": "^7.2.0",
59
+ "bumpp": "^9.2.0",
60
+ "eslint": "8.47.0",
61
+ "execa": "^8.0.1",
61
62
  "nuxt": "^3.6.5",
62
- "vitest": "^0.34.1"
63
+ "vitest": "^0.34.2"
63
64
  },
64
65
  "scripts": {
65
66
  "build": "npm run prepack",