@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/README.md +51 -1
- package/dist/index.js +1191 -227
- package/dist/types/check.d.ts +9 -0
- package/dist/types/check.d.ts.map +1 -1
- package/dist/types/live-schema.d.ts +20 -0
- package/dist/types/live-schema.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/check.ts +49 -3
- package/src/index.ts +3 -10
- package/src/live-schema.ts +0 -0
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
22293
|
-
|
|
22294
|
-
|
|
22295
|
-
|
|
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/
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
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
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
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
|
|
22514
|
-
const
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
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
|
-
|
|
22525
|
-
|
|
22526
|
-
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
const
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22533
|
-
const
|
|
22534
|
-
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
|
|
22538
|
-
|
|
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
|
|
22554
|
-
const files = new Map;
|
|
23257
|
+
const collections = await loadCollections(fs, config);
|
|
22555
23258
|
const stems = new Map;
|
|
22556
|
-
for (const [name,
|
|
22557
|
-
|
|
22558
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
22590
|
-
if (
|
|
22591
|
-
findings.push({ severity: "error", code: "entry/syntax", file, message: `Frontmatter does not parse: ${
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
24636
|
-
if (!
|
|
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(
|
|
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:
|
|
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 =
|
|
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:
|
|
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) =>
|
|
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
|
|
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
|
|
25668
|
+
for (const name of CONFIG_NAMES2) {
|
|
24700
25669
|
const p = join(cwd, name);
|
|
24701
|
-
if (
|
|
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
|
|
25767
|
+
var CONFIG_NAMES2;
|
|
24799
25768
|
var init_utils = __esm(() => {
|
|
24800
|
-
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 =
|
|
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 =
|
|
25318
|
-
const relPath =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
25645
|
-
if (!fullPath.startsWith(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 =
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
25667
26636
|
}
|
|
25668
26637
|
function relativeImportPath2(fromFile, toFile) {
|
|
25669
|
-
const fromDir =
|
|
25670
|
-
let rel =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
27352
|
+
import path10 from "path";
|
|
26384
27353
|
function isHugoStyleEntry(entryAbsPath) {
|
|
26385
|
-
return HUGO_INDEX_RE.test(
|
|
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 =
|
|
27360
|
+
const entryDir = path10.dirname(args.entryAbsPath);
|
|
26392
27361
|
const isHugoStyle = isHugoStyleEntry(args.entryAbsPath);
|
|
26393
|
-
const safeFilename =
|
|
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 =
|
|
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: `./${
|
|
27372
|
+
return { absPath: candidateAbs, relPath: `./${path10.basename(candidateAbs)}` };
|
|
26404
27373
|
}
|
|
26405
27374
|
async function pickSiblingTarget(dir, filename, buf) {
|
|
26406
|
-
const safe =
|
|
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 =
|
|
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 =
|
|
26418
|
-
const stem =
|
|
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 =
|
|
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
|
|
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(
|
|
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:
|
|
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
|
|
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 &&
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
32604
|
-
const targetAbs = await pickSiblingTarget(
|
|
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) =>
|
|
32615
|
-
const diskPath = newSrc.startsWith("/src/") ?
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
35133
|
+
return path15.join(this.outDir, "index.json");
|
|
34165
35134
|
}
|
|
34166
35135
|
const cleanPath = pagePath.replace(/^\//, "");
|
|
34167
|
-
return
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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:
|
|
35878
|
+
originalFilename: path16.basename(current),
|
|
34910
35879
|
compareBuffer: sourceBuf
|
|
34911
35880
|
});
|
|
34912
35881
|
if (!dryRun) {
|
|
34913
|
-
await fs11.mkdir(
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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: ${
|
|
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": {
|