@nuasite/cli 0.53.0 → 0.54.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/dist/index.js CHANGED
@@ -47,6 +47,39 @@ 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
+ // ../cms-core/src/asset-paths.ts
51
+ function assetBaseDir(file) {
52
+ const lastSlash = file.lastIndexOf("/");
53
+ return lastSlash >= 0 ? file.slice(0, lastSlash) : "";
54
+ }
55
+ function normalizeSegments(base, rel) {
56
+ const out = base.slice();
57
+ for (const seg of rel.replace(/^\/+/, "").split("/")) {
58
+ if (seg === "" || seg === ".")
59
+ continue;
60
+ if (seg === "..") {
61
+ if (out.length === 0)
62
+ return null;
63
+ out.pop();
64
+ continue;
65
+ }
66
+ out.push(seg);
67
+ }
68
+ return out.join("/");
69
+ }
70
+ function resolveAssetCandidates(baseDir, rel) {
71
+ if (rel.startsWith("/")) {
72
+ const fromRoot = normalizeSegments([], rel);
73
+ if (fromRoot === null || fromRoot === "")
74
+ return [];
75
+ return [`public/${fromRoot}`, fromRoot];
76
+ }
77
+ if (baseDir === undefined)
78
+ return [];
79
+ const joined = normalizeSegments(baseDir.split("/").filter(Boolean), rel);
80
+ return joined === null ? [] : [joined];
81
+ }
82
+
50
83
  // ../../node_modules/yaml/dist/nodes/identity.js
51
84
  var require_identity = __commonJS((exports) => {
52
85
  var ALIAS = Symbol.for("yaml.alias");
@@ -7011,6 +7044,497 @@ var require_dist = __commonJS((exports) => {
7011
7044
  exports.visitAsync = visit.visitAsync;
7012
7045
  });
7013
7046
 
7047
+ // ../cms-core/src/check-entries.ts
7048
+ import path from "path";
7049
+ function isPlainObject(value) {
7050
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
7051
+ }
7052
+ async function collectEntryFiles(fs, collection, base) {
7053
+ const pattern = collection.loaderPattern ?? "**/*.{md,mdx,json,yaml,yml}";
7054
+ const files = await fs.glob(`${base}/${pattern}`);
7055
+ return files.filter((file) => !path.basename(file).startsWith("_")).sort();
7056
+ }
7057
+ function parseEntry(file, raw) {
7058
+ const extension = path.extname(file);
7059
+ if (DATA_EXTENSIONS.has(extension)) {
7060
+ try {
7061
+ const parsed = extension === ".json" ? JSON.parse(raw) : import_yaml.default.parse(raw);
7062
+ return { frontmatter: isPlainObject(parsed) ? parsed : {} };
7063
+ } catch (error) {
7064
+ return { error: error instanceof Error ? error.message.split(`
7065
+ `)[0] : String(error) };
7066
+ }
7067
+ }
7068
+ const trimmed = raw.trimStart();
7069
+ if (!trimmed.startsWith("---"))
7070
+ return { frontmatter: {} };
7071
+ const lines = trimmed.split(`
7072
+ `);
7073
+ const end = lines.findIndex((line, index) => index > 0 && line.trimEnd() === "---");
7074
+ if (end === -1)
7075
+ return { error: "frontmatter is never closed (missing the second `---`)" };
7076
+ try {
7077
+ const parsed = import_yaml.default.parse(lines.slice(1, end).join(`
7078
+ `).trim());
7079
+ return { frontmatter: isPlainObject(parsed) ? parsed : {} };
7080
+ } catch (error) {
7081
+ return { error: error instanceof Error ? error.message.split(`
7082
+ `)[0] : String(error) };
7083
+ }
7084
+ }
7085
+ function collectionKind(collection, loaderPattern) {
7086
+ const extensions = collection.entries.map((entry) => path.extname(entry.file));
7087
+ if (extensions.length > 0)
7088
+ return extensions.some((extension) => extension === ".md" || extension === ".mdx") ? "markdown" : "data";
7089
+ return loaderPattern === undefined || /\bmdx?\b/.test(loaderPattern) ? "markdown" : "data";
7090
+ }
7091
+ async function loadCollections(fs, config) {
7092
+ const loaded = new Map;
7093
+ for (const [name, collection] of config) {
7094
+ const base = normalizeBase(collection.loaderBase ?? `src/content/${name}`);
7095
+ if (!await fs.exists(base)) {
7096
+ loaded.set(name, { name, base, exists: false, entries: [] });
7097
+ continue;
7098
+ }
7099
+ const entries = [];
7100
+ for (const file of await collectEntryFiles(fs, collection, base)) {
7101
+ const parsed = parseEntry(file, await fs.readFile(file));
7102
+ entries.push({
7103
+ file,
7104
+ stem: entryStem(file),
7105
+ ..."error" in parsed ? { parseError: parsed.error } : { frontmatter: parsed.frontmatter }
7106
+ });
7107
+ }
7108
+ loaded.set(name, { name, base, exists: true, entries });
7109
+ }
7110
+ return loaded;
7111
+ }
7112
+ var import_yaml, DATA_EXTENSIONS, normalizeBase = (base) => path.normalize(base).replace(/^\.\//, "").replace(/\/+$/, ""), entryStem = (file) => path.basename(file).replace(/\.(md|mdx|json|ya?ml)$/, "");
7113
+ var init_check_entries = __esm(() => {
7114
+ import_yaml = __toESM(require_dist(), 1);
7115
+ DATA_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
7116
+ });
7117
+
7118
+ // ../cms-core/src/schema-port.ts
7119
+ function describeIssue(issue) {
7120
+ if (issue.path.length === 0)
7121
+ return { message: issue.message };
7122
+ const field = issue.path.join(".");
7123
+ return { field, message: `${field}: ${issue.message}` };
7124
+ }
7125
+ function schemaFor(schemas, collection) {
7126
+ return Object.hasOwn(schemas, collection) ? schemas[collection] : undefined;
7127
+ }
7128
+
7129
+ // ../cms-core/src/check-live.ts
7130
+ async function checkAgainstSchemas(input) {
7131
+ const findings = [];
7132
+ for (const collection of input.collections.values()) {
7133
+ const schema = schemaFor(input.schemas, collection.name);
7134
+ if (!schema)
7135
+ continue;
7136
+ for (const entry of collection.entries) {
7137
+ if (entry.frontmatter === undefined)
7138
+ continue;
7139
+ let result;
7140
+ try {
7141
+ result = await schema.safeParse(entry.frontmatter);
7142
+ } catch (error) {
7143
+ findings.push({
7144
+ severity: "error",
7145
+ code: "entry/schema-rejected",
7146
+ file: entry.file,
7147
+ message: `The collection schema threw while validating this entry: ${error instanceof Error ? error.message : String(error)}`
7148
+ });
7149
+ continue;
7150
+ }
7151
+ if (result.success)
7152
+ continue;
7153
+ for (const issue of result.issues) {
7154
+ findings.push({ severity: "error", code: "entry/schema-rejected", file: entry.file, ...describeIssue(issue) });
7155
+ }
7156
+ }
7157
+ }
7158
+ return findings;
7159
+ }
7160
+ var init_check_live = () => {};
7161
+
7162
+ // ../cms-core/src/schema-probe.ts
7163
+ function mutated(point, mutation) {
7164
+ let copy;
7165
+ try {
7166
+ copy = structuredClone(point.record);
7167
+ } catch {
7168
+ return null;
7169
+ }
7170
+ let parent = copy;
7171
+ for (const segment of point.path.slice(0, -1)) {
7172
+ if (!isContainer(parent))
7173
+ return null;
7174
+ parent = parent[segment];
7175
+ }
7176
+ const last = point.path.at(-1);
7177
+ if (last === undefined || !isContainer(parent))
7178
+ return null;
7179
+ if ("remove" in mutation)
7180
+ delete parent[last];
7181
+ else
7182
+ parent[last] = mutation.set;
7183
+ return copy;
7184
+ }
7185
+ async function probe(schema, point, mutation) {
7186
+ const value = mutated(point, mutation);
7187
+ if (value === null)
7188
+ return "unknown";
7189
+ try {
7190
+ const result = await schema.safeParse(value);
7191
+ if (result.success)
7192
+ return "accepted";
7193
+ const here = result.issues.some((issue) => point.path.every((segment, index) => issue.path[index] === segment));
7194
+ return here ? "rejected-here" : "rejected-elsewhere";
7195
+ } catch {
7196
+ return "unknown";
7197
+ }
7198
+ }
7199
+ async function governsPath(schema, point) {
7200
+ for (const value of GOVERNANCE_PROBES) {
7201
+ if (await probe(schema, point, { set: value }) === "rejected-here")
7202
+ return true;
7203
+ }
7204
+ return false;
7205
+ }
7206
+ async function acceptsMissing(schema, point) {
7207
+ return await probe(schema, point, { remove: true }) === "accepted";
7208
+ }
7209
+ async function rejectsValueAt(schema, point, value) {
7210
+ return await probe(schema, point, { set: value }) === "rejected-here";
7211
+ }
7212
+ async function firstAcceptedEntry(schema, entries) {
7213
+ for (const entry of entries) {
7214
+ if (!entry.frontmatter)
7215
+ continue;
7216
+ try {
7217
+ const result = await schema.safeParse(entry.frontmatter);
7218
+ if (result.success)
7219
+ return entry.frontmatter;
7220
+ } catch {}
7221
+ }
7222
+ return;
7223
+ }
7224
+ var pointAt = (seed, field) => ({ record: seed, path: [field] }), GOVERNANCE_PROBES, isContainer = (value) => typeof value === "object" && value !== null;
7225
+ var init_schema_probe = __esm(() => {
7226
+ GOVERNANCE_PROBES = [{ __nuaProbe: true }, ["__nua_probe__"], 918273.456, null];
7227
+ });
7228
+
7229
+ // ../cms-core/src/check-shape.ts
7230
+ async function requiredDrift(name, field, seed, schemas) {
7231
+ const schema = schemaFor(schemas, name);
7232
+ if (!schema || !field.required || field.layout?.hidden)
7233
+ return null;
7234
+ const point = pointAt(seed, field.name);
7235
+ if (!await acceptsMissing(schema, point))
7236
+ return null;
7237
+ if (!await governsPath(schema, point))
7238
+ return null;
7239
+ return {
7240
+ severity: "warning",
7241
+ code: "cms/required-drift",
7242
+ file: CONFIG_FILE,
7243
+ field: `${name}.${field.name}`,
7244
+ message: `The editor treats "${name}.${field.name}" as required and refuses to save it blank, but the schema accepts an entry without it.`,
7245
+ hint: "Either the schema should require it, or the config should show the parser that it is optional. Wrapping a field schema in a helper call (`field.image(z.string().optional())`) or reaching it through an import hides the `.optional()` from the parser, which then reads the field as required \u2014 put the wrapper outside, as `field.image(z.string()).optional()`."
7246
+ };
7247
+ }
7248
+ async function degradedField(name, field, seed, input) {
7249
+ const schema = schemaFor(input.schemas, name);
7250
+ if (!schema || field.type !== undefined || field.layout?.hidden)
7251
+ return null;
7252
+ if (anyEntryHasValue(input.collections.get(name)?.entries ?? [], field.name))
7253
+ return null;
7254
+ const point = pointAt(seed, field.name);
7255
+ for (const text of TEXT_PROBES) {
7256
+ if (!await rejectsValueAt(schema, point, text))
7257
+ return null;
7258
+ }
7259
+ return {
7260
+ severity: "error",
7261
+ code: "cms/field-degraded",
7262
+ file: CONFIG_FILE,
7263
+ field: `${name}.${field.name}`,
7264
+ message: `The editor will render "${name}.${field.name}" as a plain text input \u2014 the parser pinned no type and no entry supplies one \u2014 and the schema rejects text there.`,
7265
+ hint: "Write the field's type chain inline in the content config (`n.number()`, `n.date()`, `n.enum([\u2026])`). A type reached through an import, a helper with arguments, or a modifier applied after the object is built is invisible to the parser, which then leaves the editor guessing."
7266
+ };
7267
+ }
7268
+ async function checkFieldShapes(input) {
7269
+ const findings = [];
7270
+ for (const [name, collection] of input.config) {
7271
+ const schema = schemaFor(input.schemas, name);
7272
+ if (!schema || collection.fields.length === 0)
7273
+ continue;
7274
+ const seed = await firstAcceptedEntry(schema, input.collections.get(name)?.entries ?? []);
7275
+ if (!seed)
7276
+ continue;
7277
+ for (const field of collection.fields) {
7278
+ const drift = await requiredDrift(name, field, seed, input.schemas);
7279
+ if (drift)
7280
+ findings.push(drift);
7281
+ const degraded = await degradedField(name, field, seed, input);
7282
+ if (degraded)
7283
+ findings.push(degraded);
7284
+ }
7285
+ }
7286
+ return findings;
7287
+ }
7288
+ var CONFIG_FILE = "src/content.config.ts", TEXT_PROBES, anyEntryHasValue = (entries, field) => entries.some((entry) => entry.frontmatter !== undefined && entry.frontmatter[field] !== undefined);
7289
+ var init_check_shape = __esm(() => {
7290
+ init_schema_probe();
7291
+ TEXT_PROBES = ["", "nua", "https://nua.test/probe", "2024-01-02", "123"];
7292
+ });
7293
+
7294
+ // ../cms-types/src/field-values.ts
7295
+ function isBlankFieldValue(value) {
7296
+ return value === undefined || value === null || value === "";
7297
+ }
7298
+ function defaultValueForNewEntry(field, today = () => new Date) {
7299
+ if (field.defaultValue !== undefined)
7300
+ return field.defaultValue;
7301
+ switch (field.type) {
7302
+ case "boolean":
7303
+ return false;
7304
+ case "number":
7305
+ return 0;
7306
+ case "array":
7307
+ return [];
7308
+ case "date":
7309
+ return today().toISOString().split("T")[0];
7310
+ default:
7311
+ return "";
7312
+ }
7313
+ }
7314
+ function newRepeaterItem(fields, today) {
7315
+ const item = {};
7316
+ for (const field of fields) {
7317
+ if (!field.required || field.hidden)
7318
+ continue;
7319
+ item[field.name] = defaultValueForNewEntry(field, today);
7320
+ }
7321
+ return item;
7322
+ }
7323
+ function blankRequiredFields(fields, frontmatter) {
7324
+ return fields.filter((field) => field.required && !(field.hidden ?? field.layout?.hidden) && isBlankFieldValue(frontmatter[field.name])).map((field) => field.name);
7325
+ }
7326
+ // ../cms-types/src/index.ts
7327
+ function isFieldType(value) {
7328
+ return FIELD_TYPES.includes(value);
7329
+ }
7330
+ var FIELD_TYPES;
7331
+ var init_src = __esm(() => {
7332
+ FIELD_TYPES = [
7333
+ "text",
7334
+ "textarea",
7335
+ "markdown",
7336
+ "date",
7337
+ "datetime",
7338
+ "time",
7339
+ "year",
7340
+ "month",
7341
+ "boolean",
7342
+ "number",
7343
+ "image",
7344
+ "file",
7345
+ "url",
7346
+ "email",
7347
+ "tel",
7348
+ "color",
7349
+ "select",
7350
+ "array",
7351
+ "object",
7352
+ "reference"
7353
+ ];
7354
+ });
7355
+
7356
+ // ../cms-core/src/editor-write-model.ts
7357
+ function newEntryFrontmatter(fields, today) {
7358
+ const frontmatter = {};
7359
+ for (const field of fields) {
7360
+ if (field.name === "title" || field.hidden)
7361
+ continue;
7362
+ frontmatter[field.name] = defaultValueForNewEntry(field, today);
7363
+ }
7364
+ return frontmatter;
7365
+ }
7366
+ function applyCreateRouteFields(frontmatter, kind, today = () => new Date) {
7367
+ const title = "Untitled";
7368
+ if (kind === "data")
7369
+ return { title, ...frontmatter };
7370
+ return { title, date: today().toISOString().split("T")[0], ...frontmatter };
7371
+ }
7372
+ function omitEmptyOnCreate(frontmatter) {
7373
+ const out = {};
7374
+ for (const [key, value] of Object.entries(frontmatter)) {
7375
+ if (value !== undefined && value !== "")
7376
+ out[key] = value;
7377
+ }
7378
+ return out;
7379
+ }
7380
+ var init_editor_write_model = __esm(() => {
7381
+ init_src();
7382
+ init_src();
7383
+ });
7384
+
7385
+ // ../cms-core/src/check-write.ts
7386
+ async function parseWrite(schema, value) {
7387
+ try {
7388
+ const result = await schema.safeParse(value);
7389
+ return { issues: result.success ? [] : result.issues };
7390
+ } catch (error) {
7391
+ return { threw: errorText(error) };
7392
+ }
7393
+ }
7394
+ function valueAt(record, path2) {
7395
+ let current = record;
7396
+ for (const segment of path2) {
7397
+ if (Array.isArray(current)) {
7398
+ const index = typeof segment === "number" ? segment : Number(segment);
7399
+ if (!Number.isInteger(index) || index < 0 || index >= current.length)
7400
+ return { present: false };
7401
+ current = current[index];
7402
+ continue;
7403
+ }
7404
+ if (!isPlainObject(current) || !Object.hasOwn(current, String(segment)))
7405
+ return { present: false };
7406
+ current = current[String(segment)];
7407
+ }
7408
+ return current === undefined ? { present: false } : { present: true, value: current };
7409
+ }
7410
+ function hintFor(action, found) {
7411
+ if (action === "create") {
7412
+ if (!found.present) {
7413
+ return "The create form leaves an untouched field out of the write entirely. `.optional()` or `.default(\u2026)` accepts a missing key; `.nullable()` does not \u2014 it accepts `null`, not absence.";
7414
+ }
7415
+ return `The create form starts this field at ${JSON.stringify(found.value)} and nothing makes the editor change it before saving. Either the schema accepts that value, or mark the field \`hidden\` so the write omits it and let \`.default(\u2026)\` supply one.`;
7416
+ }
7417
+ if (!found.present) {
7418
+ return 'The appended item does not carry this key \u2014 "+ Add" seeds only the item fields the config declares required. Declaring it required there is what makes the editor seed it; if it is not required, `.optional()` or `.default(\u2026)` says so to the schema, and `.nullable()` does not \u2014 it accepts `null`, not absence.';
7419
+ }
7420
+ return `The appended item carries ${JSON.stringify(found.value)} for this key, and it is written the moment "+ Add" is clicked. \`.default(\u2026)\` on the inner field is the way out: the parser then reads the field as optional, so the item leaves the key out and the schema supplies the value itself.`;
7421
+ }
7422
+ function rejectionFinding(action, issue, kind, written) {
7423
+ const { field, message } = describeIssue(issue);
7424
+ const finding = {
7425
+ severity: "error",
7426
+ code: "cms/empty-write",
7427
+ file: CONFIG_FILE2,
7428
+ message: `${action} produces a write the schema rejects. ${message}`
7429
+ };
7430
+ if (field !== undefined)
7431
+ finding.field = field;
7432
+ finding.hint = hintFor(kind, valueAt(written, issue.path));
7433
+ return finding;
7434
+ }
7435
+ function blankItemsFor(field, current, today) {
7436
+ const itemFields = field.fields ?? [];
7437
+ if (itemFields.length > 0) {
7438
+ return [newRepeaterItem(itemFields.map((item) => ({ ...toWriteModelField(item), required: item.required })), today)];
7439
+ }
7440
+ if (current.length === 0)
7441
+ return [{}, { name: "" }];
7442
+ const first = current[0];
7443
+ const template = isPlainObject(first) ? Object.fromEntries(Object.keys(first).map((key) => [key, ""])) : {};
7444
+ return [{}, template];
7445
+ }
7446
+ async function checkEditorWrites(input) {
7447
+ const findings = [];
7448
+ const seen = new Set;
7449
+ const report = (key, finding) => {
7450
+ const identity = `${key} ${finding.field ?? ""}`;
7451
+ if (seen.has(identity))
7452
+ return;
7453
+ seen.add(identity);
7454
+ findings.push(finding);
7455
+ };
7456
+ for (const [name, collection] of input.config) {
7457
+ const schema = schemaFor(input.schemas, name);
7458
+ if (!schema)
7459
+ continue;
7460
+ const createKey = `${name} create`;
7461
+ if (collection.fields.length === 0) {
7462
+ report(createKey, {
7463
+ severity: "warning",
7464
+ code: "cms/empty-write-unchecked",
7465
+ file: CONFIG_FILE2,
7466
+ field: name,
7467
+ message: `Could not check what creating a new entry in "${name}" writes: its schema is not readable from the content config, so there are no fields to predict the write from.`,
7468
+ hint: "Write the field types inline in the content config rather than importing the shape from another module \u2014 the prediction reads the config source, and a schema assembled elsewhere leaves it nothing to read."
7469
+ });
7470
+ continue;
7471
+ }
7472
+ const loaded = input.collections.get(name);
7473
+ const entries = loaded?.entries ?? [];
7474
+ const kind = loaded ? collectionKind(loaded, collection.loaderPattern) : "markdown";
7475
+ const created = applyCreateRouteFields(omitEmptyOnCreate(newEntryFrontmatter(collection.fields.map(toWriteModelField), input.today)), kind, input.today);
7476
+ const guarded = new Set(blankRequiredFields(collection.fields, created));
7477
+ const createAction = `Creating a new entry in "${name}"`;
7478
+ const createVerdict = await parseWrite(schema, created);
7479
+ if ("threw" in createVerdict) {
7480
+ report(createKey, throwFinding(createAction, createVerdict.threw));
7481
+ } else {
7482
+ for (const issue of createVerdict.issues) {
7483
+ const top = issue.path[0];
7484
+ if (typeof top === "string" && guarded.has(top))
7485
+ continue;
7486
+ report(createKey, rejectionFinding(createAction, issue, "create", created));
7487
+ }
7488
+ }
7489
+ const repeaters = collection.fields.filter((field) => field.type === "array" && field.itemType === "object");
7490
+ if (repeaters.length === 0)
7491
+ continue;
7492
+ const seed = await firstAcceptedEntry(schema, entries);
7493
+ for (const field of repeaters) {
7494
+ const key = `${name} add:${field.name}`;
7495
+ if (!seed) {
7496
+ const hasEntries = entries.some((entry) => entry.frontmatter !== undefined);
7497
+ const reason = hasEntries ? "no existing entry passes the schema, so there is no valid record to add an item to" : "the collection has no entry to add an item to";
7498
+ report(key, {
7499
+ severity: "warning",
7500
+ code: "cms/empty-write-unchecked",
7501
+ file: CONFIG_FILE2,
7502
+ field: field.name,
7503
+ message: `Could not check what "+ Add" on "${name}.${field.name}" writes: ${reason}.`,
7504
+ hint: hasEntries ? `Fix the \`entry/schema-rejected\` findings in "${name}" and run this again \u2014 the prediction needs one entry the schema accepts.` : `Add one entry to "${name}" and run this again.`
7505
+ });
7506
+ continue;
7507
+ }
7508
+ const current = seed[field.name];
7509
+ const existing = Array.isArray(current) ? current : [];
7510
+ const action = `Clicking "+ Add" on "${name}.${field.name}"`;
7511
+ for (const item of blankItemsFor(field, existing, input.today)) {
7512
+ const written = { ...seed, [field.name]: [...existing, item] };
7513
+ const verdict = await parseWrite(schema, written);
7514
+ if ("threw" in verdict) {
7515
+ report(key, throwFinding(action, verdict.threw));
7516
+ continue;
7517
+ }
7518
+ for (const issue of verdict.issues)
7519
+ report(key, rejectionFinding(action, issue, "add", written));
7520
+ }
7521
+ }
7522
+ }
7523
+ return findings;
7524
+ }
7525
+ var CONFIG_FILE2 = "src/content.config.ts", toWriteModelField = (field) => ({ name: field.name, type: field.type, hidden: field.layout?.hidden }), errorText = (error) => error instanceof Error ? error.message : String(error), throwFinding = (action, threw) => ({
7526
+ severity: "error",
7527
+ code: "cms/empty-write",
7528
+ file: CONFIG_FILE2,
7529
+ message: `${action} produces a write the schema throws on: ${threw}`,
7530
+ hint: "A schema that throws instead of returning an issue is usually a refinement reading a field this write does not carry. Guard it against a missing value \u2014 `astro sync` runs the same schema over the same record."
7531
+ });
7532
+ var init_check_write = __esm(() => {
7533
+ init_check_entries();
7534
+ init_editor_write_model();
7535
+ init_schema_probe();
7536
+ });
7537
+
7014
7538
  // ../../node_modules/@babel/parser/lib/index.js
7015
7539
  var require_lib = __commonJS((exports) => {
7016
7540
  Object.defineProperty(exports, "__esModule", {
@@ -21794,40 +22318,16 @@ var require_lib = __commonJS((exports) => {
21794
22318
  exports.tokTypes = tokTypes;
21795
22319
  });
21796
22320
 
21797
- // ../cms-types/src/index.ts
21798
- function isFieldType(value) {
21799
- return FIELD_TYPES.includes(value);
21800
- }
21801
- var FIELD_TYPES;
21802
- var init_src = __esm(() => {
21803
- FIELD_TYPES = [
21804
- "text",
21805
- "textarea",
21806
- "markdown",
21807
- "date",
21808
- "datetime",
21809
- "time",
21810
- "year",
21811
- "month",
21812
- "boolean",
21813
- "number",
21814
- "image",
21815
- "file",
21816
- "url",
21817
- "email",
21818
- "tel",
21819
- "color",
21820
- "select",
21821
- "array",
21822
- "object",
21823
- "reference"
21824
- ];
21825
- });
21826
-
21827
22321
  // ../cms-core/src/content-config-ast.ts
21828
22322
  function resolveExpression(node, bindings, visited = new Set) {
21829
22323
  let current = node;
21830
- while (current.type === "Identifier") {
22324
+ for (;; ) {
22325
+ if (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSNonNullExpression" || current.type === "TSTypeAssertion") {
22326
+ current = current.expression;
22327
+ continue;
22328
+ }
22329
+ if (current.type !== "Identifier")
22330
+ return current;
21831
22331
  if (visited.has(current.name))
21832
22332
  return current;
21833
22333
  visited.add(current.name);
@@ -21836,7 +22336,80 @@ function resolveExpression(node, bindings, visited = new Set) {
21836
22336
  return current;
21837
22337
  current = next;
21838
22338
  }
21839
- return current;
22339
+ }
22340
+ function isAstNode(value) {
22341
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
22342
+ }
22343
+ function walkNodes(node, visit) {
22344
+ visit(node);
22345
+ const children = Object.values(node);
22346
+ for (const child of children) {
22347
+ if (Array.isArray(child)) {
22348
+ for (const item of child) {
22349
+ if (isAstNode(item))
22350
+ walkNodes(item, visit);
22351
+ }
22352
+ } else if (isAstNode(child)) {
22353
+ walkNodes(child, visit);
22354
+ }
22355
+ }
22356
+ }
22357
+ function declaredPatternNames(node, record) {
22358
+ switch (node.type) {
22359
+ case "Identifier":
22360
+ record(node.name);
22361
+ return;
22362
+ case "ObjectPattern":
22363
+ for (const prop of node.properties)
22364
+ declaredPatternNames(prop.type === "RestElement" ? prop.argument : prop.value, record);
22365
+ return;
22366
+ case "ArrayPattern":
22367
+ for (const el of node.elements) {
22368
+ if (el)
22369
+ declaredPatternNames(el, record);
22370
+ }
22371
+ return;
22372
+ case "AssignmentPattern":
22373
+ declaredPatternNames(node.left, record);
22374
+ return;
22375
+ case "RestElement":
22376
+ declaredPatternNames(node.argument, record);
22377
+ return;
22378
+ }
22379
+ }
22380
+ function shadowedNames(ast) {
22381
+ const seen = new Set;
22382
+ const shadowed = new Set;
22383
+ const record = (name) => {
22384
+ if (seen.has(name))
22385
+ shadowed.add(name);
22386
+ seen.add(name);
22387
+ };
22388
+ walkNodes(ast, (node) => {
22389
+ switch (node.type) {
22390
+ case "VariableDeclarator":
22391
+ declaredPatternNames(node.id, record);
22392
+ return;
22393
+ case "FunctionDeclaration":
22394
+ case "FunctionExpression":
22395
+ case "ArrowFunctionExpression":
22396
+ for (const param of node.params)
22397
+ declaredPatternNames(param, record);
22398
+ if (node.type !== "ArrowFunctionExpression" && node.id)
22399
+ record(node.id.name);
22400
+ return;
22401
+ case "ClassDeclaration":
22402
+ if (node.id)
22403
+ record(node.id.name);
22404
+ return;
22405
+ case "ImportSpecifier":
22406
+ case "ImportDefaultSpecifier":
22407
+ case "ImportNamespaceSpecifier":
22408
+ record(node.local.name);
22409
+ return;
22410
+ }
22411
+ });
22412
+ return shadowed;
21840
22413
  }
21841
22414
  function parseSource(source) {
21842
22415
  try {
@@ -21916,6 +22489,8 @@ function parseConfigSource(source, _sourcePath) {
21916
22489
  }
21917
22490
  }
21918
22491
  }
22492
+ for (const name of shadowedNames(ast))
22493
+ bindings.delete(name);
21919
22494
  const collectionObjects = new Map(inlineCollections);
21920
22495
  for (const [varName, collectionName] of exportMap) {
21921
22496
  const decl = collectionDecls.get(varName);
@@ -21974,6 +22549,7 @@ function parseConfigSource(source, _sourcePath) {
21974
22549
  result.set(collectionName, {
21975
22550
  name: collectionName,
21976
22551
  fields: parseSchemaFields(schemaObject, bindings),
22552
+ ...hasSkippedMembers(schemaObject) ? { partialFields: true } : {},
21977
22553
  loaderPattern,
21978
22554
  loaderBase,
21979
22555
  layout,
@@ -22032,6 +22608,15 @@ function parseLayoutSection(node) {
22032
22608
  return null;
22033
22609
  return collapsed ? { title, fields, collapsed } : { title, fields };
22034
22610
  }
22611
+ function readEnumOptions(node) {
22612
+ const options = [];
22613
+ for (const el of node.elements) {
22614
+ if (el?.type !== "StringLiteral")
22615
+ return null;
22616
+ options.push(el.value);
22617
+ }
22618
+ return options.length > 0 ? options : null;
22619
+ }
22035
22620
  function stringArray(node) {
22036
22621
  const out = [];
22037
22622
  for (const el of node.elements) {
@@ -22218,7 +22803,10 @@ function parseSchemaFields(schemaObject, bindings) {
22218
22803
  }
22219
22804
  return fields;
22220
22805
  }
22221
- function analyzeFieldExpression(node, field, bindings) {
22806
+ function hasSkippedMembers(schemaObject) {
22807
+ return schemaObject.properties.some((prop) => prop.type !== "ObjectProperty" || !propertyKeyName(prop.key));
22808
+ }
22809
+ function analyzeFieldExpression(node, field, bindings, helpers = new Set) {
22222
22810
  let current = resolveExpression(node, bindings);
22223
22811
  while (current) {
22224
22812
  if (current.type !== "CallExpression")
@@ -22227,10 +22815,30 @@ function analyzeFieldExpression(node, field, bindings) {
22227
22815
  analyzeBaseCall(current, field, bindings);
22228
22816
  return;
22229
22817
  }
22818
+ if (current.callee.type === "Identifier") {
22819
+ const returned = sameFileHelperReturn(current, bindings, helpers);
22820
+ if (returned)
22821
+ analyzeFieldExpression(returned, field, bindings, helpers);
22822
+ return;
22823
+ }
22230
22824
  if (current.callee.type !== "MemberExpression")
22231
22825
  return;
22232
22826
  const property = current.callee.property;
22233
22827
  const methodName = property.type === "Identifier" ? property.name : "";
22828
+ if (methodName === "array") {
22829
+ const item = { name: "__item__", required: true };
22830
+ analyzeFieldExpression(current.callee.object, item, bindings, helpers);
22831
+ if (item.reference) {
22832
+ field.reference = { target: item.reference.target, isArray: true };
22833
+ return;
22834
+ }
22835
+ field.type = "array";
22836
+ if (item.type)
22837
+ field.itemType = item.type;
22838
+ if (item.fields)
22839
+ field.fields = item.fields;
22840
+ return;
22841
+ }
22234
22842
  if (WRAPPER_METHODS.has(methodName)) {
22235
22843
  field.required = false;
22236
22844
  } else if (methodName === "orderBy") {
@@ -22241,6 +22849,25 @@ function analyzeFieldExpression(node, field, bindings) {
22241
22849
  current = resolveExpression(current.callee.object, bindings);
22242
22850
  }
22243
22851
  }
22852
+ function sameFileHelperReturn(call, bindings, seen) {
22853
+ if (call.callee.type !== "Identifier" || call.arguments.length > 0)
22854
+ return null;
22855
+ if (seen.has(call.callee.name))
22856
+ return null;
22857
+ seen.add(call.callee.name);
22858
+ const fn = resolveExpression(call.callee, bindings);
22859
+ if (fn.type !== "ArrowFunctionExpression" && fn.type !== "FunctionExpression")
22860
+ return null;
22861
+ if (fn.params.length > 0)
22862
+ return null;
22863
+ if (fn.body.type !== "BlockStatement")
22864
+ return fn.body;
22865
+ for (const stmt of fn.body.body) {
22866
+ if (stmt.type === "ReturnStatement" && stmt.argument)
22867
+ return stmt.argument;
22868
+ }
22869
+ return null;
22870
+ }
22244
22871
  function isBaseCall(node) {
22245
22872
  const callee = node.callee;
22246
22873
  if (callee.type === "Identifier") {
@@ -22287,18 +22914,16 @@ function analyzeBaseCall(node, field, bindings) {
22287
22914
  }
22288
22915
  return;
22289
22916
  }
22917
+ if ((ns === "z" || ns === "n") && fn === "boolean") {
22918
+ field.type = "boolean";
22919
+ return;
22920
+ }
22290
22921
  if ((ns === "z" || ns === "n") && fn === "enum") {
22291
- const arg = node.arguments[0];
22292
- if (arg?.type === "ArrayExpression") {
22293
- const options = [];
22294
- for (const el of arg.elements) {
22295
- if (el?.type === "StringLiteral")
22296
- options.push(el.value);
22297
- }
22298
- if (options.length > 0) {
22299
- field.type = "select";
22300
- field.options = options;
22301
- }
22922
+ const arg = node.arguments[0] ? resolveExpression(node.arguments[0], bindings) : undefined;
22923
+ const options = arg?.type === "ArrayExpression" ? readEnumOptions(arg) : null;
22924
+ if (options) {
22925
+ field.type = "select";
22926
+ field.options = options;
22302
22927
  }
22303
22928
  const hintsArg = node.arguments[1];
22304
22929
  if (hintsArg?.type === "ObjectExpression") {
@@ -22459,11 +23084,44 @@ var init_content_config_ast = __esm(() => {
22459
23084
  WRAPPER_METHODS = new Set(["optional", "nullable", "nullish", "default"]);
22460
23085
  });
22461
23086
 
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);
23087
+ // ../cms-core/src/shared.ts
23088
+ function pathSegmentValue(raw) {
23089
+ if (typeof raw === "string")
23090
+ return raw;
23091
+ if (typeof raw === "number" || typeof raw === "boolean")
23092
+ return String(raw);
23093
+ if (raw instanceof Date)
23094
+ return raw.toISOString().slice(0, 10);
23095
+ return;
23096
+ }
23097
+ function computePathnameFromSpec(spec, data) {
23098
+ const segments = [];
23099
+ for (const seg of spec) {
23100
+ if ("literal" in seg) {
23101
+ segments.push(seg.literal);
23102
+ continue;
23103
+ }
23104
+ const value = pathSegmentValue(data[seg.field]);
23105
+ if (value === undefined)
23106
+ return;
23107
+ segments.push(seg.map && Object.hasOwn(seg.map, value) ? seg.map[value] : value);
23108
+ }
23109
+ const path2 = ("/" + segments.join("/")).replace(/\/{2,}/g, "/").replace(/\/+$/, "");
23110
+ return path2 === "" ? "/" : path2;
23111
+ }
23112
+ function resolvePathnameFromSpec(def, data) {
23113
+ if (!def.pathname)
23114
+ return;
23115
+ return computePathnameFromSpec(def.pathname, data ?? {});
22466
23116
  }
23117
+ function slugifyHref(text) {
23118
+ return "/" + text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
23119
+ }
23120
+ var init_shared = __esm(() => {
23121
+ init_src();
23122
+ });
23123
+
23124
+ // ../cms-core/src/check.ts
22467
23125
  function typeMismatch(field, value) {
22468
23126
  switch (field.type) {
22469
23127
  case "number":
@@ -22505,40 +23163,86 @@ function typeMismatch(field, value) {
22505
23163
  return null;
22506
23164
  }
22507
23165
  }
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();
23166
+ function projectAssetPath(value) {
23167
+ if (value === "" || value.startsWith("//"))
23168
+ return null;
23169
+ if (/^[a-z][a-z\d+\-.]*:/i.test(value))
23170
+ return null;
23171
+ if (/^[~@#]/.test(value))
23172
+ return null;
23173
+ const withoutQuery = value.split(/[?#]/)[0] ?? "";
23174
+ return withoutQuery === "" ? null : withoutQuery;
22512
23175
  }
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) };
23176
+ async function checkAssets(fs, fields, data, file, prefix, findings) {
23177
+ for (const field of fields) {
23178
+ const value = data[field.name];
23179
+ if (value === undefined || value === null)
23180
+ continue;
23181
+ const label = prefix === "" ? field.name : `${prefix}.${field.name}`;
23182
+ if (field.type === "object" && field.fields && isPlainObject(value)) {
23183
+ await checkAssets(fs, field.fields, value, file, label, findings);
23184
+ continue;
23185
+ }
23186
+ if (field.type === "array" && field.itemType === "object" && field.fields && Array.isArray(value)) {
23187
+ for (const [index, item] of value.entries()) {
23188
+ if (isPlainObject(item))
23189
+ await checkAssets(fs, field.fields, item, file, `${label}[${index}]`, findings);
23190
+ }
23191
+ continue;
22522
23192
  }
23193
+ if (field.type !== "image" || typeof value !== "string")
23194
+ continue;
23195
+ const assetPath = projectAssetPath(value);
23196
+ if (assetPath === null)
23197
+ continue;
23198
+ const candidates = resolveAssetCandidates(assetBaseDir(file), assetPath);
23199
+ if (candidates.length === 0)
23200
+ continue;
23201
+ let found = false;
23202
+ for (const candidate of candidates) {
23203
+ if (await fs.exists(candidate)) {
23204
+ found = true;
23205
+ break;
23206
+ }
23207
+ }
23208
+ if (found)
23209
+ continue;
23210
+ findings.push({
23211
+ severity: "warning",
23212
+ code: "entry/missing-asset",
23213
+ file,
23214
+ field: label,
23215
+ message: `${label}: "${value}" points at no file (tried ${candidates.join(", ")}).`
23216
+ });
22523
23217
  }
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) };
23218
+ }
23219
+ function pathnameCollisions(collection, entries) {
23220
+ if (!collection.pathname)
23221
+ return [];
23222
+ const findings = [];
23223
+ const firstAt = new Map;
23224
+ for (const entry of entries) {
23225
+ if (!entry.frontmatter)
23226
+ continue;
23227
+ const pathname = computePathnameFromSpec(collection.pathname, entry.frontmatter);
23228
+ if (pathname === undefined)
23229
+ continue;
23230
+ const first = firstAt.get(pathname);
23231
+ if (!first) {
23232
+ firstAt.set(pathname, entry);
23233
+ continue;
23234
+ }
23235
+ findings.push({
23236
+ severity: "warning",
23237
+ code: "entry/pathname-collision",
23238
+ file: entry.file,
23239
+ field: collection.name,
23240
+ message: `The pathname rule maps this entry and ${first.file} to ${pathname} \u2014 they collide on one page.`
23241
+ });
22539
23242
  }
23243
+ return findings;
22540
23244
  }
22541
- async function checkContent(fs) {
23245
+ async function checkContent(fs, options = {}) {
22542
23246
  const findings = [];
22543
23247
  const config = await parseContentConfig(fs, new Map);
22544
23248
  if (config.size === 0) {
@@ -22550,51 +23254,47 @@ async function checkContent(fs) {
22550
23254
  });
22551
23255
  return { findings, collections: 0, entries: 0 };
22552
23256
  }
22553
- const bases = new Map;
22554
- const files = new Map;
23257
+ const collections = await loadCollections(fs, config);
22555
23258
  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)) {
23259
+ for (const [name, loaded] of collections) {
23260
+ stems.set(name, new Set(loaded.entries.map((entry) => entry.stem)));
23261
+ if (!loaded.exists) {
22560
23262
  findings.push({
22561
23263
  severity: "error",
22562
23264
  code: "config/missing-dir",
22563
23265
  file: "src/content.config.ts",
22564
23266
  field: name,
22565
- message: `Collection "${name}" loads from ${base}/, which does not exist.`
23267
+ message: `Collection "${name}" loads from ${loaded.base}/, which does not exist.`
22566
23268
  });
22567
- files.set(name, []);
22568
- stems.set(name, new Set);
22569
23269
  continue;
22570
23270
  }
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) {
23271
+ if (loaded.entries.length === 0) {
22575
23272
  findings.push({
22576
23273
  severity: "warning",
22577
23274
  code: "config/empty-collection",
22578
- file: base,
23275
+ file: loaded.base,
22579
23276
  field: name,
22580
- message: `Collection "${name}" matched no entries under ${base}/.`
23277
+ message: `Collection "${name}" matched no entries under ${loaded.base}/.`
22581
23278
  });
22582
23279
  }
22583
23280
  }
22584
23281
  let entries = 0;
22585
23282
  for (const [name, collection] of config) {
22586
23283
  const typedFields = collection.fields.filter((field) => field.type !== undefined || field.required || field.reference);
22587
- for (const file of files.get(name) ?? []) {
23284
+ const declared = new Set(collection.fields.map((field) => field.name));
23285
+ const loadedEntries = collections.get(name)?.entries ?? [];
23286
+ const schemaJudgesEntries = options.schemas !== undefined && schemaFor(options.schemas, name) !== undefined;
23287
+ for (const entry of loadedEntries) {
22588
23288
  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}` });
23289
+ const file = entry.file;
23290
+ if (entry.frontmatter === undefined) {
23291
+ findings.push({ severity: "error", code: "entry/syntax", file, message: `Frontmatter does not parse: ${entry.parseError}` });
22592
23292
  continue;
22593
23293
  }
22594
23294
  for (const field of typedFields) {
22595
- const value = parsed.frontmatter[field.name];
23295
+ const value = entry.frontmatter[field.name];
22596
23296
  if (value === undefined || value === null) {
22597
- if (field.required) {
23297
+ if (field.required && !schemaJudgesEntries) {
22598
23298
  findings.push({
22599
23299
  severity: "error",
22600
23300
  code: "entry/missing-required",
@@ -22605,7 +23305,7 @@ async function checkContent(fs) {
22605
23305
  }
22606
23306
  continue;
22607
23307
  }
22608
- const mismatch = field.type === undefined ? null : typeMismatch(field, value);
23308
+ const mismatch = field.type === undefined || schemaJudgesEntries ? null : typeMismatch(field, value);
22609
23309
  if (mismatch) {
22610
23310
  findings.push({ severity: "error", code: "entry/field-type", file, field: field.name, message: `${field.name}: ${mismatch}` });
22611
23311
  continue;
@@ -22629,7 +23329,26 @@ async function checkContent(fs) {
22629
23329
  });
22630
23330
  }
22631
23331
  }
23332
+ await checkAssets(fs, collection.fields, entry.frontmatter, file, "", findings);
23333
+ if (declared.size > 0 && !collection.partialFields) {
23334
+ for (const key of Object.keys(entry.frontmatter)) {
23335
+ if (declared.has(key))
23336
+ continue;
23337
+ findings.push({
23338
+ severity: "warning",
23339
+ code: "entry/unknown-key",
23340
+ file,
23341
+ field: key,
23342
+ message: `"${key}" is not a field of "${name}" \u2014 nothing reads it.`
23343
+ });
23344
+ }
23345
+ }
22632
23346
  }
23347
+ findings.push(...pathnameCollisions(collection, loadedEntries));
23348
+ }
23349
+ if (options.schemas) {
23350
+ const input = { config, collections, schemas: options.schemas };
23351
+ findings.push(...await checkAgainstSchemas(input), ...await checkEditorWrites(input), ...await checkFieldShapes(input));
22633
23352
  }
22634
23353
  return { findings, collections: config.size, entries };
22635
23354
  }
@@ -22647,15 +23366,19 @@ function formatCheckReport(report) {
22647
23366
  const lines = [];
22648
23367
  for (const [file, findings] of byFile) {
22649
23368
  lines.push(file);
23369
+ let lastHint;
22650
23370
  for (const finding of findings) {
22651
23371
  const at = finding.line === undefined ? "" : `${finding.line}:${finding.column ?? 0}`;
22652
23372
  lines.push(` ${at.padEnd(8)}${(finding.severity === "error" ? "error" : "warn").padEnd(6)} ${finding.message} [${finding.code}]`);
23373
+ if (finding.hint !== undefined && finding.hint !== lastHint)
23374
+ lines.push(` ${" ".repeat(14)}\u2192 ${finding.hint}`);
23375
+ lastHint = finding.hint;
22653
23376
  }
22654
23377
  }
22655
23378
  return lines.join(`
22656
23379
  `);
22657
23380
  }
22658
- 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) => {
23381
+ var typeName = (value) => {
22659
23382
  if (value === null)
22660
23383
  return "null";
22661
23384
  if (Array.isArray(value))
@@ -22665,45 +23388,14 @@ var import_yaml, DATA_EXTENSIONS, normalizeBase = (base) => path.normalize(base)
22665
23388
  return typeof value;
22666
23389
  };
22667
23390
  var init_check = __esm(() => {
23391
+ init_check_entries();
23392
+ init_check_live();
23393
+ init_check_shape();
23394
+ init_check_write();
22668
23395
  init_content_config_ast();
22669
- import_yaml = __toESM(require_dist(), 1);
22670
- DATA_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
23396
+ init_shared();
22671
23397
  });
22672
23398
 
22673
- // ../cms-core/src/shared.ts
22674
- function pathSegmentValue(raw) {
22675
- if (typeof raw === "string")
22676
- return raw;
22677
- if (typeof raw === "number" || typeof raw === "boolean")
22678
- return String(raw);
22679
- if (raw instanceof Date)
22680
- return raw.toISOString().slice(0, 10);
22681
- return;
22682
- }
22683
- function computePathnameFromSpec(spec, data) {
22684
- const segments = [];
22685
- for (const seg of spec) {
22686
- if ("literal" in seg) {
22687
- segments.push(seg.literal);
22688
- continue;
22689
- }
22690
- const value = pathSegmentValue(data[seg.field]);
22691
- if (value === undefined)
22692
- return;
22693
- segments.push(seg.map && Object.hasOwn(seg.map, value) ? seg.map[value] : value);
22694
- }
22695
- const path2 = ("/" + segments.join("/")).replace(/\/{2,}/g, "/").replace(/\/+$/, "");
22696
- return path2 === "" ? "/" : path2;
22697
- }
22698
- function resolvePathnameFromSpec(def, data) {
22699
- if (!def.pathname)
22700
- return;
22701
- return computePathnameFromSpec(def.pathname, data ?? {});
22702
- }
22703
- function slugifyHref(text) {
22704
- return "/" + text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
22705
- }
22706
-
22707
23399
  // ../cms-core/src/collection-scanner.ts
22708
23400
  import path2 from "path";
22709
23401
  function normalizeFieldName(name) {
@@ -23496,6 +24188,7 @@ function warnOnPathnameCollisions(collections) {
23496
24188
  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;
23497
24189
  var init_collection_scanner = __esm(() => {
23498
24190
  init_content_config_ast();
24191
+ init_shared();
23499
24192
  import_yaml2 = __toESM(require_dist(), 1);
23500
24193
  DATE_PATTERN = /^\d{4}-\d{2}-\d{2}/;
23501
24194
  URL_PATTERN = /^(https?:\/\/|\/)/;
@@ -23575,12 +24268,16 @@ var import_yaml3;
23575
24268
  var init_entry_ops = __esm(() => {
23576
24269
  init_collection_scanner();
23577
24270
  init_content_config_ast();
24271
+ init_editor_write_model();
23578
24272
  init_local();
24273
+ init_shared();
23579
24274
  import_yaml3 = __toESM(require_dist(), 1);
23580
24275
  });
23581
24276
 
23582
24277
  // ../cms-core/src/handlers/page-ops.ts
23583
- var init_page_ops = () => {};
24278
+ var init_page_ops = __esm(() => {
24279
+ init_shared();
24280
+ });
23584
24281
  // ../cms-core/src/core.ts
23585
24282
  var init_core = __esm(() => {
23586
24283
  init_collection_scanner();
@@ -23817,14 +24514,21 @@ var init_project_config_ast = __esm(() => {
23817
24514
  // ../cms-core/src/index.ts
23818
24515
  var init_src2 = __esm(() => {
23819
24516
  init_check();
24517
+ init_check_entries();
24518
+ init_check_live();
24519
+ init_check_shape();
24520
+ init_check_write();
23820
24521
  init_collection_scanner();
23821
24522
  init_component_registry();
23822
24523
  init_content_config_ast();
23823
24524
  init_core();
24525
+ init_editor_write_model();
23824
24526
  init_node_fs();
23825
24527
  init_entry_ops();
23826
24528
  init_media();
23827
24529
  init_project_config_ast();
24530
+ init_schema_probe();
24531
+ init_shared();
23828
24532
  });
23829
24533
 
23830
24534
  // ../../node_modules/@astrojs/compiler/dist/chunk-W5DTLHV4.js
@@ -24621,45 +25325,307 @@ var init_check_code = __esm(() => {
24621
25325
  IGNORED = ["node_modules", "dist", ".astro", ".git", ".output", ".vercel", ".netlify"];
24622
25326
  });
24623
25327
 
25328
+ // src/live-schema.ts
25329
+ import { existsSync, realpathSync } from "fs";
25330
+ import path5 from "path";
25331
+ function reasonOf(error, fallback = "unknown error") {
25332
+ try {
25333
+ if (typeof error === "string" && error !== "")
25334
+ return error;
25335
+ if (error instanceof Error && error.message !== "")
25336
+ return error.message;
25337
+ if (isRecord(error) && typeof error.message === "string" && error.message !== "")
25338
+ return error.message;
25339
+ } catch {}
25340
+ return fallback;
25341
+ }
25342
+ function findConfig(projectRoot) {
25343
+ for (const name of CONFIG_NAMES) {
25344
+ const candidate = path5.join(projectRoot, name);
25345
+ if (existsSync(candidate))
25346
+ return candidate;
25347
+ }
25348
+ return null;
25349
+ }
25350
+ function projectAstroDir(projectRoot) {
25351
+ let dir = projectRoot;
25352
+ while (true) {
25353
+ const candidate = path5.join(dir, "node_modules", "astro");
25354
+ if (existsSync(path5.join(candidate, "package.json")))
25355
+ return candidate;
25356
+ const parent = path5.dirname(dir);
25357
+ if (parent === dir)
25358
+ return null;
25359
+ dir = parent;
25360
+ }
25361
+ }
25362
+ function zodEntryOf(module) {
25363
+ if (!isRecord(module))
25364
+ return null;
25365
+ const namespace = module.z;
25366
+ if (!isRecord(namespace))
25367
+ return null;
25368
+ const string = namespace.string;
25369
+ const object = namespace.object;
25370
+ const union = namespace.union;
25371
+ const any = namespace.any;
25372
+ if (typeof string !== "function" || typeof object !== "function" || typeof union !== "function" || typeof any !== "function")
25373
+ return null;
25374
+ return {
25375
+ namespace,
25376
+ any: () => Reflect.apply(any, namespace, []),
25377
+ reference: () => {
25378
+ const text = () => Reflect.apply(string, namespace, []);
25379
+ const lookup = (key) => Reflect.apply(object, namespace, [{ [key]: text(), collection: text() }]);
25380
+ return Reflect.apply(union, namespace, [[text(), lookup("id"), lookup("slug")]]);
25381
+ }
25382
+ };
25383
+ }
25384
+ function installContentStub(zodPath, zod) {
25385
+ Bun.plugin({
25386
+ name: "astro-content-stub",
25387
+ setup(build) {
25388
+ build.module("astro:content", () => ({
25389
+ loader: "object",
25390
+ exports: {
25391
+ defineCollection: (collection) => collection,
25392
+ reference: () => zod.reference(),
25393
+ z: zod.namespace
25394
+ }
25395
+ }));
25396
+ }
25397
+ });
25398
+ stubbedZod = zodPath;
25399
+ }
25400
+ function parseMethod(schema) {
25401
+ if (!isRecord(schema))
25402
+ return null;
25403
+ for (const name of ["safeParseAsync", "safeParse"]) {
25404
+ const method = schema[name];
25405
+ if (typeof method === "function")
25406
+ return (value) => Reflect.apply(method, schema, [value]);
25407
+ }
25408
+ return null;
25409
+ }
25410
+ function dedupe(issues) {
25411
+ const seen = new Set;
25412
+ const unique = [];
25413
+ for (const issue of issues) {
25414
+ const key = `${issue.path.join("\x00")}\x01${issue.message}`;
25415
+ if (seen.has(key))
25416
+ continue;
25417
+ seen.add(key);
25418
+ unique.push(issue);
25419
+ }
25420
+ return unique;
25421
+ }
25422
+ function unionIssues(raw) {
25423
+ return asArray(raw.errors ?? raw.unionErrors).flatMap((branch) => asArray(isRecord(branch) ? branch.issues ?? branch.errors : branch).flatMap(toIssues));
25424
+ }
25425
+ function toIssues(raw) {
25426
+ if (!isRecord(raw))
25427
+ return [{ path: [], message: reasonOf(raw, "invalid value") }];
25428
+ const base = asArray(raw.path).map((segment) => typeof segment === "number" ? segment : String(segment));
25429
+ const branches = raw.code === "invalid_union" ? unionIssues(raw) : [];
25430
+ if (branches.length > 0)
25431
+ return dedupe(branches.map((issue) => ({ path: [...base, ...issue.path], message: issue.message })));
25432
+ return [{ path: base, message: typeof raw.message === "string" ? raw.message : "invalid value" }];
25433
+ }
25434
+ function normalizeIssues(error) {
25435
+ const raw = isRecord(error) ? asArray(error.issues ?? error.errors) : [];
25436
+ if (raw.length > 0)
25437
+ return raw.flatMap(toIssues);
25438
+ return [{ path: [], message: reasonOf(error, "the schema rejected this value without saying why") }];
25439
+ }
25440
+ function liveSchema(parse3) {
25441
+ return {
25442
+ async safeParse(value) {
25443
+ try {
25444
+ const result = await parse3(value);
25445
+ if (!isRecord(result))
25446
+ return { success: false, issues: [{ path: [], message: "the schema returned no parse result" }] };
25447
+ if (result.success === true)
25448
+ return { success: true };
25449
+ return { success: false, issues: normalizeIssues(result.error) };
25450
+ } catch (error) {
25451
+ return { success: false, issues: [{ path: [], message: reasonOf(error, "the schema threw while parsing this entry") }] };
25452
+ }
25453
+ }
25454
+ };
25455
+ }
25456
+ async function collectSchemas(module, zod, configName) {
25457
+ const collections = isRecord(module) ? module.collections : undefined;
25458
+ if (!isRecord(collections))
25459
+ return { skipped: `${configName} does not export a \`collections\` object.` };
25460
+ const schemas = {};
25461
+ for (const [name, definition] of Object.entries(collections)) {
25462
+ if (!isRecord(definition))
25463
+ continue;
25464
+ const factory = definition.schema;
25465
+ if (factory === undefined)
25466
+ continue;
25467
+ let resolved;
25468
+ try {
25469
+ resolved = typeof factory === "function" ? await Reflect.apply(factory, definition, [{ image: () => zod.any() }]) : factory;
25470
+ } catch (error) {
25471
+ return { skipped: `The schema of collection "${name}" in ${configName} threw while being built: ${reasonOf(error)}` };
25472
+ }
25473
+ const parse3 = parseMethod(resolved);
25474
+ if (!parse3) {
25475
+ return { skipped: `The schema of collection "${name}" in ${configName} has no safeParse method \u2014 is it a zod schema?` };
25476
+ }
25477
+ schemas[name] = liveSchema(parse3);
25478
+ }
25479
+ return { schemas };
25480
+ }
25481
+ async function loadLiveSchemas(projectRoot) {
25482
+ const root = path5.resolve(projectRoot);
25483
+ const configPath = findConfig(root);
25484
+ if (!configPath) {
25485
+ return { skipped: `No content config in ${root} \u2014 expected src/content.config.ts or src/content/config.ts.` };
25486
+ }
25487
+ const configName = path5.relative(root, configPath);
25488
+ const astroDir = projectAstroDir(root);
25489
+ if (!astroDir) {
25490
+ return { skipped: `No node_modules/astro above ${root} \u2014 install the project's dependencies and run this again.` };
25491
+ }
25492
+ let zodPath;
25493
+ try {
25494
+ zodPath = realOf(Bun.resolveSync("astro/zod", root));
25495
+ } catch (error) {
25496
+ return { skipped: `Could not resolve astro/zod from ${root}: ${reasonOf(error)}` };
25497
+ }
25498
+ if (!contains(astroDir, zodPath)) {
25499
+ return {
25500
+ skipped: `astro/zod resolved to ${zodPath}, outside the project's own ${astroDir} \u2014 reinstall the project's dependencies and run this again.`
25501
+ };
25502
+ }
25503
+ let zodModule;
25504
+ try {
25505
+ zodModule = await import(zodPath);
25506
+ } catch (error) {
25507
+ return { skipped: `Loading astro/zod from ${root} failed: ${reasonOf(error)}` };
25508
+ }
25509
+ let zod;
25510
+ try {
25511
+ zod = zodEntryOf(zodModule);
25512
+ } catch {
25513
+ zod = null;
25514
+ }
25515
+ if (!zod) {
25516
+ return { skipped: `astro/zod in ${root} does not export a usable \`z\` \u2014 the installed Astro version may be unsupported.` };
25517
+ }
25518
+ if (stubbedZod === null) {
25519
+ try {
25520
+ installContentStub(zodPath, zod);
25521
+ } catch (error) {
25522
+ return { skipped: `Installing the astro:content stub failed: ${reasonOf(error)}` };
25523
+ }
25524
+ } else if (stubbedZod !== zodPath) {
25525
+ return {
25526
+ skipped: `Live schemas were already loaded against ${stubbedZod}; the astro:content stub is process-global, so ${root} needs a run of its own.`
25527
+ };
25528
+ }
25529
+ let module;
25530
+ try {
25531
+ module = await import(configPath);
25532
+ } catch (error) {
25533
+ return { skipped: `${configName} failed to load: ${reasonOf(error)}` };
25534
+ }
25535
+ try {
25536
+ return await collectSchemas(module, zod, configName);
25537
+ } catch (error) {
25538
+ return { skipped: `Reading the collections in ${configName} failed: ${reasonOf(error)}` };
25539
+ }
25540
+ }
25541
+ var CONFIG_NAMES, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), asArray = (value) => Array.isArray(value) ? value : [], realOf = (target) => {
25542
+ try {
25543
+ return realpathSync(target);
25544
+ } catch {
25545
+ return target;
25546
+ }
25547
+ }, contains = (dir, file) => {
25548
+ const relative = path5.relative(realOf(dir), realOf(file));
25549
+ return relative !== "" && !relative.startsWith("..") && !path5.isAbsolute(relative);
25550
+ }, stubbedZod = null;
25551
+ var init_live_schema = __esm(() => {
25552
+ CONFIG_NAMES = [
25553
+ "src/content.config.mjs",
25554
+ "src/content.config.js",
25555
+ "src/content.config.mts",
25556
+ "src/content.config.ts",
25557
+ "src/content/config.ts",
25558
+ "src/content/config.js",
25559
+ "src/content/config.mjs",
25560
+ "src/content/config.mts"
25561
+ ];
25562
+ });
25563
+
24624
25564
  // src/check.ts
24625
25565
  var exports_check = {};
24626
25566
  __export(exports_check, {
25567
+ parseCheckArgs: () => parseCheckArgs,
24627
25568
  check: () => check,
24628
25569
  astroRoots: () => astroRoots
24629
25570
  });
24630
- import { existsSync, readdirSync } from "fs";
24631
- import path5 from "path";
25571
+ import { existsSync as existsSync2, readdirSync } from "fs";
25572
+ import path6 from "path";
25573
+ function parseCheckArgs(args, cwd) {
25574
+ const contentOnly = args.includes("--content-only");
25575
+ return {
25576
+ cwd,
25577
+ json: args.includes("--json"),
25578
+ strict: args.includes("--strict"),
25579
+ contentOnly,
25580
+ live: !contentOnly && !args.includes("--no-live")
25581
+ };
25582
+ }
24632
25583
  function astroRoots(cwd) {
24633
25584
  if (hasAstroConfig(cwd))
24634
25585
  return ["."];
24635
- const packagesDir = path5.join(cwd, "packages");
24636
- if (!existsSync(packagesDir))
25586
+ const packagesDir = path6.join(cwd, "packages");
25587
+ if (!existsSync2(packagesDir))
24637
25588
  return [];
24638
- return readdirSync(packagesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().filter((name) => hasAstroConfig(path5.join(packagesDir, name))).map((name) => path5.join("packages", name));
25589
+ return readdirSync(packagesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().filter((name) => hasAstroConfig(path6.join(packagesDir, name))).map((name) => path6.join("packages", name));
24639
25590
  }
24640
25591
  function prefixed(report, root) {
24641
25592
  if (root === ".")
24642
25593
  return report;
24643
25594
  return {
24644
25595
  ...report,
24645
- findings: report.findings.map((finding) => ({ ...finding, file: path5.join(root, finding.file) }))
25596
+ findings: report.findings.map((finding) => ({ ...finding, file: path6.join(root, finding.file) }))
24646
25597
  };
24647
25598
  }
25599
+ async function liveSchemasFor(options, roots) {
25600
+ if (options.contentOnly) {
25601
+ return { skipped: "--content-only was passed, and reading the real schemas means executing the project's content config." };
25602
+ }
25603
+ if (!options.live)
25604
+ return { skipped: "--no-live was passed." };
25605
+ const first = roots[0];
25606
+ if (roots.length !== 1 || first === undefined) {
25607
+ const where = roots.join(", ");
25608
+ return { skipped: `They need exactly one Astro project; this run covers ${roots.length} (${where}). Check each one from its own directory.` };
25609
+ }
25610
+ return loadLiveSchemas(path6.join(options.cwd, first));
25611
+ }
24648
25612
  async function check(options) {
24649
25613
  const roots = astroRoots(options.cwd);
24650
25614
  if (roots.length === 0) {
24651
25615
  console.error(`No astro.config.* found in ${options.cwd} or its packages/*. Run this in an Astro project.`);
24652
25616
  return 1;
24653
25617
  }
25618
+ const live = await liveSchemasFor(options, roots);
25619
+ const schemas = "schemas" in live ? live.schemas : undefined;
24654
25620
  const reports = await Promise.all(roots.map(async (root) => {
24655
- const absolute = path5.join(options.cwd, root);
24656
- const content = prefixed(await checkContent(createNodeFs(absolute)), root);
25621
+ const absolute = path6.join(options.cwd, root);
25622
+ const content = prefixed(await checkContent(createNodeFs(absolute), schemas ? { schemas } : {}), root);
24657
25623
  if (options.contentOnly)
24658
25624
  return { ...content, files: 0 };
24659
25625
  const code = await checkCode(absolute);
24660
25626
  return {
24661
25627
  ...content,
24662
- findings: [...content.findings, ...code.findings.map((finding) => ({ ...finding, file: path5.join(root, finding.file) }))],
25628
+ findings: [...content.findings, ...code.findings.map((finding) => ({ ...finding, file: path6.join(root, finding.file) }))],
24663
25629
  files: code.files
24664
25630
  };
24665
25631
  }));
@@ -24672,7 +25638,7 @@ async function check(options) {
24672
25638
  const errors = report.findings.filter((finding) => finding.severity === "error").length;
24673
25639
  const warnings = report.findings.length - errors;
24674
25640
  if (options.json) {
24675
- console.log(JSON.stringify({ ...report, roots }, null, 2));
25641
+ console.log(JSON.stringify({ ...report, roots, liveSchemas: "schemas" in live ? "ran" : live.skipped }, null, 2));
24676
25642
  } else {
24677
25643
  const formatted = formatCheckReport(report);
24678
25644
  if (formatted)
@@ -24682,23 +25648,26 @@ async function check(options) {
24682
25648
  const code = options.contentOnly ? "" : `, ${sourceFiles} source file(s)`;
24683
25649
  const counts = `${report.collections} collection(s), ${report.entries} entries${code}${where}`;
24684
25650
  console.log(errors === 0 && warnings === 0 ? `${counts} \u2014 no problems found` : `${counts} \u2014 ${errors} error(s), ${warnings} warning(s)`);
25651
+ if ("skipped" in live)
25652
+ console.log(`Live schema rules did not run: ${live.skipped}`);
24685
25653
  }
24686
25654
  return errors > 0 || options.strict && warnings > 0 ? 1 : 0;
24687
25655
  }
24688
- var ASTRO_CONFIG_NAMES, hasAstroConfig = (dir) => ASTRO_CONFIG_NAMES.some((name) => existsSync(path5.join(dir, name)));
25656
+ var ASTRO_CONFIG_NAMES, hasAstroConfig = (dir) => ASTRO_CONFIG_NAMES.some((name) => existsSync2(path6.join(dir, name)));
24689
25657
  var init_check2 = __esm(() => {
24690
25658
  init_src2();
24691
25659
  init_check_code();
25660
+ init_live_schema();
24692
25661
  ASTRO_CONFIG_NAMES = ["astro.config.ts", "astro.config.mts", "astro.config.mjs", "astro.config.js"];
24693
25662
  });
24694
25663
 
24695
25664
  // src/utils.ts
24696
- import { existsSync as existsSync2 } from "fs";
25665
+ import { existsSync as existsSync3 } from "fs";
24697
25666
  import { join } from "path";
24698
25667
  function findAstroConfig(cwd = process.cwd()) {
24699
- for (const name of CONFIG_NAMES) {
25668
+ for (const name of CONFIG_NAMES2) {
24700
25669
  const p = join(cwd, name);
24701
- if (existsSync2(p))
25670
+ if (existsSync3(p))
24702
25671
  return p;
24703
25672
  }
24704
25673
  return null;
@@ -24795,9 +25764,9 @@ function assembleConfig(imports, body) {
24795
25764
  `;
24796
25765
  return result;
24797
25766
  }
24798
- var CONFIG_NAMES;
25767
+ var CONFIG_NAMES2;
24799
25768
  var init_utils = __esm(() => {
24800
- CONFIG_NAMES = [
25769
+ CONFIG_NAMES2 = [
24801
25770
  "astro.config.ts",
24802
25771
  "astro.config.mts",
24803
25772
  "astro.config.mjs",
@@ -24815,7 +25784,7 @@ __export(exports_init, {
24815
25784
  detectNuaManagedImports: () => detectNuaManagedImports,
24816
25785
  cleanEmptyStructures: () => cleanEmptyStructures
24817
25786
  });
24818
- import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
25787
+ import { existsSync as existsSync4, readFileSync, writeFileSync } from "fs";
24819
25788
  import { basename, join as join2 } from "path";
24820
25789
  function detectNuaManagedImports(content) {
24821
25790
  const managed = new Map;
@@ -24959,7 +25928,7 @@ async function init({ cwd = process.cwd(), dryRun = false, yes = false } = {}) {
24959
25928
  process.exit(1);
24960
25929
  }
24961
25930
  const pkgPath = join2(cwd, "package.json");
24962
- if (!existsSync3(pkgPath)) {
25931
+ if (!existsSync4(pkgPath)) {
24963
25932
  console.error("No package.json found.");
24964
25933
  process.exit(1);
24965
25934
  }
@@ -25037,7 +26006,7 @@ __export(exports_clean, {
25037
26006
  detectDisabledFeatures: () => detectDisabledFeatures,
25038
26007
  clean: () => clean
25039
26008
  });
25040
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
26009
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
25041
26010
  import { basename as basename2, join as join3 } from "path";
25042
26011
  function detectDisabledFeatures(content) {
25043
26012
  const disabled = new Set;
@@ -25146,7 +26115,7 @@ function transformPackageJson2(pkg, disabled, usedRuntimePackages = []) {
25146
26115
  }
25147
26116
  function scanForNuasiteUsage(cwd) {
25148
26117
  const srcDir = join3(cwd, "src");
25149
- if (!existsSync4(srcDir))
26118
+ if (!existsSync5(srcDir))
25150
26119
  return [];
25151
26120
  const results = [];
25152
26121
  try {
@@ -25181,7 +26150,7 @@ async function clean({ cwd = process.cwd(), dryRun = false, yes = false } = {})
25181
26150
  return;
25182
26151
  }
25183
26152
  const pkgPath = join3(cwd, "package.json");
25184
- if (!existsSync4(pkgPath)) {
26153
+ if (!existsSync5(pkgPath)) {
25185
26154
  console.error("No package.json found.");
25186
26155
  process.exit(1);
25187
26156
  }
@@ -25289,7 +26258,7 @@ var init_config = () => {};
25289
26258
 
25290
26259
  // ../cms/src/component-registry.ts
25291
26260
  import fs2 from "fs/promises";
25292
- import path6 from "path";
26261
+ import path7 from "path";
25293
26262
 
25294
26263
  class ComponentRegistry {
25295
26264
  components = new Map;
@@ -25299,7 +26268,7 @@ class ComponentRegistry {
25299
26268
  }
25300
26269
  async scan() {
25301
26270
  for (const dir of this.componentDirs) {
25302
- const fullPath = path6.join(getProjectRoot(), dir);
26271
+ const fullPath = path7.join(getProjectRoot(), dir);
25303
26272
  try {
25304
26273
  await this.scanDirectory(fullPath, dir);
25305
26274
  } catch {}
@@ -25314,8 +26283,8 @@ class ComponentRegistry {
25314
26283
  async scanDirectory(dir, relativePath) {
25315
26284
  const entries = await fs2.readdir(dir, { withFileTypes: true });
25316
26285
  for (const entry of entries) {
25317
- const fullPath = path6.join(dir, entry.name);
25318
- const relPath = path6.join(relativePath, entry.name);
26286
+ const fullPath = path7.join(dir, entry.name);
26287
+ const relPath = path7.join(relativePath, entry.name);
25319
26288
  if (entry.isDirectory()) {
25320
26289
  await this.scanDirectory(fullPath, relPath);
25321
26290
  } else if (entry.isFile() && entry.name.endsWith(".astro")) {
@@ -25326,7 +26295,7 @@ class ComponentRegistry {
25326
26295
  async parseComponent(filePath, relativePath) {
25327
26296
  try {
25328
26297
  const content = await fs2.readFile(filePath, "utf-8");
25329
- const componentName = path6.basename(filePath, ".astro");
26298
+ const componentName = path7.basename(filePath, ".astro");
25330
26299
  const props = await this.extractProps(content);
25331
26300
  const slots = this.extractSlots(content);
25332
26301
  const description = this.extractDescription(content);
@@ -25581,7 +26550,7 @@ function slugify2(text) {
25581
26550
 
25582
26551
  // ../cms/src/utils.ts
25583
26552
  import { createHash } from "crypto";
25584
- import path7 from "path";
26553
+ import path8 from "path";
25585
26554
  function normalizePagePath(url) {
25586
26555
  let pathname;
25587
26556
  try {
@@ -25634,21 +26603,21 @@ function escapeReplacement(str) {
25634
26603
  return str.replace(/\$/g, "$$$$");
25635
26604
  }
25636
26605
  function resolveSourcePath(sourcePath) {
25637
- return path7.isAbsolute(sourcePath) ? sourcePath : path7.join(getProjectRoot(), sourcePath);
26606
+ return path8.isAbsolute(sourcePath) ? sourcePath : path8.join(getProjectRoot(), sourcePath);
25638
26607
  }
25639
26608
  function resolveAndValidatePath(filePath) {
25640
26609
  const projectRoot = getProjectRoot();
25641
- const resolvedRoot = path7.resolve(projectRoot);
26610
+ const resolvedRoot = path8.resolve(projectRoot);
25642
26611
  const isAbsoluteFs = filePath.startsWith(resolvedRoot);
25643
26612
  const normalizedPath = !isAbsoluteFs && filePath.startsWith("/") ? filePath.slice(1) : filePath;
25644
- const fullPath = path7.isAbsolute(normalizedPath) ? path7.resolve(normalizedPath) : path7.resolve(projectRoot, normalizedPath);
25645
- if (!fullPath.startsWith(resolvedRoot + path7.sep) && fullPath !== resolvedRoot) {
26613
+ const fullPath = path8.isAbsolute(normalizedPath) ? path8.resolve(normalizedPath) : path8.resolve(projectRoot, normalizedPath);
26614
+ if (!fullPath.startsWith(resolvedRoot + path8.sep) && fullPath !== resolvedRoot) {
25646
26615
  throw new Error(`Path traversal detected: ${filePath}`);
25647
26616
  }
25648
26617
  return fullPath;
25649
26618
  }
25650
26619
  async function acquireFileLock(filePath) {
25651
- const key = path7.resolve(filePath);
26620
+ const key = path8.resolve(filePath);
25652
26621
  while (fileLocks.has(key)) {
25653
26622
  await fileLocks.get(key);
25654
26623
  }
@@ -25666,8 +26635,8 @@ function escapeHtml2(text) {
25666
26635
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
25667
26636
  }
25668
26637
  function relativeImportPath2(fromFile, toFile) {
25669
- const fromDir = path7.dirname(fromFile);
25670
- let rel = path7.relative(fromDir, toFile).split(path7.sep).join("/");
26638
+ const fromDir = path8.dirname(fromFile);
26639
+ let rel = path8.relative(fromDir, toFile).split(path8.sep).join("/");
25671
26640
  if (!rel.startsWith("."))
25672
26641
  rel = `./${rel}`;
25673
26642
  return rel;
@@ -25680,7 +26649,7 @@ var init_utils2 = __esm(() => {
25680
26649
 
25681
26650
  // ../cms/src/handlers/component-ops.ts
25682
26651
  import fs3 from "fs/promises";
25683
- import path8 from "path";
26652
+ import path9 from "path";
25684
26653
  async function handleInsertComponent(request, manifestWriter) {
25685
26654
  const { position, referenceComponentId, componentName, props, meta } = request;
25686
26655
  if (!meta?.url) {
@@ -25898,7 +26867,7 @@ function getComponentOccurrenceIndex(manifest, referenceComponent) {
25898
26867
  async function findComponentInvocationFile(projectRoot, pageUrl, manifest, referenceComponent) {
25899
26868
  if (referenceComponent.invocationSourcePath) {
25900
26869
  const filePath = normalizeFilePath(referenceComponent.invocationSourcePath);
25901
- const fullPath = path8.resolve(projectRoot, filePath);
26870
+ const fullPath = path9.resolve(projectRoot, filePath);
25902
26871
  try {
25903
26872
  const content = await fs3.readFile(fullPath, "utf-8");
25904
26873
  const lines = content.split(`
@@ -25912,7 +26881,7 @@ async function findComponentInvocationFile(projectRoot, pageUrl, manifest, refer
25912
26881
  const candidates = getPageFileCandidates(pageUrl);
25913
26882
  const occurrenceIndex = getComponentOccurrenceIndex(manifest, referenceComponent);
25914
26883
  for (const candidate of candidates) {
25915
- const fullPath = path8.resolve(projectRoot, candidate);
26884
+ const fullPath = path9.resolve(projectRoot, candidate);
25916
26885
  try {
25917
26886
  const content = await fs3.readFile(fullPath, "utf-8");
25918
26887
  const lines = content.split(`
@@ -25972,7 +26941,7 @@ function getIndentation(line) {
25972
26941
  function normalizeFilePath(p) {
25973
26942
  if (!p.startsWith("/"))
25974
26943
  return p;
25975
- const projectRoot = path8.resolve(getProjectRoot());
26944
+ const projectRoot = path9.resolve(getProjectRoot());
25976
26945
  if (p.startsWith(projectRoot))
25977
26946
  return p;
25978
26947
  return p.slice(1);
@@ -26380,45 +27349,45 @@ var init_array_ops = __esm(() => {
26380
27349
  // ../cms/src/astro-image-paths.ts
26381
27350
  import { createHash as createHash2 } from "crypto";
26382
27351
  import fs5 from "fs/promises";
26383
- import path9 from "path";
27352
+ import path10 from "path";
26384
27353
  function isHugoStyleEntry(entryAbsPath) {
26385
- return HUGO_INDEX_RE.test(path9.basename(entryAbsPath));
27354
+ return HUGO_INDEX_RE.test(path10.basename(entryAbsPath));
26386
27355
  }
26387
27356
  function shortContentHash(buf) {
26388
27357
  return createHash2("sha256").update(buf).digest("hex").slice(0, 8);
26389
27358
  }
26390
27359
  async function pickAstroImageTarget(args) {
26391
- const entryDir = path9.dirname(args.entryAbsPath);
27360
+ const entryDir = path10.dirname(args.entryAbsPath);
26392
27361
  const isHugoStyle = isHugoStyleEntry(args.entryAbsPath);
26393
- const safeFilename = path9.basename(args.originalFilename);
27362
+ const safeFilename = path10.basename(args.originalFilename);
26394
27363
  if (!safeFilename || safeFilename === "." || safeFilename === "..") {
26395
27364
  throw new Error(`Invalid filename: ${args.originalFilename}`);
26396
27365
  }
26397
27366
  const baseName = isHugoStyle ? safeFilename : `${args.slug}-${safeFilename}`;
26398
- const baseAbs = path9.join(entryDir, baseName);
27367
+ const baseAbs = path10.join(entryDir, baseName);
26399
27368
  if (await isFreeOrMatching(baseAbs, args.compareBuffer)) {
26400
27369
  return { absPath: baseAbs, relPath: `./${baseName}` };
26401
27370
  }
26402
27371
  const candidateAbs = await pickHashedSibling(entryDir, baseName, args.compareBuffer);
26403
- return { absPath: candidateAbs, relPath: `./${path9.basename(candidateAbs)}` };
27372
+ return { absPath: candidateAbs, relPath: `./${path10.basename(candidateAbs)}` };
26404
27373
  }
26405
27374
  async function pickSiblingTarget(dir, filename, buf) {
26406
- const safe = path9.basename(filename);
27375
+ const safe = path10.basename(filename);
26407
27376
  if (!safe || safe === "." || safe === "..") {
26408
27377
  throw new Error(`Invalid filename: ${filename}`);
26409
27378
  }
26410
- const baseAbs = path9.join(dir, safe);
27379
+ const baseAbs = path10.join(dir, safe);
26411
27380
  if (await isFreeOrMatching(baseAbs, buf))
26412
27381
  return baseAbs;
26413
27382
  return pickHashedSibling(dir, safe, buf);
26414
27383
  }
26415
27384
  async function pickHashedSibling(dir, baseName, buf) {
26416
27385
  const hash = shortContentHash(buf);
26417
- const ext = path9.extname(baseName);
26418
- const stem = path9.basename(baseName, ext);
27386
+ const ext = path10.extname(baseName);
27387
+ const stem = path10.basename(baseName, ext);
26419
27388
  for (let attempt = 0;attempt < 5; attempt++) {
26420
27389
  const suffix = attempt === 0 ? hash : `${hash}-${attempt}`;
26421
- const candidateAbs = path9.join(dir, `${stem}-${suffix}${ext}`);
27390
+ const candidateAbs = path10.join(dir, `${stem}-${suffix}${ext}`);
26422
27391
  if (await isFreeOrMatching(candidateAbs, buf))
26423
27392
  return candidateAbs;
26424
27393
  }
@@ -26442,7 +27411,7 @@ var init_astro_image_paths = __esm(() => {
26442
27411
 
26443
27412
  // ../cms/src/handlers/astro-image-upload.ts
26444
27413
  import fs6 from "fs/promises";
26445
- import path10 from "path";
27414
+ import path11 from "path";
26446
27415
  async function tryAstroImageUpload(args) {
26447
27416
  const { collection, entry, field } = args.context;
26448
27417
  if (!collection || !entry || !field)
@@ -26464,12 +27433,12 @@ async function tryAstroImageUpload(args) {
26464
27433
  originalFilename: args.originalFilename,
26465
27434
  compareBuffer: args.fileBuffer
26466
27435
  });
26467
- await fs6.mkdir(path10.dirname(target.absPath), { recursive: true });
27436
+ await fs6.mkdir(path11.dirname(target.absPath), { recursive: true });
26468
27437
  await fs6.writeFile(target.absPath, args.fileBuffer);
26469
27438
  return {
26470
27439
  success: true,
26471
27440
  url: target.relPath,
26472
- filename: path10.basename(target.absPath)
27441
+ filename: path11.basename(target.absPath)
26473
27442
  };
26474
27443
  }
26475
27444
  var init_astro_image_upload = __esm(() => {
@@ -26661,7 +27630,7 @@ var require_he = __commonJS((exports, module) => {
26661
27630
  var has = function(object2, propertyName) {
26662
27631
  return hasOwnProperty2.call(object2, propertyName);
26663
27632
  };
26664
- var contains = function(array, value) {
27633
+ var contains2 = function(array, value) {
26665
27634
  var index = -1;
26666
27635
  var length = array.length;
26667
27636
  while (++index < length) {
@@ -26696,7 +27665,7 @@ var require_he = __commonJS((exports, module) => {
26696
27665
  }
26697
27666
  return decodeMapNumeric[codePoint];
26698
27667
  }
26699
- if (strict && contains(invalidReferenceCodePoints, codePoint)) {
27668
+ if (strict && contains2(invalidReferenceCodePoints, codePoint)) {
26700
27669
  parseError("disallowed character reference");
26701
27670
  }
26702
27671
  if (codePoint > 65535) {
@@ -30184,7 +31153,7 @@ var require_filters = __commonJS((exports) => {
30184
31153
  exports.filters = {
30185
31154
  contains: function(next, text, _a) {
30186
31155
  var adapter = _a.adapter;
30187
- return function contains(elem) {
31156
+ return function contains2(elem) {
30188
31157
  return next(elem) && adapter.getText(elem).includes(text);
30189
31158
  };
30190
31159
  },
@@ -32297,7 +33266,7 @@ var init_snippet_utils = __esm(() => {
32297
33266
 
32298
33267
  // ../cms/src/handlers/source-writer.ts
32299
33268
  import fs8 from "fs/promises";
32300
- import path11 from "path";
33269
+ import path12 from "path";
32301
33270
  async function handleUpdate(request, manifestWriter) {
32302
33271
  const { changes, meta } = request;
32303
33272
  const errors = [];
@@ -32334,7 +33303,7 @@ async function handleUpdate(request, manifestWriter) {
32334
33303
  }
32335
33304
  if (appliedCount > 0 && newContent !== currentContent) {
32336
33305
  for (const op of fileOps) {
32337
- await fs8.mkdir(path11.dirname(op.target), { recursive: true });
33306
+ await fs8.mkdir(path12.dirname(op.target), { recursive: true });
32338
33307
  await fs8.writeFile(op.target, op.bytes);
32339
33308
  }
32340
33309
  await fs8.writeFile(fullPath, newContent, "utf-8");
@@ -32600,8 +33569,8 @@ async function tryRewriteAssetImport(content, importInfo, newSrc, absFilePath, o
32600
33569
  const resolved = await resolveNewSrcBytes(newSrc, originUrl);
32601
33570
  if (!resolved)
32602
33571
  return null;
32603
- const originalAssetAbs = path11.resolve(path11.dirname(absFilePath), importInfo.source);
32604
- const targetAbs = await pickSiblingTarget(path11.dirname(originalAssetAbs), resolved.filename, resolved.bytes);
33572
+ const originalAssetAbs = path12.resolve(path12.dirname(absFilePath), importInfo.source);
33573
+ const targetAbs = await pickSiblingTarget(path12.dirname(originalAssetAbs), resolved.filename, resolved.bytes);
32605
33574
  const newRelImport = relativeImportPath2(absFilePath, targetAbs);
32606
33575
  const newContent = content.slice(0, importInfo.sourceStart) + newRelImport + content.slice(importInfo.sourceEnd);
32607
33576
  return {
@@ -32611,8 +33580,8 @@ async function tryRewriteAssetImport(content, importInfo, newSrc, absFilePath, o
32611
33580
  };
32612
33581
  }
32613
33582
  async function resolveNewSrcBytes(newSrc, originUrl) {
32614
- const filenameFromPath = (p) => path11.basename(p.split("?")[0] ?? p);
32615
- const diskPath = newSrc.startsWith("/src/") ? path11.join(getProjectRoot(), newSrc.slice(1)) : newSrc.startsWith("/") && !newSrc.startsWith("//") ? path11.join(getProjectRoot(), "public", newSrc.replace(/^\/+/, "")) : null;
33583
+ const filenameFromPath = (p) => path12.basename(p.split("?")[0] ?? p);
33584
+ const diskPath = newSrc.startsWith("/src/") ? path12.join(getProjectRoot(), newSrc.slice(1)) : newSrc.startsWith("/") && !newSrc.startsWith("//") ? path12.join(getProjectRoot(), "public", newSrc.replace(/^\/+/, "")) : null;
32616
33585
  if (diskPath) {
32617
33586
  try {
32618
33587
  return { bytes: await fs8.readFile(diskPath), filename: filenameFromPath(newSrc) };
@@ -33160,7 +34129,7 @@ var init_source_writer = __esm(() => {
33160
34129
  });
33161
34130
 
33162
34131
  // ../cms/src/handlers/api-routes.ts
33163
- import path12 from "path";
34132
+ import path13 from "path";
33164
34133
  function requireMedia(ctx) {
33165
34134
  if (!ctx.mediaAdapter) {
33166
34135
  sendError(ctx.res, "Media storage not configured", 501);
@@ -33323,7 +34292,7 @@ var init_api_routes = __esm(() => {
33323
34292
  sendJson(res, { success: false, error: `Invalid content path: ${body.filePath}` }, 400);
33324
34293
  return;
33325
34294
  }
33326
- const fullPath = path12.resolve(getProjectRoot(), body.filePath.replace(/^\//, ""));
34295
+ const fullPath = path13.resolve(getProjectRoot(), body.filePath.replace(/^\//, ""));
33327
34296
  expectedDeletions.add(fullPath);
33328
34297
  const result = await core.deleteEntry(entry.collection, entry.slug);
33329
34298
  if (result.success) {
@@ -33430,7 +34399,7 @@ var init_api_routes = __esm(() => {
33430
34399
  const body = await parseJsonBody(req);
33431
34400
  const result = await core.deletePage(body);
33432
34401
  if (result.success && result.filePath) {
33433
- expectedDeletions.add(path12.resolve(getProjectRoot(), result.filePath));
34402
+ expectedDeletions.add(path13.resolve(getProjectRoot(), result.filePath));
33434
34403
  }
33435
34404
  if (result.success && body.createRedirect && body.redirectTo) {
33436
34405
  await core.addRedirect({ source: body.pagePath, destination: body.redirectTo, statusCode: 307 });
@@ -33535,7 +34504,7 @@ var init_color_patterns = __esm(() => {
33535
34504
 
33536
34505
  // ../cms/src/tailwind-colors.ts
33537
34506
  import fs9 from "fs/promises";
33538
- import path13 from "path";
34507
+ import path14 from "path";
33539
34508
  async function parseTailwindConfig(projectRoot = getProjectRoot()) {
33540
34509
  const cssFiles = [
33541
34510
  "src/styles/global.css",
@@ -33549,7 +34518,7 @@ async function parseTailwindConfig(projectRoot = getProjectRoot()) {
33549
34518
  ];
33550
34519
  let customColors = [];
33551
34520
  for (const cssFile of cssFiles) {
33552
- const fullPath = path13.join(projectRoot, cssFile);
34521
+ const fullPath = path14.join(projectRoot, cssFile);
33553
34522
  try {
33554
34523
  const content = await fs9.readFile(fullPath, "utf-8");
33555
34524
  customColors = extractColorsFromCss(content);
@@ -33622,7 +34591,7 @@ async function parseTextStyles(projectRoot = getProjectRoot()) {
33622
34591
  ];
33623
34592
  let customTextStyles = {};
33624
34593
  for (const cssFile of cssFiles) {
33625
- const fullPath = path13.join(projectRoot, cssFile);
34594
+ const fullPath = path14.join(projectRoot, cssFile);
33626
34595
  try {
33627
34596
  const content = await fs9.readFile(fullPath, "utf-8");
33628
34597
  customTextStyles = extractTextStylesFromCss(content);
@@ -34090,7 +35059,7 @@ var init_local_admin = __esm(() => {
34090
35059
 
34091
35060
  // ../cms/src/manifest-writer.ts
34092
35061
  import fs10 from "fs/promises";
34093
- import path14 from "path";
35062
+ import path15 from "path";
34094
35063
 
34095
35064
  class ManifestWriter {
34096
35065
  globalManifest;
@@ -34161,10 +35130,10 @@ class ManifestWriter {
34161
35130
  }
34162
35131
  getPageManifestPath(pagePath) {
34163
35132
  if (pagePath === "/" || pagePath === "") {
34164
- return path14.join(this.outDir, "index.json");
35133
+ return path15.join(this.outDir, "index.json");
34165
35134
  }
34166
35135
  const cleanPath = pagePath.replace(/^\//, "");
34167
- return path14.join(this.outDir, `${cleanPath}.json`);
35136
+ return path15.join(this.outDir, `${cleanPath}.json`);
34168
35137
  }
34169
35138
  addPage(pagePath, entries, components, collection, seo) {
34170
35139
  this.pageManifests.set(pagePath, { entries, components, collection, seo });
@@ -34190,7 +35159,7 @@ class ManifestWriter {
34190
35159
  }
34191
35160
  async writePageManifest(pagePath, entries, components, collection, seo) {
34192
35161
  const manifestPath = this.getPageManifestPath(pagePath);
34193
- const manifestDir = path14.dirname(manifestPath);
35162
+ const manifestDir = path15.dirname(manifestPath);
34194
35163
  await fs10.mkdir(manifestDir, { recursive: true });
34195
35164
  const metadata = {
34196
35165
  version: MANIFEST_VERSION,
@@ -34246,7 +35215,7 @@ class ManifestWriter {
34246
35215
  }
34247
35216
  }
34248
35217
  if (this.outDir) {
34249
- const globalManifestPath = path14.join(this.outDir, this.manifestFile);
35218
+ const globalManifestPath = path15.join(this.outDir, this.manifestFile);
34250
35219
  const globalSettings = {
34251
35220
  componentDefinitions: this.componentDefinitions,
34252
35221
  pages
@@ -34858,7 +35827,7 @@ var init_field_types = __esm(() => {
34858
35827
 
34859
35828
  // ../cms/src/migrate-astro-image.ts
34860
35829
  import fs11 from "fs/promises";
34861
- import path15 from "path";
35830
+ import path16 from "path";
34862
35831
  async function migrateAstroImages(options = {}) {
34863
35832
  const projectRoot = options.projectRoot ?? getProjectRoot();
34864
35833
  const dryRun = options.dryRun ?? false;
@@ -34870,7 +35839,7 @@ async function migrateAstroImages(options = {}) {
34870
35839
  if (astroFields.length === 0 || !def.entries)
34871
35840
  continue;
34872
35841
  for (const entry of def.entries) {
34873
- const entryAbs = path15.isAbsolute(entry.sourcePath) ? entry.sourcePath : path15.join(projectRoot, entry.sourcePath);
35842
+ const entryAbs = path16.isAbsolute(entry.sourcePath) ? entry.sourcePath : path16.join(projectRoot, entry.sourcePath);
34874
35843
  let raw;
34875
35844
  try {
34876
35845
  raw = await fs11.readFile(entryAbs, "utf-8");
@@ -34885,7 +35854,7 @@ async function migrateAstroImages(options = {}) {
34885
35854
  }
34886
35855
  const [fullFm, fmStart, yamlBody, fmEnd] = fmMatch;
34887
35856
  const doc2 = import_yaml7.parseDocument(yamlBody);
34888
- let mutated = false;
35857
+ let mutated2 = false;
34889
35858
  for (const field of astroFields) {
34890
35859
  const current = doc2.get(field.name);
34891
35860
  if (typeof current !== "string") {
@@ -34895,7 +35864,7 @@ async function migrateAstroImages(options = {}) {
34895
35864
  }
34896
35865
  if (!current.startsWith("/") || current.startsWith("//"))
34897
35866
  continue;
34898
- const sourceAbs = path15.join(projectRoot, "public", current.replace(/^\/+/, ""));
35867
+ const sourceAbs = path16.join(projectRoot, "public", current.replace(/^\/+/, ""));
34899
35868
  let sourceBuf;
34900
35869
  try {
34901
35870
  sourceBuf = await fs11.readFile(sourceAbs);
@@ -34906,15 +35875,15 @@ async function migrateAstroImages(options = {}) {
34906
35875
  const target = await pickAstroImageTarget({
34907
35876
  entryAbsPath: entryAbs,
34908
35877
  slug: entry.slug,
34909
- originalFilename: path15.basename(current),
35878
+ originalFilename: path16.basename(current),
34910
35879
  compareBuffer: sourceBuf
34911
35880
  });
34912
35881
  if (!dryRun) {
34913
- await fs11.mkdir(path15.dirname(target.absPath), { recursive: true });
35882
+ await fs11.mkdir(path16.dirname(target.absPath), { recursive: true });
34914
35883
  await fs11.writeFile(target.absPath, sourceBuf);
34915
35884
  }
34916
35885
  doc2.set(field.name, target.relPath);
34917
- mutated = true;
35886
+ mutated2 = true;
34918
35887
  migrations.push({
34919
35888
  entrySourcePath: entry.sourcePath,
34920
35889
  fieldName: field.name,
@@ -34924,7 +35893,7 @@ async function migrateAstroImages(options = {}) {
34924
35893
  copiedTo: target.absPath
34925
35894
  });
34926
35895
  }
34927
- if (mutated && !dryRun) {
35896
+ if (mutated2 && !dryRun) {
34928
35897
  const newYaml = doc2.toString().replace(/\n$/, "");
34929
35898
  const newRaw = raw.replace(fullFm, `${fmStart}${newYaml}${fmEnd}`);
34930
35899
  await fs11.writeFile(entryAbs, newRaw, "utf-8");
@@ -34973,7 +35942,7 @@ var exports_migrate = {};
34973
35942
  __export(exports_migrate, {
34974
35943
  migrate: () => migrate
34975
35944
  });
34976
- import path16 from "path";
35945
+ import path17 from "path";
34977
35946
  async function migrate(args) {
34978
35947
  if (args.target !== "astro-image") {
34979
35948
  console.error(`Unknown migrate target: ${args.target}`);
@@ -34992,7 +35961,7 @@ async function migrate(args) {
34992
35961
  for (const m2 of result.migrations) {
34993
35962
  console.log(` ${m2.entrySourcePath}`);
34994
35963
  console.log(` ${m2.fieldName}: ${m2.originalValue} \u2192 ${m2.newValue}`);
34995
- console.log(` copy: ${path16.relative(process.cwd(), m2.copiedFrom)} \u2192 ${path16.relative(process.cwd(), m2.copiedTo)}`);
35964
+ console.log(` copy: ${path17.relative(process.cwd(), m2.copiedFrom)} \u2192 ${path17.relative(process.cwd(), m2.copiedTo)}`);
34996
35965
  }
34997
35966
  }
34998
35967
  if (result.skipped.length > 0) {
@@ -35034,7 +36003,7 @@ Commands:`);
35034
36003
  console.log(" build Run astro build with the Nua defaults");
35035
36004
  console.log(" dev Run astro dev with the Nua defaults");
35036
36005
  console.log(" preview Run astro preview with the Nua defaults");
35037
- console.log(" check Validate collections and parse the source (no build). --json, --strict, --content-only");
36006
+ console.log(" check Validate collections and parse the source (no build). --json, --strict, --content-only, --no-live");
35038
36007
  console.log(" init Convert a standard Astro project to use Nua");
35039
36008
  console.log(" clean Eject to a standard Astro project (remove @nuasite/* deps)");
35040
36009
  console.log(" migrate <target> Run a content migration. Targets: astro-image");
@@ -35048,13 +36017,8 @@ if (command && ["build", "dev", "preview"].includes(command)) {
35048
36017
  } else {
35049
36018
  switch (command) {
35050
36019
  case "check": {
35051
- const { check: check2 } = await Promise.resolve().then(() => (init_check2(), exports_check));
35052
- process.exit(await check2({
35053
- cwd: process.cwd(),
35054
- json: args.includes("--json"),
35055
- strict: args.includes("--strict"),
35056
- contentOnly: args.includes("--content-only")
35057
- }));
36020
+ const { check: check2, parseCheckArgs: parseCheckArgs2 } = await Promise.resolve().then(() => (init_check2(), exports_check));
36021
+ process.exit(await check2(parseCheckArgs2(args, process.cwd())));
35058
36022
  break;
35059
36023
  }
35060
36024
  case "init": {