@nuasite/cli 0.53.0 → 0.55.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 +1263 -228
- 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,504 @@ 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 isDeclaredDerived(field) {
|
|
7324
|
+
return field.derivedDeclared === true || field.layout?.derivedFrom !== undefined;
|
|
7325
|
+
}
|
|
7326
|
+
function blankRequiredFields(fields, frontmatter) {
|
|
7327
|
+
return fields.filter((field) => field.required && !(field.hidden ?? field.layout?.hidden) && !isDeclaredDerived(field) && isBlankFieldValue(frontmatter[field.name])).map((field) => field.name);
|
|
7328
|
+
}
|
|
7329
|
+
// ../cms-types/src/index.ts
|
|
7330
|
+
function isFieldType(value) {
|
|
7331
|
+
return FIELD_TYPES.includes(value);
|
|
7332
|
+
}
|
|
7333
|
+
function isDerivedTransform(value) {
|
|
7334
|
+
return DERIVED_TRANSFORMS.includes(value);
|
|
7335
|
+
}
|
|
7336
|
+
var FIELD_TYPES, DERIVED_TRANSFORMS;
|
|
7337
|
+
var init_src = __esm(() => {
|
|
7338
|
+
FIELD_TYPES = [
|
|
7339
|
+
"text",
|
|
7340
|
+
"textarea",
|
|
7341
|
+
"markdown",
|
|
7342
|
+
"date",
|
|
7343
|
+
"datetime",
|
|
7344
|
+
"time",
|
|
7345
|
+
"year",
|
|
7346
|
+
"month",
|
|
7347
|
+
"boolean",
|
|
7348
|
+
"number",
|
|
7349
|
+
"image",
|
|
7350
|
+
"file",
|
|
7351
|
+
"url",
|
|
7352
|
+
"email",
|
|
7353
|
+
"tel",
|
|
7354
|
+
"color",
|
|
7355
|
+
"select",
|
|
7356
|
+
"array",
|
|
7357
|
+
"object",
|
|
7358
|
+
"reference"
|
|
7359
|
+
];
|
|
7360
|
+
DERIVED_TRANSFORMS = ["slugifyHref", "slug", "copy"];
|
|
7361
|
+
});
|
|
7362
|
+
|
|
7363
|
+
// ../cms-core/src/editor-write-model.ts
|
|
7364
|
+
function newEntryFrontmatter(fields, today) {
|
|
7365
|
+
const frontmatter = {};
|
|
7366
|
+
for (const field of fields) {
|
|
7367
|
+
if (field.name === "title" || field.hidden)
|
|
7368
|
+
continue;
|
|
7369
|
+
frontmatter[field.name] = defaultValueForNewEntry(field, today);
|
|
7370
|
+
}
|
|
7371
|
+
return frontmatter;
|
|
7372
|
+
}
|
|
7373
|
+
function applyCreateRouteFields(frontmatter, kind, today = () => new Date) {
|
|
7374
|
+
const title = "Untitled";
|
|
7375
|
+
if (kind === "data")
|
|
7376
|
+
return { title, ...frontmatter };
|
|
7377
|
+
return { title, date: today().toISOString().split("T")[0], ...frontmatter };
|
|
7378
|
+
}
|
|
7379
|
+
function omitEmptyOnCreate(frontmatter) {
|
|
7380
|
+
const out = {};
|
|
7381
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
7382
|
+
if (value !== undefined && value !== "")
|
|
7383
|
+
out[key] = value;
|
|
7384
|
+
}
|
|
7385
|
+
return out;
|
|
7386
|
+
}
|
|
7387
|
+
var init_editor_write_model = __esm(() => {
|
|
7388
|
+
init_src();
|
|
7389
|
+
init_src();
|
|
7390
|
+
});
|
|
7391
|
+
|
|
7392
|
+
// ../cms-core/src/check-write.ts
|
|
7393
|
+
async function parseWrite(schema, value) {
|
|
7394
|
+
try {
|
|
7395
|
+
const result = await schema.safeParse(value);
|
|
7396
|
+
return { issues: result.success ? [] : result.issues };
|
|
7397
|
+
} catch (error) {
|
|
7398
|
+
return { threw: errorText(error) };
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
function valueAt(record, path2) {
|
|
7402
|
+
let current = record;
|
|
7403
|
+
for (const segment of path2) {
|
|
7404
|
+
if (Array.isArray(current)) {
|
|
7405
|
+
const index = typeof segment === "number" ? segment : Number(segment);
|
|
7406
|
+
if (!Number.isInteger(index) || index < 0 || index >= current.length)
|
|
7407
|
+
return { present: false };
|
|
7408
|
+
current = current[index];
|
|
7409
|
+
continue;
|
|
7410
|
+
}
|
|
7411
|
+
if (!isPlainObject(current) || !Object.hasOwn(current, String(segment)))
|
|
7412
|
+
return { present: false };
|
|
7413
|
+
current = current[String(segment)];
|
|
7414
|
+
}
|
|
7415
|
+
return current === undefined ? { present: false } : { present: true, value: current };
|
|
7416
|
+
}
|
|
7417
|
+
function hintFor(action, found) {
|
|
7418
|
+
if (action === "create") {
|
|
7419
|
+
if (!found.present) {
|
|
7420
|
+
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.";
|
|
7421
|
+
}
|
|
7422
|
+
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.`;
|
|
7423
|
+
}
|
|
7424
|
+
if (!found.present) {
|
|
7425
|
+
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.';
|
|
7426
|
+
}
|
|
7427
|
+
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.`;
|
|
7428
|
+
}
|
|
7429
|
+
function rejectionFinding(action, issue, kind, written) {
|
|
7430
|
+
const { field, message } = describeIssue(issue);
|
|
7431
|
+
const finding = {
|
|
7432
|
+
severity: "error",
|
|
7433
|
+
code: "cms/empty-write",
|
|
7434
|
+
file: CONFIG_FILE2,
|
|
7435
|
+
message: `${action} produces a write the schema rejects. ${message}`
|
|
7436
|
+
};
|
|
7437
|
+
if (field !== undefined)
|
|
7438
|
+
finding.field = field;
|
|
7439
|
+
finding.hint = hintFor(kind, valueAt(written, issue.path));
|
|
7440
|
+
return finding;
|
|
7441
|
+
}
|
|
7442
|
+
function blankItemsFor(field, current, today) {
|
|
7443
|
+
const itemFields = field.fields ?? [];
|
|
7444
|
+
if (itemFields.length > 0) {
|
|
7445
|
+
return [newRepeaterItem(itemFields.map((item) => ({ ...toWriteModelField(item), required: item.required })), today)];
|
|
7446
|
+
}
|
|
7447
|
+
if (current.length === 0)
|
|
7448
|
+
return [{}, { name: "" }];
|
|
7449
|
+
const first = current[0];
|
|
7450
|
+
const template = isPlainObject(first) ? Object.fromEntries(Object.keys(first).map((key) => [key, ""])) : {};
|
|
7451
|
+
return [{}, template];
|
|
7452
|
+
}
|
|
7453
|
+
async function checkEditorWrites(input) {
|
|
7454
|
+
const findings = [];
|
|
7455
|
+
const seen = new Set;
|
|
7456
|
+
const report = (key, finding) => {
|
|
7457
|
+
const identity = `${key} ${finding.field ?? ""}`;
|
|
7458
|
+
if (seen.has(identity))
|
|
7459
|
+
return;
|
|
7460
|
+
seen.add(identity);
|
|
7461
|
+
findings.push(finding);
|
|
7462
|
+
};
|
|
7463
|
+
for (const [name, collection] of input.config) {
|
|
7464
|
+
const schema = schemaFor(input.schemas, name);
|
|
7465
|
+
if (!schema)
|
|
7466
|
+
continue;
|
|
7467
|
+
const createKey = `${name} create`;
|
|
7468
|
+
if (collection.fields.length === 0) {
|
|
7469
|
+
report(createKey, {
|
|
7470
|
+
severity: "warning",
|
|
7471
|
+
code: "cms/empty-write-unchecked",
|
|
7472
|
+
file: CONFIG_FILE2,
|
|
7473
|
+
field: name,
|
|
7474
|
+
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.`,
|
|
7475
|
+
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."
|
|
7476
|
+
});
|
|
7477
|
+
continue;
|
|
7478
|
+
}
|
|
7479
|
+
const loaded = input.collections.get(name);
|
|
7480
|
+
const entries = loaded?.entries ?? [];
|
|
7481
|
+
const kind = loaded ? collectionKind(loaded, collection.loaderPattern) : "markdown";
|
|
7482
|
+
const created = applyCreateRouteFields(omitEmptyOnCreate(newEntryFrontmatter(collection.fields.map(toWriteModelField), input.today)), kind, input.today);
|
|
7483
|
+
const guarded = new Set(blankRequiredFields(collection.fields, created));
|
|
7484
|
+
const createAction = `Creating a new entry in "${name}"`;
|
|
7485
|
+
const createVerdict = await parseWrite(schema, created);
|
|
7486
|
+
if ("threw" in createVerdict) {
|
|
7487
|
+
report(createKey, throwFinding(createAction, createVerdict.threw));
|
|
7488
|
+
} else {
|
|
7489
|
+
for (const issue of createVerdict.issues) {
|
|
7490
|
+
const top = issue.path[0];
|
|
7491
|
+
if (typeof top === "string" && guarded.has(top))
|
|
7492
|
+
continue;
|
|
7493
|
+
report(createKey, rejectionFinding(createAction, issue, "create", created));
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
const repeaters = collection.fields.filter((field) => field.type === "array" && field.itemType === "object");
|
|
7497
|
+
if (repeaters.length === 0)
|
|
7498
|
+
continue;
|
|
7499
|
+
const seed = await firstAcceptedEntry(schema, entries);
|
|
7500
|
+
for (const field of repeaters) {
|
|
7501
|
+
const key = `${name} add:${field.name}`;
|
|
7502
|
+
if (!seed) {
|
|
7503
|
+
const hasEntries = entries.some((entry) => entry.frontmatter !== undefined);
|
|
7504
|
+
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";
|
|
7505
|
+
report(key, {
|
|
7506
|
+
severity: "warning",
|
|
7507
|
+
code: "cms/empty-write-unchecked",
|
|
7508
|
+
file: CONFIG_FILE2,
|
|
7509
|
+
field: field.name,
|
|
7510
|
+
message: `Could not check what "+ Add" on "${name}.${field.name}" writes: ${reason}.`,
|
|
7511
|
+
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.`
|
|
7512
|
+
});
|
|
7513
|
+
continue;
|
|
7514
|
+
}
|
|
7515
|
+
const current = seed[field.name];
|
|
7516
|
+
const existing = Array.isArray(current) ? current : [];
|
|
7517
|
+
const action = `Clicking "+ Add" on "${name}.${field.name}"`;
|
|
7518
|
+
for (const item of blankItemsFor(field, existing, input.today)) {
|
|
7519
|
+
const written = { ...seed, [field.name]: [...existing, item] };
|
|
7520
|
+
const verdict = await parseWrite(schema, written);
|
|
7521
|
+
if ("threw" in verdict) {
|
|
7522
|
+
report(key, throwFinding(action, verdict.threw));
|
|
7523
|
+
continue;
|
|
7524
|
+
}
|
|
7525
|
+
for (const issue of verdict.issues)
|
|
7526
|
+
report(key, rejectionFinding(action, issue, "add", written));
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
return findings;
|
|
7531
|
+
}
|
|
7532
|
+
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) => ({
|
|
7533
|
+
severity: "error",
|
|
7534
|
+
code: "cms/empty-write",
|
|
7535
|
+
file: CONFIG_FILE2,
|
|
7536
|
+
message: `${action} produces a write the schema throws on: ${threw}`,
|
|
7537
|
+
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."
|
|
7538
|
+
});
|
|
7539
|
+
var init_check_write = __esm(() => {
|
|
7540
|
+
init_check_entries();
|
|
7541
|
+
init_editor_write_model();
|
|
7542
|
+
init_schema_probe();
|
|
7543
|
+
});
|
|
7544
|
+
|
|
7014
7545
|
// ../../node_modules/@babel/parser/lib/index.js
|
|
7015
7546
|
var require_lib = __commonJS((exports) => {
|
|
7016
7547
|
Object.defineProperty(exports, "__esModule", {
|
|
@@ -21794,40 +22325,16 @@ var require_lib = __commonJS((exports) => {
|
|
|
21794
22325
|
exports.tokTypes = tokTypes;
|
|
21795
22326
|
});
|
|
21796
22327
|
|
|
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
22328
|
// ../cms-core/src/content-config-ast.ts
|
|
21828
22329
|
function resolveExpression(node, bindings, visited = new Set) {
|
|
21829
22330
|
let current = node;
|
|
21830
|
-
|
|
22331
|
+
for (;; ) {
|
|
22332
|
+
if (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSNonNullExpression" || current.type === "TSTypeAssertion") {
|
|
22333
|
+
current = current.expression;
|
|
22334
|
+
continue;
|
|
22335
|
+
}
|
|
22336
|
+
if (current.type !== "Identifier")
|
|
22337
|
+
return current;
|
|
21831
22338
|
if (visited.has(current.name))
|
|
21832
22339
|
return current;
|
|
21833
22340
|
visited.add(current.name);
|
|
@@ -21836,7 +22343,80 @@ function resolveExpression(node, bindings, visited = new Set) {
|
|
|
21836
22343
|
return current;
|
|
21837
22344
|
current = next;
|
|
21838
22345
|
}
|
|
21839
|
-
|
|
22346
|
+
}
|
|
22347
|
+
function isAstNode(value) {
|
|
22348
|
+
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
22349
|
+
}
|
|
22350
|
+
function walkNodes(node, visit) {
|
|
22351
|
+
visit(node);
|
|
22352
|
+
const children = Object.values(node);
|
|
22353
|
+
for (const child of children) {
|
|
22354
|
+
if (Array.isArray(child)) {
|
|
22355
|
+
for (const item of child) {
|
|
22356
|
+
if (isAstNode(item))
|
|
22357
|
+
walkNodes(item, visit);
|
|
22358
|
+
}
|
|
22359
|
+
} else if (isAstNode(child)) {
|
|
22360
|
+
walkNodes(child, visit);
|
|
22361
|
+
}
|
|
22362
|
+
}
|
|
22363
|
+
}
|
|
22364
|
+
function declaredPatternNames(node, record) {
|
|
22365
|
+
switch (node.type) {
|
|
22366
|
+
case "Identifier":
|
|
22367
|
+
record(node.name);
|
|
22368
|
+
return;
|
|
22369
|
+
case "ObjectPattern":
|
|
22370
|
+
for (const prop of node.properties)
|
|
22371
|
+
declaredPatternNames(prop.type === "RestElement" ? prop.argument : prop.value, record);
|
|
22372
|
+
return;
|
|
22373
|
+
case "ArrayPattern":
|
|
22374
|
+
for (const el of node.elements) {
|
|
22375
|
+
if (el)
|
|
22376
|
+
declaredPatternNames(el, record);
|
|
22377
|
+
}
|
|
22378
|
+
return;
|
|
22379
|
+
case "AssignmentPattern":
|
|
22380
|
+
declaredPatternNames(node.left, record);
|
|
22381
|
+
return;
|
|
22382
|
+
case "RestElement":
|
|
22383
|
+
declaredPatternNames(node.argument, record);
|
|
22384
|
+
return;
|
|
22385
|
+
}
|
|
22386
|
+
}
|
|
22387
|
+
function shadowedNames(ast) {
|
|
22388
|
+
const seen = new Set;
|
|
22389
|
+
const shadowed = new Set;
|
|
22390
|
+
const record = (name) => {
|
|
22391
|
+
if (seen.has(name))
|
|
22392
|
+
shadowed.add(name);
|
|
22393
|
+
seen.add(name);
|
|
22394
|
+
};
|
|
22395
|
+
walkNodes(ast, (node) => {
|
|
22396
|
+
switch (node.type) {
|
|
22397
|
+
case "VariableDeclarator":
|
|
22398
|
+
declaredPatternNames(node.id, record);
|
|
22399
|
+
return;
|
|
22400
|
+
case "FunctionDeclaration":
|
|
22401
|
+
case "FunctionExpression":
|
|
22402
|
+
case "ArrowFunctionExpression":
|
|
22403
|
+
for (const param of node.params)
|
|
22404
|
+
declaredPatternNames(param, record);
|
|
22405
|
+
if (node.type !== "ArrowFunctionExpression" && node.id)
|
|
22406
|
+
record(node.id.name);
|
|
22407
|
+
return;
|
|
22408
|
+
case "ClassDeclaration":
|
|
22409
|
+
if (node.id)
|
|
22410
|
+
record(node.id.name);
|
|
22411
|
+
return;
|
|
22412
|
+
case "ImportSpecifier":
|
|
22413
|
+
case "ImportDefaultSpecifier":
|
|
22414
|
+
case "ImportNamespaceSpecifier":
|
|
22415
|
+
record(node.local.name);
|
|
22416
|
+
return;
|
|
22417
|
+
}
|
|
22418
|
+
});
|
|
22419
|
+
return shadowed;
|
|
21840
22420
|
}
|
|
21841
22421
|
function parseSource(source) {
|
|
21842
22422
|
try {
|
|
@@ -21916,6 +22496,8 @@ function parseConfigSource(source, _sourcePath) {
|
|
|
21916
22496
|
}
|
|
21917
22497
|
}
|
|
21918
22498
|
}
|
|
22499
|
+
for (const name of shadowedNames(ast))
|
|
22500
|
+
bindings.delete(name);
|
|
21919
22501
|
const collectionObjects = new Map(inlineCollections);
|
|
21920
22502
|
for (const [varName, collectionName] of exportMap) {
|
|
21921
22503
|
const decl = collectionDecls.get(varName);
|
|
@@ -21971,9 +22553,12 @@ function parseConfigSource(source, _sourcePath) {
|
|
|
21971
22553
|
});
|
|
21972
22554
|
continue;
|
|
21973
22555
|
}
|
|
22556
|
+
const fields = parseSchemaFields(schemaObject, bindings);
|
|
22557
|
+
checkDerivedFromDeclarations(collectionName, fields);
|
|
21974
22558
|
result.set(collectionName, {
|
|
21975
22559
|
name: collectionName,
|
|
21976
|
-
fields
|
|
22560
|
+
fields,
|
|
22561
|
+
...hasSkippedMembers(schemaObject) ? { partialFields: true } : {},
|
|
21977
22562
|
loaderPattern,
|
|
21978
22563
|
loaderBase,
|
|
21979
22564
|
layout,
|
|
@@ -22032,6 +22617,15 @@ function parseLayoutSection(node) {
|
|
|
22032
22617
|
return null;
|
|
22033
22618
|
return collapsed ? { title, fields, collapsed } : { title, fields };
|
|
22034
22619
|
}
|
|
22620
|
+
function readEnumOptions(node) {
|
|
22621
|
+
const options = [];
|
|
22622
|
+
for (const el of node.elements) {
|
|
22623
|
+
if (el?.type !== "StringLiteral")
|
|
22624
|
+
return null;
|
|
22625
|
+
options.push(el.value);
|
|
22626
|
+
}
|
|
22627
|
+
return options.length > 0 ? options : null;
|
|
22628
|
+
}
|
|
22035
22629
|
function stringArray(node) {
|
|
22036
22630
|
const out = [];
|
|
22037
22631
|
for (const el of node.elements) {
|
|
@@ -22218,7 +22812,10 @@ function parseSchemaFields(schemaObject, bindings) {
|
|
|
22218
22812
|
}
|
|
22219
22813
|
return fields;
|
|
22220
22814
|
}
|
|
22221
|
-
function
|
|
22815
|
+
function hasSkippedMembers(schemaObject) {
|
|
22816
|
+
return schemaObject.properties.some((prop) => prop.type !== "ObjectProperty" || !propertyKeyName(prop.key));
|
|
22817
|
+
}
|
|
22818
|
+
function analyzeFieldExpression(node, field, bindings, helpers = new Set) {
|
|
22222
22819
|
let current = resolveExpression(node, bindings);
|
|
22223
22820
|
while (current) {
|
|
22224
22821
|
if (current.type !== "CallExpression")
|
|
@@ -22227,10 +22824,30 @@ function analyzeFieldExpression(node, field, bindings) {
|
|
|
22227
22824
|
analyzeBaseCall(current, field, bindings);
|
|
22228
22825
|
return;
|
|
22229
22826
|
}
|
|
22827
|
+
if (current.callee.type === "Identifier") {
|
|
22828
|
+
const returned = sameFileHelperReturn(current, bindings, helpers);
|
|
22829
|
+
if (returned)
|
|
22830
|
+
analyzeFieldExpression(returned, field, bindings, helpers);
|
|
22831
|
+
return;
|
|
22832
|
+
}
|
|
22230
22833
|
if (current.callee.type !== "MemberExpression")
|
|
22231
22834
|
return;
|
|
22232
22835
|
const property = current.callee.property;
|
|
22233
22836
|
const methodName = property.type === "Identifier" ? property.name : "";
|
|
22837
|
+
if (methodName === "array") {
|
|
22838
|
+
const item = { name: "__item__", required: true };
|
|
22839
|
+
analyzeFieldExpression(current.callee.object, item, bindings, helpers);
|
|
22840
|
+
if (item.reference) {
|
|
22841
|
+
field.reference = { target: item.reference.target, isArray: true };
|
|
22842
|
+
return;
|
|
22843
|
+
}
|
|
22844
|
+
field.type = "array";
|
|
22845
|
+
if (item.type)
|
|
22846
|
+
field.itemType = item.type;
|
|
22847
|
+
if (item.fields)
|
|
22848
|
+
field.fields = item.fields;
|
|
22849
|
+
return;
|
|
22850
|
+
}
|
|
22234
22851
|
if (WRAPPER_METHODS.has(methodName)) {
|
|
22235
22852
|
field.required = false;
|
|
22236
22853
|
} else if (methodName === "orderBy") {
|
|
@@ -22241,6 +22858,25 @@ function analyzeFieldExpression(node, field, bindings) {
|
|
|
22241
22858
|
current = resolveExpression(current.callee.object, bindings);
|
|
22242
22859
|
}
|
|
22243
22860
|
}
|
|
22861
|
+
function sameFileHelperReturn(call, bindings, seen) {
|
|
22862
|
+
if (call.callee.type !== "Identifier" || call.arguments.length > 0)
|
|
22863
|
+
return null;
|
|
22864
|
+
if (seen.has(call.callee.name))
|
|
22865
|
+
return null;
|
|
22866
|
+
seen.add(call.callee.name);
|
|
22867
|
+
const fn = resolveExpression(call.callee, bindings);
|
|
22868
|
+
if (fn.type !== "ArrowFunctionExpression" && fn.type !== "FunctionExpression")
|
|
22869
|
+
return null;
|
|
22870
|
+
if (fn.params.length > 0)
|
|
22871
|
+
return null;
|
|
22872
|
+
if (fn.body.type !== "BlockStatement")
|
|
22873
|
+
return fn.body;
|
|
22874
|
+
for (const stmt of fn.body.body) {
|
|
22875
|
+
if (stmt.type === "ReturnStatement" && stmt.argument)
|
|
22876
|
+
return stmt.argument;
|
|
22877
|
+
}
|
|
22878
|
+
return null;
|
|
22879
|
+
}
|
|
22244
22880
|
function isBaseCall(node) {
|
|
22245
22881
|
const callee = node.callee;
|
|
22246
22882
|
if (callee.type === "Identifier") {
|
|
@@ -22287,18 +22923,16 @@ function analyzeBaseCall(node, field, bindings) {
|
|
|
22287
22923
|
}
|
|
22288
22924
|
return;
|
|
22289
22925
|
}
|
|
22926
|
+
if ((ns === "z" || ns === "n") && fn === "boolean") {
|
|
22927
|
+
field.type = "boolean";
|
|
22928
|
+
return;
|
|
22929
|
+
}
|
|
22290
22930
|
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
|
-
}
|
|
22931
|
+
const arg = node.arguments[0] ? resolveExpression(node.arguments[0], bindings) : undefined;
|
|
22932
|
+
const options = arg?.type === "ArrayExpression" ? readEnumOptions(arg) : null;
|
|
22933
|
+
if (options) {
|
|
22934
|
+
field.type = "select";
|
|
22935
|
+
field.options = options;
|
|
22302
22936
|
}
|
|
22303
22937
|
const hintsArg = node.arguments[1];
|
|
22304
22938
|
if (hintsArg?.type === "ObjectExpression") {
|
|
@@ -22421,10 +23055,64 @@ function parseFieldLayoutFromObject(obj) {
|
|
|
22421
23055
|
if (value.type === "BooleanLiteral")
|
|
22422
23056
|
layout.hidden = value.value;
|
|
22423
23057
|
break;
|
|
23058
|
+
case "derivedFrom":
|
|
23059
|
+
parseDerivedFromValue(value, layout);
|
|
23060
|
+
break;
|
|
22424
23061
|
}
|
|
22425
23062
|
}
|
|
22426
23063
|
return Object.keys(layout).length > 0 ? layout : undefined;
|
|
22427
23064
|
}
|
|
23065
|
+
function parseDerivedFromValue(value, layout) {
|
|
23066
|
+
if (value.type === "StringLiteral") {
|
|
23067
|
+
if (value.value)
|
|
23068
|
+
layout.derivedFrom = value.value;
|
|
23069
|
+
return;
|
|
23070
|
+
}
|
|
23071
|
+
if (value.type !== "ObjectExpression")
|
|
23072
|
+
return;
|
|
23073
|
+
let field;
|
|
23074
|
+
let transform;
|
|
23075
|
+
for (const prop of value.properties) {
|
|
23076
|
+
if (prop.type !== "ObjectProperty")
|
|
23077
|
+
continue;
|
|
23078
|
+
const key = propertyKeyName(prop.key);
|
|
23079
|
+
if (prop.value.type !== "StringLiteral")
|
|
23080
|
+
continue;
|
|
23081
|
+
if (key === "field")
|
|
23082
|
+
field = prop.value.value;
|
|
23083
|
+
else if (key === "transform" && isDerivedTransform(prop.value.value))
|
|
23084
|
+
transform = prop.value.value;
|
|
23085
|
+
}
|
|
23086
|
+
if (!field)
|
|
23087
|
+
return;
|
|
23088
|
+
layout.derivedFrom = field;
|
|
23089
|
+
if (transform)
|
|
23090
|
+
layout.derivedTransform = transform;
|
|
23091
|
+
}
|
|
23092
|
+
function checkDerivedFromDeclarations(collectionName, fields) {
|
|
23093
|
+
const siblingNames = new Set(fields.map((f) => f.name));
|
|
23094
|
+
for (const field of fields) {
|
|
23095
|
+
const source = field.layout?.derivedFrom;
|
|
23096
|
+
if (source !== undefined && !siblingNames.has(source)) {
|
|
23097
|
+
console.warn(`[cms] collection "${collectionName}": field "${field.name}" derives from "${source}", which is not a field of this collection` + ` \u2014 the field is hidden and nothing is ever computed for it`);
|
|
23098
|
+
}
|
|
23099
|
+
dropNestedDerivedFrom(collectionName, field.name, field.fields);
|
|
23100
|
+
}
|
|
23101
|
+
}
|
|
23102
|
+
function dropNestedDerivedFrom(collectionName, path2, fields) {
|
|
23103
|
+
if (!fields)
|
|
23104
|
+
return;
|
|
23105
|
+
for (const field of fields) {
|
|
23106
|
+
const fieldPath = `${path2}.${field.name}`;
|
|
23107
|
+
const layout = field.layout;
|
|
23108
|
+
if (layout?.derivedFrom !== undefined) {
|
|
23109
|
+
console.warn(`[cms] collection "${collectionName}": field "${fieldPath}" declares \`derivedFrom\` on a nested field, which is not supported` + ` \u2014 only top-level fields derive, so the declaration is ignored and the field stays editable`);
|
|
23110
|
+
delete layout.derivedFrom;
|
|
23111
|
+
delete layout.derivedTransform;
|
|
23112
|
+
}
|
|
23113
|
+
dropNestedDerivedFrom(collectionName, fieldPath, field.fields);
|
|
23114
|
+
}
|
|
23115
|
+
}
|
|
22428
23116
|
var import_parser, FIELD_HELPER_TYPES, VALID_HINT_KEYS, WRAPPER_METHODS;
|
|
22429
23117
|
var init_content_config_ast = __esm(() => {
|
|
22430
23118
|
init_src();
|
|
@@ -22459,11 +23147,44 @@ var init_content_config_ast = __esm(() => {
|
|
|
22459
23147
|
WRAPPER_METHODS = new Set(["optional", "nullable", "nullish", "default"]);
|
|
22460
23148
|
});
|
|
22461
23149
|
|
|
22462
|
-
// ../cms-core/src/
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
23150
|
+
// ../cms-core/src/shared.ts
|
|
23151
|
+
function pathSegmentValue(raw) {
|
|
23152
|
+
if (typeof raw === "string")
|
|
23153
|
+
return raw;
|
|
23154
|
+
if (typeof raw === "number" || typeof raw === "boolean")
|
|
23155
|
+
return String(raw);
|
|
23156
|
+
if (raw instanceof Date)
|
|
23157
|
+
return raw.toISOString().slice(0, 10);
|
|
23158
|
+
return;
|
|
23159
|
+
}
|
|
23160
|
+
function computePathnameFromSpec(spec, data) {
|
|
23161
|
+
const segments = [];
|
|
23162
|
+
for (const seg of spec) {
|
|
23163
|
+
if ("literal" in seg) {
|
|
23164
|
+
segments.push(seg.literal);
|
|
23165
|
+
continue;
|
|
23166
|
+
}
|
|
23167
|
+
const value = pathSegmentValue(data[seg.field]);
|
|
23168
|
+
if (value === undefined)
|
|
23169
|
+
return;
|
|
23170
|
+
segments.push(seg.map && Object.hasOwn(seg.map, value) ? seg.map[value] : value);
|
|
23171
|
+
}
|
|
23172
|
+
const path2 = ("/" + segments.join("/")).replace(/\/{2,}/g, "/").replace(/\/+$/, "");
|
|
23173
|
+
return path2 === "" ? "/" : path2;
|
|
23174
|
+
}
|
|
23175
|
+
function resolvePathnameFromSpec(def, data) {
|
|
23176
|
+
if (!def.pathname)
|
|
23177
|
+
return;
|
|
23178
|
+
return computePathnameFromSpec(def.pathname, data ?? {});
|
|
22466
23179
|
}
|
|
23180
|
+
function slugifyHref(text) {
|
|
23181
|
+
return "/" + text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23182
|
+
}
|
|
23183
|
+
var init_shared = __esm(() => {
|
|
23184
|
+
init_src();
|
|
23185
|
+
});
|
|
23186
|
+
|
|
23187
|
+
// ../cms-core/src/check.ts
|
|
22467
23188
|
function typeMismatch(field, value) {
|
|
22468
23189
|
switch (field.type) {
|
|
22469
23190
|
case "number":
|
|
@@ -22505,40 +23226,86 @@ function typeMismatch(field, value) {
|
|
|
22505
23226
|
return null;
|
|
22506
23227
|
}
|
|
22507
23228
|
}
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
23229
|
+
function projectAssetPath(value) {
|
|
23230
|
+
if (value === "" || value.startsWith("//"))
|
|
23231
|
+
return null;
|
|
23232
|
+
if (/^[a-z][a-z\d+\-.]*:/i.test(value))
|
|
23233
|
+
return null;
|
|
23234
|
+
if (/^[~@#]/.test(value))
|
|
23235
|
+
return null;
|
|
23236
|
+
const withoutQuery = value.split(/[?#]/)[0] ?? "";
|
|
23237
|
+
return withoutQuery === "" ? null : withoutQuery;
|
|
22512
23238
|
}
|
|
22513
|
-
function
|
|
22514
|
-
const
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
23239
|
+
async function checkAssets(fs, fields, data, file, prefix, findings) {
|
|
23240
|
+
for (const field of fields) {
|
|
23241
|
+
const value = data[field.name];
|
|
23242
|
+
if (value === undefined || value === null)
|
|
23243
|
+
continue;
|
|
23244
|
+
const label = prefix === "" ? field.name : `${prefix}.${field.name}`;
|
|
23245
|
+
if (field.type === "object" && field.fields && isPlainObject(value)) {
|
|
23246
|
+
await checkAssets(fs, field.fields, value, file, label, findings);
|
|
23247
|
+
continue;
|
|
23248
|
+
}
|
|
23249
|
+
if (field.type === "array" && field.itemType === "object" && field.fields && Array.isArray(value)) {
|
|
23250
|
+
for (const [index, item] of value.entries()) {
|
|
23251
|
+
if (isPlainObject(item))
|
|
23252
|
+
await checkAssets(fs, field.fields, item, file, `${label}[${index}]`, findings);
|
|
23253
|
+
}
|
|
23254
|
+
continue;
|
|
23255
|
+
}
|
|
23256
|
+
if (field.type !== "image" || typeof value !== "string")
|
|
23257
|
+
continue;
|
|
23258
|
+
const assetPath = projectAssetPath(value);
|
|
23259
|
+
if (assetPath === null)
|
|
23260
|
+
continue;
|
|
23261
|
+
const candidates = resolveAssetCandidates(assetBaseDir(file), assetPath);
|
|
23262
|
+
if (candidates.length === 0)
|
|
23263
|
+
continue;
|
|
23264
|
+
let found = false;
|
|
23265
|
+
for (const candidate of candidates) {
|
|
23266
|
+
if (await fs.exists(candidate)) {
|
|
23267
|
+
found = true;
|
|
23268
|
+
break;
|
|
23269
|
+
}
|
|
22522
23270
|
}
|
|
23271
|
+
if (found)
|
|
23272
|
+
continue;
|
|
23273
|
+
findings.push({
|
|
23274
|
+
severity: "warning",
|
|
23275
|
+
code: "entry/missing-asset",
|
|
23276
|
+
file,
|
|
23277
|
+
field: label,
|
|
23278
|
+
message: `${label}: "${value}" points at no file (tried ${candidates.join(", ")}).`
|
|
23279
|
+
});
|
|
22523
23280
|
}
|
|
22524
|
-
|
|
22525
|
-
|
|
22526
|
-
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
const
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22533
|
-
const
|
|
22534
|
-
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
|
|
22538
|
-
|
|
23281
|
+
}
|
|
23282
|
+
function pathnameCollisions(collection, entries) {
|
|
23283
|
+
if (!collection.pathname)
|
|
23284
|
+
return [];
|
|
23285
|
+
const findings = [];
|
|
23286
|
+
const firstAt = new Map;
|
|
23287
|
+
for (const entry of entries) {
|
|
23288
|
+
if (!entry.frontmatter)
|
|
23289
|
+
continue;
|
|
23290
|
+
const pathname = computePathnameFromSpec(collection.pathname, entry.frontmatter);
|
|
23291
|
+
if (pathname === undefined)
|
|
23292
|
+
continue;
|
|
23293
|
+
const first = firstAt.get(pathname);
|
|
23294
|
+
if (!first) {
|
|
23295
|
+
firstAt.set(pathname, entry);
|
|
23296
|
+
continue;
|
|
23297
|
+
}
|
|
23298
|
+
findings.push({
|
|
23299
|
+
severity: "warning",
|
|
23300
|
+
code: "entry/pathname-collision",
|
|
23301
|
+
file: entry.file,
|
|
23302
|
+
field: collection.name,
|
|
23303
|
+
message: `The pathname rule maps this entry and ${first.file} to ${pathname} \u2014 they collide on one page.`
|
|
23304
|
+
});
|
|
22539
23305
|
}
|
|
23306
|
+
return findings;
|
|
22540
23307
|
}
|
|
22541
|
-
async function checkContent(fs) {
|
|
23308
|
+
async function checkContent(fs, options = {}) {
|
|
22542
23309
|
const findings = [];
|
|
22543
23310
|
const config = await parseContentConfig(fs, new Map);
|
|
22544
23311
|
if (config.size === 0) {
|
|
@@ -22550,51 +23317,47 @@ async function checkContent(fs) {
|
|
|
22550
23317
|
});
|
|
22551
23318
|
return { findings, collections: 0, entries: 0 };
|
|
22552
23319
|
}
|
|
22553
|
-
const
|
|
22554
|
-
const files = new Map;
|
|
23320
|
+
const collections = await loadCollections(fs, config);
|
|
22555
23321
|
const stems = new Map;
|
|
22556
|
-
for (const [name,
|
|
22557
|
-
|
|
22558
|
-
|
|
22559
|
-
if (!await fs.exists(base)) {
|
|
23322
|
+
for (const [name, loaded] of collections) {
|
|
23323
|
+
stems.set(name, new Set(loaded.entries.map((entry) => entry.stem)));
|
|
23324
|
+
if (!loaded.exists) {
|
|
22560
23325
|
findings.push({
|
|
22561
23326
|
severity: "error",
|
|
22562
23327
|
code: "config/missing-dir",
|
|
22563
23328
|
file: "src/content.config.ts",
|
|
22564
23329
|
field: name,
|
|
22565
|
-
message: `Collection "${name}" loads from ${base}/, which does not exist.`
|
|
23330
|
+
message: `Collection "${name}" loads from ${loaded.base}/, which does not exist.`
|
|
22566
23331
|
});
|
|
22567
|
-
files.set(name, []);
|
|
22568
|
-
stems.set(name, new Set);
|
|
22569
23332
|
continue;
|
|
22570
23333
|
}
|
|
22571
|
-
|
|
22572
|
-
files.set(name, collectionFiles);
|
|
22573
|
-
stems.set(name, new Set(collectionFiles.map(entryStem)));
|
|
22574
|
-
if (collectionFiles.length === 0) {
|
|
23334
|
+
if (loaded.entries.length === 0) {
|
|
22575
23335
|
findings.push({
|
|
22576
23336
|
severity: "warning",
|
|
22577
23337
|
code: "config/empty-collection",
|
|
22578
|
-
file: base,
|
|
23338
|
+
file: loaded.base,
|
|
22579
23339
|
field: name,
|
|
22580
|
-
message: `Collection "${name}" matched no entries under ${base}/.`
|
|
23340
|
+
message: `Collection "${name}" matched no entries under ${loaded.base}/.`
|
|
22581
23341
|
});
|
|
22582
23342
|
}
|
|
22583
23343
|
}
|
|
22584
23344
|
let entries = 0;
|
|
22585
23345
|
for (const [name, collection] of config) {
|
|
22586
23346
|
const typedFields = collection.fields.filter((field) => field.type !== undefined || field.required || field.reference);
|
|
22587
|
-
|
|
23347
|
+
const declared = new Set(collection.fields.map((field) => field.name));
|
|
23348
|
+
const loadedEntries = collections.get(name)?.entries ?? [];
|
|
23349
|
+
const schemaJudgesEntries = options.schemas !== undefined && schemaFor(options.schemas, name) !== undefined;
|
|
23350
|
+
for (const entry of loadedEntries) {
|
|
22588
23351
|
entries++;
|
|
22589
|
-
const
|
|
22590
|
-
if (
|
|
22591
|
-
findings.push({ severity: "error", code: "entry/syntax", file, message: `Frontmatter does not parse: ${
|
|
23352
|
+
const file = entry.file;
|
|
23353
|
+
if (entry.frontmatter === undefined) {
|
|
23354
|
+
findings.push({ severity: "error", code: "entry/syntax", file, message: `Frontmatter does not parse: ${entry.parseError}` });
|
|
22592
23355
|
continue;
|
|
22593
23356
|
}
|
|
22594
23357
|
for (const field of typedFields) {
|
|
22595
|
-
const value =
|
|
23358
|
+
const value = entry.frontmatter[field.name];
|
|
22596
23359
|
if (value === undefined || value === null) {
|
|
22597
|
-
if (field.required) {
|
|
23360
|
+
if (field.required && !schemaJudgesEntries) {
|
|
22598
23361
|
findings.push({
|
|
22599
23362
|
severity: "error",
|
|
22600
23363
|
code: "entry/missing-required",
|
|
@@ -22605,7 +23368,7 @@ async function checkContent(fs) {
|
|
|
22605
23368
|
}
|
|
22606
23369
|
continue;
|
|
22607
23370
|
}
|
|
22608
|
-
const mismatch = field.type === undefined ? null : typeMismatch(field, value);
|
|
23371
|
+
const mismatch = field.type === undefined || schemaJudgesEntries ? null : typeMismatch(field, value);
|
|
22609
23372
|
if (mismatch) {
|
|
22610
23373
|
findings.push({ severity: "error", code: "entry/field-type", file, field: field.name, message: `${field.name}: ${mismatch}` });
|
|
22611
23374
|
continue;
|
|
@@ -22629,7 +23392,26 @@ async function checkContent(fs) {
|
|
|
22629
23392
|
});
|
|
22630
23393
|
}
|
|
22631
23394
|
}
|
|
23395
|
+
await checkAssets(fs, collection.fields, entry.frontmatter, file, "", findings);
|
|
23396
|
+
if (declared.size > 0 && !collection.partialFields) {
|
|
23397
|
+
for (const key of Object.keys(entry.frontmatter)) {
|
|
23398
|
+
if (declared.has(key))
|
|
23399
|
+
continue;
|
|
23400
|
+
findings.push({
|
|
23401
|
+
severity: "warning",
|
|
23402
|
+
code: "entry/unknown-key",
|
|
23403
|
+
file,
|
|
23404
|
+
field: key,
|
|
23405
|
+
message: `"${key}" is not a field of "${name}" \u2014 nothing reads it.`
|
|
23406
|
+
});
|
|
23407
|
+
}
|
|
23408
|
+
}
|
|
22632
23409
|
}
|
|
23410
|
+
findings.push(...pathnameCollisions(collection, loadedEntries));
|
|
23411
|
+
}
|
|
23412
|
+
if (options.schemas) {
|
|
23413
|
+
const input = { config, collections, schemas: options.schemas };
|
|
23414
|
+
findings.push(...await checkAgainstSchemas(input), ...await checkEditorWrites(input), ...await checkFieldShapes(input));
|
|
22633
23415
|
}
|
|
22634
23416
|
return { findings, collections: config.size, entries };
|
|
22635
23417
|
}
|
|
@@ -22647,15 +23429,19 @@ function formatCheckReport(report) {
|
|
|
22647
23429
|
const lines = [];
|
|
22648
23430
|
for (const [file, findings] of byFile) {
|
|
22649
23431
|
lines.push(file);
|
|
23432
|
+
let lastHint;
|
|
22650
23433
|
for (const finding of findings) {
|
|
22651
23434
|
const at = finding.line === undefined ? "" : `${finding.line}:${finding.column ?? 0}`;
|
|
22652
23435
|
lines.push(` ${at.padEnd(8)}${(finding.severity === "error" ? "error" : "warn").padEnd(6)} ${finding.message} [${finding.code}]`);
|
|
23436
|
+
if (finding.hint !== undefined && finding.hint !== lastHint)
|
|
23437
|
+
lines.push(` ${" ".repeat(14)}\u2192 ${finding.hint}`);
|
|
23438
|
+
lastHint = finding.hint;
|
|
22653
23439
|
}
|
|
22654
23440
|
}
|
|
22655
23441
|
return lines.join(`
|
|
22656
23442
|
`);
|
|
22657
23443
|
}
|
|
22658
|
-
var
|
|
23444
|
+
var typeName = (value) => {
|
|
22659
23445
|
if (value === null)
|
|
22660
23446
|
return "null";
|
|
22661
23447
|
if (Array.isArray(value))
|
|
@@ -22665,45 +23451,14 @@ var import_yaml, DATA_EXTENSIONS, normalizeBase = (base) => path.normalize(base)
|
|
|
22665
23451
|
return typeof value;
|
|
22666
23452
|
};
|
|
22667
23453
|
var init_check = __esm(() => {
|
|
23454
|
+
init_check_entries();
|
|
23455
|
+
init_check_live();
|
|
23456
|
+
init_check_shape();
|
|
23457
|
+
init_check_write();
|
|
22668
23458
|
init_content_config_ast();
|
|
22669
|
-
|
|
22670
|
-
DATA_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
|
|
23459
|
+
init_shared();
|
|
22671
23460
|
});
|
|
22672
23461
|
|
|
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
23462
|
// ../cms-core/src/collection-scanner.ts
|
|
22708
23463
|
import path2 from "path";
|
|
22709
23464
|
function normalizeFieldName(name) {
|
|
@@ -23116,6 +23871,14 @@ function applyParsedFieldLayout(field, pf) {
|
|
|
23116
23871
|
field.order = layout.order;
|
|
23117
23872
|
if (layout.sidebar)
|
|
23118
23873
|
field.position = "sidebar";
|
|
23874
|
+
if (layout.derivedFrom) {
|
|
23875
|
+
field.derivedFrom = layout.derivedFrom;
|
|
23876
|
+
field.derivedDeclared = true;
|
|
23877
|
+
if (layout.derivedTransform)
|
|
23878
|
+
field.derivedTransform = layout.derivedTransform;
|
|
23879
|
+
field.hidden = layout.hidden ?? true;
|
|
23880
|
+
return;
|
|
23881
|
+
}
|
|
23119
23882
|
if (layout.hidden)
|
|
23120
23883
|
field.hidden = true;
|
|
23121
23884
|
}
|
|
@@ -23496,6 +24259,7 @@ function warnOnPathnameCollisions(collections) {
|
|
|
23496
24259
|
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
24260
|
var init_collection_scanner = __esm(() => {
|
|
23498
24261
|
init_content_config_ast();
|
|
24262
|
+
init_shared();
|
|
23499
24263
|
import_yaml2 = __toESM(require_dist(), 1);
|
|
23500
24264
|
DATE_PATTERN = /^\d{4}-\d{2}-\d{2}/;
|
|
23501
24265
|
URL_PATTERN = /^(https?:\/\/|\/)/;
|
|
@@ -23575,12 +24339,16 @@ var import_yaml3;
|
|
|
23575
24339
|
var init_entry_ops = __esm(() => {
|
|
23576
24340
|
init_collection_scanner();
|
|
23577
24341
|
init_content_config_ast();
|
|
24342
|
+
init_editor_write_model();
|
|
23578
24343
|
init_local();
|
|
24344
|
+
init_shared();
|
|
23579
24345
|
import_yaml3 = __toESM(require_dist(), 1);
|
|
23580
24346
|
});
|
|
23581
24347
|
|
|
23582
24348
|
// ../cms-core/src/handlers/page-ops.ts
|
|
23583
|
-
var init_page_ops = () => {
|
|
24349
|
+
var init_page_ops = __esm(() => {
|
|
24350
|
+
init_shared();
|
|
24351
|
+
});
|
|
23584
24352
|
// ../cms-core/src/core.ts
|
|
23585
24353
|
var init_core = __esm(() => {
|
|
23586
24354
|
init_collection_scanner();
|
|
@@ -23817,14 +24585,21 @@ var init_project_config_ast = __esm(() => {
|
|
|
23817
24585
|
// ../cms-core/src/index.ts
|
|
23818
24586
|
var init_src2 = __esm(() => {
|
|
23819
24587
|
init_check();
|
|
24588
|
+
init_check_entries();
|
|
24589
|
+
init_check_live();
|
|
24590
|
+
init_check_shape();
|
|
24591
|
+
init_check_write();
|
|
23820
24592
|
init_collection_scanner();
|
|
23821
24593
|
init_component_registry();
|
|
23822
24594
|
init_content_config_ast();
|
|
23823
24595
|
init_core();
|
|
24596
|
+
init_editor_write_model();
|
|
23824
24597
|
init_node_fs();
|
|
23825
24598
|
init_entry_ops();
|
|
23826
24599
|
init_media();
|
|
23827
24600
|
init_project_config_ast();
|
|
24601
|
+
init_schema_probe();
|
|
24602
|
+
init_shared();
|
|
23828
24603
|
});
|
|
23829
24604
|
|
|
23830
24605
|
// ../../node_modules/@astrojs/compiler/dist/chunk-W5DTLHV4.js
|
|
@@ -24621,45 +25396,307 @@ var init_check_code = __esm(() => {
|
|
|
24621
25396
|
IGNORED = ["node_modules", "dist", ".astro", ".git", ".output", ".vercel", ".netlify"];
|
|
24622
25397
|
});
|
|
24623
25398
|
|
|
25399
|
+
// src/live-schema.ts
|
|
25400
|
+
import { existsSync, realpathSync } from "fs";
|
|
25401
|
+
import path5 from "path";
|
|
25402
|
+
function reasonOf(error, fallback = "unknown error") {
|
|
25403
|
+
try {
|
|
25404
|
+
if (typeof error === "string" && error !== "")
|
|
25405
|
+
return error;
|
|
25406
|
+
if (error instanceof Error && error.message !== "")
|
|
25407
|
+
return error.message;
|
|
25408
|
+
if (isRecord(error) && typeof error.message === "string" && error.message !== "")
|
|
25409
|
+
return error.message;
|
|
25410
|
+
} catch {}
|
|
25411
|
+
return fallback;
|
|
25412
|
+
}
|
|
25413
|
+
function findConfig(projectRoot) {
|
|
25414
|
+
for (const name of CONFIG_NAMES) {
|
|
25415
|
+
const candidate = path5.join(projectRoot, name);
|
|
25416
|
+
if (existsSync(candidate))
|
|
25417
|
+
return candidate;
|
|
25418
|
+
}
|
|
25419
|
+
return null;
|
|
25420
|
+
}
|
|
25421
|
+
function projectAstroDir(projectRoot) {
|
|
25422
|
+
let dir = projectRoot;
|
|
25423
|
+
while (true) {
|
|
25424
|
+
const candidate = path5.join(dir, "node_modules", "astro");
|
|
25425
|
+
if (existsSync(path5.join(candidate, "package.json")))
|
|
25426
|
+
return candidate;
|
|
25427
|
+
const parent = path5.dirname(dir);
|
|
25428
|
+
if (parent === dir)
|
|
25429
|
+
return null;
|
|
25430
|
+
dir = parent;
|
|
25431
|
+
}
|
|
25432
|
+
}
|
|
25433
|
+
function zodEntryOf(module) {
|
|
25434
|
+
if (!isRecord(module))
|
|
25435
|
+
return null;
|
|
25436
|
+
const namespace = module.z;
|
|
25437
|
+
if (!isRecord(namespace))
|
|
25438
|
+
return null;
|
|
25439
|
+
const string = namespace.string;
|
|
25440
|
+
const object = namespace.object;
|
|
25441
|
+
const union = namespace.union;
|
|
25442
|
+
const any = namespace.any;
|
|
25443
|
+
if (typeof string !== "function" || typeof object !== "function" || typeof union !== "function" || typeof any !== "function")
|
|
25444
|
+
return null;
|
|
25445
|
+
return {
|
|
25446
|
+
namespace,
|
|
25447
|
+
any: () => Reflect.apply(any, namespace, []),
|
|
25448
|
+
reference: () => {
|
|
25449
|
+
const text = () => Reflect.apply(string, namespace, []);
|
|
25450
|
+
const lookup = (key) => Reflect.apply(object, namespace, [{ [key]: text(), collection: text() }]);
|
|
25451
|
+
return Reflect.apply(union, namespace, [[text(), lookup("id"), lookup("slug")]]);
|
|
25452
|
+
}
|
|
25453
|
+
};
|
|
25454
|
+
}
|
|
25455
|
+
function installContentStub(zodPath, zod) {
|
|
25456
|
+
Bun.plugin({
|
|
25457
|
+
name: "astro-content-stub",
|
|
25458
|
+
setup(build) {
|
|
25459
|
+
build.module("astro:content", () => ({
|
|
25460
|
+
loader: "object",
|
|
25461
|
+
exports: {
|
|
25462
|
+
defineCollection: (collection) => collection,
|
|
25463
|
+
reference: () => zod.reference(),
|
|
25464
|
+
z: zod.namespace
|
|
25465
|
+
}
|
|
25466
|
+
}));
|
|
25467
|
+
}
|
|
25468
|
+
});
|
|
25469
|
+
stubbedZod = zodPath;
|
|
25470
|
+
}
|
|
25471
|
+
function parseMethod(schema) {
|
|
25472
|
+
if (!isRecord(schema))
|
|
25473
|
+
return null;
|
|
25474
|
+
for (const name of ["safeParseAsync", "safeParse"]) {
|
|
25475
|
+
const method = schema[name];
|
|
25476
|
+
if (typeof method === "function")
|
|
25477
|
+
return (value) => Reflect.apply(method, schema, [value]);
|
|
25478
|
+
}
|
|
25479
|
+
return null;
|
|
25480
|
+
}
|
|
25481
|
+
function dedupe(issues) {
|
|
25482
|
+
const seen = new Set;
|
|
25483
|
+
const unique = [];
|
|
25484
|
+
for (const issue of issues) {
|
|
25485
|
+
const key = `${issue.path.join("\x00")}\x01${issue.message}`;
|
|
25486
|
+
if (seen.has(key))
|
|
25487
|
+
continue;
|
|
25488
|
+
seen.add(key);
|
|
25489
|
+
unique.push(issue);
|
|
25490
|
+
}
|
|
25491
|
+
return unique;
|
|
25492
|
+
}
|
|
25493
|
+
function unionIssues(raw) {
|
|
25494
|
+
return asArray(raw.errors ?? raw.unionErrors).flatMap((branch) => asArray(isRecord(branch) ? branch.issues ?? branch.errors : branch).flatMap(toIssues));
|
|
25495
|
+
}
|
|
25496
|
+
function toIssues(raw) {
|
|
25497
|
+
if (!isRecord(raw))
|
|
25498
|
+
return [{ path: [], message: reasonOf(raw, "invalid value") }];
|
|
25499
|
+
const base = asArray(raw.path).map((segment) => typeof segment === "number" ? segment : String(segment));
|
|
25500
|
+
const branches = raw.code === "invalid_union" ? unionIssues(raw) : [];
|
|
25501
|
+
if (branches.length > 0)
|
|
25502
|
+
return dedupe(branches.map((issue) => ({ path: [...base, ...issue.path], message: issue.message })));
|
|
25503
|
+
return [{ path: base, message: typeof raw.message === "string" ? raw.message : "invalid value" }];
|
|
25504
|
+
}
|
|
25505
|
+
function normalizeIssues(error) {
|
|
25506
|
+
const raw = isRecord(error) ? asArray(error.issues ?? error.errors) : [];
|
|
25507
|
+
if (raw.length > 0)
|
|
25508
|
+
return raw.flatMap(toIssues);
|
|
25509
|
+
return [{ path: [], message: reasonOf(error, "the schema rejected this value without saying why") }];
|
|
25510
|
+
}
|
|
25511
|
+
function liveSchema(parse3) {
|
|
25512
|
+
return {
|
|
25513
|
+
async safeParse(value) {
|
|
25514
|
+
try {
|
|
25515
|
+
const result = await parse3(value);
|
|
25516
|
+
if (!isRecord(result))
|
|
25517
|
+
return { success: false, issues: [{ path: [], message: "the schema returned no parse result" }] };
|
|
25518
|
+
if (result.success === true)
|
|
25519
|
+
return { success: true };
|
|
25520
|
+
return { success: false, issues: normalizeIssues(result.error) };
|
|
25521
|
+
} catch (error) {
|
|
25522
|
+
return { success: false, issues: [{ path: [], message: reasonOf(error, "the schema threw while parsing this entry") }] };
|
|
25523
|
+
}
|
|
25524
|
+
}
|
|
25525
|
+
};
|
|
25526
|
+
}
|
|
25527
|
+
async function collectSchemas(module, zod, configName) {
|
|
25528
|
+
const collections = isRecord(module) ? module.collections : undefined;
|
|
25529
|
+
if (!isRecord(collections))
|
|
25530
|
+
return { skipped: `${configName} does not export a \`collections\` object.` };
|
|
25531
|
+
const schemas = {};
|
|
25532
|
+
for (const [name, definition] of Object.entries(collections)) {
|
|
25533
|
+
if (!isRecord(definition))
|
|
25534
|
+
continue;
|
|
25535
|
+
const factory = definition.schema;
|
|
25536
|
+
if (factory === undefined)
|
|
25537
|
+
continue;
|
|
25538
|
+
let resolved;
|
|
25539
|
+
try {
|
|
25540
|
+
resolved = typeof factory === "function" ? await Reflect.apply(factory, definition, [{ image: () => zod.any() }]) : factory;
|
|
25541
|
+
} catch (error) {
|
|
25542
|
+
return { skipped: `The schema of collection "${name}" in ${configName} threw while being built: ${reasonOf(error)}` };
|
|
25543
|
+
}
|
|
25544
|
+
const parse3 = parseMethod(resolved);
|
|
25545
|
+
if (!parse3) {
|
|
25546
|
+
return { skipped: `The schema of collection "${name}" in ${configName} has no safeParse method \u2014 is it a zod schema?` };
|
|
25547
|
+
}
|
|
25548
|
+
schemas[name] = liveSchema(parse3);
|
|
25549
|
+
}
|
|
25550
|
+
return { schemas };
|
|
25551
|
+
}
|
|
25552
|
+
async function loadLiveSchemas(projectRoot) {
|
|
25553
|
+
const root = path5.resolve(projectRoot);
|
|
25554
|
+
const configPath = findConfig(root);
|
|
25555
|
+
if (!configPath) {
|
|
25556
|
+
return { skipped: `No content config in ${root} \u2014 expected src/content.config.ts or src/content/config.ts.` };
|
|
25557
|
+
}
|
|
25558
|
+
const configName = path5.relative(root, configPath);
|
|
25559
|
+
const astroDir = projectAstroDir(root);
|
|
25560
|
+
if (!astroDir) {
|
|
25561
|
+
return { skipped: `No node_modules/astro above ${root} \u2014 install the project's dependencies and run this again.` };
|
|
25562
|
+
}
|
|
25563
|
+
let zodPath;
|
|
25564
|
+
try {
|
|
25565
|
+
zodPath = realOf(Bun.resolveSync("astro/zod", root));
|
|
25566
|
+
} catch (error) {
|
|
25567
|
+
return { skipped: `Could not resolve astro/zod from ${root}: ${reasonOf(error)}` };
|
|
25568
|
+
}
|
|
25569
|
+
if (!contains(astroDir, zodPath)) {
|
|
25570
|
+
return {
|
|
25571
|
+
skipped: `astro/zod resolved to ${zodPath}, outside the project's own ${astroDir} \u2014 reinstall the project's dependencies and run this again.`
|
|
25572
|
+
};
|
|
25573
|
+
}
|
|
25574
|
+
let zodModule;
|
|
25575
|
+
try {
|
|
25576
|
+
zodModule = await import(zodPath);
|
|
25577
|
+
} catch (error) {
|
|
25578
|
+
return { skipped: `Loading astro/zod from ${root} failed: ${reasonOf(error)}` };
|
|
25579
|
+
}
|
|
25580
|
+
let zod;
|
|
25581
|
+
try {
|
|
25582
|
+
zod = zodEntryOf(zodModule);
|
|
25583
|
+
} catch {
|
|
25584
|
+
zod = null;
|
|
25585
|
+
}
|
|
25586
|
+
if (!zod) {
|
|
25587
|
+
return { skipped: `astro/zod in ${root} does not export a usable \`z\` \u2014 the installed Astro version may be unsupported.` };
|
|
25588
|
+
}
|
|
25589
|
+
if (stubbedZod === null) {
|
|
25590
|
+
try {
|
|
25591
|
+
installContentStub(zodPath, zod);
|
|
25592
|
+
} catch (error) {
|
|
25593
|
+
return { skipped: `Installing the astro:content stub failed: ${reasonOf(error)}` };
|
|
25594
|
+
}
|
|
25595
|
+
} else if (stubbedZod !== zodPath) {
|
|
25596
|
+
return {
|
|
25597
|
+
skipped: `Live schemas were already loaded against ${stubbedZod}; the astro:content stub is process-global, so ${root} needs a run of its own.`
|
|
25598
|
+
};
|
|
25599
|
+
}
|
|
25600
|
+
let module;
|
|
25601
|
+
try {
|
|
25602
|
+
module = await import(configPath);
|
|
25603
|
+
} catch (error) {
|
|
25604
|
+
return { skipped: `${configName} failed to load: ${reasonOf(error)}` };
|
|
25605
|
+
}
|
|
25606
|
+
try {
|
|
25607
|
+
return await collectSchemas(module, zod, configName);
|
|
25608
|
+
} catch (error) {
|
|
25609
|
+
return { skipped: `Reading the collections in ${configName} failed: ${reasonOf(error)}` };
|
|
25610
|
+
}
|
|
25611
|
+
}
|
|
25612
|
+
var CONFIG_NAMES, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), asArray = (value) => Array.isArray(value) ? value : [], realOf = (target) => {
|
|
25613
|
+
try {
|
|
25614
|
+
return realpathSync(target);
|
|
25615
|
+
} catch {
|
|
25616
|
+
return target;
|
|
25617
|
+
}
|
|
25618
|
+
}, contains = (dir, file) => {
|
|
25619
|
+
const relative = path5.relative(realOf(dir), realOf(file));
|
|
25620
|
+
return relative !== "" && !relative.startsWith("..") && !path5.isAbsolute(relative);
|
|
25621
|
+
}, stubbedZod = null;
|
|
25622
|
+
var init_live_schema = __esm(() => {
|
|
25623
|
+
CONFIG_NAMES = [
|
|
25624
|
+
"src/content.config.mjs",
|
|
25625
|
+
"src/content.config.js",
|
|
25626
|
+
"src/content.config.mts",
|
|
25627
|
+
"src/content.config.ts",
|
|
25628
|
+
"src/content/config.ts",
|
|
25629
|
+
"src/content/config.js",
|
|
25630
|
+
"src/content/config.mjs",
|
|
25631
|
+
"src/content/config.mts"
|
|
25632
|
+
];
|
|
25633
|
+
});
|
|
25634
|
+
|
|
24624
25635
|
// src/check.ts
|
|
24625
25636
|
var exports_check = {};
|
|
24626
25637
|
__export(exports_check, {
|
|
25638
|
+
parseCheckArgs: () => parseCheckArgs,
|
|
24627
25639
|
check: () => check,
|
|
24628
25640
|
astroRoots: () => astroRoots
|
|
24629
25641
|
});
|
|
24630
|
-
import { existsSync, readdirSync } from "fs";
|
|
24631
|
-
import
|
|
25642
|
+
import { existsSync as existsSync2, readdirSync } from "fs";
|
|
25643
|
+
import path6 from "path";
|
|
25644
|
+
function parseCheckArgs(args, cwd) {
|
|
25645
|
+
const contentOnly = args.includes("--content-only");
|
|
25646
|
+
return {
|
|
25647
|
+
cwd,
|
|
25648
|
+
json: args.includes("--json"),
|
|
25649
|
+
strict: args.includes("--strict"),
|
|
25650
|
+
contentOnly,
|
|
25651
|
+
live: !contentOnly && !args.includes("--no-live")
|
|
25652
|
+
};
|
|
25653
|
+
}
|
|
24632
25654
|
function astroRoots(cwd) {
|
|
24633
25655
|
if (hasAstroConfig(cwd))
|
|
24634
25656
|
return ["."];
|
|
24635
|
-
const packagesDir =
|
|
24636
|
-
if (!
|
|
25657
|
+
const packagesDir = path6.join(cwd, "packages");
|
|
25658
|
+
if (!existsSync2(packagesDir))
|
|
24637
25659
|
return [];
|
|
24638
|
-
return readdirSync(packagesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().filter((name) => hasAstroConfig(
|
|
25660
|
+
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
25661
|
}
|
|
24640
25662
|
function prefixed(report, root) {
|
|
24641
25663
|
if (root === ".")
|
|
24642
25664
|
return report;
|
|
24643
25665
|
return {
|
|
24644
25666
|
...report,
|
|
24645
|
-
findings: report.findings.map((finding) => ({ ...finding, file:
|
|
25667
|
+
findings: report.findings.map((finding) => ({ ...finding, file: path6.join(root, finding.file) }))
|
|
24646
25668
|
};
|
|
24647
25669
|
}
|
|
25670
|
+
async function liveSchemasFor(options, roots) {
|
|
25671
|
+
if (options.contentOnly) {
|
|
25672
|
+
return { skipped: "--content-only was passed, and reading the real schemas means executing the project's content config." };
|
|
25673
|
+
}
|
|
25674
|
+
if (!options.live)
|
|
25675
|
+
return { skipped: "--no-live was passed." };
|
|
25676
|
+
const first = roots[0];
|
|
25677
|
+
if (roots.length !== 1 || first === undefined) {
|
|
25678
|
+
const where = roots.join(", ");
|
|
25679
|
+
return { skipped: `They need exactly one Astro project; this run covers ${roots.length} (${where}). Check each one from its own directory.` };
|
|
25680
|
+
}
|
|
25681
|
+
return loadLiveSchemas(path6.join(options.cwd, first));
|
|
25682
|
+
}
|
|
24648
25683
|
async function check(options) {
|
|
24649
25684
|
const roots = astroRoots(options.cwd);
|
|
24650
25685
|
if (roots.length === 0) {
|
|
24651
25686
|
console.error(`No astro.config.* found in ${options.cwd} or its packages/*. Run this in an Astro project.`);
|
|
24652
25687
|
return 1;
|
|
24653
25688
|
}
|
|
25689
|
+
const live = await liveSchemasFor(options, roots);
|
|
25690
|
+
const schemas = "schemas" in live ? live.schemas : undefined;
|
|
24654
25691
|
const reports = await Promise.all(roots.map(async (root) => {
|
|
24655
|
-
const absolute =
|
|
24656
|
-
const content = prefixed(await checkContent(createNodeFs(absolute)), root);
|
|
25692
|
+
const absolute = path6.join(options.cwd, root);
|
|
25693
|
+
const content = prefixed(await checkContent(createNodeFs(absolute), schemas ? { schemas } : {}), root);
|
|
24657
25694
|
if (options.contentOnly)
|
|
24658
25695
|
return { ...content, files: 0 };
|
|
24659
25696
|
const code = await checkCode(absolute);
|
|
24660
25697
|
return {
|
|
24661
25698
|
...content,
|
|
24662
|
-
findings: [...content.findings, ...code.findings.map((finding) => ({ ...finding, file:
|
|
25699
|
+
findings: [...content.findings, ...code.findings.map((finding) => ({ ...finding, file: path6.join(root, finding.file) }))],
|
|
24663
25700
|
files: code.files
|
|
24664
25701
|
};
|
|
24665
25702
|
}));
|
|
@@ -24672,7 +25709,7 @@ async function check(options) {
|
|
|
24672
25709
|
const errors = report.findings.filter((finding) => finding.severity === "error").length;
|
|
24673
25710
|
const warnings = report.findings.length - errors;
|
|
24674
25711
|
if (options.json) {
|
|
24675
|
-
console.log(JSON.stringify({ ...report, roots }, null, 2));
|
|
25712
|
+
console.log(JSON.stringify({ ...report, roots, liveSchemas: "schemas" in live ? "ran" : live.skipped }, null, 2));
|
|
24676
25713
|
} else {
|
|
24677
25714
|
const formatted = formatCheckReport(report);
|
|
24678
25715
|
if (formatted)
|
|
@@ -24682,23 +25719,26 @@ async function check(options) {
|
|
|
24682
25719
|
const code = options.contentOnly ? "" : `, ${sourceFiles} source file(s)`;
|
|
24683
25720
|
const counts = `${report.collections} collection(s), ${report.entries} entries${code}${where}`;
|
|
24684
25721
|
console.log(errors === 0 && warnings === 0 ? `${counts} \u2014 no problems found` : `${counts} \u2014 ${errors} error(s), ${warnings} warning(s)`);
|
|
25722
|
+
if ("skipped" in live)
|
|
25723
|
+
console.log(`Live schema rules did not run: ${live.skipped}`);
|
|
24685
25724
|
}
|
|
24686
25725
|
return errors > 0 || options.strict && warnings > 0 ? 1 : 0;
|
|
24687
25726
|
}
|
|
24688
|
-
var ASTRO_CONFIG_NAMES, hasAstroConfig = (dir) => ASTRO_CONFIG_NAMES.some((name) =>
|
|
25727
|
+
var ASTRO_CONFIG_NAMES, hasAstroConfig = (dir) => ASTRO_CONFIG_NAMES.some((name) => existsSync2(path6.join(dir, name)));
|
|
24689
25728
|
var init_check2 = __esm(() => {
|
|
24690
25729
|
init_src2();
|
|
24691
25730
|
init_check_code();
|
|
25731
|
+
init_live_schema();
|
|
24692
25732
|
ASTRO_CONFIG_NAMES = ["astro.config.ts", "astro.config.mts", "astro.config.mjs", "astro.config.js"];
|
|
24693
25733
|
});
|
|
24694
25734
|
|
|
24695
25735
|
// src/utils.ts
|
|
24696
|
-
import { existsSync as
|
|
25736
|
+
import { existsSync as existsSync3 } from "fs";
|
|
24697
25737
|
import { join } from "path";
|
|
24698
25738
|
function findAstroConfig(cwd = process.cwd()) {
|
|
24699
|
-
for (const name of
|
|
25739
|
+
for (const name of CONFIG_NAMES2) {
|
|
24700
25740
|
const p = join(cwd, name);
|
|
24701
|
-
if (
|
|
25741
|
+
if (existsSync3(p))
|
|
24702
25742
|
return p;
|
|
24703
25743
|
}
|
|
24704
25744
|
return null;
|
|
@@ -24795,9 +25835,9 @@ function assembleConfig(imports, body) {
|
|
|
24795
25835
|
`;
|
|
24796
25836
|
return result;
|
|
24797
25837
|
}
|
|
24798
|
-
var
|
|
25838
|
+
var CONFIG_NAMES2;
|
|
24799
25839
|
var init_utils = __esm(() => {
|
|
24800
|
-
|
|
25840
|
+
CONFIG_NAMES2 = [
|
|
24801
25841
|
"astro.config.ts",
|
|
24802
25842
|
"astro.config.mts",
|
|
24803
25843
|
"astro.config.mjs",
|
|
@@ -24815,7 +25855,7 @@ __export(exports_init, {
|
|
|
24815
25855
|
detectNuaManagedImports: () => detectNuaManagedImports,
|
|
24816
25856
|
cleanEmptyStructures: () => cleanEmptyStructures
|
|
24817
25857
|
});
|
|
24818
|
-
import { existsSync as
|
|
25858
|
+
import { existsSync as existsSync4, readFileSync, writeFileSync } from "fs";
|
|
24819
25859
|
import { basename, join as join2 } from "path";
|
|
24820
25860
|
function detectNuaManagedImports(content) {
|
|
24821
25861
|
const managed = new Map;
|
|
@@ -24959,7 +25999,7 @@ async function init({ cwd = process.cwd(), dryRun = false, yes = false } = {}) {
|
|
|
24959
25999
|
process.exit(1);
|
|
24960
26000
|
}
|
|
24961
26001
|
const pkgPath = join2(cwd, "package.json");
|
|
24962
|
-
if (!
|
|
26002
|
+
if (!existsSync4(pkgPath)) {
|
|
24963
26003
|
console.error("No package.json found.");
|
|
24964
26004
|
process.exit(1);
|
|
24965
26005
|
}
|
|
@@ -25037,7 +26077,7 @@ __export(exports_clean, {
|
|
|
25037
26077
|
detectDisabledFeatures: () => detectDisabledFeatures,
|
|
25038
26078
|
clean: () => clean
|
|
25039
26079
|
});
|
|
25040
|
-
import { existsSync as
|
|
26080
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
25041
26081
|
import { basename as basename2, join as join3 } from "path";
|
|
25042
26082
|
function detectDisabledFeatures(content) {
|
|
25043
26083
|
const disabled = new Set;
|
|
@@ -25146,7 +26186,7 @@ function transformPackageJson2(pkg, disabled, usedRuntimePackages = []) {
|
|
|
25146
26186
|
}
|
|
25147
26187
|
function scanForNuasiteUsage(cwd) {
|
|
25148
26188
|
const srcDir = join3(cwd, "src");
|
|
25149
|
-
if (!
|
|
26189
|
+
if (!existsSync5(srcDir))
|
|
25150
26190
|
return [];
|
|
25151
26191
|
const results = [];
|
|
25152
26192
|
try {
|
|
@@ -25181,7 +26221,7 @@ async function clean({ cwd = process.cwd(), dryRun = false, yes = false } = {})
|
|
|
25181
26221
|
return;
|
|
25182
26222
|
}
|
|
25183
26223
|
const pkgPath = join3(cwd, "package.json");
|
|
25184
|
-
if (!
|
|
26224
|
+
if (!existsSync5(pkgPath)) {
|
|
25185
26225
|
console.error("No package.json found.");
|
|
25186
26226
|
process.exit(1);
|
|
25187
26227
|
}
|
|
@@ -25289,7 +26329,7 @@ var init_config = () => {};
|
|
|
25289
26329
|
|
|
25290
26330
|
// ../cms/src/component-registry.ts
|
|
25291
26331
|
import fs2 from "fs/promises";
|
|
25292
|
-
import
|
|
26332
|
+
import path7 from "path";
|
|
25293
26333
|
|
|
25294
26334
|
class ComponentRegistry {
|
|
25295
26335
|
components = new Map;
|
|
@@ -25299,7 +26339,7 @@ class ComponentRegistry {
|
|
|
25299
26339
|
}
|
|
25300
26340
|
async scan() {
|
|
25301
26341
|
for (const dir of this.componentDirs) {
|
|
25302
|
-
const fullPath =
|
|
26342
|
+
const fullPath = path7.join(getProjectRoot(), dir);
|
|
25303
26343
|
try {
|
|
25304
26344
|
await this.scanDirectory(fullPath, dir);
|
|
25305
26345
|
} catch {}
|
|
@@ -25314,8 +26354,8 @@ class ComponentRegistry {
|
|
|
25314
26354
|
async scanDirectory(dir, relativePath) {
|
|
25315
26355
|
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
25316
26356
|
for (const entry of entries) {
|
|
25317
|
-
const fullPath =
|
|
25318
|
-
const relPath =
|
|
26357
|
+
const fullPath = path7.join(dir, entry.name);
|
|
26358
|
+
const relPath = path7.join(relativePath, entry.name);
|
|
25319
26359
|
if (entry.isDirectory()) {
|
|
25320
26360
|
await this.scanDirectory(fullPath, relPath);
|
|
25321
26361
|
} else if (entry.isFile() && entry.name.endsWith(".astro")) {
|
|
@@ -25326,7 +26366,7 @@ class ComponentRegistry {
|
|
|
25326
26366
|
async parseComponent(filePath, relativePath) {
|
|
25327
26367
|
try {
|
|
25328
26368
|
const content = await fs2.readFile(filePath, "utf-8");
|
|
25329
|
-
const componentName =
|
|
26369
|
+
const componentName = path7.basename(filePath, ".astro");
|
|
25330
26370
|
const props = await this.extractProps(content);
|
|
25331
26371
|
const slots = this.extractSlots(content);
|
|
25332
26372
|
const description = this.extractDescription(content);
|
|
@@ -25581,7 +26621,7 @@ function slugify2(text) {
|
|
|
25581
26621
|
|
|
25582
26622
|
// ../cms/src/utils.ts
|
|
25583
26623
|
import { createHash } from "crypto";
|
|
25584
|
-
import
|
|
26624
|
+
import path8 from "path";
|
|
25585
26625
|
function normalizePagePath(url) {
|
|
25586
26626
|
let pathname;
|
|
25587
26627
|
try {
|
|
@@ -25634,21 +26674,21 @@ function escapeReplacement(str) {
|
|
|
25634
26674
|
return str.replace(/\$/g, "$$$$");
|
|
25635
26675
|
}
|
|
25636
26676
|
function resolveSourcePath(sourcePath) {
|
|
25637
|
-
return
|
|
26677
|
+
return path8.isAbsolute(sourcePath) ? sourcePath : path8.join(getProjectRoot(), sourcePath);
|
|
25638
26678
|
}
|
|
25639
26679
|
function resolveAndValidatePath(filePath) {
|
|
25640
26680
|
const projectRoot = getProjectRoot();
|
|
25641
|
-
const resolvedRoot =
|
|
26681
|
+
const resolvedRoot = path8.resolve(projectRoot);
|
|
25642
26682
|
const isAbsoluteFs = filePath.startsWith(resolvedRoot);
|
|
25643
26683
|
const normalizedPath = !isAbsoluteFs && filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
25644
|
-
const fullPath =
|
|
25645
|
-
if (!fullPath.startsWith(resolvedRoot +
|
|
26684
|
+
const fullPath = path8.isAbsolute(normalizedPath) ? path8.resolve(normalizedPath) : path8.resolve(projectRoot, normalizedPath);
|
|
26685
|
+
if (!fullPath.startsWith(resolvedRoot + path8.sep) && fullPath !== resolvedRoot) {
|
|
25646
26686
|
throw new Error(`Path traversal detected: ${filePath}`);
|
|
25647
26687
|
}
|
|
25648
26688
|
return fullPath;
|
|
25649
26689
|
}
|
|
25650
26690
|
async function acquireFileLock(filePath) {
|
|
25651
|
-
const key =
|
|
26691
|
+
const key = path8.resolve(filePath);
|
|
25652
26692
|
while (fileLocks.has(key)) {
|
|
25653
26693
|
await fileLocks.get(key);
|
|
25654
26694
|
}
|
|
@@ -25666,8 +26706,8 @@ function escapeHtml2(text) {
|
|
|
25666
26706
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
25667
26707
|
}
|
|
25668
26708
|
function relativeImportPath2(fromFile, toFile) {
|
|
25669
|
-
const fromDir =
|
|
25670
|
-
let rel =
|
|
26709
|
+
const fromDir = path8.dirname(fromFile);
|
|
26710
|
+
let rel = path8.relative(fromDir, toFile).split(path8.sep).join("/");
|
|
25671
26711
|
if (!rel.startsWith("."))
|
|
25672
26712
|
rel = `./${rel}`;
|
|
25673
26713
|
return rel;
|
|
@@ -25680,7 +26720,7 @@ var init_utils2 = __esm(() => {
|
|
|
25680
26720
|
|
|
25681
26721
|
// ../cms/src/handlers/component-ops.ts
|
|
25682
26722
|
import fs3 from "fs/promises";
|
|
25683
|
-
import
|
|
26723
|
+
import path9 from "path";
|
|
25684
26724
|
async function handleInsertComponent(request, manifestWriter) {
|
|
25685
26725
|
const { position, referenceComponentId, componentName, props, meta } = request;
|
|
25686
26726
|
if (!meta?.url) {
|
|
@@ -25898,7 +26938,7 @@ function getComponentOccurrenceIndex(manifest, referenceComponent) {
|
|
|
25898
26938
|
async function findComponentInvocationFile(projectRoot, pageUrl, manifest, referenceComponent) {
|
|
25899
26939
|
if (referenceComponent.invocationSourcePath) {
|
|
25900
26940
|
const filePath = normalizeFilePath(referenceComponent.invocationSourcePath);
|
|
25901
|
-
const fullPath =
|
|
26941
|
+
const fullPath = path9.resolve(projectRoot, filePath);
|
|
25902
26942
|
try {
|
|
25903
26943
|
const content = await fs3.readFile(fullPath, "utf-8");
|
|
25904
26944
|
const lines = content.split(`
|
|
@@ -25912,7 +26952,7 @@ async function findComponentInvocationFile(projectRoot, pageUrl, manifest, refer
|
|
|
25912
26952
|
const candidates = getPageFileCandidates(pageUrl);
|
|
25913
26953
|
const occurrenceIndex = getComponentOccurrenceIndex(manifest, referenceComponent);
|
|
25914
26954
|
for (const candidate of candidates) {
|
|
25915
|
-
const fullPath =
|
|
26955
|
+
const fullPath = path9.resolve(projectRoot, candidate);
|
|
25916
26956
|
try {
|
|
25917
26957
|
const content = await fs3.readFile(fullPath, "utf-8");
|
|
25918
26958
|
const lines = content.split(`
|
|
@@ -25972,7 +27012,7 @@ function getIndentation(line) {
|
|
|
25972
27012
|
function normalizeFilePath(p) {
|
|
25973
27013
|
if (!p.startsWith("/"))
|
|
25974
27014
|
return p;
|
|
25975
|
-
const projectRoot =
|
|
27015
|
+
const projectRoot = path9.resolve(getProjectRoot());
|
|
25976
27016
|
if (p.startsWith(projectRoot))
|
|
25977
27017
|
return p;
|
|
25978
27018
|
return p.slice(1);
|
|
@@ -26380,45 +27420,45 @@ var init_array_ops = __esm(() => {
|
|
|
26380
27420
|
// ../cms/src/astro-image-paths.ts
|
|
26381
27421
|
import { createHash as createHash2 } from "crypto";
|
|
26382
27422
|
import fs5 from "fs/promises";
|
|
26383
|
-
import
|
|
27423
|
+
import path10 from "path";
|
|
26384
27424
|
function isHugoStyleEntry(entryAbsPath) {
|
|
26385
|
-
return HUGO_INDEX_RE.test(
|
|
27425
|
+
return HUGO_INDEX_RE.test(path10.basename(entryAbsPath));
|
|
26386
27426
|
}
|
|
26387
27427
|
function shortContentHash(buf) {
|
|
26388
27428
|
return createHash2("sha256").update(buf).digest("hex").slice(0, 8);
|
|
26389
27429
|
}
|
|
26390
27430
|
async function pickAstroImageTarget(args) {
|
|
26391
|
-
const entryDir =
|
|
27431
|
+
const entryDir = path10.dirname(args.entryAbsPath);
|
|
26392
27432
|
const isHugoStyle = isHugoStyleEntry(args.entryAbsPath);
|
|
26393
|
-
const safeFilename =
|
|
27433
|
+
const safeFilename = path10.basename(args.originalFilename);
|
|
26394
27434
|
if (!safeFilename || safeFilename === "." || safeFilename === "..") {
|
|
26395
27435
|
throw new Error(`Invalid filename: ${args.originalFilename}`);
|
|
26396
27436
|
}
|
|
26397
27437
|
const baseName = isHugoStyle ? safeFilename : `${args.slug}-${safeFilename}`;
|
|
26398
|
-
const baseAbs =
|
|
27438
|
+
const baseAbs = path10.join(entryDir, baseName);
|
|
26399
27439
|
if (await isFreeOrMatching(baseAbs, args.compareBuffer)) {
|
|
26400
27440
|
return { absPath: baseAbs, relPath: `./${baseName}` };
|
|
26401
27441
|
}
|
|
26402
27442
|
const candidateAbs = await pickHashedSibling(entryDir, baseName, args.compareBuffer);
|
|
26403
|
-
return { absPath: candidateAbs, relPath: `./${
|
|
27443
|
+
return { absPath: candidateAbs, relPath: `./${path10.basename(candidateAbs)}` };
|
|
26404
27444
|
}
|
|
26405
27445
|
async function pickSiblingTarget(dir, filename, buf) {
|
|
26406
|
-
const safe =
|
|
27446
|
+
const safe = path10.basename(filename);
|
|
26407
27447
|
if (!safe || safe === "." || safe === "..") {
|
|
26408
27448
|
throw new Error(`Invalid filename: ${filename}`);
|
|
26409
27449
|
}
|
|
26410
|
-
const baseAbs =
|
|
27450
|
+
const baseAbs = path10.join(dir, safe);
|
|
26411
27451
|
if (await isFreeOrMatching(baseAbs, buf))
|
|
26412
27452
|
return baseAbs;
|
|
26413
27453
|
return pickHashedSibling(dir, safe, buf);
|
|
26414
27454
|
}
|
|
26415
27455
|
async function pickHashedSibling(dir, baseName, buf) {
|
|
26416
27456
|
const hash = shortContentHash(buf);
|
|
26417
|
-
const ext =
|
|
26418
|
-
const stem =
|
|
27457
|
+
const ext = path10.extname(baseName);
|
|
27458
|
+
const stem = path10.basename(baseName, ext);
|
|
26419
27459
|
for (let attempt = 0;attempt < 5; attempt++) {
|
|
26420
27460
|
const suffix = attempt === 0 ? hash : `${hash}-${attempt}`;
|
|
26421
|
-
const candidateAbs =
|
|
27461
|
+
const candidateAbs = path10.join(dir, `${stem}-${suffix}${ext}`);
|
|
26422
27462
|
if (await isFreeOrMatching(candidateAbs, buf))
|
|
26423
27463
|
return candidateAbs;
|
|
26424
27464
|
}
|
|
@@ -26442,7 +27482,7 @@ var init_astro_image_paths = __esm(() => {
|
|
|
26442
27482
|
|
|
26443
27483
|
// ../cms/src/handlers/astro-image-upload.ts
|
|
26444
27484
|
import fs6 from "fs/promises";
|
|
26445
|
-
import
|
|
27485
|
+
import path11 from "path";
|
|
26446
27486
|
async function tryAstroImageUpload(args) {
|
|
26447
27487
|
const { collection, entry, field } = args.context;
|
|
26448
27488
|
if (!collection || !entry || !field)
|
|
@@ -26464,12 +27504,12 @@ async function tryAstroImageUpload(args) {
|
|
|
26464
27504
|
originalFilename: args.originalFilename,
|
|
26465
27505
|
compareBuffer: args.fileBuffer
|
|
26466
27506
|
});
|
|
26467
|
-
await fs6.mkdir(
|
|
27507
|
+
await fs6.mkdir(path11.dirname(target.absPath), { recursive: true });
|
|
26468
27508
|
await fs6.writeFile(target.absPath, args.fileBuffer);
|
|
26469
27509
|
return {
|
|
26470
27510
|
success: true,
|
|
26471
27511
|
url: target.relPath,
|
|
26472
|
-
filename:
|
|
27512
|
+
filename: path11.basename(target.absPath)
|
|
26473
27513
|
};
|
|
26474
27514
|
}
|
|
26475
27515
|
var init_astro_image_upload = __esm(() => {
|
|
@@ -26661,7 +27701,7 @@ var require_he = __commonJS((exports, module) => {
|
|
|
26661
27701
|
var has = function(object2, propertyName) {
|
|
26662
27702
|
return hasOwnProperty2.call(object2, propertyName);
|
|
26663
27703
|
};
|
|
26664
|
-
var
|
|
27704
|
+
var contains2 = function(array, value) {
|
|
26665
27705
|
var index = -1;
|
|
26666
27706
|
var length = array.length;
|
|
26667
27707
|
while (++index < length) {
|
|
@@ -26696,7 +27736,7 @@ var require_he = __commonJS((exports, module) => {
|
|
|
26696
27736
|
}
|
|
26697
27737
|
return decodeMapNumeric[codePoint];
|
|
26698
27738
|
}
|
|
26699
|
-
if (strict &&
|
|
27739
|
+
if (strict && contains2(invalidReferenceCodePoints, codePoint)) {
|
|
26700
27740
|
parseError("disallowed character reference");
|
|
26701
27741
|
}
|
|
26702
27742
|
if (codePoint > 65535) {
|
|
@@ -30184,7 +31224,7 @@ var require_filters = __commonJS((exports) => {
|
|
|
30184
31224
|
exports.filters = {
|
|
30185
31225
|
contains: function(next, text, _a) {
|
|
30186
31226
|
var adapter = _a.adapter;
|
|
30187
|
-
return function
|
|
31227
|
+
return function contains2(elem) {
|
|
30188
31228
|
return next(elem) && adapter.getText(elem).includes(text);
|
|
30189
31229
|
};
|
|
30190
31230
|
},
|
|
@@ -32297,7 +33337,7 @@ var init_snippet_utils = __esm(() => {
|
|
|
32297
33337
|
|
|
32298
33338
|
// ../cms/src/handlers/source-writer.ts
|
|
32299
33339
|
import fs8 from "fs/promises";
|
|
32300
|
-
import
|
|
33340
|
+
import path12 from "path";
|
|
32301
33341
|
async function handleUpdate(request, manifestWriter) {
|
|
32302
33342
|
const { changes, meta } = request;
|
|
32303
33343
|
const errors = [];
|
|
@@ -32334,7 +33374,7 @@ async function handleUpdate(request, manifestWriter) {
|
|
|
32334
33374
|
}
|
|
32335
33375
|
if (appliedCount > 0 && newContent !== currentContent) {
|
|
32336
33376
|
for (const op of fileOps) {
|
|
32337
|
-
await fs8.mkdir(
|
|
33377
|
+
await fs8.mkdir(path12.dirname(op.target), { recursive: true });
|
|
32338
33378
|
await fs8.writeFile(op.target, op.bytes);
|
|
32339
33379
|
}
|
|
32340
33380
|
await fs8.writeFile(fullPath, newContent, "utf-8");
|
|
@@ -32600,8 +33640,8 @@ async function tryRewriteAssetImport(content, importInfo, newSrc, absFilePath, o
|
|
|
32600
33640
|
const resolved = await resolveNewSrcBytes(newSrc, originUrl);
|
|
32601
33641
|
if (!resolved)
|
|
32602
33642
|
return null;
|
|
32603
|
-
const originalAssetAbs =
|
|
32604
|
-
const targetAbs = await pickSiblingTarget(
|
|
33643
|
+
const originalAssetAbs = path12.resolve(path12.dirname(absFilePath), importInfo.source);
|
|
33644
|
+
const targetAbs = await pickSiblingTarget(path12.dirname(originalAssetAbs), resolved.filename, resolved.bytes);
|
|
32605
33645
|
const newRelImport = relativeImportPath2(absFilePath, targetAbs);
|
|
32606
33646
|
const newContent = content.slice(0, importInfo.sourceStart) + newRelImport + content.slice(importInfo.sourceEnd);
|
|
32607
33647
|
return {
|
|
@@ -32611,8 +33651,8 @@ async function tryRewriteAssetImport(content, importInfo, newSrc, absFilePath, o
|
|
|
32611
33651
|
};
|
|
32612
33652
|
}
|
|
32613
33653
|
async function resolveNewSrcBytes(newSrc, originUrl) {
|
|
32614
|
-
const filenameFromPath = (p) =>
|
|
32615
|
-
const diskPath = newSrc.startsWith("/src/") ?
|
|
33654
|
+
const filenameFromPath = (p) => path12.basename(p.split("?")[0] ?? p);
|
|
33655
|
+
const diskPath = newSrc.startsWith("/src/") ? path12.join(getProjectRoot(), newSrc.slice(1)) : newSrc.startsWith("/") && !newSrc.startsWith("//") ? path12.join(getProjectRoot(), "public", newSrc.replace(/^\/+/, "")) : null;
|
|
32616
33656
|
if (diskPath) {
|
|
32617
33657
|
try {
|
|
32618
33658
|
return { bytes: await fs8.readFile(diskPath), filename: filenameFromPath(newSrc) };
|
|
@@ -33160,7 +34200,7 @@ var init_source_writer = __esm(() => {
|
|
|
33160
34200
|
});
|
|
33161
34201
|
|
|
33162
34202
|
// ../cms/src/handlers/api-routes.ts
|
|
33163
|
-
import
|
|
34203
|
+
import path13 from "path";
|
|
33164
34204
|
function requireMedia(ctx) {
|
|
33165
34205
|
if (!ctx.mediaAdapter) {
|
|
33166
34206
|
sendError(ctx.res, "Media storage not configured", 501);
|
|
@@ -33323,7 +34363,7 @@ var init_api_routes = __esm(() => {
|
|
|
33323
34363
|
sendJson(res, { success: false, error: `Invalid content path: ${body.filePath}` }, 400);
|
|
33324
34364
|
return;
|
|
33325
34365
|
}
|
|
33326
|
-
const fullPath =
|
|
34366
|
+
const fullPath = path13.resolve(getProjectRoot(), body.filePath.replace(/^\//, ""));
|
|
33327
34367
|
expectedDeletions.add(fullPath);
|
|
33328
34368
|
const result = await core.deleteEntry(entry.collection, entry.slug);
|
|
33329
34369
|
if (result.success) {
|
|
@@ -33430,7 +34470,7 @@ var init_api_routes = __esm(() => {
|
|
|
33430
34470
|
const body = await parseJsonBody(req);
|
|
33431
34471
|
const result = await core.deletePage(body);
|
|
33432
34472
|
if (result.success && result.filePath) {
|
|
33433
|
-
expectedDeletions.add(
|
|
34473
|
+
expectedDeletions.add(path13.resolve(getProjectRoot(), result.filePath));
|
|
33434
34474
|
}
|
|
33435
34475
|
if (result.success && body.createRedirect && body.redirectTo) {
|
|
33436
34476
|
await core.addRedirect({ source: body.pagePath, destination: body.redirectTo, statusCode: 307 });
|
|
@@ -33535,7 +34575,7 @@ var init_color_patterns = __esm(() => {
|
|
|
33535
34575
|
|
|
33536
34576
|
// ../cms/src/tailwind-colors.ts
|
|
33537
34577
|
import fs9 from "fs/promises";
|
|
33538
|
-
import
|
|
34578
|
+
import path14 from "path";
|
|
33539
34579
|
async function parseTailwindConfig(projectRoot = getProjectRoot()) {
|
|
33540
34580
|
const cssFiles = [
|
|
33541
34581
|
"src/styles/global.css",
|
|
@@ -33549,7 +34589,7 @@ async function parseTailwindConfig(projectRoot = getProjectRoot()) {
|
|
|
33549
34589
|
];
|
|
33550
34590
|
let customColors = [];
|
|
33551
34591
|
for (const cssFile of cssFiles) {
|
|
33552
|
-
const fullPath =
|
|
34592
|
+
const fullPath = path14.join(projectRoot, cssFile);
|
|
33553
34593
|
try {
|
|
33554
34594
|
const content = await fs9.readFile(fullPath, "utf-8");
|
|
33555
34595
|
customColors = extractColorsFromCss(content);
|
|
@@ -33622,7 +34662,7 @@ async function parseTextStyles(projectRoot = getProjectRoot()) {
|
|
|
33622
34662
|
];
|
|
33623
34663
|
let customTextStyles = {};
|
|
33624
34664
|
for (const cssFile of cssFiles) {
|
|
33625
|
-
const fullPath =
|
|
34665
|
+
const fullPath = path14.join(projectRoot, cssFile);
|
|
33626
34666
|
try {
|
|
33627
34667
|
const content = await fs9.readFile(fullPath, "utf-8");
|
|
33628
34668
|
customTextStyles = extractTextStylesFromCss(content);
|
|
@@ -34090,7 +35130,7 @@ var init_local_admin = __esm(() => {
|
|
|
34090
35130
|
|
|
34091
35131
|
// ../cms/src/manifest-writer.ts
|
|
34092
35132
|
import fs10 from "fs/promises";
|
|
34093
|
-
import
|
|
35133
|
+
import path15 from "path";
|
|
34094
35134
|
|
|
34095
35135
|
class ManifestWriter {
|
|
34096
35136
|
globalManifest;
|
|
@@ -34161,10 +35201,10 @@ class ManifestWriter {
|
|
|
34161
35201
|
}
|
|
34162
35202
|
getPageManifestPath(pagePath) {
|
|
34163
35203
|
if (pagePath === "/" || pagePath === "") {
|
|
34164
|
-
return
|
|
35204
|
+
return path15.join(this.outDir, "index.json");
|
|
34165
35205
|
}
|
|
34166
35206
|
const cleanPath = pagePath.replace(/^\//, "");
|
|
34167
|
-
return
|
|
35207
|
+
return path15.join(this.outDir, `${cleanPath}.json`);
|
|
34168
35208
|
}
|
|
34169
35209
|
addPage(pagePath, entries, components, collection, seo) {
|
|
34170
35210
|
this.pageManifests.set(pagePath, { entries, components, collection, seo });
|
|
@@ -34190,7 +35230,7 @@ class ManifestWriter {
|
|
|
34190
35230
|
}
|
|
34191
35231
|
async writePageManifest(pagePath, entries, components, collection, seo) {
|
|
34192
35232
|
const manifestPath = this.getPageManifestPath(pagePath);
|
|
34193
|
-
const manifestDir =
|
|
35233
|
+
const manifestDir = path15.dirname(manifestPath);
|
|
34194
35234
|
await fs10.mkdir(manifestDir, { recursive: true });
|
|
34195
35235
|
const metadata = {
|
|
34196
35236
|
version: MANIFEST_VERSION,
|
|
@@ -34246,7 +35286,7 @@ class ManifestWriter {
|
|
|
34246
35286
|
}
|
|
34247
35287
|
}
|
|
34248
35288
|
if (this.outDir) {
|
|
34249
|
-
const globalManifestPath =
|
|
35289
|
+
const globalManifestPath = path15.join(this.outDir, this.manifestFile);
|
|
34250
35290
|
const globalSettings = {
|
|
34251
35291
|
componentDefinitions: this.componentDefinitions,
|
|
34252
35292
|
pages
|
|
@@ -34858,7 +35898,7 @@ var init_field_types = __esm(() => {
|
|
|
34858
35898
|
|
|
34859
35899
|
// ../cms/src/migrate-astro-image.ts
|
|
34860
35900
|
import fs11 from "fs/promises";
|
|
34861
|
-
import
|
|
35901
|
+
import path16 from "path";
|
|
34862
35902
|
async function migrateAstroImages(options = {}) {
|
|
34863
35903
|
const projectRoot = options.projectRoot ?? getProjectRoot();
|
|
34864
35904
|
const dryRun = options.dryRun ?? false;
|
|
@@ -34870,7 +35910,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
34870
35910
|
if (astroFields.length === 0 || !def.entries)
|
|
34871
35911
|
continue;
|
|
34872
35912
|
for (const entry of def.entries) {
|
|
34873
|
-
const entryAbs =
|
|
35913
|
+
const entryAbs = path16.isAbsolute(entry.sourcePath) ? entry.sourcePath : path16.join(projectRoot, entry.sourcePath);
|
|
34874
35914
|
let raw;
|
|
34875
35915
|
try {
|
|
34876
35916
|
raw = await fs11.readFile(entryAbs, "utf-8");
|
|
@@ -34885,7 +35925,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
34885
35925
|
}
|
|
34886
35926
|
const [fullFm, fmStart, yamlBody, fmEnd] = fmMatch;
|
|
34887
35927
|
const doc2 = import_yaml7.parseDocument(yamlBody);
|
|
34888
|
-
let
|
|
35928
|
+
let mutated2 = false;
|
|
34889
35929
|
for (const field of astroFields) {
|
|
34890
35930
|
const current = doc2.get(field.name);
|
|
34891
35931
|
if (typeof current !== "string") {
|
|
@@ -34895,7 +35935,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
34895
35935
|
}
|
|
34896
35936
|
if (!current.startsWith("/") || current.startsWith("//"))
|
|
34897
35937
|
continue;
|
|
34898
|
-
const sourceAbs =
|
|
35938
|
+
const sourceAbs = path16.join(projectRoot, "public", current.replace(/^\/+/, ""));
|
|
34899
35939
|
let sourceBuf;
|
|
34900
35940
|
try {
|
|
34901
35941
|
sourceBuf = await fs11.readFile(sourceAbs);
|
|
@@ -34906,15 +35946,15 @@ async function migrateAstroImages(options = {}) {
|
|
|
34906
35946
|
const target = await pickAstroImageTarget({
|
|
34907
35947
|
entryAbsPath: entryAbs,
|
|
34908
35948
|
slug: entry.slug,
|
|
34909
|
-
originalFilename:
|
|
35949
|
+
originalFilename: path16.basename(current),
|
|
34910
35950
|
compareBuffer: sourceBuf
|
|
34911
35951
|
});
|
|
34912
35952
|
if (!dryRun) {
|
|
34913
|
-
await fs11.mkdir(
|
|
35953
|
+
await fs11.mkdir(path16.dirname(target.absPath), { recursive: true });
|
|
34914
35954
|
await fs11.writeFile(target.absPath, sourceBuf);
|
|
34915
35955
|
}
|
|
34916
35956
|
doc2.set(field.name, target.relPath);
|
|
34917
|
-
|
|
35957
|
+
mutated2 = true;
|
|
34918
35958
|
migrations.push({
|
|
34919
35959
|
entrySourcePath: entry.sourcePath,
|
|
34920
35960
|
fieldName: field.name,
|
|
@@ -34924,7 +35964,7 @@ async function migrateAstroImages(options = {}) {
|
|
|
34924
35964
|
copiedTo: target.absPath
|
|
34925
35965
|
});
|
|
34926
35966
|
}
|
|
34927
|
-
if (
|
|
35967
|
+
if (mutated2 && !dryRun) {
|
|
34928
35968
|
const newYaml = doc2.toString().replace(/\n$/, "");
|
|
34929
35969
|
const newRaw = raw.replace(fullFm, `${fmStart}${newYaml}${fmEnd}`);
|
|
34930
35970
|
await fs11.writeFile(entryAbs, newRaw, "utf-8");
|
|
@@ -34973,7 +36013,7 @@ var exports_migrate = {};
|
|
|
34973
36013
|
__export(exports_migrate, {
|
|
34974
36014
|
migrate: () => migrate
|
|
34975
36015
|
});
|
|
34976
|
-
import
|
|
36016
|
+
import path17 from "path";
|
|
34977
36017
|
async function migrate(args) {
|
|
34978
36018
|
if (args.target !== "astro-image") {
|
|
34979
36019
|
console.error(`Unknown migrate target: ${args.target}`);
|
|
@@ -34992,7 +36032,7 @@ async function migrate(args) {
|
|
|
34992
36032
|
for (const m2 of result.migrations) {
|
|
34993
36033
|
console.log(` ${m2.entrySourcePath}`);
|
|
34994
36034
|
console.log(` ${m2.fieldName}: ${m2.originalValue} \u2192 ${m2.newValue}`);
|
|
34995
|
-
console.log(` copy: ${
|
|
36035
|
+
console.log(` copy: ${path17.relative(process.cwd(), m2.copiedFrom)} \u2192 ${path17.relative(process.cwd(), m2.copiedTo)}`);
|
|
34996
36036
|
}
|
|
34997
36037
|
}
|
|
34998
36038
|
if (result.skipped.length > 0) {
|
|
@@ -35034,7 +36074,7 @@ Commands:`);
|
|
|
35034
36074
|
console.log(" build Run astro build with the Nua defaults");
|
|
35035
36075
|
console.log(" dev Run astro dev with the Nua defaults");
|
|
35036
36076
|
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");
|
|
36077
|
+
console.log(" check Validate collections and parse the source (no build). --json, --strict, --content-only, --no-live");
|
|
35038
36078
|
console.log(" init Convert a standard Astro project to use Nua");
|
|
35039
36079
|
console.log(" clean Eject to a standard Astro project (remove @nuasite/* deps)");
|
|
35040
36080
|
console.log(" migrate <target> Run a content migration. Targets: astro-image");
|
|
@@ -35048,13 +36088,8 @@ if (command && ["build", "dev", "preview"].includes(command)) {
|
|
|
35048
36088
|
} else {
|
|
35049
36089
|
switch (command) {
|
|
35050
36090
|
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
|
-
}));
|
|
36091
|
+
const { check: check2, parseCheckArgs: parseCheckArgs2 } = await Promise.resolve().then(() => (init_check2(), exports_check));
|
|
36092
|
+
process.exit(await check2(parseCheckArgs2(args, process.cwd())));
|
|
35058
36093
|
break;
|
|
35059
36094
|
}
|
|
35060
36095
|
case "init": {
|