@nuasite/cli 0.50.1 → 0.51.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/README.md +22 -1
- package/dist/index.js +1857 -1609
- package/dist/types/check.d.ts +9 -0
- package/dist/types/check.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/check.ts +30 -0
- package/src/index.ts +12 -0
- package/src/tsconfig.json +1 -1
package/dist/index.js
CHANGED
|
@@ -47,594 +47,6 @@ var __export = (target, all) => {
|
|
|
47
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
48
|
var __require = import.meta.require;
|
|
49
49
|
|
|
50
|
-
// src/utils.ts
|
|
51
|
-
import { existsSync } from "fs";
|
|
52
|
-
import { join } from "path";
|
|
53
|
-
function findAstroConfig(cwd = process.cwd()) {
|
|
54
|
-
for (const name of CONFIG_NAMES) {
|
|
55
|
-
const p = join(cwd, name);
|
|
56
|
-
if (existsSync(p))
|
|
57
|
-
return p;
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
function findMatchingClose(text, start) {
|
|
62
|
-
const open = text[start];
|
|
63
|
-
const close = open === "{" ? "}" : open === "[" ? "]" : ")";
|
|
64
|
-
let depth = 0;
|
|
65
|
-
let inString = false;
|
|
66
|
-
for (let i = start;i < text.length; i++) {
|
|
67
|
-
const ch = text[i];
|
|
68
|
-
if (inString) {
|
|
69
|
-
if (ch === "\\") {
|
|
70
|
-
i++;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
if (ch === inString)
|
|
74
|
-
inString = false;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (ch === "'" || ch === '"' || ch === "`") {
|
|
78
|
-
inString = ch;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (ch === open)
|
|
82
|
-
depth++;
|
|
83
|
-
if (ch === close) {
|
|
84
|
-
depth--;
|
|
85
|
-
if (depth === 0)
|
|
86
|
-
return i;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return -1;
|
|
90
|
-
}
|
|
91
|
-
function extractConfigBody(content) {
|
|
92
|
-
const match = content.match(/defineConfig\s*\(\s*\{/);
|
|
93
|
-
if (!match || match.index === undefined)
|
|
94
|
-
return "";
|
|
95
|
-
const openBrace = content.indexOf("{", match.index + "defineConfig".length);
|
|
96
|
-
const closeBrace = findMatchingClose(content, openBrace);
|
|
97
|
-
if (closeBrace === -1)
|
|
98
|
-
return "";
|
|
99
|
-
return content.slice(openBrace + 1, closeBrace);
|
|
100
|
-
}
|
|
101
|
-
function removeProperty(body, propName) {
|
|
102
|
-
const regex = new RegExp(`(\\n[ \\t]*)${propName}\\s*:\\s*`);
|
|
103
|
-
const match = regex.exec(body);
|
|
104
|
-
if (!match || match.index === undefined)
|
|
105
|
-
return body;
|
|
106
|
-
const propLineStart = match.index + 1;
|
|
107
|
-
const afterMatch = match.index + match[0].length;
|
|
108
|
-
let i = afterMatch;
|
|
109
|
-
while (i < body.length && (body[i] === " " || body[i] === "\t"))
|
|
110
|
-
i++;
|
|
111
|
-
let valueEnd;
|
|
112
|
-
if (body[i] === "{" || body[i] === "[") {
|
|
113
|
-
valueEnd = findMatchingClose(body, i);
|
|
114
|
-
if (valueEnd === -1)
|
|
115
|
-
return body;
|
|
116
|
-
valueEnd++;
|
|
117
|
-
} else {
|
|
118
|
-
while (i < body.length && body[i] !== "," && body[i] !== `
|
|
119
|
-
`)
|
|
120
|
-
i++;
|
|
121
|
-
valueEnd = i;
|
|
122
|
-
}
|
|
123
|
-
let end = valueEnd;
|
|
124
|
-
while (end < body.length && (body[end] === " " || body[end] === "\t"))
|
|
125
|
-
end++;
|
|
126
|
-
if (end < body.length && body[end] === ",")
|
|
127
|
-
end++;
|
|
128
|
-
while (end < body.length && (body[end] === " " || body[end] === "\t"))
|
|
129
|
-
end++;
|
|
130
|
-
if (end < body.length && body[end] === `
|
|
131
|
-
`)
|
|
132
|
-
end++;
|
|
133
|
-
return body.slice(0, propLineStart) + body.slice(end);
|
|
134
|
-
}
|
|
135
|
-
function assembleConfig(imports, body) {
|
|
136
|
-
const bodyLines = body.split(`
|
|
137
|
-
`).filter((line) => line.trim() !== "");
|
|
138
|
-
let result = imports.join(`
|
|
139
|
-
`) + `
|
|
140
|
-
|
|
141
|
-
`;
|
|
142
|
-
result += `export default defineConfig({
|
|
143
|
-
`;
|
|
144
|
-
if (bodyLines.length > 0) {
|
|
145
|
-
result += bodyLines.join(`
|
|
146
|
-
`) + `
|
|
147
|
-
`;
|
|
148
|
-
}
|
|
149
|
-
result += `})
|
|
150
|
-
`;
|
|
151
|
-
return result;
|
|
152
|
-
}
|
|
153
|
-
var CONFIG_NAMES;
|
|
154
|
-
var init_utils = __esm(() => {
|
|
155
|
-
CONFIG_NAMES = [
|
|
156
|
-
"astro.config.ts",
|
|
157
|
-
"astro.config.mts",
|
|
158
|
-
"astro.config.mjs",
|
|
159
|
-
"astro.config.js"
|
|
160
|
-
];
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// src/init.ts
|
|
164
|
-
var exports_init = {};
|
|
165
|
-
__export(exports_init, {
|
|
166
|
-
transformPackageJson: () => transformPackageJson,
|
|
167
|
-
transformConfig: () => transformConfig,
|
|
168
|
-
removeCallFromArray: () => removeCallFromArray,
|
|
169
|
-
init: () => init,
|
|
170
|
-
detectNuaManagedImports: () => detectNuaManagedImports,
|
|
171
|
-
cleanEmptyStructures: () => cleanEmptyStructures
|
|
172
|
-
});
|
|
173
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
|
|
174
|
-
import { basename, join as join2 } from "path";
|
|
175
|
-
function detectNuaManagedImports(content) {
|
|
176
|
-
const managed = new Map;
|
|
177
|
-
for (const pkg of NUA_MANAGED_PACKAGES) {
|
|
178
|
-
const regex = new RegExp(`import\\s+(\\w+)\\s+from\\s+['"]${pkg.replace("/", "\\/")}['"]`);
|
|
179
|
-
const match = regex.exec(content);
|
|
180
|
-
if (match) {
|
|
181
|
-
managed.set(pkg, match[1]);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return managed;
|
|
185
|
-
}
|
|
186
|
-
function removeCallFromArray(body, arrayProp, callName) {
|
|
187
|
-
const propRegex = new RegExp(`\\b${arrayProp}\\s*:\\s*\\[`);
|
|
188
|
-
const propMatch = propRegex.exec(body);
|
|
189
|
-
if (!propMatch || propMatch.index === undefined)
|
|
190
|
-
return body;
|
|
191
|
-
const arrayStart = body.indexOf("[", propMatch.index);
|
|
192
|
-
const arrayEnd = findMatchingClose(body, arrayStart);
|
|
193
|
-
if (arrayEnd === -1)
|
|
194
|
-
return body;
|
|
195
|
-
const arrayContent = body.slice(arrayStart + 1, arrayEnd);
|
|
196
|
-
const callRegex = new RegExp(`\\b${callName}\\s*\\(`);
|
|
197
|
-
const callMatch = callRegex.exec(arrayContent);
|
|
198
|
-
if (!callMatch || callMatch.index === undefined)
|
|
199
|
-
return body;
|
|
200
|
-
const parenStart = arrayContent.indexOf("(", callMatch.index);
|
|
201
|
-
const parenEnd = findMatchingClose(arrayContent, parenStart);
|
|
202
|
-
if (parenEnd === -1)
|
|
203
|
-
return body;
|
|
204
|
-
let removeStart = callMatch.index;
|
|
205
|
-
let removeEnd = parenEnd + 1;
|
|
206
|
-
let after = removeEnd;
|
|
207
|
-
while (after < arrayContent.length && (arrayContent[after] === " " || arrayContent[after] === "\t" || arrayContent[after] === `
|
|
208
|
-
`)) {
|
|
209
|
-
after++;
|
|
210
|
-
}
|
|
211
|
-
if (after < arrayContent.length && arrayContent[after] === ",") {
|
|
212
|
-
removeEnd = after + 1;
|
|
213
|
-
while (removeEnd < arrayContent.length && (arrayContent[removeEnd] === " " || arrayContent[removeEnd] === "\t" || arrayContent[removeEnd] === `
|
|
214
|
-
`))
|
|
215
|
-
removeEnd++;
|
|
216
|
-
}
|
|
217
|
-
if (removeEnd === parenEnd + 1) {
|
|
218
|
-
let before = removeStart - 1;
|
|
219
|
-
while (before >= 0 && (arrayContent[before] === " " || arrayContent[before] === "\t" || arrayContent[before] === `
|
|
220
|
-
`))
|
|
221
|
-
before--;
|
|
222
|
-
if (before >= 0 && arrayContent[before] === ",") {
|
|
223
|
-
removeStart = before;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
const newArrayContent = arrayContent.slice(0, removeStart) + arrayContent.slice(removeEnd);
|
|
227
|
-
return body.slice(0, arrayStart + 1) + newArrayContent + body.slice(arrayEnd);
|
|
228
|
-
}
|
|
229
|
-
function cleanEmptyStructures(body) {
|
|
230
|
-
body = body.replace(/\n[ \t]*sourcemap\s*:\s*true\s*,?[ \t]*/g, `
|
|
231
|
-
`);
|
|
232
|
-
body = body.replace(/\n[ \t]*build\s*:\s*\{[\s,]*\}\s*,?[ \t]*/g, `
|
|
233
|
-
`);
|
|
234
|
-
body = body.replace(/\n[ \t]*plugins\s*:\s*\[[\s,]*\]\s*,?[ \t]*/g, `
|
|
235
|
-
`);
|
|
236
|
-
body = body.replace(/\n[ \t]*integrations\s*:\s*\[[\s,]*\]\s*,?[ \t]*/g, `
|
|
237
|
-
`);
|
|
238
|
-
body = body.replace(/\n[ \t]*vite\s*:\s*\{[\s,]*\}\s*,?[ \t]*/g, `
|
|
239
|
-
`);
|
|
240
|
-
return body;
|
|
241
|
-
}
|
|
242
|
-
function transformConfig(content, managedImports) {
|
|
243
|
-
const lines = content.split(`
|
|
244
|
-
`);
|
|
245
|
-
const newImports = [];
|
|
246
|
-
for (const line of lines) {
|
|
247
|
-
if (!/^\s*import\s/.test(line))
|
|
248
|
-
continue;
|
|
249
|
-
if (line.includes("astro/config") && line.includes("defineConfig")) {
|
|
250
|
-
newImports.push(`import { defineConfig } from '@nuasite/nua/config'`);
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
const isManagedImport = [...managedImports.keys()].some((pkg) => line.includes(pkg));
|
|
254
|
-
if (isManagedImport)
|
|
255
|
-
continue;
|
|
256
|
-
newImports.push(line);
|
|
257
|
-
}
|
|
258
|
-
let body = extractConfigBody(content);
|
|
259
|
-
for (const [pkg, localName] of managedImports) {
|
|
260
|
-
if (pkg === "@tailwindcss/vite") {
|
|
261
|
-
body = removeCallFromArray(body, "plugins", localName);
|
|
262
|
-
} else {
|
|
263
|
-
body = removeCallFromArray(body, "integrations", localName);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
body = cleanEmptyStructures(body);
|
|
267
|
-
return assembleConfig(newImports, body);
|
|
268
|
-
}
|
|
269
|
-
function resolveNuaVersion() {
|
|
270
|
-
const cliPkgPath = new URL("../package.json", import.meta.url);
|
|
271
|
-
const cliPkg = JSON.parse(readFileSync(cliPkgPath, "utf-8"));
|
|
272
|
-
return `^${cliPkg.version}`;
|
|
273
|
-
}
|
|
274
|
-
function transformPackageJson(pkg, nuaVersion) {
|
|
275
|
-
const result = structuredClone(pkg);
|
|
276
|
-
if (result.scripts) {
|
|
277
|
-
for (const [key, value] of Object.entries(result.scripts)) {
|
|
278
|
-
if (typeof value === "string") {
|
|
279
|
-
result.scripts[key] = value.replace(/\bastro build\b/g, "nua build").replace(/\bastro dev\b/g, "nua dev").replace(/\bastro preview\b/g, "nua preview");
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
for (const field of ["dependencies", "devDependencies"]) {
|
|
284
|
-
if (!result[field])
|
|
285
|
-
continue;
|
|
286
|
-
for (const name of NUA_PROVIDED_PACKAGES) {
|
|
287
|
-
delete result[field][name];
|
|
288
|
-
}
|
|
289
|
-
if (Object.keys(result[field]).length === 0) {
|
|
290
|
-
delete result[field];
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
if (!result.dependencies)
|
|
294
|
-
result.dependencies = {};
|
|
295
|
-
if (!result.dependencies["@nuasite/nua"]) {
|
|
296
|
-
result.dependencies["@nuasite/nua"] = nuaVersion;
|
|
297
|
-
}
|
|
298
|
-
result.dependencies = Object.fromEntries(Object.entries(result.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
299
|
-
return result;
|
|
300
|
-
}
|
|
301
|
-
async function init({ cwd = process.cwd(), dryRun = false, yes = false } = {}) {
|
|
302
|
-
const configPath = findAstroConfig(cwd);
|
|
303
|
-
if (!configPath) {
|
|
304
|
-
console.error("No Astro config file found.");
|
|
305
|
-
process.exit(1);
|
|
306
|
-
}
|
|
307
|
-
const configContent = readFileSync(configPath, "utf-8");
|
|
308
|
-
if (configContent.includes("@nuasite/nua")) {
|
|
309
|
-
console.log("This project already uses @nuasite/nua. Nothing to do.");
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
if (!configContent.includes("defineConfig")) {
|
|
313
|
-
console.error("Could not find defineConfig in Astro config.");
|
|
314
|
-
process.exit(1);
|
|
315
|
-
}
|
|
316
|
-
const pkgPath = join2(cwd, "package.json");
|
|
317
|
-
if (!existsSync2(pkgPath)) {
|
|
318
|
-
console.error("No package.json found.");
|
|
319
|
-
process.exit(1);
|
|
320
|
-
}
|
|
321
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
322
|
-
const managedImports = detectNuaManagedImports(configContent);
|
|
323
|
-
const nuaVersion = resolveNuaVersion();
|
|
324
|
-
const configName = basename(configPath);
|
|
325
|
-
console.log("");
|
|
326
|
-
console.log("nua init \u2014 adopt the Nua toolchain");
|
|
327
|
-
console.log("");
|
|
328
|
-
console.log(` ${configName}`);
|
|
329
|
-
console.log(" - Replace astro/config with @nuasite/nua/config");
|
|
330
|
-
if (managedImports.size > 0) {
|
|
331
|
-
console.log(` - Remove Nua-managed imports: ${[...managedImports.keys()].join(", ")}`);
|
|
332
|
-
console.log(" - Remove managed integration/plugin calls");
|
|
333
|
-
}
|
|
334
|
-
console.log(" - Clean up empty config structures");
|
|
335
|
-
console.log("");
|
|
336
|
-
console.log(" package.json");
|
|
337
|
-
const removable = NUA_PROVIDED_PACKAGES.filter((name) => pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
|
|
338
|
-
if (removable.length > 0) {
|
|
339
|
-
console.log(` - Remove Nua-provided deps: ${removable.join(", ")}`);
|
|
340
|
-
}
|
|
341
|
-
console.log(` - Add @nuasite/nua ${nuaVersion}`);
|
|
342
|
-
console.log(" - Update scripts: astro \u2192 nua");
|
|
343
|
-
if (dryRun) {
|
|
344
|
-
console.log("");
|
|
345
|
-
console.log(" (--dry-run: no changes made)");
|
|
346
|
-
console.log("");
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (!yes) {
|
|
350
|
-
console.log("");
|
|
351
|
-
const answer = prompt("Proceed? [y/N] ");
|
|
352
|
-
if (answer?.toLowerCase() !== "y") {
|
|
353
|
-
console.log("Cancelled.");
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const newConfig = transformConfig(configContent, managedImports);
|
|
358
|
-
writeFileSync(configPath, newConfig);
|
|
359
|
-
console.log(` Updated ${configName}`);
|
|
360
|
-
const newPkg = transformPackageJson(pkg, nuaVersion);
|
|
361
|
-
writeFileSync(pkgPath, JSON.stringify(newPkg, null, "\t") + `
|
|
362
|
-
`);
|
|
363
|
-
console.log(" Updated package.json");
|
|
364
|
-
console.log("");
|
|
365
|
-
console.log("Next steps:");
|
|
366
|
-
console.log(" 1. bun install");
|
|
367
|
-
console.log(" 2. Review the updated config");
|
|
368
|
-
console.log(" 3. nua dev");
|
|
369
|
-
console.log("");
|
|
370
|
-
}
|
|
371
|
-
var NUA_PROVIDED_PACKAGES, NUA_MANAGED_PACKAGES;
|
|
372
|
-
var init_init = __esm(() => {
|
|
373
|
-
init_utils();
|
|
374
|
-
NUA_PROVIDED_PACKAGES = [
|
|
375
|
-
"@astrojs/mdx",
|
|
376
|
-
"@astrojs/sitemap",
|
|
377
|
-
"@tailwindcss/vite",
|
|
378
|
-
"tailwindcss"
|
|
379
|
-
];
|
|
380
|
-
NUA_MANAGED_PACKAGES = [
|
|
381
|
-
"@astrojs/mdx",
|
|
382
|
-
"@astrojs/sitemap",
|
|
383
|
-
"@tailwindcss/vite"
|
|
384
|
-
];
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// src/clean.ts
|
|
388
|
-
var exports_clean = {};
|
|
389
|
-
__export(exports_clean, {
|
|
390
|
-
transformPackageJson: () => transformPackageJson2,
|
|
391
|
-
transformConfig: () => transformConfig2,
|
|
392
|
-
detectDisabledFeatures: () => detectDisabledFeatures,
|
|
393
|
-
clean: () => clean
|
|
394
|
-
});
|
|
395
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
396
|
-
import { basename as basename2, join as join3 } from "path";
|
|
397
|
-
function detectDisabledFeatures(content) {
|
|
398
|
-
const disabled = new Set;
|
|
399
|
-
for (const key of FEATURE_KEYS) {
|
|
400
|
-
if (new RegExp(`\\b${key}\\s*:\\s*false\\b`).test(content)) {
|
|
401
|
-
disabled.add(key);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return disabled;
|
|
405
|
-
}
|
|
406
|
-
function prependToArrayProperty(lines, property, items) {
|
|
407
|
-
const pattern = new RegExp(`\\b${property}\\s*:\\s*\\[`);
|
|
408
|
-
for (let i = 0;i < lines.length; i++) {
|
|
409
|
-
if (pattern.test(lines[i])) {
|
|
410
|
-
lines[i] = lines[i].replace(new RegExp(`(\\b${property}\\s*:\\s*\\[)`), `$1${items}, `);
|
|
411
|
-
return true;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
|
-
function transformConfig2(content, disabled) {
|
|
417
|
-
const userImports = content.split(`
|
|
418
|
-
`).filter((line) => /^\s*import\s/.test(line)).filter((line) => !line.includes("@nuasite/")).filter((line) => !line.includes("defineConfig"));
|
|
419
|
-
let body = extractConfigBody(content);
|
|
420
|
-
body = removeProperty(body, "nua");
|
|
421
|
-
if (content.includes("@nuasite/nua/integration")) {
|
|
422
|
-
body = body.replace(/\bnua\s*\([^)]*\)\s*,?\s*/g, "");
|
|
423
|
-
body = body.replace(/\bintegrations\s*:\s*\[\s*,?\s*\]\s*,?/g, "");
|
|
424
|
-
}
|
|
425
|
-
const imports = [`import { defineConfig } from 'astro/config'`];
|
|
426
|
-
if (!disabled.has("tailwindcss"))
|
|
427
|
-
imports.push(`import tailwindcss from '@tailwindcss/vite'`);
|
|
428
|
-
if (!disabled.has("mdx"))
|
|
429
|
-
imports.push(`import mdx from '@astrojs/mdx'`);
|
|
430
|
-
if (!disabled.has("sitemap"))
|
|
431
|
-
imports.push(`import sitemap from '@astrojs/sitemap'`);
|
|
432
|
-
imports.push(...userImports);
|
|
433
|
-
const integrationCalls = [];
|
|
434
|
-
if (!disabled.has("mdx"))
|
|
435
|
-
integrationCalls.push("mdx()");
|
|
436
|
-
if (!disabled.has("sitemap"))
|
|
437
|
-
integrationCalls.push("sitemap()");
|
|
438
|
-
const bodyLines = body.split(`
|
|
439
|
-
`).filter((line) => line.trim() !== "");
|
|
440
|
-
const hasIntegrations = bodyLines.some((line) => /^\s*integrations\s*:/.test(line));
|
|
441
|
-
const hasVite = bodyLines.some((line) => /^\s*vite\s*:/.test(line));
|
|
442
|
-
if (hasIntegrations && integrationCalls.length > 0) {
|
|
443
|
-
prependToArrayProperty(bodyLines, "integrations", integrationCalls.join(", "));
|
|
444
|
-
}
|
|
445
|
-
if (!disabled.has("tailwindcss") && hasVite) {
|
|
446
|
-
prependToArrayProperty(bodyLines, "plugins", "tailwindcss()");
|
|
447
|
-
}
|
|
448
|
-
const newPropLines = [];
|
|
449
|
-
if (!disabled.has("tailwindcss") && !hasVite) {
|
|
450
|
-
newPropLines.push("\tvite: {", "\t\tbuild: {", "\t\t\tsourcemap: true,", "\t\t},", "\t\tplugins: [tailwindcss()],", "\t},");
|
|
451
|
-
}
|
|
452
|
-
if (!hasIntegrations && integrationCalls.length > 0) {
|
|
453
|
-
newPropLines.push(` integrations: [${integrationCalls.join(", ")}],`);
|
|
454
|
-
}
|
|
455
|
-
const allLines = [...bodyLines, ...newPropLines];
|
|
456
|
-
return assembleConfig(imports, allLines.join(`
|
|
457
|
-
`));
|
|
458
|
-
}
|
|
459
|
-
function transformPackageJson2(pkg, disabled, usedRuntimePackages = []) {
|
|
460
|
-
const result = structuredClone(pkg);
|
|
461
|
-
const nuaVersion = result.dependencies?.["@nuasite/nua"] ?? result.devDependencies?.["@nuasite/nua"];
|
|
462
|
-
if (result.scripts) {
|
|
463
|
-
for (const [key, value] of Object.entries(result.scripts)) {
|
|
464
|
-
if (typeof value === "string") {
|
|
465
|
-
result.scripts[key] = value.replace(/\bnua build\b/g, "astro build").replace(/\bnua dev\b/g, "astro dev").replace(/\bnua preview\b/g, "astro preview");
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
470
|
-
if (!result[field])
|
|
471
|
-
continue;
|
|
472
|
-
for (const name of NUASITE_TOOLING) {
|
|
473
|
-
delete result[field][name];
|
|
474
|
-
}
|
|
475
|
-
if (Object.keys(result[field]).length === 0) {
|
|
476
|
-
delete result[field];
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
if (!result.dependencies)
|
|
480
|
-
result.dependencies = {};
|
|
481
|
-
for (const [name, version] of Object.entries(PACKAGES_TO_ADD)) {
|
|
482
|
-
if (result.dependencies[name])
|
|
483
|
-
continue;
|
|
484
|
-
if (name === "@astrojs/mdx" && disabled.has("mdx"))
|
|
485
|
-
continue;
|
|
486
|
-
if (name === "@astrojs/sitemap" && disabled.has("sitemap"))
|
|
487
|
-
continue;
|
|
488
|
-
if (name === "@tailwindcss/vite" && disabled.has("tailwindcss"))
|
|
489
|
-
continue;
|
|
490
|
-
if (name === "tailwindcss" && disabled.has("tailwindcss"))
|
|
491
|
-
continue;
|
|
492
|
-
result.dependencies[name] = version;
|
|
493
|
-
}
|
|
494
|
-
for (const name of usedRuntimePackages) {
|
|
495
|
-
if (!result.dependencies[name]) {
|
|
496
|
-
result.dependencies[name] = nuaVersion ?? "^0.16.0";
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
result.dependencies = Object.fromEntries(Object.entries(result.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
500
|
-
return result;
|
|
501
|
-
}
|
|
502
|
-
function scanForNuasiteUsage(cwd) {
|
|
503
|
-
const srcDir = join3(cwd, "src");
|
|
504
|
-
if (!existsSync3(srcDir))
|
|
505
|
-
return [];
|
|
506
|
-
const results = [];
|
|
507
|
-
try {
|
|
508
|
-
const files = readdirSync(srcDir, { recursive: true });
|
|
509
|
-
for (const entry of files) {
|
|
510
|
-
const fileName = String(entry);
|
|
511
|
-
if (!/\.(astro|ts|tsx|js|jsx)$/.test(fileName))
|
|
512
|
-
continue;
|
|
513
|
-
try {
|
|
514
|
-
const content = readFileSync2(join3(srcDir, fileName), "utf-8");
|
|
515
|
-
const matches = content.match(/@nuasite\/[\w-]+/g);
|
|
516
|
-
if (matches) {
|
|
517
|
-
results.push({
|
|
518
|
-
file: join3("src", fileName),
|
|
519
|
-
packages: [...new Set(matches)]
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
} catch {}
|
|
523
|
-
}
|
|
524
|
-
} catch {}
|
|
525
|
-
return results;
|
|
526
|
-
}
|
|
527
|
-
async function clean({ cwd = process.cwd(), dryRun = false, yes = false } = {}) {
|
|
528
|
-
const configPath = findAstroConfig(cwd);
|
|
529
|
-
if (!configPath) {
|
|
530
|
-
console.error("No Astro config file found.");
|
|
531
|
-
process.exit(1);
|
|
532
|
-
}
|
|
533
|
-
const configContent = readFileSync2(configPath, "utf-8");
|
|
534
|
-
if (!configContent.includes("@nuasite/nua")) {
|
|
535
|
-
console.log("This project does not use @nuasite/nua. Nothing to clean.");
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
const pkgPath = join3(cwd, "package.json");
|
|
539
|
-
if (!existsSync3(pkgPath)) {
|
|
540
|
-
console.error("No package.json found.");
|
|
541
|
-
process.exit(1);
|
|
542
|
-
}
|
|
543
|
-
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
544
|
-
const disabled = detectDisabledFeatures(configContent);
|
|
545
|
-
const nuasiteUsage = scanForNuasiteUsage(cwd);
|
|
546
|
-
const configName = basename2(configPath);
|
|
547
|
-
const usedRuntimePackages = new Set;
|
|
548
|
-
const toolingUsage = [];
|
|
549
|
-
for (const entry of nuasiteUsage) {
|
|
550
|
-
const runtime = entry.packages.filter((p) => !NUASITE_TOOLING.includes(p));
|
|
551
|
-
const tooling = entry.packages.filter((p) => NUASITE_TOOLING.includes(p));
|
|
552
|
-
for (const p of runtime)
|
|
553
|
-
usedRuntimePackages.add(p);
|
|
554
|
-
if (tooling.length > 0) {
|
|
555
|
-
toolingUsage.push({ file: entry.file, packages: tooling });
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
console.log("");
|
|
559
|
-
console.log("nua clean \u2014 eject to standard Astro project");
|
|
560
|
-
console.log("");
|
|
561
|
-
console.log(` ${configName}`);
|
|
562
|
-
console.log(" - Replace @nuasite/nua with explicit Astro integrations");
|
|
563
|
-
console.log(" - Add mdx, sitemap, tailwindcss imports");
|
|
564
|
-
if (disabled.size > 0) {
|
|
565
|
-
console.log(` - Skipping disabled: ${[...disabled].join(", ")}`);
|
|
566
|
-
}
|
|
567
|
-
console.log("");
|
|
568
|
-
console.log(" package.json");
|
|
569
|
-
console.log(" - Remove @nuasite/* tooling dependencies");
|
|
570
|
-
if (usedRuntimePackages.size > 0) {
|
|
571
|
-
console.log(` - Keep as explicit deps: ${[...usedRuntimePackages].join(", ")}`);
|
|
572
|
-
}
|
|
573
|
-
console.log(" - Add standard Astro packages");
|
|
574
|
-
console.log(" - Update scripts: nua \u2192 astro");
|
|
575
|
-
if (toolingUsage.length > 0) {
|
|
576
|
-
console.log("");
|
|
577
|
-
console.log(" Warning: @nuasite tooling imports found in source files:");
|
|
578
|
-
for (const { file, packages } of toolingUsage) {
|
|
579
|
-
console.log(` ${file} (${packages.join(", ")})`);
|
|
580
|
-
}
|
|
581
|
-
console.log(" These will need manual removal.");
|
|
582
|
-
}
|
|
583
|
-
if (dryRun) {
|
|
584
|
-
console.log("");
|
|
585
|
-
console.log(" (--dry-run: no changes made)");
|
|
586
|
-
console.log("");
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
if (!yes) {
|
|
590
|
-
console.log("");
|
|
591
|
-
const answer = prompt("Proceed? [y/N] ");
|
|
592
|
-
if (answer?.toLowerCase() !== "y") {
|
|
593
|
-
console.log("Cancelled.");
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
const newConfig = transformConfig2(configContent, disabled);
|
|
598
|
-
writeFileSync2(configPath, newConfig);
|
|
599
|
-
console.log(` Updated ${configName}`);
|
|
600
|
-
const newPkg = transformPackageJson2(pkg, disabled, [...usedRuntimePackages]);
|
|
601
|
-
writeFileSync2(pkgPath, JSON.stringify(newPkg, null, "\t") + `
|
|
602
|
-
`);
|
|
603
|
-
console.log(" Updated package.json");
|
|
604
|
-
console.log("");
|
|
605
|
-
console.log("Next steps:");
|
|
606
|
-
console.log(" 1. bun install");
|
|
607
|
-
console.log(" 2. Review the updated config");
|
|
608
|
-
console.log(" 3. astro dev");
|
|
609
|
-
if (toolingUsage.length > 0) {
|
|
610
|
-
console.log(" 4. Remove @nuasite tooling imports from source files");
|
|
611
|
-
}
|
|
612
|
-
console.log("");
|
|
613
|
-
}
|
|
614
|
-
var FEATURE_KEYS, NUASITE_TOOLING, PACKAGES_TO_ADD;
|
|
615
|
-
var init_clean = __esm(() => {
|
|
616
|
-
init_utils();
|
|
617
|
-
FEATURE_KEYS = ["cms", "pageMarkdown", "mdx", "sitemap", "tailwindcss", "checks"];
|
|
618
|
-
NUASITE_TOOLING = [
|
|
619
|
-
"@nuasite/nua",
|
|
620
|
-
"@nuasite/core",
|
|
621
|
-
"@nuasite/cli",
|
|
622
|
-
"@nuasite/cms",
|
|
623
|
-
"@nuasite/llm-enhancements",
|
|
624
|
-
"@nuasite/checks",
|
|
625
|
-
"@nuasite/agent-summary"
|
|
626
|
-
];
|
|
627
|
-
PACKAGES_TO_ADD = {
|
|
628
|
-
astro: "^6.0.2",
|
|
629
|
-
"@astrojs/check": "^0.9.7",
|
|
630
|
-
"@astrojs/mdx": "^5.0.0",
|
|
631
|
-
"@astrojs/sitemap": "^3.7.1",
|
|
632
|
-
"@tailwindcss/vite": "^4.2.1",
|
|
633
|
-
tailwindcss: "^4.2.1",
|
|
634
|
-
typescript: "^5"
|
|
635
|
-
};
|
|
636
|
-
});
|
|
637
|
-
|
|
638
50
|
// ../../node_modules/yaml/dist/nodes/identity.js
|
|
639
51
|
var require_identity = __commonJS((exports) => {
|
|
640
52
|
var ALIAS = Symbol.for("yaml.alias");
|
|
@@ -11458,13 +10870,13 @@ var require_lib = __commonJS((exports) => {
|
|
|
11458
10870
|
flowEnumMemberRaw() {
|
|
11459
10871
|
const loc = this.state.startLoc;
|
|
11460
10872
|
const id = this.parseIdentifier(true);
|
|
11461
|
-
const
|
|
10873
|
+
const init = this.eat(29) ? this.flowEnumMemberInit() : {
|
|
11462
10874
|
type: "none",
|
|
11463
10875
|
loc
|
|
11464
10876
|
};
|
|
11465
10877
|
return {
|
|
11466
10878
|
id,
|
|
11467
|
-
init
|
|
10879
|
+
init
|
|
11468
10880
|
};
|
|
11469
10881
|
}
|
|
11470
10882
|
flowEnumCheckExplicitTypeMismatch(loc, context, expectedType) {
|
|
@@ -11498,7 +10910,7 @@ var require_lib = __commonJS((exports) => {
|
|
|
11498
10910
|
const memberNode = this.startNode();
|
|
11499
10911
|
const {
|
|
11500
10912
|
id,
|
|
11501
|
-
init
|
|
10913
|
+
init
|
|
11502
10914
|
} = this.flowEnumMemberRaw();
|
|
11503
10915
|
const memberName = id.name;
|
|
11504
10916
|
if (memberName === "") {
|
|
@@ -11524,35 +10936,35 @@ var require_lib = __commonJS((exports) => {
|
|
|
11524
10936
|
memberName
|
|
11525
10937
|
};
|
|
11526
10938
|
memberNode.id = id;
|
|
11527
|
-
switch (
|
|
10939
|
+
switch (init.type) {
|
|
11528
10940
|
case "boolean": {
|
|
11529
|
-
this.flowEnumCheckExplicitTypeMismatch(
|
|
11530
|
-
memberNode.init =
|
|
10941
|
+
this.flowEnumCheckExplicitTypeMismatch(init.loc, context, "boolean");
|
|
10942
|
+
memberNode.init = init.value;
|
|
11531
10943
|
members.booleanMembers.push(this.finishNode(memberNode, "EnumBooleanMember"));
|
|
11532
10944
|
break;
|
|
11533
10945
|
}
|
|
11534
10946
|
case "number": {
|
|
11535
|
-
this.flowEnumCheckExplicitTypeMismatch(
|
|
11536
|
-
memberNode.init =
|
|
10947
|
+
this.flowEnumCheckExplicitTypeMismatch(init.loc, context, "number");
|
|
10948
|
+
memberNode.init = init.value;
|
|
11537
10949
|
members.numberMembers.push(this.finishNode(memberNode, "EnumNumberMember"));
|
|
11538
10950
|
break;
|
|
11539
10951
|
}
|
|
11540
10952
|
case "string": {
|
|
11541
|
-
this.flowEnumCheckExplicitTypeMismatch(
|
|
11542
|
-
memberNode.init =
|
|
10953
|
+
this.flowEnumCheckExplicitTypeMismatch(init.loc, context, "string");
|
|
10954
|
+
memberNode.init = init.value;
|
|
11543
10955
|
members.stringMembers.push(this.finishNode(memberNode, "EnumStringMember"));
|
|
11544
10956
|
break;
|
|
11545
10957
|
}
|
|
11546
10958
|
case "invalid": {
|
|
11547
|
-
throw this.flowEnumErrorInvalidMemberInitializer(
|
|
10959
|
+
throw this.flowEnumErrorInvalidMemberInitializer(init.loc, context);
|
|
11548
10960
|
}
|
|
11549
10961
|
case "none": {
|
|
11550
10962
|
switch (explicitType) {
|
|
11551
10963
|
case "boolean":
|
|
11552
|
-
this.flowEnumErrorBooleanMemberNotInitialized(
|
|
10964
|
+
this.flowEnumErrorBooleanMemberNotInitialized(init.loc, context);
|
|
11553
10965
|
break;
|
|
11554
10966
|
case "number":
|
|
11555
|
-
this.flowEnumErrorNumberMemberNotInitialized(
|
|
10967
|
+
this.flowEnumErrorNumberMemberNotInitialized(init.loc, context);
|
|
11556
10968
|
break;
|
|
11557
10969
|
default:
|
|
11558
10970
|
members.defaultedMembers.push(this.finishNode(memberNode, "EnumDefaultedMember"));
|
|
@@ -17177,14 +16589,14 @@ var require_lib = __commonJS((exports) => {
|
|
|
17177
16589
|
}
|
|
17178
16590
|
for (const {
|
|
17179
16591
|
id,
|
|
17180
|
-
init
|
|
16592
|
+
init
|
|
17181
16593
|
} of declaration.declarations) {
|
|
17182
|
-
if (!
|
|
16594
|
+
if (!init)
|
|
17183
16595
|
continue;
|
|
17184
16596
|
if (kind === "var" || kind === "let" || !!id.typeAnnotation) {
|
|
17185
|
-
this.raise(TSErrors.InitializerNotAllowedInAmbientContext,
|
|
17186
|
-
} else if (!isValidAmbientConstInitializer(
|
|
17187
|
-
this.raise(TSErrors.ConstInitializerMustBeStringOrNumericLiteralOrLiteralEnumReference,
|
|
16597
|
+
this.raise(TSErrors.InitializerNotAllowedInAmbientContext, init);
|
|
16598
|
+
} else if (!isValidAmbientConstInitializer(init, this.hasPlugin("estree"))) {
|
|
16599
|
+
this.raise(TSErrors.ConstInitializerMustBeStringOrNumericLiteralOrLiteralEnumReference, init);
|
|
17188
16600
|
}
|
|
17189
16601
|
}
|
|
17190
16602
|
return declaration;
|
|
@@ -20864,47 +20276,47 @@ var require_lib = __commonJS((exports) => {
|
|
|
20864
20276
|
}
|
|
20865
20277
|
this.next();
|
|
20866
20278
|
this.parseVar(initNode, true, kind);
|
|
20867
|
-
const
|
|
20279
|
+
const init2 = this.finishNode(initNode, "VariableDeclaration");
|
|
20868
20280
|
const isForIn = this.match(58);
|
|
20869
20281
|
if (isForIn && starsWithUsingDeclaration) {
|
|
20870
|
-
this.raise(Errors.ForInUsing,
|
|
20282
|
+
this.raise(Errors.ForInUsing, init2);
|
|
20871
20283
|
}
|
|
20872
|
-
if ((isForIn || this.isContextual(102)) &&
|
|
20873
|
-
return this.parseForIn(node,
|
|
20284
|
+
if ((isForIn || this.isContextual(102)) && init2.declarations.length === 1) {
|
|
20285
|
+
return this.parseForIn(node, init2, awaitAt);
|
|
20874
20286
|
}
|
|
20875
20287
|
if (awaitAt !== null) {
|
|
20876
20288
|
this.unexpected(awaitAt);
|
|
20877
20289
|
}
|
|
20878
|
-
return this.parseFor(node,
|
|
20290
|
+
return this.parseFor(node, init2);
|
|
20879
20291
|
}
|
|
20880
20292
|
}
|
|
20881
20293
|
const startsWithAsync = this.isContextual(95);
|
|
20882
20294
|
const refExpressionErrors = new ExpressionErrors;
|
|
20883
|
-
const
|
|
20295
|
+
const init = this.parseExpression(true, refExpressionErrors);
|
|
20884
20296
|
const isForOf = this.isContextual(102);
|
|
20885
20297
|
if (isForOf) {
|
|
20886
20298
|
if (startsWithLet) {
|
|
20887
|
-
this.raise(Errors.ForOfLet,
|
|
20299
|
+
this.raise(Errors.ForOfLet, init);
|
|
20888
20300
|
}
|
|
20889
|
-
if (awaitAt === null && startsWithAsync &&
|
|
20890
|
-
this.raise(Errors.ForOfAsync,
|
|
20301
|
+
if (awaitAt === null && startsWithAsync && init.type === "Identifier") {
|
|
20302
|
+
this.raise(Errors.ForOfAsync, init);
|
|
20891
20303
|
}
|
|
20892
20304
|
}
|
|
20893
20305
|
if (isForOf || this.match(58)) {
|
|
20894
20306
|
this.checkDestructuringPrivate(refExpressionErrors);
|
|
20895
|
-
this.toAssignable(
|
|
20307
|
+
this.toAssignable(init, true);
|
|
20896
20308
|
const type = isForOf ? "ForOfStatement" : "ForInStatement";
|
|
20897
|
-
this.checkLVal(
|
|
20309
|
+
this.checkLVal(init, {
|
|
20898
20310
|
type
|
|
20899
20311
|
});
|
|
20900
|
-
return this.parseForIn(node,
|
|
20312
|
+
return this.parseForIn(node, init, awaitAt);
|
|
20901
20313
|
} else {
|
|
20902
20314
|
this.checkExpressionErrors(refExpressionErrors, true);
|
|
20903
20315
|
}
|
|
20904
20316
|
if (awaitAt !== null) {
|
|
20905
20317
|
this.unexpected(awaitAt);
|
|
20906
20318
|
}
|
|
20907
|
-
return this.parseFor(node,
|
|
20319
|
+
return this.parseFor(node, init);
|
|
20908
20320
|
}
|
|
20909
20321
|
parseFunctionStatement(node, isAsync, isHangingDeclaration) {
|
|
20910
20322
|
this.next();
|
|
@@ -21123,8 +20535,8 @@ var require_lib = __commonJS((exports) => {
|
|
|
21123
20535
|
}
|
|
21124
20536
|
this.next();
|
|
21125
20537
|
}
|
|
21126
|
-
parseFor(node,
|
|
21127
|
-
node.init =
|
|
20538
|
+
parseFor(node, init) {
|
|
20539
|
+
node.init = init;
|
|
21128
20540
|
this.semicolon(false);
|
|
21129
20541
|
node.test = this.match(13) ? null : this.parseExpression();
|
|
21130
20542
|
this.semicolon(false);
|
|
@@ -21135,7 +20547,7 @@ var require_lib = __commonJS((exports) => {
|
|
|
21135
20547
|
this.state.labels.pop();
|
|
21136
20548
|
return this.finishNode(node, "ForStatement");
|
|
21137
20549
|
}
|
|
21138
|
-
parseForIn(node,
|
|
20550
|
+
parseForIn(node, init, awaitAt) {
|
|
21139
20551
|
const isForIn = this.match(58);
|
|
21140
20552
|
this.next();
|
|
21141
20553
|
if (isForIn) {
|
|
@@ -21144,19 +20556,19 @@ var require_lib = __commonJS((exports) => {
|
|
|
21144
20556
|
} else {
|
|
21145
20557
|
node.await = awaitAt !== null;
|
|
21146
20558
|
}
|
|
21147
|
-
if (
|
|
21148
|
-
this.raise(Errors.ForInOfLoopInitializer,
|
|
20559
|
+
if (init.type === "VariableDeclaration" && init.declarations[0].init != null && (!isForIn || !this.options.annexB || this.state.strict || init.kind !== "var" || init.declarations[0].id.type !== "Identifier")) {
|
|
20560
|
+
this.raise(Errors.ForInOfLoopInitializer, init, {
|
|
21149
20561
|
type: isForIn ? "ForInStatement" : "ForOfStatement"
|
|
21150
20562
|
});
|
|
21151
20563
|
}
|
|
21152
|
-
if (
|
|
21153
|
-
this.raise(Errors.InvalidLhs,
|
|
20564
|
+
if (init.type === "AssignmentPattern") {
|
|
20565
|
+
this.raise(Errors.InvalidLhs, init, {
|
|
21154
20566
|
ancestor: {
|
|
21155
20567
|
type: "ForStatement"
|
|
21156
20568
|
}
|
|
21157
20569
|
});
|
|
21158
20570
|
}
|
|
21159
|
-
node.left =
|
|
20571
|
+
node.left = init;
|
|
21160
20572
|
node.right = isForIn ? this.parseExpression() : this.parseMaybeAssignAllowIn();
|
|
21161
20573
|
this.expect(11);
|
|
21162
20574
|
node.body = this.withSmartMixTopicForbiddingContext(() => this.parseStatement());
|
|
@@ -23047,6 +22459,216 @@ var init_content_config_ast = __esm(() => {
|
|
|
23047
22459
|
WRAPPER_METHODS = new Set(["optional", "nullable", "nullish", "default"]);
|
|
23048
22460
|
});
|
|
23049
22461
|
|
|
22462
|
+
// ../cms-core/src/check.ts
|
|
22463
|
+
import path from "path";
|
|
22464
|
+
function isPlainObject(value) {
|
|
22465
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
22466
|
+
}
|
|
22467
|
+
function typeMismatch(field, value) {
|
|
22468
|
+
switch (field.type) {
|
|
22469
|
+
case "number":
|
|
22470
|
+
case "year":
|
|
22471
|
+
case "month":
|
|
22472
|
+
return typeof value === "number" ? null : `expected a number, found ${typeName(value)}`;
|
|
22473
|
+
case "boolean":
|
|
22474
|
+
return typeof value === "boolean" ? null : `expected true/false, found ${typeName(value)}`;
|
|
22475
|
+
case "select": {
|
|
22476
|
+
if (typeof value !== "string")
|
|
22477
|
+
return `expected one of the allowed values, found ${typeName(value)}`;
|
|
22478
|
+
const options = field.options ?? [];
|
|
22479
|
+
if (options.length === 0 || options.includes(value))
|
|
22480
|
+
return null;
|
|
22481
|
+
return `"${value}" is not one of: ${options.join(", ")}`;
|
|
22482
|
+
}
|
|
22483
|
+
case "date":
|
|
22484
|
+
case "datetime":
|
|
22485
|
+
if (value instanceof Date)
|
|
22486
|
+
return Number.isNaN(value.getTime()) ? "is not a valid date" : null;
|
|
22487
|
+
if (typeof value !== "string")
|
|
22488
|
+
return `expected a date, found ${typeName(value)}`;
|
|
22489
|
+
return Number.isNaN(Date.parse(value)) ? `"${value}" is not a valid date` : null;
|
|
22490
|
+
case "array":
|
|
22491
|
+
return Array.isArray(value) ? null : `expected a list, found ${typeName(value)}`;
|
|
22492
|
+
case "object":
|
|
22493
|
+
return isPlainObject(value) ? null : `expected an object, found ${typeName(value)}`;
|
|
22494
|
+
case "text":
|
|
22495
|
+
case "textarea":
|
|
22496
|
+
case "markdown":
|
|
22497
|
+
case "url":
|
|
22498
|
+
case "email":
|
|
22499
|
+
case "tel":
|
|
22500
|
+
case "color":
|
|
22501
|
+
case "time":
|
|
22502
|
+
case "reference":
|
|
22503
|
+
return typeof value === "string" ? null : `expected text, found ${typeName(value)}`;
|
|
22504
|
+
default:
|
|
22505
|
+
return null;
|
|
22506
|
+
}
|
|
22507
|
+
}
|
|
22508
|
+
async function collectEntryFiles(fs, collection, base) {
|
|
22509
|
+
const pattern = collection.loaderPattern ?? "**/*.{md,mdx,json,yaml,yml}";
|
|
22510
|
+
const files = await fs.glob(`${base}/${pattern}`);
|
|
22511
|
+
return files.filter((file) => !path.basename(file).startsWith("_")).sort();
|
|
22512
|
+
}
|
|
22513
|
+
function parseEntry(file, raw) {
|
|
22514
|
+
const extension = path.extname(file);
|
|
22515
|
+
if (DATA_EXTENSIONS.has(extension)) {
|
|
22516
|
+
try {
|
|
22517
|
+
const parsed = extension === ".json" ? JSON.parse(raw) : import_yaml.default.parse(raw);
|
|
22518
|
+
return { frontmatter: isPlainObject(parsed) ? parsed : {} };
|
|
22519
|
+
} catch (error) {
|
|
22520
|
+
return { error: error instanceof Error ? error.message.split(`
|
|
22521
|
+
`)[0] : String(error) };
|
|
22522
|
+
}
|
|
22523
|
+
}
|
|
22524
|
+
const trimmed = raw.trimStart();
|
|
22525
|
+
if (!trimmed.startsWith("---"))
|
|
22526
|
+
return { frontmatter: {} };
|
|
22527
|
+
const lines = trimmed.split(`
|
|
22528
|
+
`);
|
|
22529
|
+
const end = lines.findIndex((line, index) => index > 0 && line.trimEnd() === "---");
|
|
22530
|
+
if (end === -1)
|
|
22531
|
+
return { error: "frontmatter is never closed (missing the second `---`)" };
|
|
22532
|
+
try {
|
|
22533
|
+
const parsed = import_yaml.default.parse(lines.slice(1, end).join(`
|
|
22534
|
+
`).trim());
|
|
22535
|
+
return { frontmatter: isPlainObject(parsed) ? parsed : {} };
|
|
22536
|
+
} catch (error) {
|
|
22537
|
+
return { error: error instanceof Error ? error.message.split(`
|
|
22538
|
+
`)[0] : String(error) };
|
|
22539
|
+
}
|
|
22540
|
+
}
|
|
22541
|
+
async function checkContent(fs) {
|
|
22542
|
+
const findings = [];
|
|
22543
|
+
const config = await parseContentConfig(fs, new Map);
|
|
22544
|
+
if (config.size === 0) {
|
|
22545
|
+
findings.push({
|
|
22546
|
+
severity: "error",
|
|
22547
|
+
code: "config/no-collections",
|
|
22548
|
+
file: "src/content.config.ts",
|
|
22549
|
+
message: "No collections found \u2014 the content config is missing, unreadable, or declares nothing."
|
|
22550
|
+
});
|
|
22551
|
+
return { findings, collections: 0, entries: 0 };
|
|
22552
|
+
}
|
|
22553
|
+
const bases = new Map;
|
|
22554
|
+
const files = new Map;
|
|
22555
|
+
const stems = new Map;
|
|
22556
|
+
for (const [name, collection] of config) {
|
|
22557
|
+
const base = normalizeBase(collection.loaderBase ?? `src/content/${name}`);
|
|
22558
|
+
bases.set(name, base);
|
|
22559
|
+
if (!await fs.exists(base)) {
|
|
22560
|
+
findings.push({
|
|
22561
|
+
severity: "error",
|
|
22562
|
+
code: "config/missing-dir",
|
|
22563
|
+
file: "src/content.config.ts",
|
|
22564
|
+
field: name,
|
|
22565
|
+
message: `Collection "${name}" loads from ${base}/, which does not exist.`
|
|
22566
|
+
});
|
|
22567
|
+
files.set(name, []);
|
|
22568
|
+
stems.set(name, new Set);
|
|
22569
|
+
continue;
|
|
22570
|
+
}
|
|
22571
|
+
const collectionFiles = await collectEntryFiles(fs, collection, base);
|
|
22572
|
+
files.set(name, collectionFiles);
|
|
22573
|
+
stems.set(name, new Set(collectionFiles.map(entryStem)));
|
|
22574
|
+
if (collectionFiles.length === 0) {
|
|
22575
|
+
findings.push({
|
|
22576
|
+
severity: "warning",
|
|
22577
|
+
code: "config/empty-collection",
|
|
22578
|
+
file: base,
|
|
22579
|
+
field: name,
|
|
22580
|
+
message: `Collection "${name}" matched no entries under ${base}/.`
|
|
22581
|
+
});
|
|
22582
|
+
}
|
|
22583
|
+
}
|
|
22584
|
+
let entries = 0;
|
|
22585
|
+
for (const [name, collection] of config) {
|
|
22586
|
+
const typedFields = collection.fields.filter((field) => field.type !== undefined || field.required || field.reference);
|
|
22587
|
+
for (const file of files.get(name) ?? []) {
|
|
22588
|
+
entries++;
|
|
22589
|
+
const parsed = parseEntry(file, await fs.readFile(file));
|
|
22590
|
+
if ("error" in parsed) {
|
|
22591
|
+
findings.push({ severity: "error", code: "entry/syntax", file, message: `Frontmatter does not parse: ${parsed.error}` });
|
|
22592
|
+
continue;
|
|
22593
|
+
}
|
|
22594
|
+
for (const field of typedFields) {
|
|
22595
|
+
const value = parsed.frontmatter[field.name];
|
|
22596
|
+
if (value === undefined || value === null) {
|
|
22597
|
+
if (field.required) {
|
|
22598
|
+
findings.push({
|
|
22599
|
+
severity: "error",
|
|
22600
|
+
code: "entry/missing-required",
|
|
22601
|
+
file,
|
|
22602
|
+
field: field.name,
|
|
22603
|
+
message: `Required field "${field.name}" is missing.`
|
|
22604
|
+
});
|
|
22605
|
+
}
|
|
22606
|
+
continue;
|
|
22607
|
+
}
|
|
22608
|
+
const mismatch = field.type === undefined ? null : typeMismatch(field, value);
|
|
22609
|
+
if (mismatch) {
|
|
22610
|
+
findings.push({ severity: "error", code: "entry/field-type", file, field: field.name, message: `${field.name}: ${mismatch}` });
|
|
22611
|
+
continue;
|
|
22612
|
+
}
|
|
22613
|
+
const reference = field.reference;
|
|
22614
|
+
if (!reference)
|
|
22615
|
+
continue;
|
|
22616
|
+
const targets = stems.get(reference.target);
|
|
22617
|
+
if (!targets || targets.size === 0)
|
|
22618
|
+
continue;
|
|
22619
|
+
const values = reference.isArray ? Array.isArray(value) ? value : [value] : [value];
|
|
22620
|
+
for (const item of values) {
|
|
22621
|
+
if (typeof item !== "string" || targets.has(item))
|
|
22622
|
+
continue;
|
|
22623
|
+
findings.push({
|
|
22624
|
+
severity: "warning",
|
|
22625
|
+
code: "entry/dangling-reference",
|
|
22626
|
+
file,
|
|
22627
|
+
field: field.name,
|
|
22628
|
+
message: `${field.name}: "${item}" is not an entry of "${reference.target}".`
|
|
22629
|
+
});
|
|
22630
|
+
}
|
|
22631
|
+
}
|
|
22632
|
+
}
|
|
22633
|
+
}
|
|
22634
|
+
return { findings, collections: config.size, entries };
|
|
22635
|
+
}
|
|
22636
|
+
function formatCheckReport(report) {
|
|
22637
|
+
if (report.findings.length === 0)
|
|
22638
|
+
return "";
|
|
22639
|
+
const byFile = new Map;
|
|
22640
|
+
for (const finding of report.findings) {
|
|
22641
|
+
const bucket = byFile.get(finding.file);
|
|
22642
|
+
if (bucket)
|
|
22643
|
+
bucket.push(finding);
|
|
22644
|
+
else
|
|
22645
|
+
byFile.set(finding.file, [finding]);
|
|
22646
|
+
}
|
|
22647
|
+
const lines = [];
|
|
22648
|
+
for (const [file, findings] of byFile) {
|
|
22649
|
+
lines.push(file);
|
|
22650
|
+
for (const finding of findings) {
|
|
22651
|
+
lines.push(` ${finding.severity === "error" ? "error" : "warn "} ${finding.message} [${finding.code}]`);
|
|
22652
|
+
}
|
|
22653
|
+
}
|
|
22654
|
+
return lines.join(`
|
|
22655
|
+
`);
|
|
22656
|
+
}
|
|
22657
|
+
var import_yaml, DATA_EXTENSIONS, normalizeBase = (base) => path.normalize(base).replace(/^\.\//, "").replace(/\/+$/, ""), entryStem = (file) => path.basename(file).replace(/\.(md|mdx|json|ya?ml)$/, ""), typeName = (value) => {
|
|
22658
|
+
if (value === null)
|
|
22659
|
+
return "null";
|
|
22660
|
+
if (Array.isArray(value))
|
|
22661
|
+
return "array";
|
|
22662
|
+
if (value instanceof Date)
|
|
22663
|
+
return "date";
|
|
22664
|
+
return typeof value;
|
|
22665
|
+
};
|
|
22666
|
+
var init_check = __esm(() => {
|
|
22667
|
+
init_content_config_ast();
|
|
22668
|
+
import_yaml = __toESM(require_dist(), 1);
|
|
22669
|
+
DATA_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
|
|
22670
|
+
});
|
|
22671
|
+
|
|
23050
22672
|
// ../cms-core/src/shared.ts
|
|
23051
22673
|
function pathSegmentValue(raw) {
|
|
23052
22674
|
if (typeof raw === "string")
|
|
@@ -23069,8 +22691,8 @@ function computePathnameFromSpec(spec, data) {
|
|
|
23069
22691
|
return;
|
|
23070
22692
|
segments.push(seg.map && Object.hasOwn(seg.map, value) ? seg.map[value] : value);
|
|
23071
22693
|
}
|
|
23072
|
-
const
|
|
23073
|
-
return
|
|
22694
|
+
const path2 = ("/" + segments.join("/")).replace(/\/{2,}/g, "/").replace(/\/+$/, "");
|
|
22695
|
+
return path2 === "" ? "/" : path2;
|
|
23074
22696
|
}
|
|
23075
22697
|
function resolvePathnameFromSpec(def, data) {
|
|
23076
22698
|
if (!def.pathname)
|
|
@@ -23082,7 +22704,7 @@ function slugifyHref(text) {
|
|
|
23082
22704
|
}
|
|
23083
22705
|
|
|
23084
22706
|
// ../cms-core/src/collection-scanner.ts
|
|
23085
|
-
import
|
|
22707
|
+
import path2 from "path";
|
|
23086
22708
|
function normalizeFieldName(name) {
|
|
23087
22709
|
return name.toLowerCase().replace(/[_-]/g, "");
|
|
23088
22710
|
}
|
|
@@ -23106,18 +22728,18 @@ function parseFrontmatter(content) {
|
|
|
23106
22728
|
const block = extractFrontmatterBlock(content);
|
|
23107
22729
|
if (!block)
|
|
23108
22730
|
return null;
|
|
23109
|
-
return
|
|
22731
|
+
return import_yaml2.parse(block);
|
|
23110
22732
|
}
|
|
23111
22733
|
function parseFieldDirectives(content) {
|
|
23112
22734
|
const block = extractFrontmatterBlock(content);
|
|
23113
22735
|
if (!block)
|
|
23114
22736
|
return {};
|
|
23115
|
-
const doc =
|
|
23116
|
-
if (!
|
|
22737
|
+
const doc = import_yaml2.parseDocument(block);
|
|
22738
|
+
if (!import_yaml2.isMap(doc.contents))
|
|
23117
22739
|
return {};
|
|
23118
22740
|
const result = {};
|
|
23119
22741
|
for (const pair of doc.contents.items) {
|
|
23120
|
-
if (!
|
|
22742
|
+
if (!import_yaml2.isPair(pair) || !import_yaml2.isScalar(pair.key))
|
|
23121
22743
|
continue;
|
|
23122
22744
|
const comment = pair.key.commentBefore;
|
|
23123
22745
|
if (!comment)
|
|
@@ -23191,1016 +22813,1632 @@ function inferFieldType(value, key) {
|
|
|
23191
22813
|
`) || value.length > TEXTAREA_MIN_LENGTH) {
|
|
23192
22814
|
return "textarea";
|
|
23193
22815
|
}
|
|
23194
|
-
return "text";
|
|
22816
|
+
return "text";
|
|
22817
|
+
}
|
|
22818
|
+
return "text";
|
|
22819
|
+
}
|
|
22820
|
+
function mergeFieldObservations(observations, depth = 0) {
|
|
22821
|
+
if (depth >= MAX_NESTED_FIELD_DEPTH)
|
|
22822
|
+
return [];
|
|
22823
|
+
const fields = [];
|
|
22824
|
+
for (const obs of observations) {
|
|
22825
|
+
const nonNullValues = obs.values.filter((v) => v !== null && v !== undefined);
|
|
22826
|
+
if (nonNullValues.length === 0)
|
|
22827
|
+
continue;
|
|
22828
|
+
const typeCounts = new Map;
|
|
22829
|
+
for (const value of nonNullValues) {
|
|
22830
|
+
const type = inferFieldType(value, obs.name);
|
|
22831
|
+
typeCounts.set(type, (typeCounts.get(type) || 0) + 1);
|
|
22832
|
+
}
|
|
22833
|
+
let fieldType = "text";
|
|
22834
|
+
let maxCount = 0;
|
|
22835
|
+
for (const [type, count] of typeCounts) {
|
|
22836
|
+
if (count > maxCount) {
|
|
22837
|
+
maxCount = count;
|
|
22838
|
+
fieldType = type;
|
|
22839
|
+
}
|
|
22840
|
+
}
|
|
22841
|
+
const field = {
|
|
22842
|
+
name: obs.name,
|
|
22843
|
+
type: fieldType,
|
|
22844
|
+
required: obs.presentCount === obs.totalEntries,
|
|
22845
|
+
examples: nonNullValues.slice(0, 3)
|
|
22846
|
+
};
|
|
22847
|
+
if (fieldType === "text" && !FREE_TEXT_FIELD_NAMES.has(normalizeFieldName(obs.name))) {
|
|
22848
|
+
const uniqueValues = [...new Set(nonNullValues.map((v) => String(v)))];
|
|
22849
|
+
const uniqueRatio = uniqueValues.length / nonNullValues.length;
|
|
22850
|
+
if (uniqueValues.length > 0 && uniqueValues.length <= MAX_SELECT_OPTIONS && nonNullValues.length >= 2 && uniqueRatio <= 0.8) {
|
|
22851
|
+
field.type = "select";
|
|
22852
|
+
field.options = uniqueValues.sort();
|
|
22853
|
+
}
|
|
22854
|
+
}
|
|
22855
|
+
if (fieldType === "array") {
|
|
22856
|
+
const allItems = nonNullValues.flatMap((v) => Array.isArray(v) ? v : []);
|
|
22857
|
+
if (allItems.length > 0) {
|
|
22858
|
+
const itemType = inferFieldType(allItems[0], obs.name);
|
|
22859
|
+
field.itemType = itemType;
|
|
22860
|
+
if (itemType === "text") {
|
|
22861
|
+
const uniqueItems = [...new Set(allItems.map((v) => String(v)))];
|
|
22862
|
+
if (uniqueItems.length <= MAX_SELECT_OPTIONS * 2) {
|
|
22863
|
+
field.options = uniqueItems.sort();
|
|
22864
|
+
}
|
|
22865
|
+
}
|
|
22866
|
+
if (itemType === "object") {
|
|
22867
|
+
const objectItems = allItems.filter((v) => typeof v === "object" && v !== null && !Array.isArray(v));
|
|
22868
|
+
if (objectItems.length > 0) {
|
|
22869
|
+
const subFieldMap = new Map;
|
|
22870
|
+
for (const item of objectItems) {
|
|
22871
|
+
collectFieldObservations(subFieldMap, item, objectItems.length);
|
|
22872
|
+
}
|
|
22873
|
+
field.fields = mergeFieldObservations(Array.from(subFieldMap.values()), depth + 1);
|
|
22874
|
+
}
|
|
22875
|
+
}
|
|
22876
|
+
}
|
|
22877
|
+
}
|
|
22878
|
+
if (fieldType === "object") {
|
|
22879
|
+
const objectValues = nonNullValues.filter((v) => typeof v === "object" && v !== null && !Array.isArray(v));
|
|
22880
|
+
if (objectValues.length > 0) {
|
|
22881
|
+
const subFieldMap = new Map;
|
|
22882
|
+
for (const item of objectValues) {
|
|
22883
|
+
collectFieldObservations(subFieldMap, item, objectValues.length);
|
|
22884
|
+
}
|
|
22885
|
+
field.fields = mergeFieldObservations(Array.from(subFieldMap.values()), depth + 1);
|
|
22886
|
+
}
|
|
22887
|
+
}
|
|
22888
|
+
fields.push(field);
|
|
22889
|
+
}
|
|
22890
|
+
return fields;
|
|
22891
|
+
}
|
|
22892
|
+
function collectFieldObservations(fieldMap, data, totalEntries) {
|
|
22893
|
+
for (const [key, value] of Object.entries(data)) {
|
|
22894
|
+
let obs = fieldMap.get(key);
|
|
22895
|
+
if (!obs) {
|
|
22896
|
+
obs = { name: key, values: [], presentCount: 0, totalEntries };
|
|
22897
|
+
fieldMap.set(key, obs);
|
|
22898
|
+
}
|
|
22899
|
+
obs.values.push(value);
|
|
22900
|
+
obs.presentCount++;
|
|
22901
|
+
}
|
|
22902
|
+
}
|
|
22903
|
+
function assembleCollectionDefinition(collectionName, contentDir, fieldMap, entryInfos, entryCount, extra) {
|
|
22904
|
+
for (const obs of fieldMap.values()) {
|
|
22905
|
+
obs.totalEntries = entryCount;
|
|
22906
|
+
}
|
|
22907
|
+
entryInfos.sort((a, b) => (a.title ?? a.slug).localeCompare(b.title ?? b.slug));
|
|
22908
|
+
const fields = mergeFieldObservations(Array.from(fieldMap.values()));
|
|
22909
|
+
const label = collectionName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
22910
|
+
return {
|
|
22911
|
+
name: collectionName,
|
|
22912
|
+
label,
|
|
22913
|
+
path: path2.join(contentDir, collectionName),
|
|
22914
|
+
entryCount,
|
|
22915
|
+
fields,
|
|
22916
|
+
fileExtension: "md",
|
|
22917
|
+
entries: entryInfos,
|
|
22918
|
+
...extra
|
|
22919
|
+
};
|
|
22920
|
+
}
|
|
22921
|
+
function getCollectionSourceBasePath(basePath, collectionName, contentDir) {
|
|
22922
|
+
const defaultCollectionPath = path2.join(contentDir, collectionName);
|
|
22923
|
+
if (path2.normalize(basePath) === path2.normalize(defaultCollectionPath)) {
|
|
22924
|
+
return path2.join(contentDir, collectionName);
|
|
22925
|
+
}
|
|
22926
|
+
return basePath;
|
|
22927
|
+
}
|
|
22928
|
+
async function buildCollectionDefinition(fs, basePath, sources, collectionName, contentDir) {
|
|
22929
|
+
if (sources.length === 0)
|
|
22930
|
+
return null;
|
|
22931
|
+
const sourceBasePath = getCollectionSourceBasePath(basePath, collectionName, contentDir);
|
|
22932
|
+
const hasMd = sources.some((s) => s.relPath.endsWith(".md"));
|
|
22933
|
+
const fileExtension = hasMd ? "md" : "mdx";
|
|
22934
|
+
const fieldMap = new Map;
|
|
22935
|
+
const allDirectives = {};
|
|
22936
|
+
const entryInfos = [];
|
|
22937
|
+
let hasDraft = false;
|
|
22938
|
+
const fileContents = await Promise.all(sources.map((s) => fs.readFile(path2.join(basePath, s.relPath)).catch(() => null)));
|
|
22939
|
+
for (let i = 0;i < sources.length; i++) {
|
|
22940
|
+
const source = sources[i];
|
|
22941
|
+
const content = fileContents[i];
|
|
22942
|
+
if (content === null)
|
|
22943
|
+
continue;
|
|
22944
|
+
const frontmatter = parseFrontmatter(content);
|
|
22945
|
+
const directives = parseFieldDirectives(content);
|
|
22946
|
+
for (const [key, value] of Object.entries(directives)) {
|
|
22947
|
+
if (!allDirectives[key]) {
|
|
22948
|
+
allDirectives[key] = value;
|
|
22949
|
+
}
|
|
22950
|
+
}
|
|
22951
|
+
const entryInfo = {
|
|
22952
|
+
slug: source.slug,
|
|
22953
|
+
sourcePath: path2.join(sourceBasePath, source.relPath)
|
|
22954
|
+
};
|
|
22955
|
+
if (frontmatter) {
|
|
22956
|
+
const title = deriveEntryTitle(frontmatter);
|
|
22957
|
+
if (title !== undefined) {
|
|
22958
|
+
entryInfo.title = title;
|
|
22959
|
+
}
|
|
22960
|
+
if (typeof frontmatter.draft === "boolean" && frontmatter.draft) {
|
|
22961
|
+
entryInfo.draft = true;
|
|
22962
|
+
}
|
|
22963
|
+
entryInfo.data = frontmatter;
|
|
22964
|
+
}
|
|
22965
|
+
entryInfos.push(entryInfo);
|
|
22966
|
+
if (!frontmatter)
|
|
22967
|
+
continue;
|
|
22968
|
+
if (frontmatter.draft === true)
|
|
22969
|
+
hasDraft = true;
|
|
22970
|
+
collectFieldObservations(fieldMap, frontmatter, sources.length);
|
|
22971
|
+
}
|
|
22972
|
+
const def = assembleCollectionDefinition(collectionName, contentDir, fieldMap, entryInfos, sources.length, {
|
|
22973
|
+
path: sourceBasePath,
|
|
22974
|
+
supportsDraft: hasDraft,
|
|
22975
|
+
fileExtension
|
|
22976
|
+
});
|
|
22977
|
+
assignFieldMetadata(def.fields, allDirectives);
|
|
22978
|
+
return def;
|
|
22979
|
+
}
|
|
22980
|
+
async function buildDataCollectionDefinition(fs, basePath, sources, collectionName, contentDir) {
|
|
22981
|
+
if (sources.length === 0)
|
|
22982
|
+
return null;
|
|
22983
|
+
const sourceBasePath = getCollectionSourceBasePath(basePath, collectionName, contentDir);
|
|
22984
|
+
const fieldMap = new Map;
|
|
22985
|
+
const entryInfos = [];
|
|
22986
|
+
const ext = sources.some((s) => s.relPath.endsWith(".json")) ? "json" : sources.some((s) => s.relPath.endsWith(".yaml")) ? "yaml" : "yml";
|
|
22987
|
+
const fileContents = await Promise.all(sources.map((s) => fs.readFile(path2.join(basePath, s.relPath)).catch(() => null)));
|
|
22988
|
+
for (let i = 0;i < sources.length; i++) {
|
|
22989
|
+
const source = sources[i];
|
|
22990
|
+
const raw = fileContents[i];
|
|
22991
|
+
if (raw === null)
|
|
22992
|
+
continue;
|
|
22993
|
+
let data = null;
|
|
22994
|
+
try {
|
|
22995
|
+
data = source.relPath.endsWith(".json") ? JSON.parse(raw) : import_yaml2.parse(raw);
|
|
22996
|
+
} catch {
|
|
22997
|
+
continue;
|
|
22998
|
+
}
|
|
22999
|
+
if (!data || typeof data !== "object")
|
|
23000
|
+
continue;
|
|
23001
|
+
const title = deriveEntryTitle(data);
|
|
23002
|
+
entryInfos.push({
|
|
23003
|
+
slug: source.slug,
|
|
23004
|
+
title,
|
|
23005
|
+
sourcePath: path2.join(sourceBasePath, source.relPath),
|
|
23006
|
+
data
|
|
23007
|
+
});
|
|
23008
|
+
collectFieldObservations(fieldMap, data, sources.length);
|
|
23009
|
+
}
|
|
23010
|
+
return assembleCollectionDefinition(collectionName, contentDir, fieldMap, entryInfos, sources.length, {
|
|
23011
|
+
type: "data",
|
|
23012
|
+
fileExtension: ext
|
|
23013
|
+
});
|
|
23014
|
+
}
|
|
23015
|
+
async function scanCollection(fs, collectionPath, collectionName, contentDir) {
|
|
23016
|
+
const dirEntries = await fs.list(collectionPath);
|
|
23017
|
+
if (dirEntries.length === 0)
|
|
23018
|
+
return null;
|
|
23019
|
+
const sources = [];
|
|
23020
|
+
const takenSlugs = new Set;
|
|
23021
|
+
for (const entry of dirEntries) {
|
|
23022
|
+
if (entry.isDirectory)
|
|
23023
|
+
continue;
|
|
23024
|
+
if (!entry.name.endsWith(".md") && !entry.name.endsWith(".mdx"))
|
|
23025
|
+
continue;
|
|
23026
|
+
const slug = entry.name.replace(/\.(md|mdx)$/, "");
|
|
23027
|
+
sources.push({ slug, relPath: entry.name });
|
|
23028
|
+
takenSlugs.add(slug);
|
|
23029
|
+
}
|
|
23030
|
+
const subdirs = dirEntries.filter((e) => e.isDirectory && !e.name.startsWith("_") && !e.name.startsWith("."));
|
|
23031
|
+
const indexLookups = await Promise.all(subdirs.map(async (dir) => {
|
|
23032
|
+
if (takenSlugs.has(dir.name))
|
|
23033
|
+
return null;
|
|
23034
|
+
for (const ext of ["md", "mdx"]) {
|
|
23035
|
+
const relPath = path2.join(dir.name, `index.${ext}`);
|
|
23036
|
+
if (await fs.exists(path2.join(collectionPath, relPath))) {
|
|
23037
|
+
return { slug: dir.name, relPath };
|
|
23038
|
+
}
|
|
23039
|
+
}
|
|
23040
|
+
return null;
|
|
23041
|
+
}));
|
|
23042
|
+
for (const entry of indexLookups) {
|
|
23043
|
+
if (entry)
|
|
23044
|
+
sources.push(entry);
|
|
23045
|
+
}
|
|
23046
|
+
if (sources.length === 0)
|
|
23047
|
+
return null;
|
|
23048
|
+
return await buildCollectionDefinition(fs, collectionPath, sources, collectionName, contentDir);
|
|
23049
|
+
}
|
|
23050
|
+
async function scanGlobCollection(fs, collectionName, baseRel, pattern, contentDir) {
|
|
23051
|
+
const matches = await fs.glob(path2.join(baseRel, pattern));
|
|
23052
|
+
const rels = matches.map((rel) => path2.relative(baseRel, rel)).filter((relToBase) => !relToBase.split("/").some((seg) => seg.startsWith("_") || seg.startsWith(".")));
|
|
23053
|
+
const mdSources = rels.filter((relToBase) => relToBase.endsWith(".md") || relToBase.endsWith(".mdx")).map((relToBase) => ({ slug: relToBase.replace(/\.(md|mdx)$/, ""), relPath: relToBase }));
|
|
23054
|
+
if (mdSources.length > 0) {
|
|
23055
|
+
return await buildCollectionDefinition(fs, baseRel, mdSources, collectionName, contentDir);
|
|
23056
|
+
}
|
|
23057
|
+
const dataSources = rels.filter((relToBase) => /\.(ya?ml|json)$/.test(relToBase)).map((relToBase) => ({ slug: relToBase.replace(/\.(ya?ml|json)$/, ""), relPath: relToBase }));
|
|
23058
|
+
if (dataSources.length > 0) {
|
|
23059
|
+
return await buildDataCollectionDefinition(fs, baseRel, dataSources, collectionName, contentDir);
|
|
23060
|
+
}
|
|
23061
|
+
return null;
|
|
23062
|
+
}
|
|
23063
|
+
function applyParsedConfig(collections, parsed) {
|
|
23064
|
+
for (const [collectionName, parsedColl] of parsed) {
|
|
23065
|
+
const def = collections[collectionName];
|
|
23066
|
+
if (!def)
|
|
23067
|
+
continue;
|
|
23068
|
+
if (parsedColl.fields.length > 0) {
|
|
23069
|
+
const schemaNames = new Set(parsedColl.fields.map((f) => f.name));
|
|
23070
|
+
def.fields = def.fields.filter((f) => schemaNames.has(f.name));
|
|
23071
|
+
}
|
|
23072
|
+
const fieldsByName = new Map(def.fields.map((f) => [f.name, f]));
|
|
23073
|
+
for (const pf of parsedColl.fields) {
|
|
23074
|
+
const field = fieldsByName.get(pf.name);
|
|
23075
|
+
if (!field)
|
|
23076
|
+
continue;
|
|
23077
|
+
applyParsedFieldOverrides(field, pf);
|
|
23078
|
+
}
|
|
23079
|
+
if (parsedColl.layout)
|
|
23080
|
+
def.layout = parsedColl.layout;
|
|
23081
|
+
if (parsedColl.pathname)
|
|
23082
|
+
def.pathname = parsedColl.pathname;
|
|
23083
|
+
if (parsedColl.fragment) {
|
|
23084
|
+
def.fragment = parsedColl.fragment;
|
|
23085
|
+
if (parsedColl.previewOf)
|
|
23086
|
+
def.previewOf = parsedColl.previewOf;
|
|
23087
|
+
}
|
|
23088
|
+
if (parsedColl.titleField) {
|
|
23089
|
+
def.titleField = parsedColl.titleField;
|
|
23090
|
+
applyCollectionTitleField(def, parsedColl.titleField);
|
|
23091
|
+
}
|
|
23092
|
+
}
|
|
23093
|
+
}
|
|
23094
|
+
function applyCollectionTitleField(def, titleField) {
|
|
23095
|
+
if (!def.entries)
|
|
23096
|
+
return;
|
|
23097
|
+
for (const entry of def.entries) {
|
|
23098
|
+
entry.title = entry.data ? deriveEntryTitle(entry.data, titleField) : undefined;
|
|
23099
|
+
}
|
|
23100
|
+
def.entries.sort((a, b) => (a.title ?? a.slug).localeCompare(b.title ?? b.slug));
|
|
23101
|
+
}
|
|
23102
|
+
function applyParsedFieldLayout(field, pf) {
|
|
23103
|
+
const layout = pf.layout;
|
|
23104
|
+
if (!layout)
|
|
23105
|
+
return;
|
|
23106
|
+
if (layout.label !== undefined)
|
|
23107
|
+
field.label = layout.label;
|
|
23108
|
+
if (layout.help !== undefined)
|
|
23109
|
+
field.help = layout.help;
|
|
23110
|
+
if (layout.group !== undefined)
|
|
23111
|
+
field.group = layout.group;
|
|
23112
|
+
if (layout.width !== undefined)
|
|
23113
|
+
field.width = layout.width;
|
|
23114
|
+
if (layout.order !== undefined)
|
|
23115
|
+
field.order = layout.order;
|
|
23116
|
+
if (layout.sidebar)
|
|
23117
|
+
field.position = "sidebar";
|
|
23118
|
+
if (layout.hidden)
|
|
23119
|
+
field.hidden = true;
|
|
23120
|
+
}
|
|
23121
|
+
function applyParsedFieldOverrides(field, pf) {
|
|
23122
|
+
if (pf.type) {
|
|
23123
|
+
field.type = pf.type;
|
|
23124
|
+
if (pf.options) {
|
|
23125
|
+
field.options = pf.options;
|
|
23126
|
+
field.optionsClosed = true;
|
|
23127
|
+
}
|
|
23128
|
+
}
|
|
23129
|
+
if (pf.itemType)
|
|
23130
|
+
field.itemType = pf.itemType;
|
|
23131
|
+
if (pf.hints)
|
|
23132
|
+
field.hints = pf.hints;
|
|
23133
|
+
if (pf.astroImage)
|
|
23134
|
+
field.astroImage = true;
|
|
23135
|
+
field.required = pf.required;
|
|
23136
|
+
applyParsedFieldLayout(field, pf);
|
|
23137
|
+
if (pf.fields) {
|
|
23138
|
+
const existingByName = new Map((field.fields ?? []).map((f) => [f.name, f]));
|
|
23139
|
+
field.fields = pf.fields.map((subPf) => {
|
|
23140
|
+
const existing = existingByName.get(subPf.name);
|
|
23141
|
+
if (existing) {
|
|
23142
|
+
applyParsedFieldOverrides(existing, subPf);
|
|
23143
|
+
return existing;
|
|
23144
|
+
}
|
|
23145
|
+
return parsedFieldToFieldDefinition(subPf);
|
|
23146
|
+
});
|
|
23147
|
+
}
|
|
23148
|
+
}
|
|
23149
|
+
function parsedFieldToFieldDefinition(pf) {
|
|
23150
|
+
const fd = {
|
|
23151
|
+
name: pf.name,
|
|
23152
|
+
type: pf.type ?? (pf.fields ? "object" : "text"),
|
|
23153
|
+
required: pf.required
|
|
23154
|
+
};
|
|
23155
|
+
if (pf.options) {
|
|
23156
|
+
fd.options = pf.options;
|
|
23157
|
+
fd.optionsClosed = true;
|
|
23158
|
+
}
|
|
23159
|
+
if (pf.itemType)
|
|
23160
|
+
fd.itemType = pf.itemType;
|
|
23161
|
+
if (pf.hints)
|
|
23162
|
+
fd.hints = pf.hints;
|
|
23163
|
+
if (pf.astroImage)
|
|
23164
|
+
fd.astroImage = true;
|
|
23165
|
+
if (pf.fields)
|
|
23166
|
+
fd.fields = pf.fields.map(parsedFieldToFieldDefinition);
|
|
23167
|
+
applyParsedFieldLayout(fd, pf);
|
|
23168
|
+
return fd;
|
|
23169
|
+
}
|
|
23170
|
+
function applyCollectionOrderBy(collections, parsed) {
|
|
23171
|
+
for (const [collectionName, parsedColl] of parsed) {
|
|
23172
|
+
const orderField = parsedColl.fields.find((f) => f.orderBy);
|
|
23173
|
+
if (!orderField?.orderBy)
|
|
23174
|
+
continue;
|
|
23175
|
+
const def = collections[collectionName];
|
|
23176
|
+
if (!def)
|
|
23177
|
+
continue;
|
|
23178
|
+
const fieldName = orderField.name;
|
|
23179
|
+
const direction = orderField.orderBy.direction;
|
|
23180
|
+
def.orderBy = fieldName;
|
|
23181
|
+
def.orderDirection = direction;
|
|
23182
|
+
if (def.entries && def.entries.length > 1) {
|
|
23183
|
+
const dir = direction === "desc" ? -1 : 1;
|
|
23184
|
+
def.entries.sort((a, b) => {
|
|
23185
|
+
const aVal = a.data?.[fieldName];
|
|
23186
|
+
const bVal = b.data?.[fieldName];
|
|
23187
|
+
if (aVal == null && bVal == null)
|
|
23188
|
+
return 0;
|
|
23189
|
+
if (aVal == null)
|
|
23190
|
+
return 1;
|
|
23191
|
+
if (bVal == null)
|
|
23192
|
+
return -1;
|
|
23193
|
+
if (typeof aVal === "number" && typeof bVal === "number")
|
|
23194
|
+
return (aVal - bVal) * dir;
|
|
23195
|
+
if (aVal instanceof Date && bVal instanceof Date)
|
|
23196
|
+
return (aVal.getTime() - bVal.getTime()) * dir;
|
|
23197
|
+
return String(aVal).localeCompare(String(bVal)) * dir;
|
|
23198
|
+
});
|
|
23199
|
+
}
|
|
23195
23200
|
}
|
|
23196
|
-
return "text";
|
|
23197
23201
|
}
|
|
23198
|
-
function
|
|
23199
|
-
|
|
23200
|
-
|
|
23201
|
-
|
|
23202
|
-
|
|
23203
|
-
const nonNullValues = obs.values.filter((v) => v !== null && v !== undefined);
|
|
23204
|
-
if (nonNullValues.length === 0)
|
|
23202
|
+
function detectReferenceFields(collections, parsed) {
|
|
23203
|
+
let appliedAny = false;
|
|
23204
|
+
for (const [collectionName, parsedColl] of parsed) {
|
|
23205
|
+
const def = collections[collectionName];
|
|
23206
|
+
if (!def)
|
|
23205
23207
|
continue;
|
|
23206
|
-
const
|
|
23207
|
-
|
|
23208
|
-
|
|
23209
|
-
|
|
23210
|
-
|
|
23211
|
-
|
|
23212
|
-
|
|
23213
|
-
|
|
23214
|
-
|
|
23215
|
-
|
|
23216
|
-
|
|
23208
|
+
for (const pf of parsedColl.fields) {
|
|
23209
|
+
if (!pf.reference)
|
|
23210
|
+
continue;
|
|
23211
|
+
const field = def.fields.find((f) => f.name === pf.name);
|
|
23212
|
+
if (!field)
|
|
23213
|
+
continue;
|
|
23214
|
+
appliedAny = true;
|
|
23215
|
+
if (pf.reference.isArray) {
|
|
23216
|
+
field.type = "array";
|
|
23217
|
+
field.itemType = "reference";
|
|
23218
|
+
} else {
|
|
23219
|
+
field.type = "reference";
|
|
23217
23220
|
}
|
|
23221
|
+
field.collection = pf.reference.target;
|
|
23222
|
+
field.options = undefined;
|
|
23218
23223
|
}
|
|
23219
|
-
|
|
23220
|
-
|
|
23221
|
-
|
|
23222
|
-
|
|
23223
|
-
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
|
|
23227
|
-
|
|
23228
|
-
if (uniqueValues.length > 0 && uniqueValues.length <= MAX_SELECT_OPTIONS && nonNullValues.length >= 2 && uniqueRatio <= 0.8) {
|
|
23229
|
-
field.type = "select";
|
|
23230
|
-
field.options = uniqueValues.sort();
|
|
23231
|
-
}
|
|
23224
|
+
}
|
|
23225
|
+
if (!appliedAny)
|
|
23226
|
+
detectReferenceFieldsBySlugMatch(collections);
|
|
23227
|
+
}
|
|
23228
|
+
function detectReferenceFieldsBySlugMatch(collections) {
|
|
23229
|
+
const collectionSlugs = new Map;
|
|
23230
|
+
for (const [name, def] of Object.entries(collections)) {
|
|
23231
|
+
if (def.entries && def.entries.length > 0) {
|
|
23232
|
+
collectionSlugs.set(name, new Set(def.entries.map((e) => e.slug)));
|
|
23232
23233
|
}
|
|
23233
|
-
|
|
23234
|
-
|
|
23235
|
-
|
|
23236
|
-
|
|
23237
|
-
field.
|
|
23238
|
-
if (
|
|
23239
|
-
|
|
23240
|
-
|
|
23241
|
-
|
|
23234
|
+
}
|
|
23235
|
+
for (const [collectionName, def] of Object.entries(collections)) {
|
|
23236
|
+
for (const field of def.fields) {
|
|
23237
|
+
if ((field.type === "text" || field.type === "select") && field.examples) {
|
|
23238
|
+
const stringExamples = field.examples.filter((v) => typeof v === "string");
|
|
23239
|
+
if (stringExamples.length === 0)
|
|
23240
|
+
continue;
|
|
23241
|
+
const candidates = [];
|
|
23242
|
+
for (const [targetName, slugs] of collectionSlugs) {
|
|
23243
|
+
if (targetName === collectionName)
|
|
23244
|
+
continue;
|
|
23245
|
+
const matchCount = stringExamples.filter((v) => slugs.has(v)).length;
|
|
23246
|
+
if (matchCount > 0 && matchCount === stringExamples.length) {
|
|
23247
|
+
candidates.push({ name: targetName, slugs });
|
|
23242
23248
|
}
|
|
23243
23249
|
}
|
|
23244
|
-
|
|
23245
|
-
|
|
23246
|
-
|
|
23247
|
-
|
|
23248
|
-
|
|
23249
|
-
|
|
23250
|
+
let bestTarget;
|
|
23251
|
+
if (candidates.length === 1) {
|
|
23252
|
+
bestTarget = candidates[0].name;
|
|
23253
|
+
} else if (candidates.length > 1) {
|
|
23254
|
+
const allValues = def.entries?.flatMap((e) => {
|
|
23255
|
+
const v = e.data?.[field.name];
|
|
23256
|
+
return typeof v === "string" ? [v] : [];
|
|
23257
|
+
}) ?? stringExamples;
|
|
23258
|
+
let bestOverlap = 0;
|
|
23259
|
+
for (const c of candidates) {
|
|
23260
|
+
const overlap = allValues.filter((v) => c.slugs.has(v)).length;
|
|
23261
|
+
if (overlap > bestOverlap) {
|
|
23262
|
+
bestOverlap = overlap;
|
|
23263
|
+
bestTarget = c.name;
|
|
23264
|
+
}
|
|
23265
|
+
}
|
|
23266
|
+
}
|
|
23267
|
+
if (bestTarget) {
|
|
23268
|
+
field.type = "reference";
|
|
23269
|
+
field.collection = bestTarget;
|
|
23270
|
+
field.options = undefined;
|
|
23271
|
+
}
|
|
23272
|
+
}
|
|
23273
|
+
if (field.type === "array" && field.itemType === "text" && field.options) {
|
|
23274
|
+
let bestTarget;
|
|
23275
|
+
let bestOverlap = 0;
|
|
23276
|
+
for (const [targetName, slugs] of collectionSlugs) {
|
|
23277
|
+
if (targetName === collectionName)
|
|
23278
|
+
continue;
|
|
23279
|
+
const matchCount = field.options.filter((v) => slugs.has(v)).length;
|
|
23280
|
+
if (matchCount > 0 && matchCount >= field.options.length * 0.5) {
|
|
23281
|
+
if (matchCount > bestOverlap) {
|
|
23282
|
+
bestOverlap = matchCount;
|
|
23283
|
+
bestTarget = targetName;
|
|
23250
23284
|
}
|
|
23251
|
-
field.fields = mergeFieldObservations(Array.from(subFieldMap.values()), depth + 1);
|
|
23252
23285
|
}
|
|
23253
23286
|
}
|
|
23287
|
+
if (bestTarget) {
|
|
23288
|
+
field.type = "array";
|
|
23289
|
+
field.itemType = "reference";
|
|
23290
|
+
field.collection = bestTarget;
|
|
23291
|
+
field.options = undefined;
|
|
23292
|
+
}
|
|
23254
23293
|
}
|
|
23255
23294
|
}
|
|
23256
|
-
|
|
23257
|
-
|
|
23258
|
-
|
|
23259
|
-
|
|
23260
|
-
|
|
23261
|
-
|
|
23295
|
+
}
|
|
23296
|
+
}
|
|
23297
|
+
function assignSemanticRoles(collections) {
|
|
23298
|
+
for (const def of Object.values(collections)) {
|
|
23299
|
+
let toggle;
|
|
23300
|
+
let dateByName;
|
|
23301
|
+
let dateByType;
|
|
23302
|
+
for (const field of def.fields) {
|
|
23303
|
+
if (field.hidden || field.role)
|
|
23304
|
+
continue;
|
|
23305
|
+
const normalized = normalizeFieldName(field.name);
|
|
23306
|
+
if (!toggle && field.type === "boolean" && PUBLISH_TOGGLE_NAMES.has(normalized)) {
|
|
23307
|
+
toggle = field;
|
|
23308
|
+
} else if (!dateByName && PUBLISH_DATE_NAMES.has(normalized)) {
|
|
23309
|
+
dateByName = field;
|
|
23310
|
+
} else if (!dateByType && (field.type === "date" || field.type === "datetime")) {
|
|
23311
|
+
dateByType = field;
|
|
23312
|
+
}
|
|
23313
|
+
}
|
|
23314
|
+
if (toggle)
|
|
23315
|
+
toggle.role = "publish-toggle";
|
|
23316
|
+
const date = dateByName ?? dateByType;
|
|
23317
|
+
if (date)
|
|
23318
|
+
date.role = "publish-date";
|
|
23319
|
+
}
|
|
23320
|
+
}
|
|
23321
|
+
function detectDerivedHrefFields(collections) {
|
|
23322
|
+
for (const def of Object.values(collections)) {
|
|
23323
|
+
const fieldsByName = new Map(def.fields.map((f) => [f.name, f]));
|
|
23324
|
+
for (const field of def.fields) {
|
|
23325
|
+
if (field.hidden || field.derivedFrom)
|
|
23326
|
+
continue;
|
|
23327
|
+
const lowerName = field.name.toLowerCase();
|
|
23328
|
+
for (const suffix of HREF_SUFFIXES) {
|
|
23329
|
+
if (!lowerName.endsWith(suffix))
|
|
23330
|
+
continue;
|
|
23331
|
+
const baseName = field.name.slice(0, -suffix.length);
|
|
23332
|
+
if (!baseName)
|
|
23333
|
+
continue;
|
|
23334
|
+
let sourceField = fieldsByName.get(baseName);
|
|
23335
|
+
if (!sourceField) {
|
|
23336
|
+
const lowerBase = baseName.toLowerCase();
|
|
23337
|
+
for (const f of fieldsByName.values()) {
|
|
23338
|
+
if (f.name.toLowerCase() === lowerBase) {
|
|
23339
|
+
sourceField = f;
|
|
23340
|
+
break;
|
|
23341
|
+
}
|
|
23342
|
+
}
|
|
23343
|
+
}
|
|
23344
|
+
if (!sourceField || !sourceField.examples || !field.examples)
|
|
23345
|
+
continue;
|
|
23346
|
+
const sourceExamples = sourceField.examples.filter((v) => typeof v === "string");
|
|
23347
|
+
const derivedExamples = field.examples.filter((v) => typeof v === "string");
|
|
23348
|
+
if (sourceExamples.length === 0 || derivedExamples.length === 0)
|
|
23349
|
+
continue;
|
|
23350
|
+
const expectedHrefs = new Set(sourceExamples.map(slugifyHref));
|
|
23351
|
+
const allMatch = derivedExamples.every((v) => expectedHrefs.has(v));
|
|
23352
|
+
if (allMatch) {
|
|
23353
|
+
field.hidden = true;
|
|
23354
|
+
field.derivedFrom = sourceField.name;
|
|
23355
|
+
break;
|
|
23262
23356
|
}
|
|
23263
|
-
field.fields = mergeFieldObservations(Array.from(subFieldMap.values()), depth + 1);
|
|
23264
23357
|
}
|
|
23265
23358
|
}
|
|
23266
|
-
fields.push(field);
|
|
23267
23359
|
}
|
|
23268
|
-
return fields;
|
|
23269
23360
|
}
|
|
23270
|
-
function
|
|
23271
|
-
|
|
23272
|
-
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
|
|
23361
|
+
async function scanDataCollection(fs, collectionPath, collectionName, contentDir) {
|
|
23362
|
+
const dirEntries = await fs.list(collectionPath);
|
|
23363
|
+
if (dirEntries.length === 0)
|
|
23364
|
+
return null;
|
|
23365
|
+
const sources = [];
|
|
23366
|
+
const takenSlugs = new Set;
|
|
23367
|
+
for (const entry of dirEntries) {
|
|
23368
|
+
if (entry.isDirectory)
|
|
23369
|
+
continue;
|
|
23370
|
+
if (!entry.name.endsWith(".json") && !entry.name.endsWith(".yaml") && !entry.name.endsWith(".yml"))
|
|
23371
|
+
continue;
|
|
23372
|
+
const slug = entry.name.replace(/\.(json|ya?ml)$/, "");
|
|
23373
|
+
sources.push({ slug, relPath: entry.name });
|
|
23374
|
+
takenSlugs.add(slug);
|
|
23375
|
+
}
|
|
23376
|
+
const subdirs = dirEntries.filter((e) => e.isDirectory && !e.name.startsWith("_") && !e.name.startsWith("."));
|
|
23377
|
+
const indexLookups = await Promise.all(subdirs.map(async (dir) => {
|
|
23378
|
+
if (takenSlugs.has(dir.name))
|
|
23379
|
+
return null;
|
|
23380
|
+
for (const indexExt of ["json", "yaml", "yml"]) {
|
|
23381
|
+
const relPath = path2.join(dir.name, `index.${indexExt}`);
|
|
23382
|
+
if (await fs.exists(path2.join(collectionPath, relPath))) {
|
|
23383
|
+
return { slug: dir.name, relPath };
|
|
23384
|
+
}
|
|
23276
23385
|
}
|
|
23277
|
-
|
|
23278
|
-
|
|
23386
|
+
return null;
|
|
23387
|
+
}));
|
|
23388
|
+
for (const entry of indexLookups) {
|
|
23389
|
+
if (entry)
|
|
23390
|
+
sources.push(entry);
|
|
23279
23391
|
}
|
|
23392
|
+
return await buildDataCollectionDefinition(fs, collectionPath, sources, collectionName, contentDir);
|
|
23280
23393
|
}
|
|
23281
|
-
function
|
|
23282
|
-
|
|
23283
|
-
|
|
23394
|
+
function normalizeBasePath(basePath) {
|
|
23395
|
+
return path2.normalize(basePath.replace(/^\.[/\\]+/, "")).replace(/[/\\]+$/, "");
|
|
23396
|
+
}
|
|
23397
|
+
function patternCoversWholeCollection(pattern, definition) {
|
|
23398
|
+
const match = pattern.trim().replace(/^\.[/\\]+/, "").match(WHOLE_DIRECTORY_PATTERN);
|
|
23399
|
+
if (!match)
|
|
23400
|
+
return false;
|
|
23401
|
+
const [, braceGroup, singleExtension] = match;
|
|
23402
|
+
if (braceGroup === undefined && singleExtension === undefined)
|
|
23403
|
+
return true;
|
|
23404
|
+
const patternExtensions = new Set((braceGroup !== undefined ? braceGroup.split(",") : [singleExtension]).map((ext) => ext.trim().replace(/^\./, "").toLowerCase()).filter(Boolean));
|
|
23405
|
+
const scannedExtensions = new Set((definition.entries ?? []).map((entry) => path2.extname(entry.sourcePath).slice(1).toLowerCase()).filter(Boolean));
|
|
23406
|
+
if (scannedExtensions.size === 0)
|
|
23407
|
+
return false;
|
|
23408
|
+
for (const ext of scannedExtensions) {
|
|
23409
|
+
if (!patternExtensions.has(ext))
|
|
23410
|
+
return false;
|
|
23284
23411
|
}
|
|
23285
|
-
|
|
23286
|
-
const fields = mergeFieldObservations(Array.from(fieldMap.values()));
|
|
23287
|
-
const label = collectionName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
23288
|
-
return {
|
|
23289
|
-
name: collectionName,
|
|
23290
|
-
label,
|
|
23291
|
-
path: path.join(contentDir, collectionName),
|
|
23292
|
-
entryCount,
|
|
23293
|
-
fields,
|
|
23294
|
-
fileExtension: "md",
|
|
23295
|
-
entries: entryInfos,
|
|
23296
|
-
...extra
|
|
23297
|
-
};
|
|
23412
|
+
return true;
|
|
23298
23413
|
}
|
|
23299
|
-
function
|
|
23300
|
-
const
|
|
23301
|
-
|
|
23302
|
-
|
|
23414
|
+
async function scanCollections(fs, contentDir = "src/content", parseCache = new Map) {
|
|
23415
|
+
const collections = {};
|
|
23416
|
+
const entries = await fs.list(contentDir);
|
|
23417
|
+
const scanPromises = entries.filter((entry) => entry.isDirectory && !entry.name.startsWith("_") && !entry.name.startsWith(".")).map(async (entry) => {
|
|
23418
|
+
try {
|
|
23419
|
+
const collectionPath = path2.join(contentDir, entry.name);
|
|
23420
|
+
const definition = await scanCollection(fs, collectionPath, entry.name, contentDir) ?? await scanDataCollection(fs, collectionPath, entry.name, contentDir);
|
|
23421
|
+
if (definition) {
|
|
23422
|
+
collections[entry.name] = definition;
|
|
23423
|
+
}
|
|
23424
|
+
} catch (error) {
|
|
23425
|
+
console.warn(`[cms] Skipping collection "${entry.name}" \u2014 scan failed:`, error);
|
|
23426
|
+
}
|
|
23427
|
+
});
|
|
23428
|
+
await Promise.all(scanPromises);
|
|
23429
|
+
const parsed = await parseContentConfig(fs, parseCache);
|
|
23430
|
+
const scannedNameByBasePath = new Map;
|
|
23431
|
+
for (const [name, definition] of Object.entries(collections)) {
|
|
23432
|
+
scannedNameByBasePath.set(normalizeBasePath(definition.path), name);
|
|
23303
23433
|
}
|
|
23304
|
-
|
|
23434
|
+
const rekeyedScannedNames = new Map;
|
|
23435
|
+
for (const [collectionName, parsedCollection] of parsed) {
|
|
23436
|
+
if (collections[collectionName])
|
|
23437
|
+
continue;
|
|
23438
|
+
if (!parsedCollection.loaderBase || !parsedCollection.loaderPattern)
|
|
23439
|
+
continue;
|
|
23440
|
+
const scannedName = scannedNameByBasePath.get(normalizeBasePath(parsedCollection.loaderBase));
|
|
23441
|
+
const scannedDefinition = scannedName ? collections[scannedName] : undefined;
|
|
23442
|
+
if (scannedName && scannedDefinition && patternCoversWholeCollection(parsedCollection.loaderPattern, scannedDefinition)) {
|
|
23443
|
+
delete collections[scannedName];
|
|
23444
|
+
scannedDefinition.name = collectionName;
|
|
23445
|
+
for (const other of Object.values(collections)) {
|
|
23446
|
+
if (other.parentCollection === scannedName)
|
|
23447
|
+
other.parentCollection = collectionName;
|
|
23448
|
+
}
|
|
23449
|
+
collections[collectionName] = scannedDefinition;
|
|
23450
|
+
rekeyedScannedNames.set(scannedName, collectionName);
|
|
23451
|
+
continue;
|
|
23452
|
+
}
|
|
23453
|
+
let definition;
|
|
23454
|
+
try {
|
|
23455
|
+
definition = await scanGlobCollection(fs, collectionName, parsedCollection.loaderBase, parsedCollection.loaderPattern, contentDir);
|
|
23456
|
+
} catch (error) {
|
|
23457
|
+
console.warn(`[cms] Skipping glob collection "${collectionName}" \u2014 scan failed:`, error);
|
|
23458
|
+
continue;
|
|
23459
|
+
}
|
|
23460
|
+
if (!definition)
|
|
23461
|
+
continue;
|
|
23462
|
+
const baseName = parsedCollection.loaderBase.replace(/[/\\]+$/, "").split(/[/\\]/).pop();
|
|
23463
|
+
const parentName = !baseName ? undefined : collections[baseName] ? baseName : rekeyedScannedNames.get(baseName);
|
|
23464
|
+
if (parentName && parentName !== collectionName) {
|
|
23465
|
+
definition.parentCollection = parentName;
|
|
23466
|
+
}
|
|
23467
|
+
collections[collectionName] = definition;
|
|
23468
|
+
}
|
|
23469
|
+
applyParsedConfig(collections, parsed);
|
|
23470
|
+
detectReferenceFields(collections, parsed);
|
|
23471
|
+
detectDerivedHrefFields(collections);
|
|
23472
|
+
assignSemanticRoles(collections);
|
|
23473
|
+
applyCollectionOrderBy(collections, parsed);
|
|
23474
|
+
warnOnPathnameCollisions(collections);
|
|
23475
|
+
return collections;
|
|
23305
23476
|
}
|
|
23306
|
-
|
|
23307
|
-
|
|
23308
|
-
|
|
23309
|
-
const sourceBasePath = getCollectionSourceBasePath(basePath, collectionName, contentDir);
|
|
23310
|
-
const hasMd = sources.some((s) => s.relPath.endsWith(".md"));
|
|
23311
|
-
const fileExtension = hasMd ? "md" : "mdx";
|
|
23312
|
-
const fieldMap = new Map;
|
|
23313
|
-
const allDirectives = {};
|
|
23314
|
-
const entryInfos = [];
|
|
23315
|
-
let hasDraft = false;
|
|
23316
|
-
const fileContents = await Promise.all(sources.map((s) => fs.readFile(path.join(basePath, s.relPath)).catch(() => null)));
|
|
23317
|
-
for (let i = 0;i < sources.length; i++) {
|
|
23318
|
-
const source = sources[i];
|
|
23319
|
-
const content = fileContents[i];
|
|
23320
|
-
if (content === null)
|
|
23477
|
+
function warnOnPathnameCollisions(collections) {
|
|
23478
|
+
for (const def of Object.values(collections)) {
|
|
23479
|
+
if (!def.pathname || !def.entries)
|
|
23321
23480
|
continue;
|
|
23322
|
-
const
|
|
23323
|
-
const
|
|
23324
|
-
|
|
23325
|
-
if (!
|
|
23326
|
-
|
|
23481
|
+
const bySlug = new Map;
|
|
23482
|
+
for (const entry of def.entries) {
|
|
23483
|
+
const pathname = resolvePathnameFromSpec(def, entry.data);
|
|
23484
|
+
if (!pathname)
|
|
23485
|
+
continue;
|
|
23486
|
+
const prev = bySlug.get(pathname);
|
|
23487
|
+
if (prev) {
|
|
23488
|
+
console.warn(`[cms] collection "${def.name}": pathname rule maps both "${prev}" and "${entry.slug}" to "${pathname}" \u2014 these entries will collide on one URL`);
|
|
23489
|
+
} else {
|
|
23490
|
+
bySlug.set(pathname, entry.slug);
|
|
23327
23491
|
}
|
|
23328
23492
|
}
|
|
23329
|
-
|
|
23330
|
-
|
|
23331
|
-
|
|
23332
|
-
|
|
23333
|
-
|
|
23334
|
-
|
|
23335
|
-
|
|
23336
|
-
|
|
23493
|
+
}
|
|
23494
|
+
}
|
|
23495
|
+
var import_yaml2, DATE_PATTERN, URL_PATTERN, IMAGE_EXTENSIONS, MAX_SELECT_OPTIONS = 10, TEXTAREA_MIN_LENGTH = 200, SIDEBAR_FIELD_NAMES, DIRECTIVE_PATTERN, FREE_TEXT_FIELD_NAMES, PUBLISH_TOGGLE_NAMES, PUBLISH_DATE_NAMES, TITLE_FALLBACK_FIELDS, FRONTMATTER_PATTERN, MAX_NESTED_FIELD_DEPTH = 16, HREF_SUFFIXES, WHOLE_DIRECTORY_PATTERN;
|
|
23496
|
+
var init_collection_scanner = __esm(() => {
|
|
23497
|
+
init_content_config_ast();
|
|
23498
|
+
import_yaml2 = __toESM(require_dist(), 1);
|
|
23499
|
+
DATE_PATTERN = /^\d{4}-\d{2}-\d{2}/;
|
|
23500
|
+
URL_PATTERN = /^(https?:\/\/|\/)/;
|
|
23501
|
+
IMAGE_EXTENSIONS = /\.(jpg|jpeg|png|gif|webp|svg|avif)$/i;
|
|
23502
|
+
SIDEBAR_FIELD_NAMES = new Set([
|
|
23503
|
+
"title",
|
|
23504
|
+
"date",
|
|
23505
|
+
"pubdate",
|
|
23506
|
+
"publishdate",
|
|
23507
|
+
"draft",
|
|
23508
|
+
"image",
|
|
23509
|
+
"featuredimage",
|
|
23510
|
+
"cover",
|
|
23511
|
+
"coverimage",
|
|
23512
|
+
"thumbnail",
|
|
23513
|
+
"author"
|
|
23514
|
+
]);
|
|
23515
|
+
DIRECTIVE_PATTERN = /^\s*@(position|group)\s+(.+)$/;
|
|
23516
|
+
FREE_TEXT_FIELD_NAMES = new Set([
|
|
23517
|
+
"title",
|
|
23518
|
+
"name",
|
|
23519
|
+
"description",
|
|
23520
|
+
"summary",
|
|
23521
|
+
"excerpt",
|
|
23522
|
+
"subtitle",
|
|
23523
|
+
"heading",
|
|
23524
|
+
"headline",
|
|
23525
|
+
"slug",
|
|
23526
|
+
"alt",
|
|
23527
|
+
"caption"
|
|
23528
|
+
]);
|
|
23529
|
+
PUBLISH_TOGGLE_NAMES = new Set(["draft", "isdraft", "published", "ispublished", "unpublished"]);
|
|
23530
|
+
PUBLISH_DATE_NAMES = new Set([
|
|
23531
|
+
"date",
|
|
23532
|
+
"pubdate",
|
|
23533
|
+
"publishdate",
|
|
23534
|
+
"publisheddate",
|
|
23535
|
+
"publishedate",
|
|
23536
|
+
"publishedat",
|
|
23537
|
+
"datepublished"
|
|
23538
|
+
]);
|
|
23539
|
+
TITLE_FALLBACK_FIELDS = ["title", "name", "label"];
|
|
23540
|
+
FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
23541
|
+
HREF_SUFFIXES = ["href", "url", "link", "slug", "path"];
|
|
23542
|
+
WHOLE_DIRECTORY_PATTERN = /^(?:\*\*[/\\])*\*(?:\.(?:\{([^{}/\\]*)\}|([A-Za-z0-9]+)))?$/;
|
|
23543
|
+
});
|
|
23544
|
+
|
|
23545
|
+
// ../cms-core/src/component-registry.ts
|
|
23546
|
+
var import_parser2;
|
|
23547
|
+
var init_component_registry = __esm(() => {
|
|
23548
|
+
import_parser2 = __toESM(require_lib(), 1);
|
|
23549
|
+
});
|
|
23550
|
+
|
|
23551
|
+
// ../cms-core/src/media/local.ts
|
|
23552
|
+
function mimeFromExt(ext) {
|
|
23553
|
+
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
23554
|
+
}
|
|
23555
|
+
var MIME_BY_EXT;
|
|
23556
|
+
var init_local = __esm(() => {
|
|
23557
|
+
MIME_BY_EXT = {
|
|
23558
|
+
".jpg": "image/jpeg",
|
|
23559
|
+
".jpeg": "image/jpeg",
|
|
23560
|
+
".png": "image/png",
|
|
23561
|
+
".gif": "image/gif",
|
|
23562
|
+
".webp": "image/webp",
|
|
23563
|
+
".avif": "image/avif",
|
|
23564
|
+
".svg": "image/svg+xml",
|
|
23565
|
+
".ico": "image/x-icon",
|
|
23566
|
+
".mp4": "video/mp4",
|
|
23567
|
+
".webm": "video/webm",
|
|
23568
|
+
".pdf": "application/pdf"
|
|
23569
|
+
};
|
|
23570
|
+
});
|
|
23571
|
+
|
|
23572
|
+
// ../cms-core/src/handlers/entry-ops.ts
|
|
23573
|
+
var import_yaml3;
|
|
23574
|
+
var init_entry_ops = __esm(() => {
|
|
23575
|
+
init_collection_scanner();
|
|
23576
|
+
init_content_config_ast();
|
|
23577
|
+
init_local();
|
|
23578
|
+
import_yaml3 = __toESM(require_dist(), 1);
|
|
23579
|
+
});
|
|
23580
|
+
|
|
23581
|
+
// ../cms-core/src/handlers/page-ops.ts
|
|
23582
|
+
var init_page_ops = () => {};
|
|
23583
|
+
// ../cms-core/src/core.ts
|
|
23584
|
+
var init_core = __esm(() => {
|
|
23585
|
+
init_collection_scanner();
|
|
23586
|
+
init_component_registry();
|
|
23587
|
+
init_entry_ops();
|
|
23588
|
+
init_page_ops();
|
|
23589
|
+
});
|
|
23590
|
+
|
|
23591
|
+
// ../cms-core/src/fs/glob.ts
|
|
23592
|
+
function globToRegExp(glob) {
|
|
23593
|
+
let re = "";
|
|
23594
|
+
for (let i = 0;i < glob.length; i++) {
|
|
23595
|
+
const c = glob[i];
|
|
23596
|
+
if (c === "*") {
|
|
23597
|
+
if (glob[i + 1] === "*") {
|
|
23598
|
+
re += ".*";
|
|
23599
|
+
i++;
|
|
23600
|
+
if (glob[i + 1] === "/")
|
|
23601
|
+
i++;
|
|
23602
|
+
} else {
|
|
23603
|
+
re += "[^/]*";
|
|
23337
23604
|
}
|
|
23338
|
-
|
|
23339
|
-
|
|
23605
|
+
} else if (c === "?") {
|
|
23606
|
+
re += "[^/]";
|
|
23607
|
+
} else if (c === "{") {
|
|
23608
|
+
const end = glob.indexOf("}", i);
|
|
23609
|
+
if (end === -1) {
|
|
23610
|
+
re += "\\{";
|
|
23611
|
+
} else {
|
|
23612
|
+
const opts = glob.slice(i + 1, end).split(",").map((s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&"));
|
|
23613
|
+
re += `(?:${opts.join("|")})`;
|
|
23614
|
+
i = end;
|
|
23340
23615
|
}
|
|
23341
|
-
|
|
23616
|
+
} else if (".+^$()|[]\\".includes(c)) {
|
|
23617
|
+
re += `\\${c}`;
|
|
23618
|
+
} else {
|
|
23619
|
+
re += c;
|
|
23342
23620
|
}
|
|
23343
|
-
entryInfos.push(entryInfo);
|
|
23344
|
-
if (!frontmatter)
|
|
23345
|
-
continue;
|
|
23346
|
-
if (frontmatter.draft === true)
|
|
23347
|
-
hasDraft = true;
|
|
23348
|
-
collectFieldObservations(fieldMap, frontmatter, sources.length);
|
|
23349
23621
|
}
|
|
23350
|
-
|
|
23351
|
-
path: sourceBasePath,
|
|
23352
|
-
supportsDraft: hasDraft,
|
|
23353
|
-
fileExtension
|
|
23354
|
-
});
|
|
23355
|
-
assignFieldMetadata(def.fields, allDirectives);
|
|
23356
|
-
return def;
|
|
23622
|
+
return new RegExp(`^${re}$`);
|
|
23357
23623
|
}
|
|
23358
|
-
|
|
23359
|
-
|
|
23360
|
-
|
|
23361
|
-
|
|
23362
|
-
|
|
23363
|
-
const
|
|
23364
|
-
const
|
|
23365
|
-
const
|
|
23366
|
-
|
|
23367
|
-
|
|
23368
|
-
|
|
23369
|
-
if (raw === null)
|
|
23370
|
-
continue;
|
|
23371
|
-
let data = null;
|
|
23372
|
-
try {
|
|
23373
|
-
data = source.relPath.endsWith(".json") ? JSON.parse(raw) : import_yaml.parse(raw);
|
|
23374
|
-
} catch {
|
|
23375
|
-
continue;
|
|
23376
|
-
}
|
|
23377
|
-
if (!data || typeof data !== "object")
|
|
23378
|
-
continue;
|
|
23379
|
-
const title = deriveEntryTitle(data);
|
|
23380
|
-
entryInfos.push({
|
|
23381
|
-
slug: source.slug,
|
|
23382
|
-
title,
|
|
23383
|
-
sourcePath: path.join(sourceBasePath, source.relPath),
|
|
23384
|
-
data
|
|
23385
|
-
});
|
|
23386
|
-
collectFieldObservations(fieldMap, data, sources.length);
|
|
23624
|
+
|
|
23625
|
+
// ../cms-core/src/fs/node-fs.ts
|
|
23626
|
+
import fs from "fs/promises";
|
|
23627
|
+
import path3 from "path";
|
|
23628
|
+
function resolveWithinRoot(root, filePath) {
|
|
23629
|
+
const resolvedRoot = path3.resolve(root);
|
|
23630
|
+
const isAbsoluteFs = filePath.startsWith(resolvedRoot);
|
|
23631
|
+
const normalizedPath = !isAbsoluteFs && filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
23632
|
+
const fullPath = path3.isAbsolute(normalizedPath) ? path3.resolve(normalizedPath) : path3.resolve(resolvedRoot, normalizedPath);
|
|
23633
|
+
if (!fullPath.startsWith(resolvedRoot + path3.sep) && fullPath !== resolvedRoot) {
|
|
23634
|
+
throw new Error(`Path traversal detected: ${filePath}`);
|
|
23387
23635
|
}
|
|
23388
|
-
return
|
|
23389
|
-
type: "data",
|
|
23390
|
-
fileExtension: ext
|
|
23391
|
-
});
|
|
23636
|
+
return fullPath;
|
|
23392
23637
|
}
|
|
23393
|
-
|
|
23394
|
-
|
|
23395
|
-
|
|
23396
|
-
|
|
23397
|
-
|
|
23398
|
-
|
|
23399
|
-
|
|
23400
|
-
|
|
23401
|
-
|
|
23402
|
-
if (!entry.name.endsWith(".md") && !entry.name.endsWith(".mdx"))
|
|
23403
|
-
continue;
|
|
23404
|
-
const slug = entry.name.replace(/\.(md|mdx)$/, "");
|
|
23405
|
-
sources.push({ slug, relPath: entry.name });
|
|
23406
|
-
takenSlugs.add(slug);
|
|
23638
|
+
function isNodeError(error) {
|
|
23639
|
+
return error instanceof Error && "code" in error;
|
|
23640
|
+
}
|
|
23641
|
+
async function walkFiles(absRoot, absDir) {
|
|
23642
|
+
let dirEntries;
|
|
23643
|
+
try {
|
|
23644
|
+
dirEntries = await fs.readdir(absDir, { withFileTypes: true });
|
|
23645
|
+
} catch {
|
|
23646
|
+
return [];
|
|
23407
23647
|
}
|
|
23408
|
-
const
|
|
23409
|
-
const
|
|
23410
|
-
|
|
23411
|
-
|
|
23412
|
-
|
|
23413
|
-
|
|
23414
|
-
|
|
23415
|
-
return { slug: dir.name, relPath };
|
|
23416
|
-
}
|
|
23648
|
+
const out = [];
|
|
23649
|
+
for (const entry of dirEntries) {
|
|
23650
|
+
const abs = path3.join(absDir, entry.name);
|
|
23651
|
+
if (entry.isDirectory()) {
|
|
23652
|
+
out.push(...await walkFiles(absRoot, abs));
|
|
23653
|
+
} else if (entry.isFile()) {
|
|
23654
|
+
out.push(path3.relative(absRoot, abs).split(path3.sep).join("/"));
|
|
23417
23655
|
}
|
|
23418
|
-
return null;
|
|
23419
|
-
}));
|
|
23420
|
-
for (const entry of indexLookups) {
|
|
23421
|
-
if (entry)
|
|
23422
|
-
sources.push(entry);
|
|
23423
23656
|
}
|
|
23424
|
-
|
|
23425
|
-
return null;
|
|
23426
|
-
return await buildCollectionDefinition(fs, collectionPath, sources, collectionName, contentDir);
|
|
23657
|
+
return out;
|
|
23427
23658
|
}
|
|
23428
|
-
|
|
23429
|
-
const
|
|
23430
|
-
const
|
|
23431
|
-
|
|
23432
|
-
|
|
23433
|
-
|
|
23659
|
+
function staticPrefixDir(pattern) {
|
|
23660
|
+
const segments = pattern.split("/");
|
|
23661
|
+
const staticSegments = [];
|
|
23662
|
+
for (const segment of segments) {
|
|
23663
|
+
if (/[*?{}[\]]/.test(segment))
|
|
23664
|
+
break;
|
|
23665
|
+
staticSegments.push(segment);
|
|
23434
23666
|
}
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
return await buildDataCollectionDefinition(fs, baseRel, dataSources, collectionName, contentDir);
|
|
23667
|
+
if (staticSegments.length === segments.length) {
|
|
23668
|
+
staticSegments.pop();
|
|
23438
23669
|
}
|
|
23439
|
-
return
|
|
23670
|
+
return staticSegments.join("/");
|
|
23440
23671
|
}
|
|
23441
|
-
function
|
|
23442
|
-
|
|
23443
|
-
|
|
23444
|
-
|
|
23445
|
-
|
|
23446
|
-
|
|
23447
|
-
|
|
23448
|
-
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
const
|
|
23453
|
-
|
|
23454
|
-
|
|
23455
|
-
|
|
23456
|
-
|
|
23457
|
-
|
|
23458
|
-
|
|
23459
|
-
|
|
23460
|
-
|
|
23461
|
-
|
|
23462
|
-
|
|
23463
|
-
|
|
23464
|
-
|
|
23465
|
-
|
|
23466
|
-
|
|
23467
|
-
|
|
23468
|
-
|
|
23672
|
+
function createNodeFs(root) {
|
|
23673
|
+
const resolvedRoot = path3.resolve(root);
|
|
23674
|
+
const resolve = (p) => resolveWithinRoot(resolvedRoot, p);
|
|
23675
|
+
return {
|
|
23676
|
+
async readFile(filePath) {
|
|
23677
|
+
return fs.readFile(resolve(filePath), "utf-8");
|
|
23678
|
+
},
|
|
23679
|
+
async readBytes(filePath) {
|
|
23680
|
+
return fs.readFile(resolve(filePath));
|
|
23681
|
+
},
|
|
23682
|
+
async writeFile(filePath, content) {
|
|
23683
|
+
const fullPath = resolve(filePath);
|
|
23684
|
+
await fs.mkdir(path3.dirname(fullPath), { recursive: true });
|
|
23685
|
+
const tempPath = `${fullPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
23686
|
+
try {
|
|
23687
|
+
await fs.writeFile(tempPath, content, "utf-8");
|
|
23688
|
+
await fs.rename(tempPath, fullPath);
|
|
23689
|
+
} catch (error) {
|
|
23690
|
+
await fs.rm(tempPath, { force: true });
|
|
23691
|
+
throw error;
|
|
23692
|
+
}
|
|
23693
|
+
},
|
|
23694
|
+
async rename(from, to) {
|
|
23695
|
+
const fullTo = resolve(to);
|
|
23696
|
+
await fs.mkdir(path3.dirname(fullTo), { recursive: true });
|
|
23697
|
+
await fs.rename(resolve(from), fullTo);
|
|
23698
|
+
},
|
|
23699
|
+
async remove(filePath) {
|
|
23700
|
+
await fs.rm(resolve(filePath), { force: true });
|
|
23701
|
+
},
|
|
23702
|
+
async exists(filePath) {
|
|
23703
|
+
try {
|
|
23704
|
+
await fs.access(resolve(filePath));
|
|
23705
|
+
return true;
|
|
23706
|
+
} catch {
|
|
23707
|
+
return false;
|
|
23708
|
+
}
|
|
23709
|
+
},
|
|
23710
|
+
async list(dir) {
|
|
23711
|
+
try {
|
|
23712
|
+
const entries = await fs.readdir(resolve(dir), { withFileTypes: true });
|
|
23713
|
+
return entries.map((entry) => ({ name: entry.name, isDirectory: entry.isDirectory() }));
|
|
23714
|
+
} catch (error) {
|
|
23715
|
+
if (isNodeError(error) && error.code === "ENOENT")
|
|
23716
|
+
return [];
|
|
23717
|
+
throw error;
|
|
23718
|
+
}
|
|
23719
|
+
},
|
|
23720
|
+
async glob(pattern) {
|
|
23721
|
+
const baseRel = staticPrefixDir(pattern);
|
|
23722
|
+
const absBase = resolve(baseRel);
|
|
23723
|
+
const matcher = globToRegExp(pattern);
|
|
23724
|
+
const files = await walkFiles(resolvedRoot, absBase);
|
|
23725
|
+
return files.filter((rel) => matcher.test(rel));
|
|
23726
|
+
},
|
|
23727
|
+
async stat(filePath) {
|
|
23728
|
+
const s = await fs.stat(resolve(filePath));
|
|
23729
|
+
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
23469
23730
|
}
|
|
23470
|
-
}
|
|
23471
|
-
}
|
|
23472
|
-
function applyCollectionTitleField(def, titleField) {
|
|
23473
|
-
if (!def.entries)
|
|
23474
|
-
return;
|
|
23475
|
-
for (const entry of def.entries) {
|
|
23476
|
-
entry.title = entry.data ? deriveEntryTitle(entry.data, titleField) : undefined;
|
|
23477
|
-
}
|
|
23478
|
-
def.entries.sort((a, b) => (a.title ?? a.slug).localeCompare(b.title ?? b.slug));
|
|
23731
|
+
};
|
|
23479
23732
|
}
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23733
|
+
var init_node_fs = () => {};
|
|
23734
|
+
// ../cms-core/src/media/project-images.ts
|
|
23735
|
+
function uploadsDirRelativeToRoot(dir, root) {
|
|
23736
|
+
if (dir === undefined)
|
|
23483
23737
|
return;
|
|
23484
|
-
|
|
23485
|
-
|
|
23486
|
-
if (
|
|
23487
|
-
|
|
23488
|
-
if (
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
field.width = layout.width;
|
|
23492
|
-
if (layout.order !== undefined)
|
|
23493
|
-
field.order = layout.order;
|
|
23494
|
-
if (layout.sidebar)
|
|
23495
|
-
field.position = "sidebar";
|
|
23496
|
-
if (layout.hidden)
|
|
23497
|
-
field.hidden = true;
|
|
23738
|
+
const base = root.replace(/\/+$/, "");
|
|
23739
|
+
const trimmed = dir.replace(/\/+$/, "");
|
|
23740
|
+
if (trimmed === base)
|
|
23741
|
+
return "";
|
|
23742
|
+
if (trimmed.startsWith(`${base}/`))
|
|
23743
|
+
return trimmed.slice(base.length + 1);
|
|
23744
|
+
return trimmed.replace(/^\.\//, "").replace(/^\/+/, "");
|
|
23498
23745
|
}
|
|
23499
|
-
function
|
|
23500
|
-
|
|
23501
|
-
|
|
23502
|
-
|
|
23503
|
-
|
|
23504
|
-
|
|
23505
|
-
|
|
23506
|
-
}
|
|
23507
|
-
|
|
23508
|
-
|
|
23509
|
-
|
|
23510
|
-
|
|
23511
|
-
|
|
23512
|
-
|
|
23513
|
-
|
|
23514
|
-
applyParsedFieldLayout(field, pf);
|
|
23515
|
-
if (pf.fields) {
|
|
23516
|
-
const existingByName = new Map((field.fields ?? []).map((f) => [f.name, f]));
|
|
23517
|
-
field.fields = pf.fields.map((subPf) => {
|
|
23518
|
-
const existing = existingByName.get(subPf.name);
|
|
23519
|
-
if (existing) {
|
|
23520
|
-
applyParsedFieldOverrides(existing, subPf);
|
|
23521
|
-
return existing;
|
|
23522
|
-
}
|
|
23523
|
-
return parsedFieldToFieldDefinition(subPf);
|
|
23524
|
-
});
|
|
23525
|
-
}
|
|
23746
|
+
async function listProjectImages(fs2, options) {
|
|
23747
|
+
const normalized = options?.exclude ? uploadsDirRelativeToRoot(options.exclude.dir, options.exclude.root) : options?.excludeDir?.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
23748
|
+
const excludeDir = normalized ? normalized : null;
|
|
23749
|
+
const scanDirs = [
|
|
23750
|
+
{ dir: "public", relativeToRoot: false },
|
|
23751
|
+
{ dir: "src", relativeToRoot: true }
|
|
23752
|
+
];
|
|
23753
|
+
const results = await Promise.all(scanDirs.map(async ({ dir, relativeToRoot }) => {
|
|
23754
|
+
const items2 = [];
|
|
23755
|
+
await scanDirectory(fs2, dir, dir, relativeToRoot, excludeDir, items2);
|
|
23756
|
+
return items2;
|
|
23757
|
+
}));
|
|
23758
|
+
const items = results.flat();
|
|
23759
|
+
items.sort((a, b) => a.filename.localeCompare(b.filename) || compareCodeUnits(a.url, b.url));
|
|
23760
|
+
return items;
|
|
23526
23761
|
}
|
|
23527
|
-
function
|
|
23528
|
-
|
|
23529
|
-
name: pf.name,
|
|
23530
|
-
type: pf.type ?? (pf.fields ? "object" : "text"),
|
|
23531
|
-
required: pf.required
|
|
23532
|
-
};
|
|
23533
|
-
if (pf.options) {
|
|
23534
|
-
fd.options = pf.options;
|
|
23535
|
-
fd.optionsClosed = true;
|
|
23536
|
-
}
|
|
23537
|
-
if (pf.itemType)
|
|
23538
|
-
fd.itemType = pf.itemType;
|
|
23539
|
-
if (pf.hints)
|
|
23540
|
-
fd.hints = pf.hints;
|
|
23541
|
-
if (pf.astroImage)
|
|
23542
|
-
fd.astroImage = true;
|
|
23543
|
-
if (pf.fields)
|
|
23544
|
-
fd.fields = pf.fields.map(parsedFieldToFieldDefinition);
|
|
23545
|
-
applyParsedFieldLayout(fd, pf);
|
|
23546
|
-
return fd;
|
|
23762
|
+
function compareCodeUnits(a, b) {
|
|
23763
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
23547
23764
|
}
|
|
23548
|
-
function
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
if (
|
|
23765
|
+
async function scanDirectory(fs2, currentDir, baseDir, relativeToRoot, excludeDir, items) {
|
|
23766
|
+
if (excludeDir !== null && currentDir === excludeDir)
|
|
23767
|
+
return;
|
|
23768
|
+
const entries = await fs2.list(currentDir);
|
|
23769
|
+
const subdirs = [];
|
|
23770
|
+
for (const entry of entries) {
|
|
23771
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
23555
23772
|
continue;
|
|
23556
|
-
const
|
|
23557
|
-
|
|
23558
|
-
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
const
|
|
23562
|
-
|
|
23563
|
-
|
|
23564
|
-
|
|
23565
|
-
|
|
23566
|
-
|
|
23567
|
-
|
|
23568
|
-
|
|
23569
|
-
|
|
23570
|
-
return -1;
|
|
23571
|
-
if (typeof aVal === "number" && typeof bVal === "number")
|
|
23572
|
-
return (aVal - bVal) * dir;
|
|
23573
|
-
if (aVal instanceof Date && bVal instanceof Date)
|
|
23574
|
-
return (aVal.getTime() - bVal.getTime()) * dir;
|
|
23575
|
-
return String(aVal).localeCompare(String(bVal)) * dir;
|
|
23773
|
+
const fullPath = `${currentDir}/${entry.name}`;
|
|
23774
|
+
if (entry.isDirectory) {
|
|
23775
|
+
subdirs.push(scanDirectory(fs2, fullPath, baseDir, relativeToRoot, excludeDir, items));
|
|
23776
|
+
} else {
|
|
23777
|
+
const dotIdx = entry.name.lastIndexOf(".");
|
|
23778
|
+
const ext = dotIdx >= 0 ? entry.name.slice(dotIdx).toLowerCase() : "";
|
|
23779
|
+
if (!IMAGE_EXTENSIONS2.has(ext))
|
|
23780
|
+
continue;
|
|
23781
|
+
const url = relativeToRoot ? `/${fullPath}` : `/${fullPath.slice(baseDir.length + 1)}`;
|
|
23782
|
+
items.push({
|
|
23783
|
+
id: `project:${url}`,
|
|
23784
|
+
url,
|
|
23785
|
+
filename: entry.name,
|
|
23786
|
+
contentType: mimeFromExt(ext)
|
|
23576
23787
|
});
|
|
23577
23788
|
}
|
|
23578
23789
|
}
|
|
23790
|
+
await Promise.all(subdirs);
|
|
23579
23791
|
}
|
|
23580
|
-
|
|
23581
|
-
|
|
23582
|
-
|
|
23583
|
-
|
|
23584
|
-
|
|
23585
|
-
|
|
23586
|
-
|
|
23587
|
-
|
|
23588
|
-
|
|
23589
|
-
|
|
23590
|
-
|
|
23591
|
-
|
|
23592
|
-
|
|
23593
|
-
|
|
23594
|
-
|
|
23595
|
-
|
|
23596
|
-
|
|
23597
|
-
|
|
23598
|
-
|
|
23599
|
-
|
|
23600
|
-
|
|
23601
|
-
|
|
23792
|
+
var IMAGE_EXTENSIONS2;
|
|
23793
|
+
var init_project_images = __esm(() => {
|
|
23794
|
+
init_local();
|
|
23795
|
+
IMAGE_EXTENSIONS2 = new Set(Object.entries(MIME_BY_EXT).filter(([, mime]) => mime.startsWith("image/")).map(([ext]) => ext));
|
|
23796
|
+
});
|
|
23797
|
+
|
|
23798
|
+
// ../cms-core/src/media/s3.ts
|
|
23799
|
+
var init_s3 = __esm(() => {
|
|
23800
|
+
init_local();
|
|
23801
|
+
});
|
|
23802
|
+
|
|
23803
|
+
// ../cms-core/src/media/index.ts
|
|
23804
|
+
var init_media = __esm(() => {
|
|
23805
|
+
init_local();
|
|
23806
|
+
init_project_images();
|
|
23807
|
+
init_s3();
|
|
23808
|
+
});
|
|
23809
|
+
|
|
23810
|
+
// ../cms-core/src/project-config-ast.ts
|
|
23811
|
+
var import_parser3;
|
|
23812
|
+
var init_project_config_ast = __esm(() => {
|
|
23813
|
+
import_parser3 = __toESM(require_lib(), 1);
|
|
23814
|
+
});
|
|
23815
|
+
|
|
23816
|
+
// ../cms-core/src/index.ts
|
|
23817
|
+
var init_src2 = __esm(() => {
|
|
23818
|
+
init_check();
|
|
23819
|
+
init_collection_scanner();
|
|
23820
|
+
init_component_registry();
|
|
23821
|
+
init_content_config_ast();
|
|
23822
|
+
init_core();
|
|
23823
|
+
init_node_fs();
|
|
23824
|
+
init_entry_ops();
|
|
23825
|
+
init_media();
|
|
23826
|
+
init_project_config_ast();
|
|
23827
|
+
});
|
|
23828
|
+
|
|
23829
|
+
// src/check.ts
|
|
23830
|
+
var exports_check = {};
|
|
23831
|
+
__export(exports_check, {
|
|
23832
|
+
check: () => check
|
|
23833
|
+
});
|
|
23834
|
+
async function check(options) {
|
|
23835
|
+
const report = await checkContent(createNodeFs(options.cwd));
|
|
23836
|
+
if (options.json) {
|
|
23837
|
+
console.log(JSON.stringify(report, null, 2));
|
|
23838
|
+
} else {
|
|
23839
|
+
const formatted = formatCheckReport(report);
|
|
23840
|
+
if (formatted)
|
|
23841
|
+
console.log(formatted + `
|
|
23842
|
+
`);
|
|
23602
23843
|
}
|
|
23603
|
-
|
|
23604
|
-
|
|
23844
|
+
const errors = report.findings.filter((finding) => finding.severity === "error").length;
|
|
23845
|
+
const warnings = report.findings.length - errors;
|
|
23846
|
+
if (!options.json) {
|
|
23847
|
+
const counts = `${report.collections} collection(s), ${report.entries} entries`;
|
|
23848
|
+
console.log(errors === 0 && warnings === 0 ? `${counts} \u2014 no problems found` : `${counts} \u2014 ${errors} error(s), ${warnings} warning(s)`);
|
|
23849
|
+
}
|
|
23850
|
+
return errors > 0 || options.strict && warnings > 0 ? 1 : 0;
|
|
23605
23851
|
}
|
|
23606
|
-
|
|
23607
|
-
|
|
23608
|
-
|
|
23609
|
-
|
|
23610
|
-
|
|
23611
|
-
|
|
23852
|
+
var init_check2 = __esm(() => {
|
|
23853
|
+
init_src2();
|
|
23854
|
+
});
|
|
23855
|
+
|
|
23856
|
+
// src/utils.ts
|
|
23857
|
+
import { existsSync } from "fs";
|
|
23858
|
+
import { join } from "path";
|
|
23859
|
+
function findAstroConfig(cwd = process.cwd()) {
|
|
23860
|
+
for (const name of CONFIG_NAMES) {
|
|
23861
|
+
const p = join(cwd, name);
|
|
23862
|
+
if (existsSync(p))
|
|
23863
|
+
return p;
|
|
23612
23864
|
}
|
|
23613
|
-
|
|
23614
|
-
|
|
23615
|
-
|
|
23616
|
-
|
|
23617
|
-
|
|
23618
|
-
|
|
23619
|
-
|
|
23620
|
-
|
|
23621
|
-
|
|
23622
|
-
|
|
23623
|
-
|
|
23624
|
-
|
|
23625
|
-
|
|
23626
|
-
}
|
|
23627
|
-
}
|
|
23628
|
-
let bestTarget;
|
|
23629
|
-
if (candidates.length === 1) {
|
|
23630
|
-
bestTarget = candidates[0].name;
|
|
23631
|
-
} else if (candidates.length > 1) {
|
|
23632
|
-
const allValues = def.entries?.flatMap((e) => {
|
|
23633
|
-
const v = e.data?.[field.name];
|
|
23634
|
-
return typeof v === "string" ? [v] : [];
|
|
23635
|
-
}) ?? stringExamples;
|
|
23636
|
-
let bestOverlap = 0;
|
|
23637
|
-
for (const c of candidates) {
|
|
23638
|
-
const overlap = allValues.filter((v) => c.slugs.has(v)).length;
|
|
23639
|
-
if (overlap > bestOverlap) {
|
|
23640
|
-
bestOverlap = overlap;
|
|
23641
|
-
bestTarget = c.name;
|
|
23642
|
-
}
|
|
23643
|
-
}
|
|
23644
|
-
}
|
|
23645
|
-
if (bestTarget) {
|
|
23646
|
-
field.type = "reference";
|
|
23647
|
-
field.collection = bestTarget;
|
|
23648
|
-
field.options = undefined;
|
|
23649
|
-
}
|
|
23650
|
-
}
|
|
23651
|
-
if (field.type === "array" && field.itemType === "text" && field.options) {
|
|
23652
|
-
let bestTarget;
|
|
23653
|
-
let bestOverlap = 0;
|
|
23654
|
-
for (const [targetName, slugs] of collectionSlugs) {
|
|
23655
|
-
if (targetName === collectionName)
|
|
23656
|
-
continue;
|
|
23657
|
-
const matchCount = field.options.filter((v) => slugs.has(v)).length;
|
|
23658
|
-
if (matchCount > 0 && matchCount >= field.options.length * 0.5) {
|
|
23659
|
-
if (matchCount > bestOverlap) {
|
|
23660
|
-
bestOverlap = matchCount;
|
|
23661
|
-
bestTarget = targetName;
|
|
23662
|
-
}
|
|
23663
|
-
}
|
|
23664
|
-
}
|
|
23665
|
-
if (bestTarget) {
|
|
23666
|
-
field.type = "array";
|
|
23667
|
-
field.itemType = "reference";
|
|
23668
|
-
field.collection = bestTarget;
|
|
23669
|
-
field.options = undefined;
|
|
23670
|
-
}
|
|
23865
|
+
return null;
|
|
23866
|
+
}
|
|
23867
|
+
function findMatchingClose(text, start) {
|
|
23868
|
+
const open = text[start];
|
|
23869
|
+
const close = open === "{" ? "}" : open === "[" ? "]" : ")";
|
|
23870
|
+
let depth = 0;
|
|
23871
|
+
let inString = false;
|
|
23872
|
+
for (let i = start;i < text.length; i++) {
|
|
23873
|
+
const ch = text[i];
|
|
23874
|
+
if (inString) {
|
|
23875
|
+
if (ch === "\\") {
|
|
23876
|
+
i++;
|
|
23877
|
+
continue;
|
|
23671
23878
|
}
|
|
23879
|
+
if (ch === inString)
|
|
23880
|
+
inString = false;
|
|
23881
|
+
continue;
|
|
23882
|
+
}
|
|
23883
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
23884
|
+
inString = ch;
|
|
23885
|
+
continue;
|
|
23886
|
+
}
|
|
23887
|
+
if (ch === open)
|
|
23888
|
+
depth++;
|
|
23889
|
+
if (ch === close) {
|
|
23890
|
+
depth--;
|
|
23891
|
+
if (depth === 0)
|
|
23892
|
+
return i;
|
|
23672
23893
|
}
|
|
23673
23894
|
}
|
|
23895
|
+
return -1;
|
|
23674
23896
|
}
|
|
23675
|
-
function
|
|
23676
|
-
|
|
23677
|
-
|
|
23678
|
-
|
|
23679
|
-
|
|
23680
|
-
|
|
23681
|
-
|
|
23682
|
-
|
|
23683
|
-
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
|
|
23687
|
-
|
|
23688
|
-
|
|
23689
|
-
|
|
23690
|
-
|
|
23897
|
+
function extractConfigBody(content) {
|
|
23898
|
+
const match = content.match(/defineConfig\s*\(\s*\{/);
|
|
23899
|
+
if (!match || match.index === undefined)
|
|
23900
|
+
return "";
|
|
23901
|
+
const openBrace = content.indexOf("{", match.index + "defineConfig".length);
|
|
23902
|
+
const closeBrace = findMatchingClose(content, openBrace);
|
|
23903
|
+
if (closeBrace === -1)
|
|
23904
|
+
return "";
|
|
23905
|
+
return content.slice(openBrace + 1, closeBrace);
|
|
23906
|
+
}
|
|
23907
|
+
function removeProperty(body, propName) {
|
|
23908
|
+
const regex = new RegExp(`(\\n[ \\t]*)${propName}\\s*:\\s*`);
|
|
23909
|
+
const match = regex.exec(body);
|
|
23910
|
+
if (!match || match.index === undefined)
|
|
23911
|
+
return body;
|
|
23912
|
+
const propLineStart = match.index + 1;
|
|
23913
|
+
const afterMatch = match.index + match[0].length;
|
|
23914
|
+
let i = afterMatch;
|
|
23915
|
+
while (i < body.length && (body[i] === " " || body[i] === "\t"))
|
|
23916
|
+
i++;
|
|
23917
|
+
let valueEnd;
|
|
23918
|
+
if (body[i] === "{" || body[i] === "[") {
|
|
23919
|
+
valueEnd = findMatchingClose(body, i);
|
|
23920
|
+
if (valueEnd === -1)
|
|
23921
|
+
return body;
|
|
23922
|
+
valueEnd++;
|
|
23923
|
+
} else {
|
|
23924
|
+
while (i < body.length && body[i] !== "," && body[i] !== `
|
|
23925
|
+
`)
|
|
23926
|
+
i++;
|
|
23927
|
+
valueEnd = i;
|
|
23928
|
+
}
|
|
23929
|
+
let end = valueEnd;
|
|
23930
|
+
while (end < body.length && (body[end] === " " || body[end] === "\t"))
|
|
23931
|
+
end++;
|
|
23932
|
+
if (end < body.length && body[end] === ",")
|
|
23933
|
+
end++;
|
|
23934
|
+
while (end < body.length && (body[end] === " " || body[end] === "\t"))
|
|
23935
|
+
end++;
|
|
23936
|
+
if (end < body.length && body[end] === `
|
|
23937
|
+
`)
|
|
23938
|
+
end++;
|
|
23939
|
+
return body.slice(0, propLineStart) + body.slice(end);
|
|
23940
|
+
}
|
|
23941
|
+
function assembleConfig(imports, body) {
|
|
23942
|
+
const bodyLines = body.split(`
|
|
23943
|
+
`).filter((line) => line.trim() !== "");
|
|
23944
|
+
let result = imports.join(`
|
|
23945
|
+
`) + `
|
|
23946
|
+
|
|
23947
|
+
`;
|
|
23948
|
+
result += `export default defineConfig({
|
|
23949
|
+
`;
|
|
23950
|
+
if (bodyLines.length > 0) {
|
|
23951
|
+
result += bodyLines.join(`
|
|
23952
|
+
`) + `
|
|
23953
|
+
`;
|
|
23954
|
+
}
|
|
23955
|
+
result += `})
|
|
23956
|
+
`;
|
|
23957
|
+
return result;
|
|
23958
|
+
}
|
|
23959
|
+
var CONFIG_NAMES;
|
|
23960
|
+
var init_utils = __esm(() => {
|
|
23961
|
+
CONFIG_NAMES = [
|
|
23962
|
+
"astro.config.ts",
|
|
23963
|
+
"astro.config.mts",
|
|
23964
|
+
"astro.config.mjs",
|
|
23965
|
+
"astro.config.js"
|
|
23966
|
+
];
|
|
23967
|
+
});
|
|
23968
|
+
|
|
23969
|
+
// src/init.ts
|
|
23970
|
+
var exports_init = {};
|
|
23971
|
+
__export(exports_init, {
|
|
23972
|
+
transformPackageJson: () => transformPackageJson,
|
|
23973
|
+
transformConfig: () => transformConfig,
|
|
23974
|
+
removeCallFromArray: () => removeCallFromArray,
|
|
23975
|
+
init: () => init,
|
|
23976
|
+
detectNuaManagedImports: () => detectNuaManagedImports,
|
|
23977
|
+
cleanEmptyStructures: () => cleanEmptyStructures
|
|
23978
|
+
});
|
|
23979
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
|
|
23980
|
+
import { basename, join as join2 } from "path";
|
|
23981
|
+
function detectNuaManagedImports(content) {
|
|
23982
|
+
const managed = new Map;
|
|
23983
|
+
for (const pkg of NUA_MANAGED_PACKAGES) {
|
|
23984
|
+
const regex = new RegExp(`import\\s+(\\w+)\\s+from\\s+['"]${pkg.replace("/", "\\/")}['"]`);
|
|
23985
|
+
const match = regex.exec(content);
|
|
23986
|
+
if (match) {
|
|
23987
|
+
managed.set(pkg, match[1]);
|
|
23691
23988
|
}
|
|
23692
|
-
if (toggle)
|
|
23693
|
-
toggle.role = "publish-toggle";
|
|
23694
|
-
const date = dateByName ?? dateByType;
|
|
23695
|
-
if (date)
|
|
23696
|
-
date.role = "publish-date";
|
|
23697
23989
|
}
|
|
23990
|
+
return managed;
|
|
23698
23991
|
}
|
|
23699
|
-
function
|
|
23700
|
-
|
|
23701
|
-
|
|
23702
|
-
|
|
23703
|
-
|
|
23704
|
-
|
|
23705
|
-
|
|
23706
|
-
|
|
23707
|
-
|
|
23708
|
-
|
|
23709
|
-
|
|
23710
|
-
|
|
23711
|
-
|
|
23712
|
-
|
|
23713
|
-
|
|
23714
|
-
|
|
23715
|
-
|
|
23716
|
-
|
|
23717
|
-
|
|
23718
|
-
|
|
23719
|
-
|
|
23720
|
-
|
|
23721
|
-
|
|
23722
|
-
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
|
|
23729
|
-
|
|
23730
|
-
|
|
23731
|
-
|
|
23732
|
-
|
|
23733
|
-
|
|
23734
|
-
|
|
23735
|
-
|
|
23992
|
+
function removeCallFromArray(body, arrayProp, callName) {
|
|
23993
|
+
const propRegex = new RegExp(`\\b${arrayProp}\\s*:\\s*\\[`);
|
|
23994
|
+
const propMatch = propRegex.exec(body);
|
|
23995
|
+
if (!propMatch || propMatch.index === undefined)
|
|
23996
|
+
return body;
|
|
23997
|
+
const arrayStart = body.indexOf("[", propMatch.index);
|
|
23998
|
+
const arrayEnd = findMatchingClose(body, arrayStart);
|
|
23999
|
+
if (arrayEnd === -1)
|
|
24000
|
+
return body;
|
|
24001
|
+
const arrayContent = body.slice(arrayStart + 1, arrayEnd);
|
|
24002
|
+
const callRegex = new RegExp(`\\b${callName}\\s*\\(`);
|
|
24003
|
+
const callMatch = callRegex.exec(arrayContent);
|
|
24004
|
+
if (!callMatch || callMatch.index === undefined)
|
|
24005
|
+
return body;
|
|
24006
|
+
const parenStart = arrayContent.indexOf("(", callMatch.index);
|
|
24007
|
+
const parenEnd = findMatchingClose(arrayContent, parenStart);
|
|
24008
|
+
if (parenEnd === -1)
|
|
24009
|
+
return body;
|
|
24010
|
+
let removeStart = callMatch.index;
|
|
24011
|
+
let removeEnd = parenEnd + 1;
|
|
24012
|
+
let after = removeEnd;
|
|
24013
|
+
while (after < arrayContent.length && (arrayContent[after] === " " || arrayContent[after] === "\t" || arrayContent[after] === `
|
|
24014
|
+
`)) {
|
|
24015
|
+
after++;
|
|
24016
|
+
}
|
|
24017
|
+
if (after < arrayContent.length && arrayContent[after] === ",") {
|
|
24018
|
+
removeEnd = after + 1;
|
|
24019
|
+
while (removeEnd < arrayContent.length && (arrayContent[removeEnd] === " " || arrayContent[removeEnd] === "\t" || arrayContent[removeEnd] === `
|
|
24020
|
+
`))
|
|
24021
|
+
removeEnd++;
|
|
24022
|
+
}
|
|
24023
|
+
if (removeEnd === parenEnd + 1) {
|
|
24024
|
+
let before = removeStart - 1;
|
|
24025
|
+
while (before >= 0 && (arrayContent[before] === " " || arrayContent[before] === "\t" || arrayContent[before] === `
|
|
24026
|
+
`))
|
|
24027
|
+
before--;
|
|
24028
|
+
if (before >= 0 && arrayContent[before] === ",") {
|
|
24029
|
+
removeStart = before;
|
|
23736
24030
|
}
|
|
23737
24031
|
}
|
|
24032
|
+
const newArrayContent = arrayContent.slice(0, removeStart) + arrayContent.slice(removeEnd);
|
|
24033
|
+
return body.slice(0, arrayStart + 1) + newArrayContent + body.slice(arrayEnd);
|
|
23738
24034
|
}
|
|
23739
|
-
|
|
23740
|
-
|
|
23741
|
-
|
|
23742
|
-
|
|
23743
|
-
|
|
23744
|
-
|
|
23745
|
-
|
|
23746
|
-
|
|
24035
|
+
function cleanEmptyStructures(body) {
|
|
24036
|
+
body = body.replace(/\n[ \t]*sourcemap\s*:\s*true\s*,?[ \t]*/g, `
|
|
24037
|
+
`);
|
|
24038
|
+
body = body.replace(/\n[ \t]*build\s*:\s*\{[\s,]*\}\s*,?[ \t]*/g, `
|
|
24039
|
+
`);
|
|
24040
|
+
body = body.replace(/\n[ \t]*plugins\s*:\s*\[[\s,]*\]\s*,?[ \t]*/g, `
|
|
24041
|
+
`);
|
|
24042
|
+
body = body.replace(/\n[ \t]*integrations\s*:\s*\[[\s,]*\]\s*,?[ \t]*/g, `
|
|
24043
|
+
`);
|
|
24044
|
+
body = body.replace(/\n[ \t]*vite\s*:\s*\{[\s,]*\}\s*,?[ \t]*/g, `
|
|
24045
|
+
`);
|
|
24046
|
+
return body;
|
|
24047
|
+
}
|
|
24048
|
+
function transformConfig(content, managedImports) {
|
|
24049
|
+
const lines = content.split(`
|
|
24050
|
+
`);
|
|
24051
|
+
const newImports = [];
|
|
24052
|
+
for (const line of lines) {
|
|
24053
|
+
if (!/^\s*import\s/.test(line))
|
|
23747
24054
|
continue;
|
|
23748
|
-
if (
|
|
24055
|
+
if (line.includes("astro/config") && line.includes("defineConfig")) {
|
|
24056
|
+
newImports.push(`import { defineConfig } from '@nuasite/nua/config'`);
|
|
23749
24057
|
continue;
|
|
23750
|
-
|
|
23751
|
-
|
|
23752
|
-
|
|
24058
|
+
}
|
|
24059
|
+
const isManagedImport = [...managedImports.keys()].some((pkg) => line.includes(pkg));
|
|
24060
|
+
if (isManagedImport)
|
|
24061
|
+
continue;
|
|
24062
|
+
newImports.push(line);
|
|
23753
24063
|
}
|
|
23754
|
-
|
|
23755
|
-
const
|
|
23756
|
-
if (
|
|
23757
|
-
|
|
23758
|
-
|
|
23759
|
-
|
|
23760
|
-
if (await fs.exists(path.join(collectionPath, relPath))) {
|
|
23761
|
-
return { slug: dir.name, relPath };
|
|
23762
|
-
}
|
|
24064
|
+
let body = extractConfigBody(content);
|
|
24065
|
+
for (const [pkg, localName] of managedImports) {
|
|
24066
|
+
if (pkg === "@tailwindcss/vite") {
|
|
24067
|
+
body = removeCallFromArray(body, "plugins", localName);
|
|
24068
|
+
} else {
|
|
24069
|
+
body = removeCallFromArray(body, "integrations", localName);
|
|
23763
24070
|
}
|
|
23764
|
-
return null;
|
|
23765
|
-
}));
|
|
23766
|
-
for (const entry of indexLookups) {
|
|
23767
|
-
if (entry)
|
|
23768
|
-
sources.push(entry);
|
|
23769
24071
|
}
|
|
23770
|
-
|
|
23771
|
-
|
|
23772
|
-
function normalizeBasePath(basePath) {
|
|
23773
|
-
return path.normalize(basePath.replace(/^\.[/\\]+/, "")).replace(/[/\\]+$/, "");
|
|
24072
|
+
body = cleanEmptyStructures(body);
|
|
24073
|
+
return assembleConfig(newImports, body);
|
|
23774
24074
|
}
|
|
23775
|
-
function
|
|
23776
|
-
const
|
|
23777
|
-
|
|
23778
|
-
|
|
23779
|
-
const [, braceGroup, singleExtension] = match;
|
|
23780
|
-
if (braceGroup === undefined && singleExtension === undefined)
|
|
23781
|
-
return true;
|
|
23782
|
-
const patternExtensions = new Set((braceGroup !== undefined ? braceGroup.split(",") : [singleExtension]).map((ext) => ext.trim().replace(/^\./, "").toLowerCase()).filter(Boolean));
|
|
23783
|
-
const scannedExtensions = new Set((definition.entries ?? []).map((entry) => path.extname(entry.sourcePath).slice(1).toLowerCase()).filter(Boolean));
|
|
23784
|
-
if (scannedExtensions.size === 0)
|
|
23785
|
-
return false;
|
|
23786
|
-
for (const ext of scannedExtensions) {
|
|
23787
|
-
if (!patternExtensions.has(ext))
|
|
23788
|
-
return false;
|
|
23789
|
-
}
|
|
23790
|
-
return true;
|
|
24075
|
+
function resolveNuaVersion() {
|
|
24076
|
+
const cliPkgPath = new URL("../package.json", import.meta.url);
|
|
24077
|
+
const cliPkg = JSON.parse(readFileSync(cliPkgPath, "utf-8"));
|
|
24078
|
+
return `^${cliPkg.version}`;
|
|
23791
24079
|
}
|
|
23792
|
-
|
|
23793
|
-
const
|
|
23794
|
-
|
|
23795
|
-
|
|
23796
|
-
|
|
23797
|
-
|
|
23798
|
-
const definition = await scanCollection(fs, collectionPath, entry.name, contentDir) ?? await scanDataCollection(fs, collectionPath, entry.name, contentDir);
|
|
23799
|
-
if (definition) {
|
|
23800
|
-
collections[entry.name] = definition;
|
|
24080
|
+
function transformPackageJson(pkg, nuaVersion) {
|
|
24081
|
+
const result = structuredClone(pkg);
|
|
24082
|
+
if (result.scripts) {
|
|
24083
|
+
for (const [key, value] of Object.entries(result.scripts)) {
|
|
24084
|
+
if (typeof value === "string") {
|
|
24085
|
+
result.scripts[key] = value.replace(/\bastro build\b/g, "nua build").replace(/\bastro dev\b/g, "nua dev").replace(/\bastro preview\b/g, "nua preview");
|
|
23801
24086
|
}
|
|
23802
|
-
} catch (error) {
|
|
23803
|
-
console.warn(`[cms] Skipping collection "${entry.name}" \u2014 scan failed:`, error);
|
|
23804
24087
|
}
|
|
23805
|
-
});
|
|
23806
|
-
await Promise.all(scanPromises);
|
|
23807
|
-
const parsed = await parseContentConfig(fs, parseCache);
|
|
23808
|
-
const scannedNameByBasePath = new Map;
|
|
23809
|
-
for (const [name, definition] of Object.entries(collections)) {
|
|
23810
|
-
scannedNameByBasePath.set(normalizeBasePath(definition.path), name);
|
|
23811
24088
|
}
|
|
23812
|
-
const
|
|
23813
|
-
|
|
23814
|
-
if (collections[collectionName])
|
|
23815
|
-
continue;
|
|
23816
|
-
if (!parsedCollection.loaderBase || !parsedCollection.loaderPattern)
|
|
23817
|
-
continue;
|
|
23818
|
-
const scannedName = scannedNameByBasePath.get(normalizeBasePath(parsedCollection.loaderBase));
|
|
23819
|
-
const scannedDefinition = scannedName ? collections[scannedName] : undefined;
|
|
23820
|
-
if (scannedName && scannedDefinition && patternCoversWholeCollection(parsedCollection.loaderPattern, scannedDefinition)) {
|
|
23821
|
-
delete collections[scannedName];
|
|
23822
|
-
scannedDefinition.name = collectionName;
|
|
23823
|
-
for (const other of Object.values(collections)) {
|
|
23824
|
-
if (other.parentCollection === scannedName)
|
|
23825
|
-
other.parentCollection = collectionName;
|
|
23826
|
-
}
|
|
23827
|
-
collections[collectionName] = scannedDefinition;
|
|
23828
|
-
rekeyedScannedNames.set(scannedName, collectionName);
|
|
23829
|
-
continue;
|
|
23830
|
-
}
|
|
23831
|
-
let definition;
|
|
23832
|
-
try {
|
|
23833
|
-
definition = await scanGlobCollection(fs, collectionName, parsedCollection.loaderBase, parsedCollection.loaderPattern, contentDir);
|
|
23834
|
-
} catch (error) {
|
|
23835
|
-
console.warn(`[cms] Skipping glob collection "${collectionName}" \u2014 scan failed:`, error);
|
|
24089
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
24090
|
+
if (!result[field])
|
|
23836
24091
|
continue;
|
|
24092
|
+
for (const name of NUA_PROVIDED_PACKAGES) {
|
|
24093
|
+
delete result[field][name];
|
|
23837
24094
|
}
|
|
23838
|
-
if (
|
|
23839
|
-
|
|
23840
|
-
const baseName = parsedCollection.loaderBase.replace(/[/\\]+$/, "").split(/[/\\]/).pop();
|
|
23841
|
-
const parentName = !baseName ? undefined : collections[baseName] ? baseName : rekeyedScannedNames.get(baseName);
|
|
23842
|
-
if (parentName && parentName !== collectionName) {
|
|
23843
|
-
definition.parentCollection = parentName;
|
|
24095
|
+
if (Object.keys(result[field]).length === 0) {
|
|
24096
|
+
delete result[field];
|
|
23844
24097
|
}
|
|
23845
|
-
collections[collectionName] = definition;
|
|
23846
24098
|
}
|
|
23847
|
-
|
|
23848
|
-
|
|
23849
|
-
|
|
23850
|
-
|
|
23851
|
-
applyCollectionOrderBy(collections, parsed);
|
|
23852
|
-
warnOnPathnameCollisions(collections);
|
|
23853
|
-
return collections;
|
|
23854
|
-
}
|
|
23855
|
-
function warnOnPathnameCollisions(collections) {
|
|
23856
|
-
for (const def of Object.values(collections)) {
|
|
23857
|
-
if (!def.pathname || !def.entries)
|
|
23858
|
-
continue;
|
|
23859
|
-
const bySlug = new Map;
|
|
23860
|
-
for (const entry of def.entries) {
|
|
23861
|
-
const pathname = resolvePathnameFromSpec(def, entry.data);
|
|
23862
|
-
if (!pathname)
|
|
23863
|
-
continue;
|
|
23864
|
-
const prev = bySlug.get(pathname);
|
|
23865
|
-
if (prev) {
|
|
23866
|
-
console.warn(`[cms] collection "${def.name}": pathname rule maps both "${prev}" and "${entry.slug}" to "${pathname}" \u2014 these entries will collide on one URL`);
|
|
23867
|
-
} else {
|
|
23868
|
-
bySlug.set(pathname, entry.slug);
|
|
23869
|
-
}
|
|
23870
|
-
}
|
|
24099
|
+
if (!result.dependencies)
|
|
24100
|
+
result.dependencies = {};
|
|
24101
|
+
if (!result.dependencies["@nuasite/nua"]) {
|
|
24102
|
+
result.dependencies["@nuasite/nua"] = nuaVersion;
|
|
23871
24103
|
}
|
|
24104
|
+
result.dependencies = Object.fromEntries(Object.entries(result.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
24105
|
+
return result;
|
|
23872
24106
|
}
|
|
23873
|
-
|
|
23874
|
-
|
|
23875
|
-
|
|
23876
|
-
|
|
23877
|
-
|
|
23878
|
-
|
|
23879
|
-
|
|
23880
|
-
|
|
23881
|
-
"
|
|
23882
|
-
|
|
23883
|
-
|
|
23884
|
-
|
|
23885
|
-
"
|
|
23886
|
-
|
|
23887
|
-
|
|
23888
|
-
|
|
23889
|
-
|
|
23890
|
-
"
|
|
23891
|
-
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
23901
|
-
"
|
|
23902
|
-
|
|
23903
|
-
"
|
|
23904
|
-
"
|
|
23905
|
-
|
|
23906
|
-
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
23910
|
-
|
|
23911
|
-
"
|
|
23912
|
-
|
|
23913
|
-
|
|
23914
|
-
"
|
|
23915
|
-
|
|
23916
|
-
|
|
23917
|
-
|
|
23918
|
-
|
|
23919
|
-
|
|
23920
|
-
|
|
23921
|
-
|
|
23922
|
-
|
|
23923
|
-
|
|
23924
|
-
|
|
23925
|
-
|
|
23926
|
-
|
|
23927
|
-
}
|
|
23928
|
-
|
|
23929
|
-
|
|
23930
|
-
|
|
23931
|
-
|
|
23932
|
-
|
|
23933
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
23936
|
-
|
|
23937
|
-
|
|
23938
|
-
|
|
23939
|
-
|
|
23940
|
-
|
|
23941
|
-
|
|
23942
|
-
|
|
23943
|
-
|
|
23944
|
-
|
|
23945
|
-
|
|
23946
|
-
|
|
23947
|
-
|
|
23948
|
-
|
|
23949
|
-
|
|
23950
|
-
|
|
23951
|
-
|
|
23952
|
-
|
|
23953
|
-
|
|
23954
|
-
|
|
23955
|
-
|
|
23956
|
-
|
|
24107
|
+
async function init({ cwd = process.cwd(), dryRun = false, yes = false } = {}) {
|
|
24108
|
+
const configPath = findAstroConfig(cwd);
|
|
24109
|
+
if (!configPath) {
|
|
24110
|
+
console.error("No Astro config file found.");
|
|
24111
|
+
process.exit(1);
|
|
24112
|
+
}
|
|
24113
|
+
const configContent = readFileSync(configPath, "utf-8");
|
|
24114
|
+
if (configContent.includes("@nuasite/nua")) {
|
|
24115
|
+
console.log("This project already uses @nuasite/nua. Nothing to do.");
|
|
24116
|
+
return;
|
|
24117
|
+
}
|
|
24118
|
+
if (!configContent.includes("defineConfig")) {
|
|
24119
|
+
console.error("Could not find defineConfig in Astro config.");
|
|
24120
|
+
process.exit(1);
|
|
24121
|
+
}
|
|
24122
|
+
const pkgPath = join2(cwd, "package.json");
|
|
24123
|
+
if (!existsSync2(pkgPath)) {
|
|
24124
|
+
console.error("No package.json found.");
|
|
24125
|
+
process.exit(1);
|
|
24126
|
+
}
|
|
24127
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
24128
|
+
const managedImports = detectNuaManagedImports(configContent);
|
|
24129
|
+
const nuaVersion = resolveNuaVersion();
|
|
24130
|
+
const configName = basename(configPath);
|
|
24131
|
+
console.log("");
|
|
24132
|
+
console.log("nua init \u2014 adopt the Nua toolchain");
|
|
24133
|
+
console.log("");
|
|
24134
|
+
console.log(` ${configName}`);
|
|
24135
|
+
console.log(" - Replace astro/config with @nuasite/nua/config");
|
|
24136
|
+
if (managedImports.size > 0) {
|
|
24137
|
+
console.log(` - Remove Nua-managed imports: ${[...managedImports.keys()].join(", ")}`);
|
|
24138
|
+
console.log(" - Remove managed integration/plugin calls");
|
|
24139
|
+
}
|
|
24140
|
+
console.log(" - Clean up empty config structures");
|
|
24141
|
+
console.log("");
|
|
24142
|
+
console.log(" package.json");
|
|
24143
|
+
const removable = NUA_PROVIDED_PACKAGES.filter((name) => pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
|
|
24144
|
+
if (removable.length > 0) {
|
|
24145
|
+
console.log(` - Remove Nua-provided deps: ${removable.join(", ")}`);
|
|
24146
|
+
}
|
|
24147
|
+
console.log(` - Add @nuasite/nua ${nuaVersion}`);
|
|
24148
|
+
console.log(" - Update scripts: astro \u2192 nua");
|
|
24149
|
+
if (dryRun) {
|
|
24150
|
+
console.log("");
|
|
24151
|
+
console.log(" (--dry-run: no changes made)");
|
|
24152
|
+
console.log("");
|
|
24153
|
+
return;
|
|
24154
|
+
}
|
|
24155
|
+
if (!yes) {
|
|
24156
|
+
console.log("");
|
|
24157
|
+
const answer = prompt("Proceed? [y/N] ");
|
|
24158
|
+
if (answer?.toLowerCase() !== "y") {
|
|
24159
|
+
console.log("Cancelled.");
|
|
24160
|
+
return;
|
|
24161
|
+
}
|
|
24162
|
+
}
|
|
24163
|
+
const newConfig = transformConfig(configContent, managedImports);
|
|
24164
|
+
writeFileSync(configPath, newConfig);
|
|
24165
|
+
console.log(` Updated ${configName}`);
|
|
24166
|
+
const newPkg = transformPackageJson(pkg, nuaVersion);
|
|
24167
|
+
writeFileSync(pkgPath, JSON.stringify(newPkg, null, "\t") + `
|
|
24168
|
+
`);
|
|
24169
|
+
console.log(" Updated package.json");
|
|
24170
|
+
console.log("");
|
|
24171
|
+
console.log("Next steps:");
|
|
24172
|
+
console.log(" 1. bun install");
|
|
24173
|
+
console.log(" 2. Review the updated config");
|
|
24174
|
+
console.log(" 3. nua dev");
|
|
24175
|
+
console.log("");
|
|
24176
|
+
}
|
|
24177
|
+
var NUA_PROVIDED_PACKAGES, NUA_MANAGED_PACKAGES;
|
|
24178
|
+
var init_init = __esm(() => {
|
|
24179
|
+
init_utils();
|
|
24180
|
+
NUA_PROVIDED_PACKAGES = [
|
|
24181
|
+
"@astrojs/mdx",
|
|
24182
|
+
"@astrojs/sitemap",
|
|
24183
|
+
"@tailwindcss/vite",
|
|
24184
|
+
"tailwindcss"
|
|
24185
|
+
];
|
|
24186
|
+
NUA_MANAGED_PACKAGES = [
|
|
24187
|
+
"@astrojs/mdx",
|
|
24188
|
+
"@astrojs/sitemap",
|
|
24189
|
+
"@tailwindcss/vite"
|
|
24190
|
+
];
|
|
23957
24191
|
});
|
|
23958
24192
|
|
|
23959
|
-
//
|
|
23960
|
-
var
|
|
23961
|
-
|
|
23962
|
-
|
|
23963
|
-
|
|
23964
|
-
|
|
23965
|
-
|
|
23966
|
-
init_page_ops();
|
|
24193
|
+
// src/clean.ts
|
|
24194
|
+
var exports_clean = {};
|
|
24195
|
+
__export(exports_clean, {
|
|
24196
|
+
transformPackageJson: () => transformPackageJson2,
|
|
24197
|
+
transformConfig: () => transformConfig2,
|
|
24198
|
+
detectDisabledFeatures: () => detectDisabledFeatures,
|
|
24199
|
+
clean: () => clean
|
|
23967
24200
|
});
|
|
23968
|
-
|
|
23969
|
-
|
|
23970
|
-
function
|
|
23971
|
-
|
|
23972
|
-
for (
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
if (glob[i + 1] === "*") {
|
|
23976
|
-
re += ".*";
|
|
23977
|
-
i++;
|
|
23978
|
-
if (glob[i + 1] === "/")
|
|
23979
|
-
i++;
|
|
23980
|
-
} else {
|
|
23981
|
-
re += "[^/]*";
|
|
23982
|
-
}
|
|
23983
|
-
} else if (c === "?") {
|
|
23984
|
-
re += "[^/]";
|
|
23985
|
-
} else if (c === "{") {
|
|
23986
|
-
const end = glob.indexOf("}", i);
|
|
23987
|
-
if (end === -1) {
|
|
23988
|
-
re += "\\{";
|
|
23989
|
-
} else {
|
|
23990
|
-
const opts = glob.slice(i + 1, end).split(",").map((s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&"));
|
|
23991
|
-
re += `(?:${opts.join("|")})`;
|
|
23992
|
-
i = end;
|
|
23993
|
-
}
|
|
23994
|
-
} else if (".+^$()|[]\\".includes(c)) {
|
|
23995
|
-
re += `\\${c}`;
|
|
23996
|
-
} else {
|
|
23997
|
-
re += c;
|
|
24201
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
24202
|
+
import { basename as basename2, join as join3 } from "path";
|
|
24203
|
+
function detectDisabledFeatures(content) {
|
|
24204
|
+
const disabled = new Set;
|
|
24205
|
+
for (const key of FEATURE_KEYS) {
|
|
24206
|
+
if (new RegExp(`\\b${key}\\s*:\\s*false\\b`).test(content)) {
|
|
24207
|
+
disabled.add(key);
|
|
23998
24208
|
}
|
|
23999
24209
|
}
|
|
24000
|
-
return
|
|
24210
|
+
return disabled;
|
|
24001
24211
|
}
|
|
24002
|
-
|
|
24003
|
-
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
const normalizedPath = !isAbsoluteFs && filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
24010
|
-
const fullPath = path2.isAbsolute(normalizedPath) ? path2.resolve(normalizedPath) : path2.resolve(resolvedRoot, normalizedPath);
|
|
24011
|
-
if (!fullPath.startsWith(resolvedRoot + path2.sep) && fullPath !== resolvedRoot) {
|
|
24012
|
-
throw new Error(`Path traversal detected: ${filePath}`);
|
|
24212
|
+
function prependToArrayProperty(lines, property, items) {
|
|
24213
|
+
const pattern = new RegExp(`\\b${property}\\s*:\\s*\\[`);
|
|
24214
|
+
for (let i = 0;i < lines.length; i++) {
|
|
24215
|
+
if (pattern.test(lines[i])) {
|
|
24216
|
+
lines[i] = lines[i].replace(new RegExp(`(\\b${property}\\s*:\\s*\\[)`), `$1${items}, `);
|
|
24217
|
+
return true;
|
|
24218
|
+
}
|
|
24013
24219
|
}
|
|
24014
|
-
return
|
|
24220
|
+
return false;
|
|
24015
24221
|
}
|
|
24016
|
-
function
|
|
24017
|
-
|
|
24222
|
+
function transformConfig2(content, disabled) {
|
|
24223
|
+
const userImports = content.split(`
|
|
24224
|
+
`).filter((line) => /^\s*import\s/.test(line)).filter((line) => !line.includes("@nuasite/")).filter((line) => !line.includes("defineConfig"));
|
|
24225
|
+
let body = extractConfigBody(content);
|
|
24226
|
+
body = removeProperty(body, "nua");
|
|
24227
|
+
if (content.includes("@nuasite/nua/integration")) {
|
|
24228
|
+
body = body.replace(/\bnua\s*\([^)]*\)\s*,?\s*/g, "");
|
|
24229
|
+
body = body.replace(/\bintegrations\s*:\s*\[\s*,?\s*\]\s*,?/g, "");
|
|
24230
|
+
}
|
|
24231
|
+
const imports = [`import { defineConfig } from 'astro/config'`];
|
|
24232
|
+
if (!disabled.has("tailwindcss"))
|
|
24233
|
+
imports.push(`import tailwindcss from '@tailwindcss/vite'`);
|
|
24234
|
+
if (!disabled.has("mdx"))
|
|
24235
|
+
imports.push(`import mdx from '@astrojs/mdx'`);
|
|
24236
|
+
if (!disabled.has("sitemap"))
|
|
24237
|
+
imports.push(`import sitemap from '@astrojs/sitemap'`);
|
|
24238
|
+
imports.push(...userImports);
|
|
24239
|
+
const integrationCalls = [];
|
|
24240
|
+
if (!disabled.has("mdx"))
|
|
24241
|
+
integrationCalls.push("mdx()");
|
|
24242
|
+
if (!disabled.has("sitemap"))
|
|
24243
|
+
integrationCalls.push("sitemap()");
|
|
24244
|
+
const bodyLines = body.split(`
|
|
24245
|
+
`).filter((line) => line.trim() !== "");
|
|
24246
|
+
const hasIntegrations = bodyLines.some((line) => /^\s*integrations\s*:/.test(line));
|
|
24247
|
+
const hasVite = bodyLines.some((line) => /^\s*vite\s*:/.test(line));
|
|
24248
|
+
if (hasIntegrations && integrationCalls.length > 0) {
|
|
24249
|
+
prependToArrayProperty(bodyLines, "integrations", integrationCalls.join(", "));
|
|
24250
|
+
}
|
|
24251
|
+
if (!disabled.has("tailwindcss") && hasVite) {
|
|
24252
|
+
prependToArrayProperty(bodyLines, "plugins", "tailwindcss()");
|
|
24253
|
+
}
|
|
24254
|
+
const newPropLines = [];
|
|
24255
|
+
if (!disabled.has("tailwindcss") && !hasVite) {
|
|
24256
|
+
newPropLines.push("\tvite: {", "\t\tbuild: {", "\t\t\tsourcemap: true,", "\t\t},", "\t\tplugins: [tailwindcss()],", "\t},");
|
|
24257
|
+
}
|
|
24258
|
+
if (!hasIntegrations && integrationCalls.length > 0) {
|
|
24259
|
+
newPropLines.push(` integrations: [${integrationCalls.join(", ")}],`);
|
|
24260
|
+
}
|
|
24261
|
+
const allLines = [...bodyLines, ...newPropLines];
|
|
24262
|
+
return assembleConfig(imports, allLines.join(`
|
|
24263
|
+
`));
|
|
24018
24264
|
}
|
|
24019
|
-
|
|
24020
|
-
|
|
24021
|
-
|
|
24022
|
-
|
|
24023
|
-
|
|
24024
|
-
|
|
24265
|
+
function transformPackageJson2(pkg, disabled, usedRuntimePackages = []) {
|
|
24266
|
+
const result = structuredClone(pkg);
|
|
24267
|
+
const nuaVersion = result.dependencies?.["@nuasite/nua"] ?? result.devDependencies?.["@nuasite/nua"];
|
|
24268
|
+
if (result.scripts) {
|
|
24269
|
+
for (const [key, value] of Object.entries(result.scripts)) {
|
|
24270
|
+
if (typeof value === "string") {
|
|
24271
|
+
result.scripts[key] = value.replace(/\bnua build\b/g, "astro build").replace(/\bnua dev\b/g, "astro dev").replace(/\bnua preview\b/g, "astro preview");
|
|
24272
|
+
}
|
|
24273
|
+
}
|
|
24025
24274
|
}
|
|
24026
|
-
const
|
|
24027
|
-
|
|
24028
|
-
|
|
24029
|
-
|
|
24030
|
-
|
|
24031
|
-
}
|
|
24032
|
-
|
|
24275
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
24276
|
+
if (!result[field])
|
|
24277
|
+
continue;
|
|
24278
|
+
for (const name of NUASITE_TOOLING) {
|
|
24279
|
+
delete result[field][name];
|
|
24280
|
+
}
|
|
24281
|
+
if (Object.keys(result[field]).length === 0) {
|
|
24282
|
+
delete result[field];
|
|
24033
24283
|
}
|
|
24034
24284
|
}
|
|
24035
|
-
|
|
24036
|
-
}
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
|
|
24043
|
-
|
|
24285
|
+
if (!result.dependencies)
|
|
24286
|
+
result.dependencies = {};
|
|
24287
|
+
for (const [name, version] of Object.entries(PACKAGES_TO_ADD)) {
|
|
24288
|
+
if (result.dependencies[name])
|
|
24289
|
+
continue;
|
|
24290
|
+
if (name === "@astrojs/mdx" && disabled.has("mdx"))
|
|
24291
|
+
continue;
|
|
24292
|
+
if (name === "@astrojs/sitemap" && disabled.has("sitemap"))
|
|
24293
|
+
continue;
|
|
24294
|
+
if (name === "@tailwindcss/vite" && disabled.has("tailwindcss"))
|
|
24295
|
+
continue;
|
|
24296
|
+
if (name === "tailwindcss" && disabled.has("tailwindcss"))
|
|
24297
|
+
continue;
|
|
24298
|
+
result.dependencies[name] = version;
|
|
24044
24299
|
}
|
|
24045
|
-
|
|
24046
|
-
|
|
24300
|
+
for (const name of usedRuntimePackages) {
|
|
24301
|
+
if (!result.dependencies[name]) {
|
|
24302
|
+
result.dependencies[name] = nuaVersion ?? "^0.16.0";
|
|
24303
|
+
}
|
|
24047
24304
|
}
|
|
24048
|
-
|
|
24305
|
+
result.dependencies = Object.fromEntries(Object.entries(result.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
24306
|
+
return result;
|
|
24049
24307
|
}
|
|
24050
|
-
function
|
|
24051
|
-
const
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
}
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24061
|
-
const fullPath = resolve(filePath);
|
|
24062
|
-
await fs.mkdir(path2.dirname(fullPath), { recursive: true });
|
|
24063
|
-
const tempPath = `${fullPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
24064
|
-
try {
|
|
24065
|
-
await fs.writeFile(tempPath, content, "utf-8");
|
|
24066
|
-
await fs.rename(tempPath, fullPath);
|
|
24067
|
-
} catch (error) {
|
|
24068
|
-
await fs.rm(tempPath, { force: true });
|
|
24069
|
-
throw error;
|
|
24070
|
-
}
|
|
24071
|
-
},
|
|
24072
|
-
async rename(from, to) {
|
|
24073
|
-
const fullTo = resolve(to);
|
|
24074
|
-
await fs.mkdir(path2.dirname(fullTo), { recursive: true });
|
|
24075
|
-
await fs.rename(resolve(from), fullTo);
|
|
24076
|
-
},
|
|
24077
|
-
async remove(filePath) {
|
|
24078
|
-
await fs.rm(resolve(filePath), { force: true });
|
|
24079
|
-
},
|
|
24080
|
-
async exists(filePath) {
|
|
24081
|
-
try {
|
|
24082
|
-
await fs.access(resolve(filePath));
|
|
24083
|
-
return true;
|
|
24084
|
-
} catch {
|
|
24085
|
-
return false;
|
|
24086
|
-
}
|
|
24087
|
-
},
|
|
24088
|
-
async list(dir) {
|
|
24308
|
+
function scanForNuasiteUsage(cwd) {
|
|
24309
|
+
const srcDir = join3(cwd, "src");
|
|
24310
|
+
if (!existsSync3(srcDir))
|
|
24311
|
+
return [];
|
|
24312
|
+
const results = [];
|
|
24313
|
+
try {
|
|
24314
|
+
const files = readdirSync(srcDir, { recursive: true });
|
|
24315
|
+
for (const entry of files) {
|
|
24316
|
+
const fileName = String(entry);
|
|
24317
|
+
if (!/\.(astro|ts|tsx|js|jsx)$/.test(fileName))
|
|
24318
|
+
continue;
|
|
24089
24319
|
try {
|
|
24090
|
-
const
|
|
24091
|
-
|
|
24092
|
-
|
|
24093
|
-
|
|
24094
|
-
|
|
24095
|
-
|
|
24096
|
-
|
|
24097
|
-
|
|
24098
|
-
|
|
24099
|
-
const baseRel = staticPrefixDir(pattern);
|
|
24100
|
-
const absBase = resolve(baseRel);
|
|
24101
|
-
const matcher = globToRegExp(pattern);
|
|
24102
|
-
const files = await walkFiles(resolvedRoot, absBase);
|
|
24103
|
-
return files.filter((rel) => matcher.test(rel));
|
|
24104
|
-
},
|
|
24105
|
-
async stat(filePath) {
|
|
24106
|
-
const s = await fs.stat(resolve(filePath));
|
|
24107
|
-
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
24320
|
+
const content = readFileSync2(join3(srcDir, fileName), "utf-8");
|
|
24321
|
+
const matches = content.match(/@nuasite\/[\w-]+/g);
|
|
24322
|
+
if (matches) {
|
|
24323
|
+
results.push({
|
|
24324
|
+
file: join3("src", fileName),
|
|
24325
|
+
packages: [...new Set(matches)]
|
|
24326
|
+
});
|
|
24327
|
+
}
|
|
24328
|
+
} catch {}
|
|
24108
24329
|
}
|
|
24109
|
-
}
|
|
24330
|
+
} catch {}
|
|
24331
|
+
return results;
|
|
24110
24332
|
}
|
|
24111
|
-
|
|
24112
|
-
|
|
24113
|
-
|
|
24114
|
-
|
|
24333
|
+
async function clean({ cwd = process.cwd(), dryRun = false, yes = false } = {}) {
|
|
24334
|
+
const configPath = findAstroConfig(cwd);
|
|
24335
|
+
if (!configPath) {
|
|
24336
|
+
console.error("No Astro config file found.");
|
|
24337
|
+
process.exit(1);
|
|
24338
|
+
}
|
|
24339
|
+
const configContent = readFileSync2(configPath, "utf-8");
|
|
24340
|
+
if (!configContent.includes("@nuasite/nua")) {
|
|
24341
|
+
console.log("This project does not use @nuasite/nua. Nothing to clean.");
|
|
24115
24342
|
return;
|
|
24116
|
-
|
|
24117
|
-
const
|
|
24118
|
-
if (
|
|
24119
|
-
|
|
24120
|
-
|
|
24121
|
-
|
|
24122
|
-
|
|
24123
|
-
|
|
24124
|
-
|
|
24125
|
-
const
|
|
24126
|
-
const
|
|
24127
|
-
const
|
|
24128
|
-
|
|
24129
|
-
|
|
24130
|
-
|
|
24131
|
-
|
|
24132
|
-
|
|
24133
|
-
|
|
24134
|
-
|
|
24135
|
-
|
|
24136
|
-
|
|
24137
|
-
|
|
24138
|
-
|
|
24139
|
-
|
|
24140
|
-
|
|
24141
|
-
|
|
24142
|
-
|
|
24143
|
-
|
|
24144
|
-
|
|
24343
|
+
}
|
|
24344
|
+
const pkgPath = join3(cwd, "package.json");
|
|
24345
|
+
if (!existsSync3(pkgPath)) {
|
|
24346
|
+
console.error("No package.json found.");
|
|
24347
|
+
process.exit(1);
|
|
24348
|
+
}
|
|
24349
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
24350
|
+
const disabled = detectDisabledFeatures(configContent);
|
|
24351
|
+
const nuasiteUsage = scanForNuasiteUsage(cwd);
|
|
24352
|
+
const configName = basename2(configPath);
|
|
24353
|
+
const usedRuntimePackages = new Set;
|
|
24354
|
+
const toolingUsage = [];
|
|
24355
|
+
for (const entry of nuasiteUsage) {
|
|
24356
|
+
const runtime = entry.packages.filter((p) => !NUASITE_TOOLING.includes(p));
|
|
24357
|
+
const tooling = entry.packages.filter((p) => NUASITE_TOOLING.includes(p));
|
|
24358
|
+
for (const p of runtime)
|
|
24359
|
+
usedRuntimePackages.add(p);
|
|
24360
|
+
if (tooling.length > 0) {
|
|
24361
|
+
toolingUsage.push({ file: entry.file, packages: tooling });
|
|
24362
|
+
}
|
|
24363
|
+
}
|
|
24364
|
+
console.log("");
|
|
24365
|
+
console.log("nua clean \u2014 eject to standard Astro project");
|
|
24366
|
+
console.log("");
|
|
24367
|
+
console.log(` ${configName}`);
|
|
24368
|
+
console.log(" - Replace @nuasite/nua with explicit Astro integrations");
|
|
24369
|
+
console.log(" - Add mdx, sitemap, tailwindcss imports");
|
|
24370
|
+
if (disabled.size > 0) {
|
|
24371
|
+
console.log(` - Skipping disabled: ${[...disabled].join(", ")}`);
|
|
24372
|
+
}
|
|
24373
|
+
console.log("");
|
|
24374
|
+
console.log(" package.json");
|
|
24375
|
+
console.log(" - Remove @nuasite/* tooling dependencies");
|
|
24376
|
+
if (usedRuntimePackages.size > 0) {
|
|
24377
|
+
console.log(` - Keep as explicit deps: ${[...usedRuntimePackages].join(", ")}`);
|
|
24378
|
+
}
|
|
24379
|
+
console.log(" - Add standard Astro packages");
|
|
24380
|
+
console.log(" - Update scripts: nua \u2192 astro");
|
|
24381
|
+
if (toolingUsage.length > 0) {
|
|
24382
|
+
console.log("");
|
|
24383
|
+
console.log(" Warning: @nuasite tooling imports found in source files:");
|
|
24384
|
+
for (const { file, packages } of toolingUsage) {
|
|
24385
|
+
console.log(` ${file} (${packages.join(", ")})`);
|
|
24386
|
+
}
|
|
24387
|
+
console.log(" These will need manual removal.");
|
|
24388
|
+
}
|
|
24389
|
+
if (dryRun) {
|
|
24390
|
+
console.log("");
|
|
24391
|
+
console.log(" (--dry-run: no changes made)");
|
|
24392
|
+
console.log("");
|
|
24145
24393
|
return;
|
|
24146
|
-
|
|
24147
|
-
|
|
24148
|
-
|
|
24149
|
-
|
|
24150
|
-
|
|
24151
|
-
|
|
24152
|
-
|
|
24153
|
-
subdirs.push(scanDirectory(fs2, fullPath, baseDir, relativeToRoot, excludeDir, items));
|
|
24154
|
-
} else {
|
|
24155
|
-
const dotIdx = entry.name.lastIndexOf(".");
|
|
24156
|
-
const ext = dotIdx >= 0 ? entry.name.slice(dotIdx).toLowerCase() : "";
|
|
24157
|
-
if (!IMAGE_EXTENSIONS2.has(ext))
|
|
24158
|
-
continue;
|
|
24159
|
-
const url = relativeToRoot ? `/${fullPath}` : `/${fullPath.slice(baseDir.length + 1)}`;
|
|
24160
|
-
items.push({
|
|
24161
|
-
id: `project:${url}`,
|
|
24162
|
-
url,
|
|
24163
|
-
filename: entry.name,
|
|
24164
|
-
contentType: mimeFromExt(ext)
|
|
24165
|
-
});
|
|
24394
|
+
}
|
|
24395
|
+
if (!yes) {
|
|
24396
|
+
console.log("");
|
|
24397
|
+
const answer = prompt("Proceed? [y/N] ");
|
|
24398
|
+
if (answer?.toLowerCase() !== "y") {
|
|
24399
|
+
console.log("Cancelled.");
|
|
24400
|
+
return;
|
|
24166
24401
|
}
|
|
24167
24402
|
}
|
|
24168
|
-
|
|
24403
|
+
const newConfig = transformConfig2(configContent, disabled);
|
|
24404
|
+
writeFileSync2(configPath, newConfig);
|
|
24405
|
+
console.log(` Updated ${configName}`);
|
|
24406
|
+
const newPkg = transformPackageJson2(pkg, disabled, [...usedRuntimePackages]);
|
|
24407
|
+
writeFileSync2(pkgPath, JSON.stringify(newPkg, null, "\t") + `
|
|
24408
|
+
`);
|
|
24409
|
+
console.log(" Updated package.json");
|
|
24410
|
+
console.log("");
|
|
24411
|
+
console.log("Next steps:");
|
|
24412
|
+
console.log(" 1. bun install");
|
|
24413
|
+
console.log(" 2. Review the updated config");
|
|
24414
|
+
console.log(" 3. astro dev");
|
|
24415
|
+
if (toolingUsage.length > 0) {
|
|
24416
|
+
console.log(" 4. Remove @nuasite tooling imports from source files");
|
|
24417
|
+
}
|
|
24418
|
+
console.log("");
|
|
24169
24419
|
}
|
|
24170
|
-
var
|
|
24171
|
-
var
|
|
24172
|
-
|
|
24173
|
-
|
|
24174
|
-
|
|
24175
|
-
|
|
24176
|
-
|
|
24177
|
-
|
|
24178
|
-
|
|
24179
|
-
|
|
24180
|
-
|
|
24181
|
-
|
|
24182
|
-
|
|
24183
|
-
|
|
24184
|
-
|
|
24185
|
-
|
|
24186
|
-
|
|
24187
|
-
|
|
24188
|
-
|
|
24189
|
-
|
|
24190
|
-
|
|
24191
|
-
|
|
24192
|
-
});
|
|
24193
|
-
|
|
24194
|
-
// ../cms-core/src/index.ts
|
|
24195
|
-
var init_src2 = __esm(() => {
|
|
24196
|
-
init_collection_scanner();
|
|
24197
|
-
init_component_registry();
|
|
24198
|
-
init_content_config_ast();
|
|
24199
|
-
init_core();
|
|
24200
|
-
init_node_fs();
|
|
24201
|
-
init_entry_ops();
|
|
24202
|
-
init_media();
|
|
24203
|
-
init_project_config_ast();
|
|
24420
|
+
var FEATURE_KEYS, NUASITE_TOOLING, PACKAGES_TO_ADD;
|
|
24421
|
+
var init_clean = __esm(() => {
|
|
24422
|
+
init_utils();
|
|
24423
|
+
FEATURE_KEYS = ["cms", "pageMarkdown", "mdx", "sitemap", "tailwindcss", "checks"];
|
|
24424
|
+
NUASITE_TOOLING = [
|
|
24425
|
+
"@nuasite/nua",
|
|
24426
|
+
"@nuasite/core",
|
|
24427
|
+
"@nuasite/cli",
|
|
24428
|
+
"@nuasite/cms",
|
|
24429
|
+
"@nuasite/llm-enhancements",
|
|
24430
|
+
"@nuasite/checks",
|
|
24431
|
+
"@nuasite/agent-summary"
|
|
24432
|
+
];
|
|
24433
|
+
PACKAGES_TO_ADD = {
|
|
24434
|
+
astro: "^6.0.2",
|
|
24435
|
+
"@astrojs/check": "^0.9.7",
|
|
24436
|
+
"@astrojs/mdx": "^5.0.0",
|
|
24437
|
+
"@astrojs/sitemap": "^3.7.1",
|
|
24438
|
+
"@tailwindcss/vite": "^4.2.1",
|
|
24439
|
+
tailwindcss: "^4.2.1",
|
|
24440
|
+
typescript: "^5"
|
|
24441
|
+
};
|
|
24204
24442
|
});
|
|
24205
24443
|
|
|
24206
24444
|
// ../cms/src/config.ts
|
|
@@ -24212,7 +24450,7 @@ var init_config = () => {};
|
|
|
24212
24450
|
|
|
24213
24451
|
// ../cms/src/component-registry.ts
|
|
24214
24452
|
import fs2 from "fs/promises";
|
|
24215
|
-
import
|
|
24453
|
+
import path4 from "path";
|
|
24216
24454
|
|
|
24217
24455
|
class ComponentRegistry {
|
|
24218
24456
|
components = new Map;
|
|
@@ -24222,7 +24460,7 @@ class ComponentRegistry {
|
|
|
24222
24460
|
}
|
|
24223
24461
|
async scan() {
|
|
24224
24462
|
for (const dir of this.componentDirs) {
|
|
24225
|
-
const fullPath =
|
|
24463
|
+
const fullPath = path4.join(getProjectRoot(), dir);
|
|
24226
24464
|
try {
|
|
24227
24465
|
await this.scanDirectory(fullPath, dir);
|
|
24228
24466
|
} catch {}
|
|
@@ -24237,8 +24475,8 @@ class ComponentRegistry {
|
|
|
24237
24475
|
async scanDirectory(dir, relativePath) {
|
|
24238
24476
|
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
24239
24477
|
for (const entry of entries) {
|
|
24240
|
-
const fullPath =
|
|
24241
|
-
const relPath =
|
|
24478
|
+
const fullPath = path4.join(dir, entry.name);
|
|
24479
|
+
const relPath = path4.join(relativePath, entry.name);
|
|
24242
24480
|
if (entry.isDirectory()) {
|
|
24243
24481
|
await this.scanDirectory(fullPath, relPath);
|
|
24244
24482
|
} else if (entry.isFile() && entry.name.endsWith(".astro")) {
|
|
@@ -24249,7 +24487,7 @@ class ComponentRegistry {
|
|
|
24249
24487
|
async parseComponent(filePath, relativePath) {
|
|
24250
24488
|
try {
|
|
24251
24489
|
const content = await fs2.readFile(filePath, "utf-8");
|
|
24252
|
-
const componentName =
|
|
24490
|
+
const componentName = path4.basename(filePath, ".astro");
|
|
24253
24491
|
const props = await this.extractProps(content);
|
|
24254
24492
|
const slots = this.extractSlots(content);
|
|
24255
24493
|
const description = this.extractDescription(content);
|
|
@@ -24504,7 +24742,7 @@ function slugify2(text) {
|
|
|
24504
24742
|
|
|
24505
24743
|
// ../cms/src/utils.ts
|
|
24506
24744
|
import { createHash } from "crypto";
|
|
24507
|
-
import
|
|
24745
|
+
import path5 from "path";
|
|
24508
24746
|
function normalizePagePath(url) {
|
|
24509
24747
|
let pathname;
|
|
24510
24748
|
try {
|
|
@@ -24557,21 +24795,21 @@ function escapeReplacement(str) {
|
|
|
24557
24795
|
return str.replace(/\$/g, "$$$$");
|
|
24558
24796
|
}
|
|
24559
24797
|
function resolveSourcePath(sourcePath) {
|
|
24560
|
-
return
|
|
24798
|
+
return path5.isAbsolute(sourcePath) ? sourcePath : path5.join(getProjectRoot(), sourcePath);
|
|
24561
24799
|
}
|
|
24562
24800
|
function resolveAndValidatePath(filePath) {
|
|
24563
24801
|
const projectRoot = getProjectRoot();
|
|
24564
|
-
const resolvedRoot =
|
|
24802
|
+
const resolvedRoot = path5.resolve(projectRoot);
|
|
24565
24803
|
const isAbsoluteFs = filePath.startsWith(resolvedRoot);
|
|
24566
24804
|
const normalizedPath = !isAbsoluteFs && filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
24567
|
-
const fullPath =
|
|
24568
|
-
if (!fullPath.startsWith(resolvedRoot +
|
|
24805
|
+
const fullPath = path5.isAbsolute(normalizedPath) ? path5.resolve(normalizedPath) : path5.resolve(projectRoot, normalizedPath);
|
|
24806
|
+
if (!fullPath.startsWith(resolvedRoot + path5.sep) && fullPath !== resolvedRoot) {
|
|
24569
24807
|
throw new Error(`Path traversal detected: ${filePath}`);
|
|
24570
24808
|
}
|
|
24571
24809
|
return fullPath;
|
|
24572
24810
|
}
|
|
24573
24811
|
async function acquireFileLock(filePath) {
|
|
24574
|
-
const key =
|
|
24812
|
+
const key = path5.resolve(filePath);
|
|
24575
24813
|
while (fileLocks.has(key)) {
|
|
24576
24814
|
await fileLocks.get(key);
|
|
24577
24815
|
}
|
|
@@ -24589,8 +24827,8 @@ function escapeHtml2(text) {
|
|
|
24589
24827
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
24590
24828
|
}
|
|
24591
24829
|
function relativeImportPath2(fromFile, toFile) {
|
|
24592
|
-
const fromDir =
|
|
24593
|
-
let rel =
|
|
24830
|
+
const fromDir = path5.dirname(fromFile);
|
|
24831
|
+
let rel = path5.relative(fromDir, toFile).split(path5.sep).join("/");
|
|
24594
24832
|
if (!rel.startsWith("."))
|
|
24595
24833
|
rel = `./${rel}`;
|
|
24596
24834
|
return rel;
|
|
@@ -24603,7 +24841,7 @@ var init_utils2 = __esm(() => {
|
|
|
24603
24841
|
|
|
24604
24842
|
// ../cms/src/handlers/component-ops.ts
|
|
24605
24843
|
import fs3 from "fs/promises";
|
|
24606
|
-
import
|
|
24844
|
+
import path6 from "path";
|
|
24607
24845
|
async function handleInsertComponent(request, manifestWriter) {
|
|
24608
24846
|
const { position, referenceComponentId, componentName, props, meta } = request;
|
|
24609
24847
|
if (!meta?.url) {
|
|
@@ -24821,7 +25059,7 @@ function getComponentOccurrenceIndex(manifest, referenceComponent) {
|
|
|
24821
25059
|
async function findComponentInvocationFile(projectRoot, pageUrl, manifest, referenceComponent) {
|
|
24822
25060
|
if (referenceComponent.invocationSourcePath) {
|
|
24823
25061
|
const filePath = normalizeFilePath(referenceComponent.invocationSourcePath);
|
|
24824
|
-
const fullPath =
|
|
25062
|
+
const fullPath = path6.resolve(projectRoot, filePath);
|
|
24825
25063
|
try {
|
|
24826
25064
|
const content = await fs3.readFile(fullPath, "utf-8");
|
|
24827
25065
|
const lines = content.split(`
|
|
@@ -24835,7 +25073,7 @@ async function findComponentInvocationFile(projectRoot, pageUrl, manifest, refer
|
|
|
24835
25073
|
const candidates = getPageFileCandidates(pageUrl);
|
|
24836
25074
|
const occurrenceIndex = getComponentOccurrenceIndex(manifest, referenceComponent);
|
|
24837
25075
|
for (const candidate of candidates) {
|
|
24838
|
-
const fullPath =
|
|
25076
|
+
const fullPath = path6.resolve(projectRoot, candidate);
|
|
24839
25077
|
try {
|
|
24840
25078
|
const content = await fs3.readFile(fullPath, "utf-8");
|
|
24841
25079
|
const lines = content.split(`
|
|
@@ -24895,7 +25133,7 @@ function getIndentation(line) {
|
|
|
24895
25133
|
function normalizeFilePath(p) {
|
|
24896
25134
|
if (!p.startsWith("/"))
|
|
24897
25135
|
return p;
|
|
24898
|
-
const projectRoot =
|
|
25136
|
+
const projectRoot = path6.resolve(getProjectRoot());
|
|
24899
25137
|
if (p.startsWith(projectRoot))
|
|
24900
25138
|
return p;
|
|
24901
25139
|
return p.slice(1);
|
|
@@ -25303,45 +25541,45 @@ var init_array_ops = __esm(() => {
|
|
|
25303
25541
|
// ../cms/src/astro-image-paths.ts
|
|
25304
25542
|
import { createHash as createHash2 } from "crypto";
|
|
25305
25543
|
import fs5 from "fs/promises";
|
|
25306
|
-
import
|
|
25544
|
+
import path7 from "path";
|
|
25307
25545
|
function isHugoStyleEntry(entryAbsPath) {
|
|
25308
|
-
return HUGO_INDEX_RE.test(
|
|
25546
|
+
return HUGO_INDEX_RE.test(path7.basename(entryAbsPath));
|
|
25309
25547
|
}
|
|
25310
25548
|
function shortContentHash(buf) {
|
|
25311
25549
|
return createHash2("sha256").update(buf).digest("hex").slice(0, 8);
|
|
25312
25550
|
}
|
|
25313
25551
|
async function pickAstroImageTarget(args) {
|
|
25314
|
-
const entryDir =
|
|
25552
|
+
const entryDir = path7.dirname(args.entryAbsPath);
|
|
25315
25553
|
const isHugoStyle = isHugoStyleEntry(args.entryAbsPath);
|
|
25316
|
-
const safeFilename =
|
|
25554
|
+
const safeFilename = path7.basename(args.originalFilename);
|
|
25317
25555
|
if (!safeFilename || safeFilename === "." || safeFilename === "..") {
|
|
25318
25556
|
throw new Error(`Invalid filename: ${args.originalFilename}`);
|
|
25319
25557
|
}
|
|
25320
25558
|
const baseName = isHugoStyle ? safeFilename : `${args.slug}-${safeFilename}`;
|
|
25321
|
-
const baseAbs =
|
|
25559
|
+
const baseAbs = path7.join(entryDir, baseName);
|
|
25322
25560
|
if (await isFreeOrMatching(baseAbs, args.compareBuffer)) {
|
|
25323
25561
|
return { absPath: baseAbs, relPath: `./${baseName}` };
|
|
25324
25562
|
}
|
|
25325
25563
|
const candidateAbs = await pickHashedSibling(entryDir, baseName, args.compareBuffer);
|
|
25326
|
-
return { absPath: candidateAbs, relPath: `./${
|
|
25564
|
+
return { absPath: candidateAbs, relPath: `./${path7.basename(candidateAbs)}` };
|
|
25327
25565
|
}
|
|
25328
25566
|
async function pickSiblingTarget(dir, filename, buf) {
|
|
25329
|
-
const safe =
|
|
25567
|
+
const safe = path7.basename(filename);
|
|
25330
25568
|
if (!safe || safe === "." || safe === "..") {
|
|
25331
25569
|
throw new Error(`Invalid filename: ${filename}`);
|
|
25332
25570
|
}
|
|
25333
|
-
const baseAbs =
|
|
25571
|
+
const baseAbs = path7.join(dir, safe);
|
|
25334
25572
|
if (await isFreeOrMatching(baseAbs, buf))
|
|
25335
25573
|
return baseAbs;
|
|
25336
25574
|
return pickHashedSibling(dir, safe, buf);
|
|
25337
25575
|
}
|
|
25338
25576
|
async function pickHashedSibling(dir, baseName, buf) {
|
|
25339
25577
|
const hash = shortContentHash(buf);
|
|
25340
|
-
const ext =
|
|
25341
|
-
const stem =
|
|
25578
|
+
const ext = path7.extname(baseName);
|
|
25579
|
+
const stem = path7.basename(baseName, ext);
|
|
25342
25580
|
for (let attempt = 0;attempt < 5; attempt++) {
|
|
25343
25581
|
const suffix = attempt === 0 ? hash : `${hash}-${attempt}`;
|
|
25344
|
-
const candidateAbs =
|
|
25582
|
+
const candidateAbs = path7.join(dir, `${stem}-${suffix}${ext}`);
|
|
25345
25583
|
if (await isFreeOrMatching(candidateAbs, buf))
|
|
25346
25584
|
return candidateAbs;
|
|
25347
25585
|
}
|
|
@@ -25365,7 +25603,7 @@ var init_astro_image_paths = __esm(() => {
|
|
|
25365
25603
|
|
|
25366
25604
|
// ../cms/src/handlers/astro-image-upload.ts
|
|
25367
25605
|
import fs6 from "fs/promises";
|
|
25368
|
-
import
|
|
25606
|
+
import path8 from "path";
|
|
25369
25607
|
async function tryAstroImageUpload(args) {
|
|
25370
25608
|
const { collection, entry, field } = args.context;
|
|
25371
25609
|
if (!collection || !entry || !field)
|
|
@@ -25387,12 +25625,12 @@ async function tryAstroImageUpload(args) {
|
|
|
25387
25625
|
originalFilename: args.originalFilename,
|
|
25388
25626
|
compareBuffer: args.fileBuffer
|
|
25389
25627
|
});
|
|
25390
|
-
await fs6.mkdir(
|
|
25628
|
+
await fs6.mkdir(path8.dirname(target.absPath), { recursive: true });
|
|
25391
25629
|
await fs6.writeFile(target.absPath, args.fileBuffer);
|
|
25392
25630
|
return {
|
|
25393
25631
|
success: true,
|
|
25394
25632
|
url: target.relPath,
|
|
25395
|
-
filename:
|
|
25633
|
+
filename: path8.basename(target.absPath)
|
|
25396
25634
|
};
|
|
25397
25635
|
}
|
|
25398
25636
|
var init_astro_image_upload = __esm(() => {
|
|
@@ -31162,14 +31400,14 @@ var init_ast_parser = __esm(() => {
|
|
|
31162
31400
|
});
|
|
31163
31401
|
|
|
31164
31402
|
// ../cms/src/source-finder/collection-finder.ts
|
|
31165
|
-
var
|
|
31403
|
+
var import_yaml4, specPathnameIndexCache;
|
|
31166
31404
|
var init_collection_finder = __esm(() => {
|
|
31167
31405
|
init_src2();
|
|
31168
31406
|
init_config();
|
|
31169
31407
|
init_scan_cache();
|
|
31170
31408
|
init_cache();
|
|
31171
31409
|
init_snippet_utils();
|
|
31172
|
-
|
|
31410
|
+
import_yaml4 = __toESM(require_dist(), 1);
|
|
31173
31411
|
specPathnameIndexCache = new WeakMap;
|
|
31174
31412
|
});
|
|
31175
31413
|
|
|
@@ -31228,7 +31466,7 @@ function extractAstroImageOriginalUrl(src) {
|
|
|
31228
31466
|
} catch {}
|
|
31229
31467
|
return;
|
|
31230
31468
|
}
|
|
31231
|
-
var
|
|
31469
|
+
var import_yaml5;
|
|
31232
31470
|
var init_snippet_utils = __esm(() => {
|
|
31233
31471
|
init_astro_image_paths();
|
|
31234
31472
|
init_config();
|
|
@@ -31238,12 +31476,12 @@ var init_snippet_utils = __esm(() => {
|
|
|
31238
31476
|
init_cross_file_tracker();
|
|
31239
31477
|
init_image_finder();
|
|
31240
31478
|
init_search_index();
|
|
31241
|
-
|
|
31479
|
+
import_yaml5 = __toESM(require_dist(), 1);
|
|
31242
31480
|
});
|
|
31243
31481
|
|
|
31244
31482
|
// ../cms/src/handlers/source-writer.ts
|
|
31245
31483
|
import fs8 from "fs/promises";
|
|
31246
|
-
import
|
|
31484
|
+
import path9 from "path";
|
|
31247
31485
|
async function handleUpdate(request, manifestWriter) {
|
|
31248
31486
|
const { changes, meta } = request;
|
|
31249
31487
|
const errors = [];
|
|
@@ -31280,7 +31518,7 @@ async function handleUpdate(request, manifestWriter) {
|
|
|
31280
31518
|
}
|
|
31281
31519
|
if (appliedCount > 0 && newContent !== currentContent) {
|
|
31282
31520
|
for (const op of fileOps) {
|
|
31283
|
-
await fs8.mkdir(
|
|
31521
|
+
await fs8.mkdir(path9.dirname(op.target), { recursive: true });
|
|
31284
31522
|
await fs8.writeFile(op.target, op.bytes);
|
|
31285
31523
|
}
|
|
31286
31524
|
await fs8.writeFile(fullPath, newContent, "utf-8");
|
|
@@ -31376,7 +31614,7 @@ async function applyImageChange(content, change, absFilePath, originUrl) {
|
|
|
31376
31614
|
const yamlKeyMatch = change.sourceSnippet.match(/^\s*([\w][\w-]*):\s*/);
|
|
31377
31615
|
if (yamlKeyMatch?.[1]) {
|
|
31378
31616
|
try {
|
|
31379
|
-
const parsed =
|
|
31617
|
+
const parsed = import_yaml6.parse(change.sourceSnippet);
|
|
31380
31618
|
if (parsed && typeof parsed === "object") {
|
|
31381
31619
|
const value = parsed[yamlKeyMatch[1]];
|
|
31382
31620
|
if (typeof value === "string" && !srcCandidates.includes(value)) {
|
|
@@ -31546,8 +31784,8 @@ async function tryRewriteAssetImport(content, importInfo, newSrc, absFilePath, o
|
|
|
31546
31784
|
const resolved = await resolveNewSrcBytes(newSrc, originUrl);
|
|
31547
31785
|
if (!resolved)
|
|
31548
31786
|
return null;
|
|
31549
|
-
const originalAssetAbs =
|
|
31550
|
-
const targetAbs = await pickSiblingTarget(
|
|
31787
|
+
const originalAssetAbs = path9.resolve(path9.dirname(absFilePath), importInfo.source);
|
|
31788
|
+
const targetAbs = await pickSiblingTarget(path9.dirname(originalAssetAbs), resolved.filename, resolved.bytes);
|
|
31551
31789
|
const newRelImport = relativeImportPath2(absFilePath, targetAbs);
|
|
31552
31790
|
const newContent = content.slice(0, importInfo.sourceStart) + newRelImport + content.slice(importInfo.sourceEnd);
|
|
31553
31791
|
return {
|
|
@@ -31557,8 +31795,8 @@ async function tryRewriteAssetImport(content, importInfo, newSrc, absFilePath, o
|
|
|
31557
31795
|
};
|
|
31558
31796
|
}
|
|
31559
31797
|
async function resolveNewSrcBytes(newSrc, originUrl) {
|
|
31560
|
-
const filenameFromPath = (p) =>
|
|
31561
|
-
const diskPath = newSrc.startsWith("/src/") ?
|
|
31798
|
+
const filenameFromPath = (p) => path9.basename(p.split("?")[0] ?? p);
|
|
31799
|
+
const diskPath = newSrc.startsWith("/src/") ? path9.join(getProjectRoot(), newSrc.slice(1)) : newSrc.startsWith("/") && !newSrc.startsWith("//") ? path9.join(getProjectRoot(), "public", newSrc.replace(/^\/+/, "")) : null;
|
|
31562
31800
|
if (diskPath) {
|
|
31563
31801
|
try {
|
|
31564
31802
|
return { bytes: await fs8.readFile(diskPath), filename: filenameFromPath(newSrc) };
|
|
@@ -31993,7 +32231,7 @@ function tryYamlValueReplacement(sourceSnippet, resolvedOriginal, resolvedNewTex
|
|
|
31993
32231
|
if (!keyMatch)
|
|
31994
32232
|
return null;
|
|
31995
32233
|
try {
|
|
31996
|
-
const parsed =
|
|
32234
|
+
const parsed = import_yaml6.parse(sourceSnippet);
|
|
31997
32235
|
if (parsed == null || typeof parsed !== "object")
|
|
31998
32236
|
return null;
|
|
31999
32237
|
const value = parsed[keyMatch[2]];
|
|
@@ -32004,7 +32242,7 @@ function tryYamlValueReplacement(sourceSnippet, resolvedOriginal, resolvedNewTex
|
|
|
32004
32242
|
} catch {
|
|
32005
32243
|
return null;
|
|
32006
32244
|
}
|
|
32007
|
-
const serialized =
|
|
32245
|
+
const serialized = import_yaml6.stringify(resolvedNewText, { lineWidth: 0 }).trimEnd();
|
|
32008
32246
|
return `${keyMatch[1]}${serialized}`;
|
|
32009
32247
|
}
|
|
32010
32248
|
function tryDataFileValueReplacement(content, sourceSnippet, originalValue, newValue, sourceLine) {
|
|
@@ -32092,21 +32330,21 @@ function tryBrNormalizedChange(sourceSnippet, resolvedOriginal, resolvedNewText)
|
|
|
32092
32330
|
}
|
|
32093
32331
|
return result !== sourceSnippet ? result : null;
|
|
32094
32332
|
}
|
|
32095
|
-
var import_node_html_parser,
|
|
32333
|
+
var import_node_html_parser, import_yaml6, ASSET_IMPORT_EXT_RE, REMOTE_FETCH_TIMEOUT_MS = 15000, REMOTE_FETCH_MAX_BYTES, QUOTED_LITERAL_DELIMITERS;
|
|
32096
32334
|
var init_source_writer = __esm(() => {
|
|
32097
32335
|
init_astro_image_paths();
|
|
32098
32336
|
init_config();
|
|
32099
32337
|
init_snippet_utils();
|
|
32100
32338
|
init_utils2();
|
|
32101
32339
|
import_node_html_parser = __toESM(require_dist2(), 1);
|
|
32102
|
-
|
|
32340
|
+
import_yaml6 = __toESM(require_dist(), 1);
|
|
32103
32341
|
ASSET_IMPORT_EXT_RE = /\.(jpe?g|png|gif|webp|avif|svg|ico|bmp|tiff?)$/i;
|
|
32104
32342
|
REMOTE_FETCH_MAX_BYTES = 50 * 1024 * 1024;
|
|
32105
32343
|
QUOTED_LITERAL_DELIMITERS = [`'`, `"`, "`"];
|
|
32106
32344
|
});
|
|
32107
32345
|
|
|
32108
32346
|
// ../cms/src/handlers/api-routes.ts
|
|
32109
|
-
import
|
|
32347
|
+
import path10 from "path";
|
|
32110
32348
|
function requireMedia(ctx) {
|
|
32111
32349
|
if (!ctx.mediaAdapter) {
|
|
32112
32350
|
sendError(ctx.res, "Media storage not configured", 501);
|
|
@@ -32167,7 +32405,7 @@ function lastSlug(sourcePath) {
|
|
|
32167
32405
|
const slash = withoutExt.lastIndexOf("/");
|
|
32168
32406
|
return slash >= 0 ? withoutExt.slice(slash + 1) : withoutExt;
|
|
32169
32407
|
}
|
|
32170
|
-
var ALLOWED_UPLOAD_TYPES,
|
|
32408
|
+
var ALLOWED_UPLOAD_TYPES, DATA_EXTENSIONS2, routeMap;
|
|
32171
32409
|
var init_api_routes = __esm(() => {
|
|
32172
32410
|
init_src2();
|
|
32173
32411
|
init_config();
|
|
@@ -32188,7 +32426,7 @@ var init_api_routes = __esm(() => {
|
|
|
32188
32426
|
"image/x-icon",
|
|
32189
32427
|
"application/pdf"
|
|
32190
32428
|
]);
|
|
32191
|
-
|
|
32429
|
+
DATA_EXTENSIONS2 = new Set(["json", "yaml", "yml"]);
|
|
32192
32430
|
routeMap = new Map([
|
|
32193
32431
|
post("update", (body, mw) => handleUpdate(body, mw)),
|
|
32194
32432
|
post("insert-component", (body, mw) => handleInsertComponent(body, mw)),
|
|
@@ -32242,7 +32480,7 @@ var init_api_routes = __esm(() => {
|
|
|
32242
32480
|
custom("POST", "markdown/create", async ({ req, res, core, manifestWriter, contentDir }) => {
|
|
32243
32481
|
const body = await parseJsonBody(req);
|
|
32244
32482
|
const ext = body.fileExtension ?? "md";
|
|
32245
|
-
const isData =
|
|
32483
|
+
const isData = DATA_EXTENSIONS2.has(ext);
|
|
32246
32484
|
const frontmatter = isData ? { ...body.frontmatter ?? {} } : { title: body.title, date: new Date().toISOString().split("T")[0], ...body.frontmatter ?? {} };
|
|
32247
32485
|
const result = await core.createEntry({
|
|
32248
32486
|
collection: body.collection,
|
|
@@ -32269,7 +32507,7 @@ var init_api_routes = __esm(() => {
|
|
|
32269
32507
|
sendJson(res, { success: false, error: `Invalid content path: ${body.filePath}` }, 400);
|
|
32270
32508
|
return;
|
|
32271
32509
|
}
|
|
32272
|
-
const fullPath =
|
|
32510
|
+
const fullPath = path10.resolve(getProjectRoot(), body.filePath.replace(/^\//, ""));
|
|
32273
32511
|
expectedDeletions.add(fullPath);
|
|
32274
32512
|
const result = await core.deleteEntry(entry.collection, entry.slug);
|
|
32275
32513
|
if (result.success) {
|
|
@@ -32376,7 +32614,7 @@ var init_api_routes = __esm(() => {
|
|
|
32376
32614
|
const body = await parseJsonBody(req);
|
|
32377
32615
|
const result = await core.deletePage(body);
|
|
32378
32616
|
if (result.success && result.filePath) {
|
|
32379
|
-
expectedDeletions.add(
|
|
32617
|
+
expectedDeletions.add(path10.resolve(getProjectRoot(), result.filePath));
|
|
32380
32618
|
}
|
|
32381
32619
|
if (result.success && body.createRedirect && body.redirectTo) {
|
|
32382
32620
|
await core.addRedirect({ source: body.pagePath, destination: body.redirectTo, statusCode: 307 });
|
|
@@ -32481,7 +32719,7 @@ var init_color_patterns = __esm(() => {
|
|
|
32481
32719
|
|
|
32482
32720
|
// ../cms/src/tailwind-colors.ts
|
|
32483
32721
|
import fs9 from "fs/promises";
|
|
32484
|
-
import
|
|
32722
|
+
import path11 from "path";
|
|
32485
32723
|
async function parseTailwindConfig(projectRoot = getProjectRoot()) {
|
|
32486
32724
|
const cssFiles = [
|
|
32487
32725
|
"src/styles/global.css",
|
|
@@ -32495,7 +32733,7 @@ async function parseTailwindConfig(projectRoot = getProjectRoot()) {
|
|
|
32495
32733
|
];
|
|
32496
32734
|
let customColors = [];
|
|
32497
32735
|
for (const cssFile of cssFiles) {
|
|
32498
|
-
const fullPath =
|
|
32736
|
+
const fullPath = path11.join(projectRoot, cssFile);
|
|
32499
32737
|
try {
|
|
32500
32738
|
const content = await fs9.readFile(fullPath, "utf-8");
|
|
32501
32739
|
customColors = extractColorsFromCss(content);
|
|
@@ -32568,7 +32806,7 @@ async function parseTextStyles(projectRoot = getProjectRoot()) {
|
|
|
32568
32806
|
];
|
|
32569
32807
|
let customTextStyles = {};
|
|
32570
32808
|
for (const cssFile of cssFiles) {
|
|
32571
|
-
const fullPath =
|
|
32809
|
+
const fullPath = path11.join(projectRoot, cssFile);
|
|
32572
32810
|
try {
|
|
32573
32811
|
const content = await fs9.readFile(fullPath, "utf-8");
|
|
32574
32812
|
customTextStyles = extractTextStylesFromCss(content);
|
|
@@ -33036,7 +33274,7 @@ var init_local_admin = __esm(() => {
|
|
|
33036
33274
|
|
|
33037
33275
|
// ../cms/src/manifest-writer.ts
|
|
33038
33276
|
import fs10 from "fs/promises";
|
|
33039
|
-
import
|
|
33277
|
+
import path12 from "path";
|
|
33040
33278
|
|
|
33041
33279
|
class ManifestWriter {
|
|
33042
33280
|
globalManifest;
|
|
@@ -33107,10 +33345,10 @@ class ManifestWriter {
|
|
|
33107
33345
|
}
|
|
33108
33346
|
getPageManifestPath(pagePath) {
|
|
33109
33347
|
if (pagePath === "/" || pagePath === "") {
|
|
33110
|
-
return
|
|
33348
|
+
return path12.join(this.outDir, "index.json");
|
|
33111
33349
|
}
|
|
33112
33350
|
const cleanPath = pagePath.replace(/^\//, "");
|
|
33113
|
-
return
|
|
33351
|
+
return path12.join(this.outDir, `${cleanPath}.json`);
|
|
33114
33352
|
}
|
|
33115
33353
|
addPage(pagePath, entries, components, collection, seo) {
|
|
33116
33354
|
this.pageManifests.set(pagePath, { entries, components, collection, seo });
|
|
@@ -33136,7 +33374,7 @@ class ManifestWriter {
|
|
|
33136
33374
|
}
|
|
33137
33375
|
async writePageManifest(pagePath, entries, components, collection, seo) {
|
|
33138
33376
|
const manifestPath = this.getPageManifestPath(pagePath);
|
|
33139
|
-
const manifestDir =
|
|
33377
|
+
const manifestDir = path12.dirname(manifestPath);
|
|
33140
33378
|
await fs10.mkdir(manifestDir, { recursive: true });
|
|
33141
33379
|
const metadata = {
|
|
33142
33380
|
version: MANIFEST_VERSION,
|
|
@@ -33192,7 +33430,7 @@ class ManifestWriter {
|
|
|
33192
33430
|
}
|
|
33193
33431
|
}
|
|
33194
33432
|
if (this.outDir) {
|
|
33195
|
-
const globalManifestPath =
|
|
33433
|
+
const globalManifestPath = path12.join(this.outDir, this.manifestFile);
|
|
33196
33434
|
const globalSettings = {
|
|
33197
33435
|
componentDefinitions: this.componentDefinitions,
|
|
33198
33436
|
pages
|
|
@@ -33804,7 +34042,7 @@ var init_field_types = __esm(() => {
|
|
|
33804
34042
|
|
|
33805
34043
|
// ../cms/src/migrate-astro-image.ts
|
|
33806
34044
|
import fs11 from "fs/promises";
|
|
33807
|
-
import
|
|
34045
|
+
import path13 from "path";
|
|
33808
34046
|
async function migrateAstroImages(options = {}) {
|
|
33809
34047
|
const projectRoot = options.projectRoot ?? getProjectRoot();
|
|
33810
34048
|
const dryRun = options.dryRun ?? false;
|
|
@@ -33816,7 +34054,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
33816
34054
|
if (astroFields.length === 0 || !def.entries)
|
|
33817
34055
|
continue;
|
|
33818
34056
|
for (const entry of def.entries) {
|
|
33819
|
-
const entryAbs =
|
|
34057
|
+
const entryAbs = path13.isAbsolute(entry.sourcePath) ? entry.sourcePath : path13.join(projectRoot, entry.sourcePath);
|
|
33820
34058
|
let raw;
|
|
33821
34059
|
try {
|
|
33822
34060
|
raw = await fs11.readFile(entryAbs, "utf-8");
|
|
@@ -33830,7 +34068,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
33830
34068
|
continue;
|
|
33831
34069
|
}
|
|
33832
34070
|
const [fullFm, fmStart, yamlBody, fmEnd] = fmMatch;
|
|
33833
|
-
const doc2 =
|
|
34071
|
+
const doc2 = import_yaml7.parseDocument(yamlBody);
|
|
33834
34072
|
let mutated = false;
|
|
33835
34073
|
for (const field of astroFields) {
|
|
33836
34074
|
const current = doc2.get(field.name);
|
|
@@ -33841,7 +34079,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
33841
34079
|
}
|
|
33842
34080
|
if (!current.startsWith("/") || current.startsWith("//"))
|
|
33843
34081
|
continue;
|
|
33844
|
-
const sourceAbs =
|
|
34082
|
+
const sourceAbs = path13.join(projectRoot, "public", current.replace(/^\/+/, ""));
|
|
33845
34083
|
let sourceBuf;
|
|
33846
34084
|
try {
|
|
33847
34085
|
sourceBuf = await fs11.readFile(sourceAbs);
|
|
@@ -33852,11 +34090,11 @@ async function migrateAstroImages(options = {}) {
|
|
|
33852
34090
|
const target = await pickAstroImageTarget({
|
|
33853
34091
|
entryAbsPath: entryAbs,
|
|
33854
34092
|
slug: entry.slug,
|
|
33855
|
-
originalFilename:
|
|
34093
|
+
originalFilename: path13.basename(current),
|
|
33856
34094
|
compareBuffer: sourceBuf
|
|
33857
34095
|
});
|
|
33858
34096
|
if (!dryRun) {
|
|
33859
|
-
await fs11.mkdir(
|
|
34097
|
+
await fs11.mkdir(path13.dirname(target.absPath), { recursive: true });
|
|
33860
34098
|
await fs11.writeFile(target.absPath, sourceBuf);
|
|
33861
34099
|
}
|
|
33862
34100
|
doc2.set(field.name, target.relPath);
|
|
@@ -33879,12 +34117,12 @@ async function migrateAstroImages(options = {}) {
|
|
|
33879
34117
|
}
|
|
33880
34118
|
return { migrations, skipped };
|
|
33881
34119
|
}
|
|
33882
|
-
var
|
|
34120
|
+
var import_yaml7, FRONTMATTER_RE;
|
|
33883
34121
|
var init_migrate_astro_image = __esm(() => {
|
|
33884
34122
|
init_src2();
|
|
33885
34123
|
init_astro_image_paths();
|
|
33886
34124
|
init_config();
|
|
33887
|
-
|
|
34125
|
+
import_yaml7 = __toESM(require_dist(), 1);
|
|
33888
34126
|
FRONTMATTER_RE = /^(---\r?\n)([\s\S]*?)(\r?\n---\r?\n?)/;
|
|
33889
34127
|
});
|
|
33890
34128
|
|
|
@@ -33919,7 +34157,7 @@ var exports_migrate = {};
|
|
|
33919
34157
|
__export(exports_migrate, {
|
|
33920
34158
|
migrate: () => migrate
|
|
33921
34159
|
});
|
|
33922
|
-
import
|
|
34160
|
+
import path14 from "path";
|
|
33923
34161
|
async function migrate(args) {
|
|
33924
34162
|
if (args.target !== "astro-image") {
|
|
33925
34163
|
console.error(`Unknown migrate target: ${args.target}`);
|
|
@@ -33938,7 +34176,7 @@ async function migrate(args) {
|
|
|
33938
34176
|
for (const m of result.migrations) {
|
|
33939
34177
|
console.log(` ${m.entrySourcePath}`);
|
|
33940
34178
|
console.log(` ${m.fieldName}: ${m.originalValue} \u2192 ${m.newValue}`);
|
|
33941
|
-
console.log(` copy: ${
|
|
34179
|
+
console.log(` copy: ${path14.relative(process.cwd(), m.copiedFrom)} \u2192 ${path14.relative(process.cwd(), m.copiedTo)}`);
|
|
33942
34180
|
}
|
|
33943
34181
|
}
|
|
33944
34182
|
if (result.skipped.length > 0) {
|
|
@@ -33980,6 +34218,7 @@ Commands:`);
|
|
|
33980
34218
|
console.log(" build Run astro build with the Nua defaults");
|
|
33981
34219
|
console.log(" dev Run astro dev with the Nua defaults");
|
|
33982
34220
|
console.log(" preview Run astro preview with the Nua defaults");
|
|
34221
|
+
console.log(" check Validate the content collections (no build). --json, --strict");
|
|
33983
34222
|
console.log(" init Convert a standard Astro project to use Nua");
|
|
33984
34223
|
console.log(" clean Eject to a standard Astro project (remove @nuasite/* deps)");
|
|
33985
34224
|
console.log(" migrate <target> Run a content migration. Targets: astro-image");
|
|
@@ -33992,6 +34231,15 @@ if (command && ["build", "dev", "preview"].includes(command)) {
|
|
|
33992
34231
|
proxyToAstroCLI(command, args);
|
|
33993
34232
|
} else {
|
|
33994
34233
|
switch (command) {
|
|
34234
|
+
case "check": {
|
|
34235
|
+
const { check: check2 } = await Promise.resolve().then(() => (init_check2(), exports_check));
|
|
34236
|
+
process.exit(await check2({
|
|
34237
|
+
cwd: process.cwd(),
|
|
34238
|
+
json: args.includes("--json"),
|
|
34239
|
+
strict: args.includes("--strict")
|
|
34240
|
+
}));
|
|
34241
|
+
break;
|
|
34242
|
+
}
|
|
33995
34243
|
case "init": {
|
|
33996
34244
|
const { init: init2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
33997
34245
|
await init2({
|