@majordigital/create-acorn 1.6.3 → 1.6.5
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/package.json +1 -1
- package/template/scripts/check-links.mjs +27 -13
package/package.json
CHANGED
|
@@ -29,17 +29,29 @@ const concurrency = isLocalhost ? 2 : 5;
|
|
|
29
29
|
// Support Netlify Basic Auth (password-protected preview deploys).
|
|
30
30
|
// Set NETLIFY_AUTH=user:password in your environment before running.
|
|
31
31
|
const netlifyAuth = process.env.NETLIFY_AUTH;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
|
|
33
|
+
// Extract hostname so we can match internal URLs with or without credentials.
|
|
34
|
+
const { protocol, hostname } = new URL(baseUrl);
|
|
35
|
+
const escapedHostname = hostname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36
|
+
|
|
37
|
+
// Rewrite every internal link to include credentials so all requests authenticate.
|
|
38
|
+
// Without this, links found in HTML use clean URLs and get 401.
|
|
39
|
+
const urlRewriteExpressions = netlifyAuth
|
|
40
|
+
? [{ pattern: new RegExp(`^${protocol}//${escapedHostname}`), replacement: `${protocol}//${netlifyAuth}@${hostname}` }]
|
|
41
|
+
: [];
|
|
42
|
+
|
|
43
|
+
const crawlUrl = netlifyAuth
|
|
44
|
+
? baseUrl.replace(/^(https?:\/\/)/, `$1${netlifyAuth}@`)
|
|
45
|
+
: baseUrl;
|
|
35
46
|
|
|
36
47
|
const checker = new LinkChecker();
|
|
37
48
|
|
|
38
49
|
checker.on("link", (result) => {
|
|
39
50
|
if (result.state === "BROKEN") {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
);
|
|
51
|
+
// Strip credentials from URLs before printing
|
|
52
|
+
const url = result.url.replace(/\/\/[^@]+@/, "//");
|
|
53
|
+
const parent = result.parent?.replace(/\/\/[^@]+@/, "//");
|
|
54
|
+
console.log(` BROKEN [${result.status}] ${url} (from ${parent})`);
|
|
43
55
|
}
|
|
44
56
|
});
|
|
45
57
|
|
|
@@ -49,14 +61,14 @@ console.log(`Concurrency: ${concurrency}`);
|
|
|
49
61
|
console.log("This may take several minutes...\n");
|
|
50
62
|
|
|
51
63
|
const result = await checker.check({
|
|
52
|
-
path:
|
|
64
|
+
path: crawlUrl,
|
|
53
65
|
recurse: true,
|
|
54
66
|
concurrency,
|
|
55
67
|
timeout: 30000,
|
|
56
|
-
|
|
68
|
+
urlRewriteExpressions,
|
|
57
69
|
linksToSkip: [
|
|
58
|
-
// Skip
|
|
59
|
-
`^(?!
|
|
70
|
+
// Skip URLs that don't contain our hostname (handles both clean and credential-embedded forms)
|
|
71
|
+
`^(?!https?://[^/@]*@?${escapedHostname})`,
|
|
60
72
|
// Skip multi-filter URLs — the filter UI generates combinatorial links
|
|
61
73
|
// (e.g. ?filter=a%2Cb%2Cc) that cause a crawl explosion. Not real links.
|
|
62
74
|
".*[?&]filter=.*%2C.*",
|
|
@@ -83,6 +95,8 @@ console.log(`OK: ${ok.length}`);
|
|
|
83
95
|
console.log(`Broken: ${broken.length}`);
|
|
84
96
|
console.log(`Skipped: ${skipped.length}`);
|
|
85
97
|
|
|
98
|
+
const stripAuth = (u) => u?.replace(/\/\/[^@]+@/, "//") ?? u;
|
|
99
|
+
|
|
86
100
|
let md = `## Broken Link Check\n\n`;
|
|
87
101
|
md += `Crawled: ${baseUrl}\n\n`;
|
|
88
102
|
md += `| Metric | Count |\n|--------|-------|\n`;
|
|
@@ -94,11 +108,11 @@ md += `| Skipped | ${skipped.length} |\n\n`;
|
|
|
94
108
|
if (broken.length > 0) {
|
|
95
109
|
const grouped = new Map();
|
|
96
110
|
for (const link of broken) {
|
|
97
|
-
const key = link.url;
|
|
111
|
+
const key = stripAuth(link.url);
|
|
98
112
|
if (!grouped.has(key)) {
|
|
99
|
-
grouped.set(key, { status: link.status, url:
|
|
113
|
+
grouped.set(key, { status: link.status, url: key, foundOn: new Set() });
|
|
100
114
|
}
|
|
101
|
-
grouped.get(key).foundOn.add(link.parent);
|
|
115
|
+
grouped.get(key).foundOn.add(stripAuth(link.parent));
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
md += `### Broken Links (${grouped.size} unique)\n\n`;
|