@majordigital/create-acorn 1.6.5 → 1.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -116,23 +116,35 @@ async function setupPreDeploy() {
116
116
  cpSync(scriptsSrc, scriptsDest, { recursive: true, force: true });
117
117
  console.log('Pre-deploy scripts copied to scripts/.');
118
118
 
119
- console.log('Installing pre-deploy tools (Lighthouse, Playwright, linkinator)...');
120
- await runCommand('npm', ['install', '--save-dev', '--legacy-peer-deps',
121
- 'lighthouse', 'chrome-launcher', 'playwright', 'linkinator'
122
- ]);
123
- console.log('');
124
-
119
+ // Write devDependencies and scripts into package.json explicitly, rather than
120
+ // relying on `npm install --save-dev` to persist them. When the packages are
121
+ // already present in node_modules, npm reports "up to date" and may skip
122
+ // updating package.json — leaving the deps unrecorded.
123
+ const preDeployDeps = {
124
+ lighthouse: '^13',
125
+ 'chrome-launcher': '^1',
126
+ playwright: '^1',
127
+ linkinator: '^7',
128
+ };
125
129
  try {
130
+ if (!pkg.devDependencies) pkg.devDependencies = {};
131
+ for (const [name, range] of Object.entries(preDeployDeps)) {
132
+ if (!pkg.devDependencies[name]) pkg.devDependencies[name] = range;
133
+ }
126
134
  if (!pkg.scripts) pkg.scripts = {};
127
135
  pkg.scripts['lighthouse'] = 'node scripts/lighthouse-audit.mjs';
128
136
  pkg.scripts['check-links'] = 'node scripts/check-links.mjs';
129
137
  pkg.scripts['cross-browser'] = 'node scripts/cross-browser-check.mjs';
130
138
  writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
131
- console.log('Added pre-deploy scripts to package.json.');
139
+ console.log('Added pre-deploy scripts and dependencies to package.json.');
132
140
  } catch {
133
- console.log('Warning: Could not add pre-deploy scripts to package.json.');
141
+ console.log('Warning: Could not update package.json with pre-deploy setup.');
134
142
  }
135
143
  console.log('');
144
+
145
+ console.log('Installing pre-deploy tools (Lighthouse, Playwright, linkinator)...');
146
+ await runCommand('npm', ['install', '--legacy-peer-deps']);
147
+ console.log('');
136
148
  }
137
149
 
138
150
  async function setupNextApp() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@majordigital/create-acorn",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "Interactive starter CLI for Acorn with Storyblok/Prismic/DatoCMS, TypeScript, and Tailwind.",
5
5
  "bin": {
6
6
  "create-acorn": "bin/create-acorn.mjs",
@@ -28,30 +28,21 @@ const concurrency = isLocalhost ? 2 : 5;
28
28
 
29
29
  // Support Netlify Basic Auth (password-protected preview deploys).
30
30
  // Set NETLIFY_AUTH=user:password in your environment before running.
31
+ // NOTE: requires linkinator >=7 (the `headers` option is applied to every
32
+ // fetch). Credentials are sent via an Authorization header, not embedded in
33
+ // the URL — Node's fetch (undici) rejects URLs that contain credentials.
31
34
  const netlifyAuth = process.env.NETLIFY_AUTH;
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
+ const headers = netlifyAuth
36
+ ? { Authorization: `Basic ${Buffer.from(netlifyAuth).toString("base64")}` }
37
+ : {};
46
38
 
47
39
  const checker = new LinkChecker();
48
40
 
49
41
  checker.on("link", (result) => {
50
42
  if (result.state === "BROKEN") {
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
+ console.log(
44
+ ` BROKEN [${result.status}] ${result.url} (from ${result.parent})`,
45
+ );
55
46
  }
56
47
  });
57
48
 
@@ -61,14 +52,14 @@ console.log(`Concurrency: ${concurrency}`);
61
52
  console.log("This may take several minutes...\n");
62
53
 
63
54
  const result = await checker.check({
64
- path: crawlUrl,
55
+ path: baseUrl,
65
56
  recurse: true,
66
57
  concurrency,
67
58
  timeout: 30000,
68
- urlRewriteExpressions,
59
+ headers,
69
60
  linksToSkip: [
70
- // Skip URLs that don't contain our hostname (handles both clean and credential-embedded forms)
71
- `^(?!https?://[^/@]*@?${escapedHostname})`,
61
+ // Skip external links to avoid false positives from rate limiting
62
+ `^(?!(${baseUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}))`,
72
63
  // Skip multi-filter URLs — the filter UI generates combinatorial links
73
64
  // (e.g. ?filter=a%2Cb%2Cc) that cause a crawl explosion. Not real links.
74
65
  ".*[?&]filter=.*%2C.*",
@@ -95,8 +86,6 @@ console.log(`OK: ${ok.length}`);
95
86
  console.log(`Broken: ${broken.length}`);
96
87
  console.log(`Skipped: ${skipped.length}`);
97
88
 
98
- const stripAuth = (u) => u?.replace(/\/\/[^@]+@/, "//") ?? u;
99
-
100
89
  let md = `## Broken Link Check\n\n`;
101
90
  md += `Crawled: ${baseUrl}\n\n`;
102
91
  md += `| Metric | Count |\n|--------|-------|\n`;
@@ -108,11 +97,11 @@ md += `| Skipped | ${skipped.length} |\n\n`;
108
97
  if (broken.length > 0) {
109
98
  const grouped = new Map();
110
99
  for (const link of broken) {
111
- const key = stripAuth(link.url);
100
+ const key = link.url;
112
101
  if (!grouped.has(key)) {
113
- grouped.set(key, { status: link.status, url: key, foundOn: new Set() });
102
+ grouped.set(key, { status: link.status, url: link.url, foundOn: new Set() });
114
103
  }
115
- grouped.get(key).foundOn.add(stripAuth(link.parent));
104
+ grouped.get(key).foundOn.add(link.parent);
116
105
  }
117
106
 
118
107
  md += `### Broken Links (${grouped.size} unique)\n\n`;