@jxsuite/parser 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/content-loader.js +608 -284
- package/dist/content.js +183 -30
- package/dist/markdown.js +394 -352
- package/dist/serialize.js +262 -232
- package/package.json +5 -5
- package/src/alerts.ts +22 -7
- package/src/content-data.ts +40 -0
- package/src/content-links.ts +15 -7
- package/src/content-loader.ts +681 -221
- package/src/content-routes.ts +23 -10
- package/src/content-rules.ts +40 -29
- package/src/content.ts +108 -70
- package/src/csv.ts +18 -14
- package/src/dates.ts +18 -13
- package/src/highlight.ts +6 -5
- package/src/inert.ts +7 -5
- package/src/markdown.ts +27 -23
- package/src/md.ts +133 -64
- package/src/serialize.ts +367 -266
- package/src/transpile.ts +182 -152
- package/src/types.ts +43 -11
package/dist/content-loader.js
CHANGED
|
@@ -58,6 +58,77 @@ var __esm = (fn, res, err) => () => {
|
|
|
58
58
|
};
|
|
59
59
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
60
60
|
|
|
61
|
+
// ../../packages/schema/src/guards.ts
|
|
62
|
+
function isString(value) {
|
|
63
|
+
return typeof value === "string";
|
|
64
|
+
}
|
|
65
|
+
function isNumber(value) {
|
|
66
|
+
return typeof value === "number";
|
|
67
|
+
}
|
|
68
|
+
function isBoolean(value) {
|
|
69
|
+
return typeof value === "boolean";
|
|
70
|
+
}
|
|
71
|
+
function isNonNullObject(value) {
|
|
72
|
+
return typeof value === "object" && value !== null;
|
|
73
|
+
}
|
|
74
|
+
function isJsonObject(value) {
|
|
75
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76
|
+
}
|
|
77
|
+
function isRef(value) {
|
|
78
|
+
return isJsonObject(value) && typeof value.$ref === "string";
|
|
79
|
+
}
|
|
80
|
+
function isNodeObject(value) {
|
|
81
|
+
return isJsonObject(value);
|
|
82
|
+
}
|
|
83
|
+
function isPlainTagName(tagName) {
|
|
84
|
+
return typeof tagName === "string";
|
|
85
|
+
}
|
|
86
|
+
function isTagExpression(tagName) {
|
|
87
|
+
if (typeof tagName !== "object" || tagName === null || Array.isArray(tagName) || !isJsonObject(tagName.$expression)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const expression = tagName.$expression;
|
|
91
|
+
if (expression.operator === "?:") {
|
|
92
|
+
return typeof expression.value === "string" && typeof expression.initial === "string";
|
|
93
|
+
}
|
|
94
|
+
if (expression.operator === "switch") {
|
|
95
|
+
return isJsonObject(expression.cases) && Object.values(expression.cases).every((tag) => isString(tag)) && typeof expression.default === "string";
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
function tagNameCandidates(tagName) {
|
|
100
|
+
if (isPlainTagName(tagName)) {
|
|
101
|
+
return [tagName];
|
|
102
|
+
}
|
|
103
|
+
if (!isTagExpression(tagName)) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const expression = tagName.$expression;
|
|
107
|
+
if (expression.operator === "?:") {
|
|
108
|
+
return [...new Set([expression.value, expression.initial])];
|
|
109
|
+
}
|
|
110
|
+
return [...new Set([...Object.values(expression.cases), expression.default])];
|
|
111
|
+
}
|
|
112
|
+
var SCHEMA_KEYWORDS;
|
|
113
|
+
var init_guards = __esm(() => {
|
|
114
|
+
SCHEMA_KEYWORDS = new Set([
|
|
115
|
+
"type",
|
|
116
|
+
"enum",
|
|
117
|
+
"minimum",
|
|
118
|
+
"maximum",
|
|
119
|
+
"minLength",
|
|
120
|
+
"maxLength",
|
|
121
|
+
"pattern",
|
|
122
|
+
"format",
|
|
123
|
+
"items",
|
|
124
|
+
"properties",
|
|
125
|
+
"required",
|
|
126
|
+
"description",
|
|
127
|
+
"title",
|
|
128
|
+
"$comment"
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
|
|
61
132
|
// src/alerts.ts
|
|
62
133
|
function normalizeAlerts(config, where = "alerts") {
|
|
63
134
|
if (config === false) {
|
|
@@ -68,7 +139,7 @@ function normalizeAlerts(config, where = "alerts") {
|
|
|
68
139
|
if (config === undefined || config === true) {
|
|
69
140
|
return { disabled, types };
|
|
70
141
|
}
|
|
71
|
-
if (
|
|
142
|
+
if (!isJsonObject(config)) {
|
|
72
143
|
throw new TypeError(`${where}: expected an object mapping alert types to element names, or false`);
|
|
73
144
|
}
|
|
74
145
|
for (const [type, element] of Object.entries(config)) {
|
|
@@ -81,7 +152,7 @@ function normalizeAlerts(config, where = "alerts") {
|
|
|
81
152
|
disabled.add(key);
|
|
82
153
|
} else if (element === true) {
|
|
83
154
|
types.set(key, null);
|
|
84
|
-
} else if (
|
|
155
|
+
} else if (isString(element) && TAG_NAME.test(element)) {
|
|
85
156
|
types.set(key, element);
|
|
86
157
|
} else {
|
|
87
158
|
throw new TypeError(`${where}.${type}: expected an element name such as "doc-note", true or false, got ${JSON.stringify(element)}`);
|
|
@@ -92,7 +163,7 @@ function normalizeAlerts(config, where = "alerts") {
|
|
|
92
163
|
function inlineText(nodes) {
|
|
93
164
|
let out = "";
|
|
94
165
|
for (const node of nodes) {
|
|
95
|
-
if (
|
|
166
|
+
if (node.value !== undefined) {
|
|
96
167
|
out += node.value;
|
|
97
168
|
} else if (node.children) {
|
|
98
169
|
out += inlineText(node.children);
|
|
@@ -105,7 +176,7 @@ function textNode(value) {
|
|
|
105
176
|
}
|
|
106
177
|
function markerOf(paragraph) {
|
|
107
178
|
const [first] = paragraph.children ?? [];
|
|
108
|
-
if (first?.type !== "text" ||
|
|
179
|
+
if (first?.type !== "text" || first.value === undefined) {
|
|
109
180
|
return null;
|
|
110
181
|
}
|
|
111
182
|
const match = MARKER.exec(first.value);
|
|
@@ -131,7 +202,7 @@ function splitMarker(paragraph, settings) {
|
|
|
131
202
|
const take = (node) => {
|
|
132
203
|
if (inTitle && node.type === "break") {
|
|
133
204
|
inTitle = false;
|
|
134
|
-
} else if (inTitle && node.type === "text" &&
|
|
205
|
+
} else if (inTitle && node.type === "text" && node.value !== undefined) {
|
|
135
206
|
const lineEnd = LINE_END.exec(node.value);
|
|
136
207
|
if (lineEnd === null) {
|
|
137
208
|
title.push(node);
|
|
@@ -188,6 +259,7 @@ function transformAlerts(tree, settings, onUnknown) {
|
|
|
188
259
|
}
|
|
189
260
|
var DEFAULT_ALERT_TITLES, ALERT_NODE = "jxAlert", TAG_NAME, MARKER, LINE_END;
|
|
190
261
|
var init_alerts = __esm(() => {
|
|
262
|
+
init_guards();
|
|
191
263
|
DEFAULT_ALERT_TITLES = {
|
|
192
264
|
caution: "Caution",
|
|
193
265
|
important: "Important",
|
|
@@ -334,7 +406,7 @@ function compileExclude(patterns) {
|
|
|
334
406
|
const dirs = [];
|
|
335
407
|
let everything = false;
|
|
336
408
|
for (const pattern of patterns) {
|
|
337
|
-
if (
|
|
409
|
+
if (!isString(pattern) || pattern.trim() === "") {
|
|
338
410
|
throw new TypeError(`"exclude" entries must be non-empty strings, got ${JSON.stringify(pattern)}`);
|
|
339
411
|
}
|
|
340
412
|
if (pattern.startsWith("!")) {
|
|
@@ -408,21 +480,21 @@ function readField(data, key) {
|
|
|
408
480
|
if (!key.includes(".")) {
|
|
409
481
|
return;
|
|
410
482
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
current = current[part];
|
|
483
|
+
return readPath(data, key.split("."));
|
|
484
|
+
}
|
|
485
|
+
function readPath(holder, [part, ...rest]) {
|
|
486
|
+
if (part === undefined || !isPlainObject(holder) || !Object.hasOwn(holder, part)) {
|
|
487
|
+
return;
|
|
417
488
|
}
|
|
418
|
-
|
|
489
|
+
const value = holder[part];
|
|
490
|
+
return rest.length === 0 ? value : readPath(value, rest);
|
|
419
491
|
}
|
|
420
492
|
function order(actual, operand) {
|
|
421
493
|
const a = comparable(actual);
|
|
422
|
-
if (
|
|
494
|
+
if (isNumber(a) && isNumber(operand)) {
|
|
423
495
|
return a - operand;
|
|
424
496
|
}
|
|
425
|
-
if (
|
|
497
|
+
if (isString(a) && isString(operand)) {
|
|
426
498
|
return a < operand ? -1 : a > operand ? 1 : 0;
|
|
427
499
|
}
|
|
428
500
|
return;
|
|
@@ -444,13 +516,13 @@ function operatorTest(where, op, operand) {
|
|
|
444
516
|
return op === "$in" ? hit : (actual) => !hit(actual);
|
|
445
517
|
}
|
|
446
518
|
case "$exists": {
|
|
447
|
-
if (
|
|
519
|
+
if (!isBoolean(operand)) {
|
|
448
520
|
throw new TypeError(`${where}.$exists: expected true or false, got ${JSON.stringify(operand)}`);
|
|
449
521
|
}
|
|
450
522
|
return (actual) => (actual !== undefined && actual !== null) === operand;
|
|
451
523
|
}
|
|
452
524
|
default: {
|
|
453
|
-
if (
|
|
525
|
+
if (!isString(operand) && !isNumber(operand)) {
|
|
454
526
|
throw new TypeError(`${where}.${op}: expected a string or number, got ${JSON.stringify(operand)}`);
|
|
455
527
|
}
|
|
456
528
|
const test = (diff) => op === "$gt" ? diff > 0 : op === "$gte" ? diff >= 0 : op === "$lt" ? diff < 0 : diff <= 0;
|
|
@@ -508,6 +580,7 @@ function compileWhere(where) {
|
|
|
508
580
|
}
|
|
509
581
|
var nothing = () => {}, REGEXP_SPECIAL, MAX_STAR_RUNS = 4, OPERATORS;
|
|
510
582
|
var init_content_rules = __esm(() => {
|
|
583
|
+
init_guards();
|
|
511
584
|
REGEXP_SPECIAL = /[$()*+.?[\\\]^{|}]/g;
|
|
512
585
|
OPERATORS = new Set(["$eq", "$ne", "$in", "$nin", "$exists", "$gt", "$gte", "$lt", "$lte"]);
|
|
513
586
|
});
|
|
@@ -25568,14 +25641,14 @@ var init_decode_codepoint = __esm(() => {
|
|
|
25568
25641
|
});
|
|
25569
25642
|
|
|
25570
25643
|
// ../../node_modules/parse5/node_modules/entities/dist/esm/decode.js
|
|
25571
|
-
function
|
|
25644
|
+
function isNumber2(code) {
|
|
25572
25645
|
return code >= CharCodes.ZERO && code <= CharCodes.NINE;
|
|
25573
25646
|
}
|
|
25574
25647
|
function isHexadecimalCharacter(code) {
|
|
25575
25648
|
return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F;
|
|
25576
25649
|
}
|
|
25577
25650
|
function isAsciiAlphaNumeric(code) {
|
|
25578
|
-
return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z ||
|
|
25651
|
+
return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z || isNumber2(code);
|
|
25579
25652
|
}
|
|
25580
25653
|
function isEntityInAttributeInvalidEnd(code) {
|
|
25581
25654
|
return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code);
|
|
@@ -25649,7 +25722,7 @@ class EntityDecoder {
|
|
|
25649
25722
|
const startIndex = offset;
|
|
25650
25723
|
while (offset < input.length) {
|
|
25651
25724
|
const char = input.charCodeAt(offset);
|
|
25652
|
-
if (
|
|
25725
|
+
if (isNumber2(char) || isHexadecimalCharacter(char)) {
|
|
25653
25726
|
offset += 1;
|
|
25654
25727
|
} else {
|
|
25655
25728
|
this.addToNumericResult(input, startIndex, offset, 16);
|
|
@@ -25663,7 +25736,7 @@ class EntityDecoder {
|
|
|
25663
25736
|
const startIndex = offset;
|
|
25664
25737
|
while (offset < input.length) {
|
|
25665
25738
|
const char = input.charCodeAt(offset);
|
|
25666
|
-
if (
|
|
25739
|
+
if (isNumber2(char)) {
|
|
25667
25740
|
offset += 1;
|
|
25668
25741
|
} else {
|
|
25669
25742
|
this.addToNumericResult(input, startIndex, offset, 10);
|
|
@@ -33099,11 +33172,11 @@ function convertHastNode(node) {
|
|
|
33099
33172
|
if (whitespace(node)) {
|
|
33100
33173
|
return null;
|
|
33101
33174
|
}
|
|
33102
|
-
return node.value
|
|
33175
|
+
return node.value;
|
|
33103
33176
|
}
|
|
33104
33177
|
if (node.type === "element") {
|
|
33105
33178
|
const el = { tagName: node.tagName };
|
|
33106
|
-
if (
|
|
33179
|
+
if (Object.keys(node.properties).length > 0) {
|
|
33107
33180
|
const { style, attrs } = hastPropsToJx(node.properties);
|
|
33108
33181
|
if (Object.keys(attrs).length > 0) {
|
|
33109
33182
|
el.attributes = attrs;
|
|
@@ -33112,8 +33185,8 @@ function convertHastNode(node) {
|
|
|
33112
33185
|
el.style = style;
|
|
33113
33186
|
}
|
|
33114
33187
|
}
|
|
33115
|
-
const kids =
|
|
33116
|
-
if (kids.length === 1 &&
|
|
33188
|
+
const kids = convertHastChildren(node.children);
|
|
33189
|
+
if (kids.length === 1 && isString(kids[0])) {
|
|
33117
33190
|
[el.textContent] = kids;
|
|
33118
33191
|
} else if (kids.length > 0) {
|
|
33119
33192
|
el.children = kids;
|
|
@@ -33131,7 +33204,7 @@ function hastPropsToJx(properties) {
|
|
|
33131
33204
|
}
|
|
33132
33205
|
const info = find(html3, key);
|
|
33133
33206
|
const name = info.attribute;
|
|
33134
|
-
if (name === "style" &&
|
|
33207
|
+
if (name === "style" && isString(value)) {
|
|
33135
33208
|
parseInlineStyle(value, style);
|
|
33136
33209
|
continue;
|
|
33137
33210
|
}
|
|
@@ -33162,6 +33235,7 @@ var init_html_to_jx = __esm(() => {
|
|
|
33162
33235
|
init_hast_util_from_html();
|
|
33163
33236
|
init_hast_util_whitespace();
|
|
33164
33237
|
init_property_information();
|
|
33238
|
+
init_guards();
|
|
33165
33239
|
});
|
|
33166
33240
|
|
|
33167
33241
|
// src/transpile.ts
|
|
@@ -33177,21 +33251,24 @@ function jxKey(key) {
|
|
|
33177
33251
|
function expandDotPaths(attrs) {
|
|
33178
33252
|
const result = {};
|
|
33179
33253
|
for (const [key, value] of Object.entries(attrs)) {
|
|
33180
|
-
const
|
|
33181
|
-
if (
|
|
33254
|
+
const lastDot = key.lastIndexOf(".");
|
|
33255
|
+
if (lastDot === -1) {
|
|
33182
33256
|
result[jxKey(key)] = value;
|
|
33183
33257
|
continue;
|
|
33184
33258
|
}
|
|
33185
|
-
const segments = key.split(".");
|
|
33186
33259
|
let target = result;
|
|
33187
|
-
for (
|
|
33188
|
-
const seg = jxKey(
|
|
33189
|
-
|
|
33190
|
-
|
|
33260
|
+
for (const segment of key.slice(0, lastDot).split(".")) {
|
|
33261
|
+
const seg = jxKey(segment);
|
|
33262
|
+
const existing = seg in target ? target[seg] : undefined;
|
|
33263
|
+
if (isNonNullObject(existing)) {
|
|
33264
|
+
target = existing;
|
|
33265
|
+
} else {
|
|
33266
|
+
const nested = {};
|
|
33267
|
+
target[seg] = nested;
|
|
33268
|
+
target = nested;
|
|
33191
33269
|
}
|
|
33192
|
-
target = target[seg];
|
|
33193
33270
|
}
|
|
33194
|
-
target[jxKey(
|
|
33271
|
+
target[jxKey(key.slice(lastDot + 1))] = value;
|
|
33195
33272
|
}
|
|
33196
33273
|
return result;
|
|
33197
33274
|
}
|
|
@@ -33212,22 +33289,34 @@ function applyStyleKeyMapping(styleObj) {
|
|
|
33212
33289
|
}
|
|
33213
33290
|
function routeAttributes(attrs) {
|
|
33214
33291
|
const expanded = expandDotPaths(attrs);
|
|
33215
|
-
|
|
33216
|
-
|
|
33292
|
+
const { style } = expanded;
|
|
33293
|
+
if (isNonNullObject(style)) {
|
|
33294
|
+
expanded.style = applyStyleKeyMapping(style);
|
|
33217
33295
|
}
|
|
33218
33296
|
const props = {};
|
|
33219
33297
|
const attributes = {};
|
|
33220
33298
|
for (const [key, value] of Object.entries(expanded)) {
|
|
33221
33299
|
if (HTML_ATTR_PATTERN.test(key)) {
|
|
33222
|
-
attributes[key] = value;
|
|
33300
|
+
attributes[key] = attributeValue(value);
|
|
33223
33301
|
} else {
|
|
33224
33302
|
props[key] = value;
|
|
33225
33303
|
}
|
|
33226
33304
|
}
|
|
33227
33305
|
return { attributes, props };
|
|
33228
33306
|
}
|
|
33307
|
+
function attributeValue(value) {
|
|
33308
|
+
return value;
|
|
33309
|
+
}
|
|
33310
|
+
function setContent(el, children) {
|
|
33311
|
+
const [only] = children;
|
|
33312
|
+
if (children.length === 1 && isString(only)) {
|
|
33313
|
+
el.textContent = only;
|
|
33314
|
+
} else if (children.length > 0) {
|
|
33315
|
+
el.children = children;
|
|
33316
|
+
}
|
|
33317
|
+
}
|
|
33229
33318
|
function mdastNodeToJx(node) {
|
|
33230
|
-
if (!node
|
|
33319
|
+
if (!node) {
|
|
33231
33320
|
return null;
|
|
33232
33321
|
}
|
|
33233
33322
|
if (node.type === "yaml" || node.type === "toml") {
|
|
@@ -33248,7 +33337,7 @@ function mdastNodeToJx(node) {
|
|
|
33248
33337
|
}
|
|
33249
33338
|
return null;
|
|
33250
33339
|
}
|
|
33251
|
-
const tagFn = JX_TAG_MAP
|
|
33340
|
+
const tagFn = JX_TAG_MAP.get(node.type);
|
|
33252
33341
|
if (!tagFn) {
|
|
33253
33342
|
return null;
|
|
33254
33343
|
}
|
|
@@ -33264,12 +33353,7 @@ function mdastNodeToJx(node) {
|
|
|
33264
33353
|
case "listItem":
|
|
33265
33354
|
case "tableRow":
|
|
33266
33355
|
case "tableCell": {
|
|
33267
|
-
|
|
33268
|
-
if (children.length === 1 && typeof children[0] === "string") {
|
|
33269
|
-
[el.textContent] = children;
|
|
33270
|
-
} else if (children.length > 0) {
|
|
33271
|
-
el.children = children;
|
|
33272
|
-
}
|
|
33356
|
+
setContent(el, convertChildren(node.children ?? []));
|
|
33273
33357
|
break;
|
|
33274
33358
|
}
|
|
33275
33359
|
case "inlineCode": {
|
|
@@ -33277,34 +33361,18 @@ function mdastNodeToJx(node) {
|
|
|
33277
33361
|
break;
|
|
33278
33362
|
}
|
|
33279
33363
|
case "link": {
|
|
33280
|
-
{
|
|
33281
|
-
|
|
33282
|
-
|
|
33283
|
-
};
|
|
33284
|
-
if (node.title) {
|
|
33285
|
-
linkAttrs.title = node.title;
|
|
33286
|
-
}
|
|
33287
|
-
el.attributes = linkAttrs;
|
|
33288
|
-
}
|
|
33289
|
-
{
|
|
33290
|
-
const children = convertChildren(node.children ?? []);
|
|
33291
|
-
if (children.length === 1 && typeof children[0] === "string") {
|
|
33292
|
-
[el.textContent] = children;
|
|
33293
|
-
} else if (children.length > 0) {
|
|
33294
|
-
el.children = children;
|
|
33295
|
-
}
|
|
33364
|
+
el.attributes = { href: node.url ?? "" };
|
|
33365
|
+
if (node.title) {
|
|
33366
|
+
el.attributes.title = node.title;
|
|
33296
33367
|
}
|
|
33368
|
+
setContent(el, convertChildren(node.children ?? []));
|
|
33297
33369
|
break;
|
|
33298
33370
|
}
|
|
33299
33371
|
case "image": {
|
|
33300
|
-
|
|
33301
|
-
alt: node.alt ?? "",
|
|
33302
|
-
src: node.url ?? ""
|
|
33303
|
-
};
|
|
33372
|
+
el.attributes = { alt: node.alt ?? "", src: node.url ?? "" };
|
|
33304
33373
|
if (node.title) {
|
|
33305
|
-
|
|
33374
|
+
el.attributes.title = node.title;
|
|
33306
33375
|
}
|
|
33307
|
-
el.attributes = imageAttrs;
|
|
33308
33376
|
break;
|
|
33309
33377
|
}
|
|
33310
33378
|
case "list": {
|
|
@@ -33317,13 +33385,11 @@ function mdastNodeToJx(node) {
|
|
|
33317
33385
|
break;
|
|
33318
33386
|
}
|
|
33319
33387
|
case "code": {
|
|
33320
|
-
|
|
33321
|
-
|
|
33322
|
-
|
|
33323
|
-
|
|
33324
|
-
|
|
33325
|
-
}
|
|
33326
|
-
];
|
|
33388
|
+
const code = { tagName: "code", textContent: node.value ?? null };
|
|
33389
|
+
if (node.lang) {
|
|
33390
|
+
code.className = `language-${node.lang}`;
|
|
33391
|
+
}
|
|
33392
|
+
el.children = [code];
|
|
33327
33393
|
break;
|
|
33328
33394
|
}
|
|
33329
33395
|
case "thematicBreak":
|
|
@@ -33333,16 +33399,21 @@ function mdastNodeToJx(node) {
|
|
|
33333
33399
|
case "table": {
|
|
33334
33400
|
const rows = convertChildren(node.children ?? []);
|
|
33335
33401
|
const [headerRow] = rows;
|
|
33336
|
-
if (headerRow &&
|
|
33402
|
+
if (isNonNullObject(headerRow) && Array.isArray(headerRow.children)) {
|
|
33337
33403
|
for (const cell of headerRow.children) {
|
|
33338
|
-
if (cell &&
|
|
33404
|
+
if (isNonNullObject(cell) && cell.tagName === "td") {
|
|
33339
33405
|
cell.tagName = "th";
|
|
33340
33406
|
}
|
|
33341
33407
|
}
|
|
33342
33408
|
}
|
|
33343
|
-
const
|
|
33344
|
-
|
|
33345
|
-
|
|
33409
|
+
const sections = [];
|
|
33410
|
+
if (headerRow !== undefined) {
|
|
33411
|
+
sections.push({ children: [headerRow], tagName: "thead" });
|
|
33412
|
+
}
|
|
33413
|
+
if (rows.length > 1) {
|
|
33414
|
+
sections.push({ children: rows.slice(1), tagName: "tbody" });
|
|
33415
|
+
}
|
|
33416
|
+
el.children = sections;
|
|
33346
33417
|
break;
|
|
33347
33418
|
}
|
|
33348
33419
|
default: {
|
|
@@ -33354,10 +33425,7 @@ function mdastNodeToJx(node) {
|
|
|
33354
33425
|
function alertToJx(node) {
|
|
33355
33426
|
const body = convertChildren(node.children ?? []);
|
|
33356
33427
|
if (node.element) {
|
|
33357
|
-
const attributes = { "data-alert": node.alert };
|
|
33358
|
-
if (node.titleNodes) {
|
|
33359
|
-
attributes["data-title"] = inlineText(node.titleNodes).trim();
|
|
33360
|
-
}
|
|
33428
|
+
const attributes = node.titleNodes ? { "data-alert": node.alert, "data-title": inlineText(node.titleNodes).trim() } : { "data-alert": node.alert };
|
|
33361
33429
|
const el = { attributes, tagName: node.element };
|
|
33362
33430
|
if (body.length > 0) {
|
|
33363
33431
|
el.children = body;
|
|
@@ -33367,8 +33435,9 @@ function alertToJx(node) {
|
|
|
33367
33435
|
const title = { className: "jx-alert-title", tagName: "p" };
|
|
33368
33436
|
if (node.titleNodes) {
|
|
33369
33437
|
const inline = convertChildren(node.titleNodes);
|
|
33370
|
-
|
|
33371
|
-
|
|
33438
|
+
const [only] = inline;
|
|
33439
|
+
if (inline.length === 1 && isString(only)) {
|
|
33440
|
+
title.textContent = only;
|
|
33372
33441
|
} else {
|
|
33373
33442
|
title.children = inline;
|
|
33374
33443
|
}
|
|
@@ -33383,13 +33452,14 @@ function alertToJx(node) {
|
|
|
33383
33452
|
};
|
|
33384
33453
|
}
|
|
33385
33454
|
function directiveToJx(node) {
|
|
33386
|
-
|
|
33387
|
-
|
|
33455
|
+
const name = node.name;
|
|
33456
|
+
if (PROTOTYPE_DIRECTIVE_NAMES.has(name)) {
|
|
33457
|
+
return prototypeDirectiveToJx(node, name);
|
|
33388
33458
|
}
|
|
33389
|
-
const el = { tagName:
|
|
33459
|
+
const el = { tagName: name };
|
|
33390
33460
|
if (node.attributes && Object.keys(node.attributes).length > 0) {
|
|
33391
33461
|
const { props, attributes } = routeAttributes(node.attributes);
|
|
33392
|
-
const isCustomElement =
|
|
33462
|
+
const isCustomElement = name.includes("-");
|
|
33393
33463
|
if (isCustomElement) {
|
|
33394
33464
|
for (const [key, value] of Object.entries(props)) {
|
|
33395
33465
|
if (key === "style" || key === "children" || key === "textContent" || key === "innerHTML" || key.startsWith("$")) {
|
|
@@ -33398,7 +33468,7 @@ function directiveToJx(node) {
|
|
|
33398
33468
|
if (!el.attributes) {
|
|
33399
33469
|
el.attributes = {};
|
|
33400
33470
|
}
|
|
33401
|
-
el.attributes[key] = value;
|
|
33471
|
+
el.attributes[key] = attributeValue(value);
|
|
33402
33472
|
}
|
|
33403
33473
|
}
|
|
33404
33474
|
} else {
|
|
@@ -33409,7 +33479,7 @@ function directiveToJx(node) {
|
|
|
33409
33479
|
if (!el.attributes) {
|
|
33410
33480
|
el.attributes = {};
|
|
33411
33481
|
}
|
|
33412
|
-
el.attributes[key] = value;
|
|
33482
|
+
el.attributes[key] = attributeValue(value);
|
|
33413
33483
|
}
|
|
33414
33484
|
}
|
|
33415
33485
|
}
|
|
@@ -33419,12 +33489,7 @@ function directiveToJx(node) {
|
|
|
33419
33489
|
}
|
|
33420
33490
|
if (node.type === "textDirective") {
|
|
33421
33491
|
if (node.children && node.children.length > 0) {
|
|
33422
|
-
|
|
33423
|
-
if (children.length === 1 && typeof children[0] === "string") {
|
|
33424
|
-
[el.textContent] = children;
|
|
33425
|
-
} else if (children.length > 0) {
|
|
33426
|
-
el.children = children;
|
|
33427
|
-
}
|
|
33492
|
+
setContent(el, convertChildren(node.children));
|
|
33428
33493
|
}
|
|
33429
33494
|
return el;
|
|
33430
33495
|
}
|
|
@@ -33433,7 +33498,7 @@ function directiveToJx(node) {
|
|
|
33433
33498
|
}
|
|
33434
33499
|
if (node.children && node.children.length > 0) {
|
|
33435
33500
|
const jxChildren = [];
|
|
33436
|
-
const isPhrasingParent = PHRASING_ELEMENTS.has(
|
|
33501
|
+
const isPhrasingParent = PHRASING_ELEMENTS.has(name);
|
|
33437
33502
|
for (const child of node.children) {
|
|
33438
33503
|
if (isPhrasingParent && child.type === "paragraph") {
|
|
33439
33504
|
for (const inline of child.children ?? []) {
|
|
@@ -33459,16 +33524,14 @@ function directiveToJx(node) {
|
|
|
33459
33524
|
}
|
|
33460
33525
|
}
|
|
33461
33526
|
}
|
|
33462
|
-
if (el.children
|
|
33463
|
-
|
|
33464
|
-
} else if (jxChildren.length > 0) {
|
|
33465
|
-
el.children = jxChildren;
|
|
33527
|
+
if (!isNonNullObject(el.children) || Array.isArray(el.children)) {
|
|
33528
|
+
setContent(el, jxChildren);
|
|
33466
33529
|
}
|
|
33467
33530
|
}
|
|
33468
33531
|
return el;
|
|
33469
33532
|
}
|
|
33470
|
-
function prototypeDirectiveToJx(node) {
|
|
33471
|
-
const el = { $prototype:
|
|
33533
|
+
function prototypeDirectiveToJx(node, name) {
|
|
33534
|
+
const el = { $prototype: name };
|
|
33472
33535
|
if (node.attributes && Object.keys(node.attributes).length > 0) {
|
|
33473
33536
|
const expanded = expandDotPaths(node.attributes);
|
|
33474
33537
|
for (const [key, value] of Object.entries(expanded)) {
|
|
@@ -33479,7 +33542,7 @@ function prototypeDirectiveToJx(node) {
|
|
|
33479
33542
|
}
|
|
33480
33543
|
}
|
|
33481
33544
|
const children = convertChildren(node.children ?? []);
|
|
33482
|
-
const template = children.find((c) => c
|
|
33545
|
+
const template = children.find((c) => !isString(c));
|
|
33483
33546
|
if (template) {
|
|
33484
33547
|
el.map = template;
|
|
33485
33548
|
}
|
|
@@ -33489,17 +33552,19 @@ function convertChildren(children) {
|
|
|
33489
33552
|
if (!children) {
|
|
33490
33553
|
return [];
|
|
33491
33554
|
}
|
|
33492
|
-
return children.flatMap((n) => mdastNodeToJx(n)
|
|
33555
|
+
return children.flatMap((n) => mdastNodeToJx(n) ?? []);
|
|
33493
33556
|
}
|
|
33494
33557
|
function transpileJxMarkdown(source) {
|
|
33495
33558
|
const processor = unified().use(remarkParse).use(remarkFrontmatter, ["yaml"]).use(import_remark_parse_frontmatter.default).use(remarkGfm).use(remarkDirective);
|
|
33496
33559
|
const tree = processor.parse(source);
|
|
33497
33560
|
const vfile = { data: {} };
|
|
33498
33561
|
processor.runSync(tree, vfile);
|
|
33499
|
-
const frontmatter = vfile.data
|
|
33562
|
+
const frontmatter = vfile.data.frontmatter ?? {};
|
|
33500
33563
|
const doc = {};
|
|
33501
|
-
|
|
33502
|
-
|
|
33564
|
+
if (!isNumber(frontmatter) && !isBoolean(frontmatter)) {
|
|
33565
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
33566
|
+
doc[key] = value;
|
|
33567
|
+
}
|
|
33503
33568
|
}
|
|
33504
33569
|
const bodyNodes = tree.children.filter((n) => n.type !== "yaml" && n.type !== "toml");
|
|
33505
33570
|
const children = [];
|
|
@@ -33523,10 +33588,10 @@ function slugifyHeading(text) {
|
|
|
33523
33588
|
return text.normalize("NFC").toLowerCase().replaceAll(/[^\p{L}\p{N}_\s-]/gu, "").replaceAll(/\s+/gu, "-").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
|
|
33524
33589
|
}
|
|
33525
33590
|
function jxNodeText(node) {
|
|
33526
|
-
if (
|
|
33591
|
+
if (isString(node)) {
|
|
33527
33592
|
return node;
|
|
33528
33593
|
}
|
|
33529
|
-
let out =
|
|
33594
|
+
let out = isString(node.textContent) ? node.textContent : "";
|
|
33530
33595
|
if (Array.isArray(node.children)) {
|
|
33531
33596
|
for (const child of node.children) {
|
|
33532
33597
|
out += jxNodeText(child);
|
|
@@ -33539,13 +33604,13 @@ function assignHeadingIds(children) {
|
|
|
33539
33604
|
const seen = new Map;
|
|
33540
33605
|
function walk(nodes) {
|
|
33541
33606
|
for (const node of nodes) {
|
|
33542
|
-
if (
|
|
33607
|
+
if (!isNonNullObject(node)) {
|
|
33543
33608
|
continue;
|
|
33544
33609
|
}
|
|
33545
|
-
const tag =
|
|
33610
|
+
const tag = isString(node.tagName) ? node.tagName : "";
|
|
33546
33611
|
if (tag && HEADING_TAGS.has(tag)) {
|
|
33547
33612
|
const text = jxNodeText(node).trim();
|
|
33548
|
-
let id =
|
|
33613
|
+
let id = isString(node.id) && node.id.length > 0 ? node.id : "";
|
|
33549
33614
|
if (id) {
|
|
33550
33615
|
seen.set(id, (seen.get(id) ?? 0) + 1);
|
|
33551
33616
|
} else {
|
|
@@ -33575,6 +33640,7 @@ var init_transpile = __esm(() => {
|
|
|
33575
33640
|
init_remark_gfm();
|
|
33576
33641
|
init_remark_directive();
|
|
33577
33642
|
init_html_to_jx();
|
|
33643
|
+
init_guards();
|
|
33578
33644
|
init_alerts();
|
|
33579
33645
|
init_html_to_jx();
|
|
33580
33646
|
import_remark_parse_frontmatter = __toESM(require_dist2(), 1);
|
|
@@ -33642,25 +33708,25 @@ var init_transpile = __esm(() => {
|
|
|
33642
33708
|
"summary",
|
|
33643
33709
|
"dt"
|
|
33644
33710
|
]);
|
|
33645
|
-
JX_TAG_MAP =
|
|
33646
|
-
blockquote
|
|
33647
|
-
break
|
|
33648
|
-
code
|
|
33649
|
-
delete
|
|
33650
|
-
emphasis
|
|
33651
|
-
heading
|
|
33652
|
-
image
|
|
33653
|
-
inlineCode
|
|
33654
|
-
link
|
|
33655
|
-
list
|
|
33656
|
-
listItem
|
|
33657
|
-
paragraph
|
|
33658
|
-
strong
|
|
33659
|
-
table
|
|
33660
|
-
tableCell
|
|
33661
|
-
tableRow
|
|
33662
|
-
thematicBreak
|
|
33663
|
-
|
|
33711
|
+
JX_TAG_MAP = new Map([
|
|
33712
|
+
["blockquote", () => "blockquote"],
|
|
33713
|
+
["break", () => "br"],
|
|
33714
|
+
["code", () => "pre"],
|
|
33715
|
+
["delete", () => "del"],
|
|
33716
|
+
["emphasis", () => "em"],
|
|
33717
|
+
["heading", (n) => `h${n.depth}`],
|
|
33718
|
+
["image", () => "img"],
|
|
33719
|
+
["inlineCode", () => "code"],
|
|
33720
|
+
["link", () => "a"],
|
|
33721
|
+
["list", (n) => n.ordered ? "ol" : "ul"],
|
|
33722
|
+
["listItem", () => "li"],
|
|
33723
|
+
["paragraph", () => "p"],
|
|
33724
|
+
["strong", () => "strong"],
|
|
33725
|
+
["table", () => "table"],
|
|
33726
|
+
["tableCell", (n) => n.isHeader ? "th" : "td"],
|
|
33727
|
+
["tableRow", () => "tr"],
|
|
33728
|
+
["thematicBreak", () => "hr"]
|
|
33729
|
+
]);
|
|
33664
33730
|
HEADING_TAGS = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
33665
33731
|
});
|
|
33666
33732
|
|
|
@@ -33724,6 +33790,10 @@ import { existsSync, readFileSync, readdirSync as readdirSync2, statSync as stat
|
|
|
33724
33790
|
import { basename, dirname, extname, relative, resolve } from "node:path";
|
|
33725
33791
|
|
|
33726
33792
|
// ../../packages/schema/src/asset-paths.ts
|
|
33793
|
+
init_guards();
|
|
33794
|
+
var CONTENT_SECTION_KEY = "content";
|
|
33795
|
+
var SAFE_CONTENT_TYPE_NAME = /^[\w.~-]+$/;
|
|
33796
|
+
var LOCALE_PLACEHOLDER = "{locale}";
|
|
33727
33797
|
function normalizeDir(value) {
|
|
33728
33798
|
const forward = value.replaceAll("\\", "/");
|
|
33729
33799
|
return forward.length > 1 && forward.endsWith("/") ? forward.slice(0, -1) : forward;
|
|
@@ -33758,6 +33828,7 @@ function assetUrlFor(mounts, absolutePath) {
|
|
|
33758
33828
|
var URL_TERMINATOR = String.raw`"'\s<>()\\`;
|
|
33759
33829
|
|
|
33760
33830
|
// ../../packages/schema/src/locale.ts
|
|
33831
|
+
init_guards();
|
|
33761
33832
|
var RTL_SCRIPTS = new Set([
|
|
33762
33833
|
"Adlm",
|
|
33763
33834
|
"Arab",
|
|
@@ -33800,7 +33871,7 @@ var RTL_SCRIPTS = new Set([
|
|
|
33800
33871
|
"Yezi"
|
|
33801
33872
|
]);
|
|
33802
33873
|
function canonicalizeLocale(tag) {
|
|
33803
|
-
if (
|
|
33874
|
+
if (!isString(tag) || tag.trim() === "") {
|
|
33804
33875
|
return null;
|
|
33805
33876
|
}
|
|
33806
33877
|
try {
|
|
@@ -33813,7 +33884,7 @@ var DEFAULT_ROUTING = "prefix-except-default";
|
|
|
33813
33884
|
function resolveI18n(projectConfig) {
|
|
33814
33885
|
const raw = projectConfig.i18n;
|
|
33815
33886
|
const errors = [];
|
|
33816
|
-
if (!raw
|
|
33887
|
+
if (!isNonNullObject(raw)) {
|
|
33817
33888
|
return { errors, i18n: null };
|
|
33818
33889
|
}
|
|
33819
33890
|
const declared = Array.isArray(raw.locales) ? raw.locales : [];
|
|
@@ -33857,9 +33928,14 @@ function localeUrlPrefix(locale, i18n) {
|
|
|
33857
33928
|
}
|
|
33858
33929
|
|
|
33859
33930
|
// src/content-loader.ts
|
|
33931
|
+
init_guards();
|
|
33860
33932
|
init_alerts();
|
|
33861
33933
|
|
|
33934
|
+
// src/content-links.ts
|
|
33935
|
+
init_guards();
|
|
33936
|
+
|
|
33862
33937
|
// src/content-routes.ts
|
|
33938
|
+
init_guards();
|
|
33863
33939
|
init_content_rules();
|
|
33864
33940
|
function slugifySegment(text) {
|
|
33865
33941
|
return text.normalize("NFKD").replaceAll(/(?<=\p{Script=Latin})\p{M}+/gu, "").normalize("NFC").replaceAll("&", " and ").replaceAll(/['’]/g, "").toLowerCase().replaceAll(/[^\p{L}\p{M}\p{N}]+/gu, "-").replaceAll(/^-+|-+$/g, "");
|
|
@@ -33874,13 +33950,13 @@ var TRANSFORMS = {
|
|
|
33874
33950
|
upper: (value) => value.toUpperCase()
|
|
33875
33951
|
};
|
|
33876
33952
|
function parseRouteTemplate(source, where) {
|
|
33877
|
-
if (
|
|
33953
|
+
if (!isString(source) || !source.startsWith("/")) {
|
|
33878
33954
|
throw new TypeError(`${where}: a route template is a site-absolute path starting with "/", got ${JSON.stringify(source)}`);
|
|
33879
33955
|
}
|
|
33880
33956
|
const tokens = [];
|
|
33881
33957
|
let last = 0;
|
|
33882
33958
|
for (const match of source.matchAll(/\{([^{}]*)\}/g)) {
|
|
33883
|
-
const index = match
|
|
33959
|
+
const { index } = match;
|
|
33884
33960
|
if (index > last) {
|
|
33885
33961
|
tokens.push({ literal: source.slice(last, index) });
|
|
33886
33962
|
}
|
|
@@ -33926,10 +34002,10 @@ function directoryOf(path) {
|
|
|
33926
34002
|
return slash === -1 ? "" : path.slice(0, slash);
|
|
33927
34003
|
}
|
|
33928
34004
|
function textOf(value) {
|
|
33929
|
-
if (
|
|
34005
|
+
if (isString(value)) {
|
|
33930
34006
|
return value.trim() === "" ? null : value;
|
|
33931
34007
|
}
|
|
33932
|
-
if (
|
|
34008
|
+
if (isNumber(value) || isBoolean(value)) {
|
|
33933
34009
|
return String(value);
|
|
33934
34010
|
}
|
|
33935
34011
|
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
@@ -34182,10 +34258,10 @@ function contentOf(anchor) {
|
|
|
34182
34258
|
if (Array.isArray(anchor.children)) {
|
|
34183
34259
|
return anchor.children;
|
|
34184
34260
|
}
|
|
34185
|
-
if (
|
|
34261
|
+
if (anchor.innerHTML !== undefined) {
|
|
34186
34262
|
return [{ innerHTML: anchor.innerHTML, tagName: "span" }];
|
|
34187
34263
|
}
|
|
34188
|
-
return
|
|
34264
|
+
return isString(anchor.textContent) ? [anchor.textContent] : [];
|
|
34189
34265
|
}
|
|
34190
34266
|
function rewriteLinks(nodes, from, index, report) {
|
|
34191
34267
|
for (let i = 0;i < nodes.length; i++) {
|
|
@@ -34194,11 +34270,11 @@ function rewriteLinks(nodes, from, index, report) {
|
|
|
34194
34270
|
rewriteLinks(node, from, index, report);
|
|
34195
34271
|
continue;
|
|
34196
34272
|
}
|
|
34197
|
-
if (
|
|
34273
|
+
if (!isNonNullObject(node)) {
|
|
34198
34274
|
continue;
|
|
34199
34275
|
}
|
|
34200
34276
|
const href = node.tagName === "a" ? node.attributes?.href : undefined;
|
|
34201
|
-
if (
|
|
34277
|
+
if (isString(href)) {
|
|
34202
34278
|
const resolution = resolveHref(href, from, index);
|
|
34203
34279
|
if ("problem" in resolution) {
|
|
34204
34280
|
report({ href, reason: resolution.problem });
|
|
@@ -34222,9 +34298,10 @@ function rewriteLinks(nodes, from, index, report) {
|
|
|
34222
34298
|
init_content_rules();
|
|
34223
34299
|
|
|
34224
34300
|
// src/dates.ts
|
|
34301
|
+
init_guards();
|
|
34225
34302
|
var DATE_FORMATS = ["date", "date-time"];
|
|
34226
34303
|
function isDateFormat(format) {
|
|
34227
|
-
return
|
|
34304
|
+
return DATE_FORMATS.some((candidate) => candidate === format);
|
|
34228
34305
|
}
|
|
34229
34306
|
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
34230
34307
|
var RFC_3339 = /^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?([Zz]|[+-]\d{2}:\d{2})?$/;
|
|
@@ -34241,7 +34318,7 @@ function parseDateValue(value, format) {
|
|
|
34241
34318
|
if (value instanceof Date) {
|
|
34242
34319
|
return Number.isNaN(value.getTime()) ? null : format === "date" ? toDateOnly(value) : toInstant(value);
|
|
34243
34320
|
}
|
|
34244
|
-
if (
|
|
34321
|
+
if (!isString(value)) {
|
|
34245
34322
|
return null;
|
|
34246
34323
|
}
|
|
34247
34324
|
const text = value.trim();
|
|
@@ -34274,7 +34351,7 @@ function coerceEntryDates(entries, schema, contentTypeName) {
|
|
|
34274
34351
|
if (!properties) {
|
|
34275
34352
|
return warnings;
|
|
34276
34353
|
}
|
|
34277
|
-
const dateFields = Object.entries(properties).
|
|
34354
|
+
const dateFields = Object.entries(properties).flatMap(([field, def]) => isDateFormat(def.format) ? [[field, def.format]] : []);
|
|
34278
34355
|
if (dateFields.length === 0) {
|
|
34279
34356
|
return warnings;
|
|
34280
34357
|
}
|
|
@@ -34305,9 +34382,7 @@ function coerceEntryDates(entries, schema, contentTypeName) {
|
|
|
34305
34382
|
return warnings;
|
|
34306
34383
|
}
|
|
34307
34384
|
function parseComparable(a, b) {
|
|
34308
|
-
|
|
34309
|
-
const bDate = typeof b === "string" && (DATE_ONLY.test(b) || RFC_3339.test(b));
|
|
34310
|
-
if (aDate && bDate) {
|
|
34385
|
+
if (isString(a) && isCoercedDate(a) && isString(b) && isCoercedDate(b)) {
|
|
34311
34386
|
return [a, b];
|
|
34312
34387
|
}
|
|
34313
34388
|
return [Number(a), Number(b)];
|
|
@@ -34315,23 +34390,24 @@ function parseComparable(a, b) {
|
|
|
34315
34390
|
|
|
34316
34391
|
// src/content-loader.ts
|
|
34317
34392
|
init_walk();
|
|
34393
|
+
|
|
34394
|
+
// src/content-data.ts
|
|
34395
|
+
async function entriesOf(data, name) {
|
|
34396
|
+
return await data?.load?.(name) ?? data?.get(name);
|
|
34397
|
+
}
|
|
34398
|
+
|
|
34399
|
+
// src/content-loader.ts
|
|
34318
34400
|
function loadJSONEntries(filePath) {
|
|
34319
34401
|
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
34320
34402
|
if (Array.isArray(raw)) {
|
|
34321
|
-
return raw.map((item, i) => ({
|
|
34322
|
-
body: null,
|
|
34323
|
-
data: item,
|
|
34324
|
-
id: item.id ?? `${basename(filePath, ".json")}-${i}`
|
|
34325
|
-
}));
|
|
34403
|
+
return raw.map((item, i) => jsonEntry(item, `${basename(filePath, ".json")}-${i}`));
|
|
34326
34404
|
}
|
|
34327
|
-
|
|
34328
|
-
|
|
34329
|
-
|
|
34330
|
-
|
|
34331
|
-
|
|
34332
|
-
|
|
34333
|
-
}
|
|
34334
|
-
];
|
|
34405
|
+
return [jsonEntry(raw, basename(filePath, ".json"))];
|
|
34406
|
+
}
|
|
34407
|
+
function jsonEntry(item, fallbackId) {
|
|
34408
|
+
const data = item;
|
|
34409
|
+
const id = data.id ?? fallbackId;
|
|
34410
|
+
return { body: null, data, id };
|
|
34335
34411
|
}
|
|
34336
34412
|
function discoverJSONFiles(resolvedSource, exclude) {
|
|
34337
34413
|
if (extname(resolvedSource)) {
|
|
@@ -34339,7 +34415,6 @@ function discoverJSONFiles(resolvedSource, exclude) {
|
|
|
34339
34415
|
}
|
|
34340
34416
|
return walkFiles(resolvedSource, (name) => name.toLowerCase().endsWith(".json"), exclude);
|
|
34341
34417
|
}
|
|
34342
|
-
var LOCALE_PLACEHOLDER = "{locale}";
|
|
34343
34418
|
function localesForExpansion(projectConfig) {
|
|
34344
34419
|
return resolveI18n(projectConfig ?? {}).i18n?.locales ?? [];
|
|
34345
34420
|
}
|
|
@@ -34360,8 +34435,6 @@ function localeSource(root, source, locale) {
|
|
|
34360
34435
|
}
|
|
34361
34436
|
return source.split(LOCALE_PLACEHOLDER).join(locale);
|
|
34362
34437
|
}
|
|
34363
|
-
var SECTION_KEY = "content";
|
|
34364
|
-
var SAFE_TYPE_NAME = /^[\w.~-]+$/;
|
|
34365
34438
|
var ASSET_KEYS = ["src", "poster"];
|
|
34366
34439
|
function isNonRelativeRef(value) {
|
|
34367
34440
|
return value === "" || value.startsWith("/") || value.startsWith("#") || value.includes("${") || /^[a-z][\w+.-]*:/i.test(value);
|
|
@@ -34396,28 +34469,31 @@ function contentAssetMounts(section, root, projectConfig) {
|
|
|
34396
34469
|
return filter === undefined ? {} : { filter };
|
|
34397
34470
|
};
|
|
34398
34471
|
for (const [name, contentTypeDef] of Object.entries(section ?? {})) {
|
|
34399
|
-
|
|
34472
|
+
if (!isNonNullObject(contentTypeDef)) {
|
|
34473
|
+
continue;
|
|
34474
|
+
}
|
|
34475
|
+
const { source } = contentTypeDef;
|
|
34400
34476
|
if (!source || source.startsWith("http://") || source.startsWith("https://")) {
|
|
34401
34477
|
continue;
|
|
34402
34478
|
}
|
|
34403
34479
|
if (source.includes(LOCALE_PLACEHOLDER)) {
|
|
34404
34480
|
for (const locale of locales) {
|
|
34405
|
-
if (!
|
|
34481
|
+
if (!SAFE_CONTENT_TYPE_NAME.test(locale)) {
|
|
34406
34482
|
continue;
|
|
34407
34483
|
}
|
|
34408
34484
|
const dir = resolve(root, localeSource(root, source, locale)).split("\\").join("/");
|
|
34409
|
-
if (
|
|
34485
|
+
if (SAFE_CONTENT_TYPE_NAME.test(name) && existsSync(dir) && statSync2(dir).isDirectory()) {
|
|
34410
34486
|
mounts.push({
|
|
34411
34487
|
dir,
|
|
34412
|
-
urlPrefix: `/${
|
|
34488
|
+
urlPrefix: `/${CONTENT_SECTION_KEY}/${name}/${locale}`,
|
|
34413
34489
|
...filterOf(contentTypeDef)
|
|
34414
34490
|
});
|
|
34415
34491
|
}
|
|
34416
34492
|
}
|
|
34417
34493
|
continue;
|
|
34418
34494
|
}
|
|
34419
|
-
if (!
|
|
34420
|
-
console.warn(`Content type "${name}": name is not URL-safe — its source directory is not published ` + `at /${
|
|
34495
|
+
if (!SAFE_CONTENT_TYPE_NAME.test(name)) {
|
|
34496
|
+
console.warn(`Content type "${name}": name is not URL-safe — its source directory is not published ` + `at /${CONTENT_SECTION_KEY}/${name}, so content-relative asset references stay unresolved.`);
|
|
34421
34497
|
continue;
|
|
34422
34498
|
}
|
|
34423
34499
|
const resolvedSource = resolve(root, source).split("\\").join("/");
|
|
@@ -34426,7 +34502,7 @@ function contentAssetMounts(section, root, projectConfig) {
|
|
|
34426
34502
|
}
|
|
34427
34503
|
mounts.push({
|
|
34428
34504
|
dir: resolvedSource,
|
|
34429
|
-
urlPrefix: `/${
|
|
34505
|
+
urlPrefix: `/${CONTENT_SECTION_KEY}/${name}`,
|
|
34430
34506
|
...filterOf(contentTypeDef)
|
|
34431
34507
|
});
|
|
34432
34508
|
}
|
|
@@ -34464,15 +34540,14 @@ function rewriteNodeAssets(node, entryDir, mount, onProblem) {
|
|
|
34464
34540
|
}
|
|
34465
34541
|
return;
|
|
34466
34542
|
}
|
|
34467
|
-
if (!node
|
|
34543
|
+
if (!isNonNullObject(node)) {
|
|
34468
34544
|
return;
|
|
34469
34545
|
}
|
|
34470
|
-
const
|
|
34471
|
-
const attributes = record.attributes;
|
|
34546
|
+
const { attributes } = node;
|
|
34472
34547
|
for (const key of ASSET_KEYS) {
|
|
34473
|
-
for (const holder of [
|
|
34548
|
+
for (const holder of [node, attributes]) {
|
|
34474
34549
|
const value = holder?.[key];
|
|
34475
|
-
if (
|
|
34550
|
+
if (holder && isString(value)) {
|
|
34476
34551
|
const rewritten = rewriteRef(value, entryDir, mount, onProblem);
|
|
34477
34552
|
if (rewritten) {
|
|
34478
34553
|
holder[key] = rewritten;
|
|
@@ -34480,7 +34555,7 @@ function rewriteNodeAssets(node, entryDir, mount, onProblem) {
|
|
|
34480
34555
|
}
|
|
34481
34556
|
}
|
|
34482
34557
|
}
|
|
34483
|
-
const { children } =
|
|
34558
|
+
const { children } = node;
|
|
34484
34559
|
if (Array.isArray(children)) {
|
|
34485
34560
|
for (const child of children) {
|
|
34486
34561
|
rewriteNodeAssets(child, entryDir, mount, onProblem);
|
|
@@ -34489,22 +34564,30 @@ function rewriteNodeAssets(node, entryDir, mount, onProblem) {
|
|
|
34489
34564
|
}
|
|
34490
34565
|
function rewriteEntryAssets(entries, filePath, mount, typeName, schema) {
|
|
34491
34566
|
const entryDir = dirname(filePath);
|
|
34492
|
-
const uriFields = Object.entries(schema?.properties ?? {}).
|
|
34567
|
+
const uriFields = Object.entries(schema?.properties ?? {}).flatMap(([field, def]) => isNonNullObject(def) && (def.format === "uri-reference" || def.items?.format === "uri-reference") ? [field] : []);
|
|
34493
34568
|
for (const entry of entries) {
|
|
34494
|
-
const onProblem = (
|
|
34495
|
-
|
|
34496
|
-
rewriteNodeAssets(child, entryDir, mount, onProblem);
|
|
34497
|
-
}
|
|
34569
|
+
const onProblem = assetProblemReporter(typeName, entry);
|
|
34570
|
+
rewriteBodyAssets(entry, filePath, mount, typeName);
|
|
34498
34571
|
for (const field of uriFields) {
|
|
34499
34572
|
const value = entry.data[field];
|
|
34500
|
-
if (
|
|
34573
|
+
if (isString(value)) {
|
|
34501
34574
|
entry.data[field] = rewriteRef(value, entryDir, mount, onProblem) ?? value;
|
|
34502
34575
|
} else if (Array.isArray(value)) {
|
|
34503
|
-
entry.data[field] = value.map((item) =>
|
|
34576
|
+
entry.data[field] = value.map((item) => isString(item) ? rewriteRef(item, entryDir, mount, onProblem) ?? item : item);
|
|
34504
34577
|
}
|
|
34505
34578
|
}
|
|
34506
34579
|
}
|
|
34507
34580
|
}
|
|
34581
|
+
function assetProblemReporter(typeName, entry) {
|
|
34582
|
+
return (value, problem) => console.warn(problem === "missing" ? `Content type "${typeName}": entry "${entry.id}" references missing asset "${value}"` : `Content type "${typeName}": entry "${entry.id}" references "${value}", which the ` + `collection does not publish (it is excluded, hidden or a document), so the ` + `reference is left as written`);
|
|
34583
|
+
}
|
|
34584
|
+
function rewriteBodyAssets(entry, filePath, mount, typeName) {
|
|
34585
|
+
const entryDir = dirname(filePath);
|
|
34586
|
+
const onProblem = assetProblemReporter(typeName, entry);
|
|
34587
|
+
for (const child of entry.$children ?? []) {
|
|
34588
|
+
rewriteNodeAssets(child, entryDir, mount, onProblem);
|
|
34589
|
+
}
|
|
34590
|
+
}
|
|
34508
34591
|
function loadContentConfig(projectRoot, projectConfig) {
|
|
34509
34592
|
const contentDir = resolve(projectRoot, "content");
|
|
34510
34593
|
const config = { content: projectConfig?.content ?? {} };
|
|
@@ -34512,48 +34595,63 @@ function loadContentConfig(projectRoot, projectConfig) {
|
|
|
34512
34595
|
}
|
|
34513
34596
|
async function loadContentSection(section, root, formats, projectConfig) {
|
|
34514
34597
|
const contentTypes = new Map;
|
|
34515
|
-
const mounts =
|
|
34516
|
-
|
|
34598
|
+
const mounts = sectionMounts(section, root, projectConfig);
|
|
34599
|
+
for (const name of Object.keys(section)) {
|
|
34600
|
+
contentTypes.set(name, await loadSectionType(name, { formats, mounts, projectConfig, root, section }));
|
|
34601
|
+
}
|
|
34602
|
+
return contentTypes;
|
|
34603
|
+
}
|
|
34604
|
+
function sectionMounts(section, root, projectConfig) {
|
|
34605
|
+
return new Map(contentAssetMounts(section, root, projectConfig).map((mount) => [
|
|
34606
|
+
mount.urlPrefix.slice(`/${CONTENT_SECTION_KEY}/`.length),
|
|
34517
34607
|
mount
|
|
34518
34608
|
]));
|
|
34609
|
+
}
|
|
34610
|
+
async function loadSectionType(name, source) {
|
|
34611
|
+
const { section, root, formats, projectConfig, mounts, onDeferred } = source;
|
|
34612
|
+
const contentTypeDef = section[name];
|
|
34613
|
+
if (!contentTypeDef) {
|
|
34614
|
+
return [];
|
|
34615
|
+
}
|
|
34616
|
+
const deferral = onDeferred ? { deferBody: true, onDeferred } : {};
|
|
34617
|
+
const typeSource = contentTypeDef.source;
|
|
34618
|
+
if (!isString(typeSource) || !typeSource.includes(LOCALE_PLACEHOLDER)) {
|
|
34619
|
+
return await loadContentType(name, contentTypeDef, root, formats, {
|
|
34620
|
+
mount: mounts.get(name),
|
|
34621
|
+
projectConfig,
|
|
34622
|
+
...deferral
|
|
34623
|
+
});
|
|
34624
|
+
}
|
|
34519
34625
|
const locales = localesForExpansion(projectConfig);
|
|
34520
|
-
|
|
34521
|
-
|
|
34522
|
-
|
|
34523
|
-
|
|
34524
|
-
|
|
34525
|
-
|
|
34526
|
-
|
|
34527
|
-
|
|
34528
|
-
|
|
34529
|
-
|
|
34530
|
-
|
|
34531
|
-
|
|
34532
|
-
|
|
34533
|
-
|
|
34534
|
-
|
|
34535
|
-
const all = [];
|
|
34536
|
-
for (const locale of locales) {
|
|
34537
|
-
const localized = { ...contentTypeDef, source: localeSource(root, source, locale) };
|
|
34538
|
-
const entries = await loadContentType(name, localized, root, formats, {
|
|
34539
|
-
locale,
|
|
34540
|
-
mount: mounts.get(`${name}/${locale}`),
|
|
34541
|
-
projectConfig
|
|
34542
|
-
});
|
|
34543
|
-
for (const entry of entries) {
|
|
34544
|
-
entry._meta = { ...entry._meta, locale };
|
|
34545
|
-
}
|
|
34546
|
-
all.push(...entries);
|
|
34626
|
+
if (locales.length === 0) {
|
|
34627
|
+
console.warn(`Content type "${name}": source "${typeSource}" uses ${LOCALE_PLACEHOLDER}, but the project ` + `declares no i18n locales — nothing to expand it over, so no entries were loaded.`);
|
|
34628
|
+
return [];
|
|
34629
|
+
}
|
|
34630
|
+
const all = [];
|
|
34631
|
+
for (const locale of locales) {
|
|
34632
|
+
const localized = { ...contentTypeDef, source: localeSource(root, typeSource, locale) };
|
|
34633
|
+
const entries = await loadContentType(name, localized, root, formats, {
|
|
34634
|
+
locale,
|
|
34635
|
+
mount: mounts.get(`${name}/${locale}`),
|
|
34636
|
+
projectConfig,
|
|
34637
|
+
...deferral
|
|
34638
|
+
});
|
|
34639
|
+
for (const entry of entries) {
|
|
34640
|
+
entry._meta = { ...entry._meta, locale };
|
|
34547
34641
|
}
|
|
34548
|
-
|
|
34642
|
+
all.push(...entries);
|
|
34549
34643
|
}
|
|
34550
|
-
return
|
|
34644
|
+
return all;
|
|
34551
34645
|
}
|
|
34552
34646
|
function getContentTypeElements(projectRoot, contentTypeName, projectConfig) {
|
|
34553
34647
|
const { config } = loadContentConfig(projectRoot, projectConfig);
|
|
34554
34648
|
const def = config.content[contentTypeName];
|
|
34555
34649
|
return def?.$elements;
|
|
34556
34650
|
}
|
|
34651
|
+
var LINK_MODES = ["warn", "error", "ignore"];
|
|
34652
|
+
function isLinkMode(value) {
|
|
34653
|
+
return LINK_MODES.some((mode) => mode === value);
|
|
34654
|
+
}
|
|
34557
34655
|
var KNOWN_TYPE_KEYS = [
|
|
34558
34656
|
"$elements",
|
|
34559
34657
|
"alerts",
|
|
@@ -34595,7 +34693,7 @@ function compileTypeRules(name, def) {
|
|
|
34595
34693
|
const at = `Content type "${name}"`;
|
|
34596
34694
|
try {
|
|
34597
34695
|
for (const key of Object.keys(def)) {
|
|
34598
|
-
if (KNOWN_TYPE_KEYS.
|
|
34696
|
+
if (KNOWN_TYPE_KEYS.some((known) => known === key) || key === "$comment") {
|
|
34599
34697
|
continue;
|
|
34600
34698
|
}
|
|
34601
34699
|
const suggestion = nearestOption(key);
|
|
@@ -34604,11 +34702,11 @@ function compileTypeRules(name, def) {
|
|
|
34604
34702
|
}
|
|
34605
34703
|
console.warn(`${at}: ignores the unknown option "${key}" (the options are ${KNOWN_TYPE_KEYS.join(", ")})`);
|
|
34606
34704
|
}
|
|
34607
|
-
if (def.idField !== undefined && (
|
|
34705
|
+
if (def.idField !== undefined && (!isString(def.idField) || def.idField === "")) {
|
|
34608
34706
|
throw new TypeError(`"idField" must be a field name`);
|
|
34609
34707
|
}
|
|
34610
34708
|
const links = def.links ?? "warn";
|
|
34611
|
-
if (links
|
|
34709
|
+
if (!isLinkMode(links)) {
|
|
34612
34710
|
throw new TypeError(`"links" must be "warn", "error" or "ignore", got ${JSON.stringify(links)}`);
|
|
34613
34711
|
}
|
|
34614
34712
|
normalizeAlerts(def.alerts, `"alerts"`);
|
|
@@ -34669,7 +34767,7 @@ function finishEntries(files, ctx) {
|
|
|
34669
34767
|
if (rules.idField) {
|
|
34670
34768
|
for (const entry of entries) {
|
|
34671
34769
|
const value = entry.data[rules.idField];
|
|
34672
|
-
if (
|
|
34770
|
+
if (isString(value) && value !== "" || isNumber(value)) {
|
|
34673
34771
|
const unsafe = traversalReason(String(value));
|
|
34674
34772
|
if (unsafe === null) {
|
|
34675
34773
|
entry.id = String(value);
|
|
@@ -34700,13 +34798,48 @@ function finishEntries(files, ctx) {
|
|
|
34700
34798
|
}
|
|
34701
34799
|
if (rules.route) {
|
|
34702
34800
|
assignRoutes(entries, unpublished, ctx, rules.route);
|
|
34703
|
-
|
|
34704
|
-
|
|
34705
|
-
}
|
|
34801
|
+
const links = ctx.sourceRoot ? resolveEntryLinks(kept, unpublished, ctx) : undefined;
|
|
34802
|
+
deferBodies(kept, ctx, links);
|
|
34706
34803
|
return entries.filter((entry) => entry._meta?.route !== undefined);
|
|
34707
34804
|
}
|
|
34805
|
+
deferBodies(kept, ctx);
|
|
34708
34806
|
return entries;
|
|
34709
34807
|
}
|
|
34808
|
+
function deferBodies(kept, ctx, links) {
|
|
34809
|
+
const { reload, onDeferred } = ctx;
|
|
34810
|
+
if (!reload || !onDeferred) {
|
|
34811
|
+
return;
|
|
34812
|
+
}
|
|
34813
|
+
for (const file of kept) {
|
|
34814
|
+
const [entry] = file.entries;
|
|
34815
|
+
if (file.entries.length !== 1 || !entry || entry.$children !== undefined) {
|
|
34816
|
+
continue;
|
|
34817
|
+
}
|
|
34818
|
+
onDeferred(entry, async () => {
|
|
34819
|
+
const [full] = await reload(file.filePath);
|
|
34820
|
+
if (!full) {
|
|
34821
|
+
return;
|
|
34822
|
+
}
|
|
34823
|
+
const completed = {
|
|
34824
|
+
...full,
|
|
34825
|
+
...entry,
|
|
34826
|
+
_meta: { ...full._meta, ...entry._meta }
|
|
34827
|
+
};
|
|
34828
|
+
for (const key of Object.keys(entry)) {
|
|
34829
|
+
Reflect.deleteProperty(entry, key);
|
|
34830
|
+
}
|
|
34831
|
+
Object.assign(entry, completed);
|
|
34832
|
+
if (ctx.mount) {
|
|
34833
|
+
rewriteBodyAssets(entry, file.filePath, ctx.mount, ctx.name);
|
|
34834
|
+
}
|
|
34835
|
+
if (links && file.rel !== null) {
|
|
34836
|
+
const broken = [];
|
|
34837
|
+
collectBrokenLinks(entry, file.rel, links, broken, new Set);
|
|
34838
|
+
reportBrokenLinks(broken, ctx.name, ctx.rules.links);
|
|
34839
|
+
}
|
|
34840
|
+
});
|
|
34841
|
+
}
|
|
34842
|
+
}
|
|
34710
34843
|
function settleIndexIds(name, entries) {
|
|
34711
34844
|
const indexFolders = new Set;
|
|
34712
34845
|
for (const entry of entries) {
|
|
@@ -34729,7 +34862,7 @@ function settleIndexIds(name, entries) {
|
|
|
34729
34862
|
}
|
|
34730
34863
|
function trailingSlashOf(projectConfig) {
|
|
34731
34864
|
const value = projectConfig?.build?.trailingSlash;
|
|
34732
|
-
return
|
|
34865
|
+
return isString(value) ? value : "always";
|
|
34733
34866
|
}
|
|
34734
34867
|
function localePrefixOf(ctx) {
|
|
34735
34868
|
const { i18n } = resolveI18n(ctx.projectConfig ?? {});
|
|
@@ -34777,7 +34910,21 @@ function assignRoutes(entries, unpublished, ctx, spec) {
|
|
|
34777
34910
|
}
|
|
34778
34911
|
var UNROUTED_LISTED = 3;
|
|
34779
34912
|
function resolveEntryLinks(kept, unpublished, ctx) {
|
|
34780
|
-
const
|
|
34913
|
+
const index = buildLinkIndex(kept, unpublished, ctx);
|
|
34914
|
+
const broken = [];
|
|
34915
|
+
const seen = new Set;
|
|
34916
|
+
for (const file of kept) {
|
|
34917
|
+
for (const entry of file.entries) {
|
|
34918
|
+
if (entry.$children && file.rel !== null) {
|
|
34919
|
+
collectBrokenLinks(entry, file.rel, index, broken, seen);
|
|
34920
|
+
}
|
|
34921
|
+
}
|
|
34922
|
+
}
|
|
34923
|
+
reportBrokenLinks(broken, ctx.name, ctx.rules.links);
|
|
34924
|
+
return index;
|
|
34925
|
+
}
|
|
34926
|
+
function buildLinkIndex(kept, unpublished, ctx) {
|
|
34927
|
+
const { rules, sourceRoot } = ctx;
|
|
34781
34928
|
const files = new Map;
|
|
34782
34929
|
const indexes = new Map;
|
|
34783
34930
|
const isReadme = (rel) => /^readme\./i.test(rel.slice(rel.lastIndexOf("/") + 1));
|
|
@@ -34808,7 +34955,7 @@ function resolveEntryLinks(kept, unpublished, ctx) {
|
|
|
34808
34955
|
}
|
|
34809
34956
|
}
|
|
34810
34957
|
const onDisk = (path) => resolve(sourceRoot, path);
|
|
34811
|
-
|
|
34958
|
+
return {
|
|
34812
34959
|
asset: (path) => {
|
|
34813
34960
|
const absolute = onDisk(path);
|
|
34814
34961
|
if (!ctx.mount || !existsSync(absolute) || statSync2(absolute).isDirectory()) {
|
|
@@ -34837,27 +34984,22 @@ function resolveEntryLinks(kept, unpublished, ctx) {
|
|
|
34837
34984
|
localePrefix: localePrefixOf(ctx),
|
|
34838
34985
|
trailingSlash: trailingSlashOf(ctx.projectConfig)
|
|
34839
34986
|
};
|
|
34840
|
-
|
|
34841
|
-
|
|
34842
|
-
|
|
34843
|
-
|
|
34844
|
-
|
|
34845
|
-
|
|
34846
|
-
}
|
|
34847
|
-
rewriteLinks(entry.$children, file.rel, index, (problem) => {
|
|
34848
|
-
const key = `${file.rel}\x00${problem.href}`;
|
|
34849
|
-
if (!seen.has(key)) {
|
|
34850
|
-
seen.add(key);
|
|
34851
|
-
broken.push({ from: file.rel, href: problem.href, reason: problem.reason });
|
|
34852
|
-
}
|
|
34853
|
-
});
|
|
34987
|
+
}
|
|
34988
|
+
function collectBrokenLinks(entry, rel, index, broken, seen) {
|
|
34989
|
+
rewriteLinks(entry.$children ?? [], rel, index, (problem) => {
|
|
34990
|
+
const key = `${rel}\x00${problem.href}`;
|
|
34991
|
+
if (!seen.has(key)) {
|
|
34992
|
+
seen.add(key);
|
|
34993
|
+
broken.push({ from: rel, href: problem.href, reason: problem.reason });
|
|
34854
34994
|
}
|
|
34855
|
-
}
|
|
34856
|
-
|
|
34995
|
+
});
|
|
34996
|
+
}
|
|
34997
|
+
function reportBrokenLinks(broken, name, mode) {
|
|
34998
|
+
if (mode === "ignore" || broken.length === 0) {
|
|
34857
34999
|
return;
|
|
34858
35000
|
}
|
|
34859
35001
|
const lines = broken.map(({ from, href, reason }) => `"${from}" links to "${href}", which ${reason}`);
|
|
34860
|
-
if (
|
|
35002
|
+
if (mode === "error") {
|
|
34861
35003
|
const plural = broken.length === 1 ? "" : "s";
|
|
34862
35004
|
const list = lines.map((line) => ` - ${line}`).join(`
|
|
34863
35005
|
`);
|
|
@@ -34877,8 +35019,8 @@ async function loadContentType(name, contentTypeDef, projectRoot, registry, opti
|
|
|
34877
35019
|
if (!source) {
|
|
34878
35020
|
return [];
|
|
34879
35021
|
}
|
|
34880
|
-
const { mount, projectConfig, locale } = options;
|
|
34881
|
-
const schema = contentTypeDef
|
|
35022
|
+
const { mount, projectConfig, locale, deferBody, onDeferred } = options;
|
|
35023
|
+
const { schema } = contentTypeDef;
|
|
34882
35024
|
const rules = compileTypeRules(name, contentTypeDef);
|
|
34883
35025
|
const finishContext = (extra) => ({
|
|
34884
35026
|
coerceDates: false,
|
|
@@ -34893,7 +35035,7 @@ async function loadContentType(name, contentTypeDef, projectRoot, registry, opti
|
|
|
34893
35035
|
...extra
|
|
34894
35036
|
});
|
|
34895
35037
|
const directiveOptions = contentTypeDef.$elements?.length ? {
|
|
34896
|
-
allowedNames: contentTypeDef.$elements.
|
|
35038
|
+
allowedNames: contentTypeDef.$elements.flatMap((e) => isString(e) ? [e] : isNonNullObject(e) && e.$ref ? [e.$ref] : [])
|
|
34897
35039
|
} : undefined;
|
|
34898
35040
|
const formatName = contentTypeDef.format;
|
|
34899
35041
|
let entry;
|
|
@@ -34949,25 +35091,32 @@ async function loadContentType(name, contentTypeDef, projectRoot, registry, opti
|
|
|
34949
35091
|
if (!entry) {
|
|
34950
35092
|
throw unknownFormatError(source, ext || `content type "${name}" (directory sources need an explicit "format")`);
|
|
34951
35093
|
}
|
|
34952
|
-
const
|
|
34953
|
-
|
|
34954
|
-
|
|
34955
|
-
}
|
|
35094
|
+
const discoverOptions = { baseDir: projectRoot };
|
|
35095
|
+
if (!rules.exclude.empty) {
|
|
35096
|
+
discoverOptions.exclude = contentTypeDef.exclude;
|
|
35097
|
+
}
|
|
35098
|
+
const discovered = entry.capabilities.discover ? await entry.call("discover", source, discoverOptions) : [resolvedSource];
|
|
34956
35099
|
const files = discovered.filter((file) => !isExcluded(file));
|
|
34957
35100
|
const fileExtensions = files.map((file) => extname(file).toLowerCase());
|
|
35101
|
+
const format = entry;
|
|
35102
|
+
const loadFile = (filePath, withoutBody) => {
|
|
35103
|
+
const rel = relOf(filePath);
|
|
35104
|
+
return format.call("load", filePath, {
|
|
35105
|
+
directiveOptions,
|
|
35106
|
+
schema,
|
|
35107
|
+
...sourceRoot !== undefined && { sourceRoot },
|
|
35108
|
+
...rules.alerts !== undefined && { alerts: rules.alerts },
|
|
35109
|
+
...rules.idField !== undefined && { idField: rules.idField },
|
|
35110
|
+
...withoutBody && { deferBody: true },
|
|
35111
|
+
onUnknownAlert: (type) => console.warn(`Content callouts: "${name}": "${rel ?? filePath}" has a [!${type}] callout, but ` + `"${type.toLowerCase()}" is not an enabled type, so it stays a blockquote with the ` + `marker showing. Add "alerts": { "${type.toUpperCase()}": true } to render it.`)
|
|
35112
|
+
});
|
|
35113
|
+
};
|
|
34958
35114
|
const loaded = [];
|
|
34959
35115
|
for (const filePath of files) {
|
|
34960
35116
|
const rel = relOf(filePath);
|
|
34961
35117
|
let fileEntries;
|
|
34962
35118
|
try {
|
|
34963
|
-
fileEntries = await
|
|
34964
|
-
directiveOptions,
|
|
34965
|
-
schema,
|
|
34966
|
-
...sourceRoot !== undefined && { sourceRoot },
|
|
34967
|
-
...rules.alerts !== undefined && { alerts: rules.alerts },
|
|
34968
|
-
...rules.idField !== undefined && { idField: rules.idField },
|
|
34969
|
-
onUnknownAlert: (type) => console.warn(`Content callouts: "${name}": "${rel ?? filePath}" has a [!${type}] callout, but ` + `"${type.toLowerCase()}" is not an enabled type, so it stays a blockquote with the ` + `marker showing. Add "alerts": { "${type.toUpperCase()}": true } to render it.`)
|
|
34970
|
-
});
|
|
35119
|
+
fileEntries = await loadFile(filePath, deferBody === true);
|
|
34971
35120
|
} catch (error) {
|
|
34972
35121
|
throw unreadableFile(name, rel ?? filePath, error);
|
|
34973
35122
|
}
|
|
@@ -34979,17 +35128,30 @@ async function loadContentType(name, contentTypeDef, projectRoot, registry, opti
|
|
|
34979
35128
|
return finishEntries(loaded, finishContext({
|
|
34980
35129
|
coerceDates: true,
|
|
34981
35130
|
entryExtensions: new Set([...entry.extensions, ...fileExtensions]),
|
|
34982
|
-
sourceRoot
|
|
35131
|
+
sourceRoot,
|
|
35132
|
+
...deferBody === true && {
|
|
35133
|
+
onDeferred,
|
|
35134
|
+
reload: async (filePath) => await loadFile(filePath, false)
|
|
35135
|
+
}
|
|
34983
35136
|
}));
|
|
34984
35137
|
}
|
|
34985
|
-
function unreadableFile(type, path,
|
|
34986
|
-
return new Error(`Content type "${type}": cannot read "${path}": ${errorMessage(
|
|
35138
|
+
function unreadableFile(type, path, cause) {
|
|
35139
|
+
return new Error(`Content type "${type}": cannot read "${path}": ${errorMessage(cause)}. Fix the file, or add it ` + `to "exclude" if it is not a document (a templates folder, for example).`, { cause });
|
|
34987
35140
|
}
|
|
34988
35141
|
function unknownFormatError(path, ext) {
|
|
34989
35142
|
return new Error(`No format class registered for "${ext}" (${path}). ` + `Enable an extension providing this format in project.json "extensions", ` + `e.g. "@jxsuite/parser".`);
|
|
34990
35143
|
}
|
|
34991
|
-
function errorMessage(
|
|
34992
|
-
return
|
|
35144
|
+
function errorMessage(cause) {
|
|
35145
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
35146
|
+
}
|
|
35147
|
+
function jsTypeName(value) {
|
|
35148
|
+
if (isString(value)) {
|
|
35149
|
+
return "string";
|
|
35150
|
+
}
|
|
35151
|
+
if (isNumber(value)) {
|
|
35152
|
+
return "number";
|
|
35153
|
+
}
|
|
35154
|
+
return isBoolean(value) ? "boolean" : "object";
|
|
34993
35155
|
}
|
|
34994
35156
|
function validateEntries(entries, schema, contentTypeName) {
|
|
34995
35157
|
const required = schema.required ?? [];
|
|
@@ -35005,16 +35167,16 @@ function validateEntries(entries, schema, contentTypeName) {
|
|
|
35005
35167
|
if (value == null) {
|
|
35006
35168
|
continue;
|
|
35007
35169
|
}
|
|
35008
|
-
if (isDateFormat(def.format) &&
|
|
35170
|
+
if (isDateFormat(def.format) && isString(value) && !isCoercedDate(value)) {
|
|
35009
35171
|
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" is declared ` + `${def.format} but "${value}" was not coerced — it will sort and filter as plain text.`);
|
|
35010
|
-
} else if (def.type === "string" &&
|
|
35011
|
-
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected string, got ${
|
|
35012
|
-
} else if (def.type === "number" &&
|
|
35013
|
-
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected number, got ${
|
|
35014
|
-
} else if (def.type === "boolean" &&
|
|
35015
|
-
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected boolean, got ${
|
|
35172
|
+
} else if (def.type === "string" && !isString(value)) {
|
|
35173
|
+
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected string, got ${jsTypeName(value)}`);
|
|
35174
|
+
} else if (def.type === "number" && !isNumber(value)) {
|
|
35175
|
+
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected number, got ${jsTypeName(value)}`);
|
|
35176
|
+
} else if (def.type === "boolean" && !isBoolean(value)) {
|
|
35177
|
+
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected boolean, got ${jsTypeName(value)}`);
|
|
35016
35178
|
} else if (def.type === "array" && !Array.isArray(value)) {
|
|
35017
|
-
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected array, got ${
|
|
35179
|
+
console.warn(`Content validation: "${contentTypeName}/${entry.id}" field "${field}" expected array, got ${jsTypeName(value)}`);
|
|
35018
35180
|
}
|
|
35019
35181
|
}
|
|
35020
35182
|
}
|
|
@@ -35026,9 +35188,19 @@ function refTargetName(ref) {
|
|
|
35026
35188
|
}
|
|
35027
35189
|
return ref.slice(CONTENT_REF_PREFIX.length);
|
|
35028
35190
|
}
|
|
35191
|
+
function referencedContentTypes(def) {
|
|
35192
|
+
const targets = new Set;
|
|
35193
|
+
for (const field of Object.values(def?.schema?.properties ?? {})) {
|
|
35194
|
+
const target = refTargetName(field.$ref) ?? refTargetName(field.items?.$ref);
|
|
35195
|
+
if (target !== undefined) {
|
|
35196
|
+
targets.add(target);
|
|
35197
|
+
}
|
|
35198
|
+
}
|
|
35199
|
+
return [...targets];
|
|
35200
|
+
}
|
|
35029
35201
|
function resolveContentTypeRefs(contentTypes, section) {
|
|
35030
35202
|
for (const [name, contentTypeDef] of Object.entries(section)) {
|
|
35031
|
-
const schema = contentTypeDef
|
|
35203
|
+
const { schema } = contentTypeDef;
|
|
35032
35204
|
if (!schema?.properties) {
|
|
35033
35205
|
continue;
|
|
35034
35206
|
}
|
|
@@ -35050,7 +35222,7 @@ function resolveContentTypeRefs(contentTypes, section) {
|
|
|
35050
35222
|
}
|
|
35051
35223
|
for (const entry of entries) {
|
|
35052
35224
|
const value = entry.data[field];
|
|
35053
|
-
if (toOne &&
|
|
35225
|
+
if (toOne && isString(value)) {
|
|
35054
35226
|
const resolved = refEntries.find((e) => e.id === value);
|
|
35055
35227
|
if (resolved) {
|
|
35056
35228
|
entry.data[field] = resolved;
|
|
@@ -35059,7 +35231,7 @@ function resolveContentTypeRefs(contentTypes, section) {
|
|
|
35059
35231
|
}
|
|
35060
35232
|
} else if (toMany && Array.isArray(value)) {
|
|
35061
35233
|
entry.data[field] = value.map((id) => {
|
|
35062
|
-
if (
|
|
35234
|
+
if (!isString(id)) {
|
|
35063
35235
|
return id;
|
|
35064
35236
|
}
|
|
35065
35237
|
const resolved = refEntries.find((e) => e.id === id);
|
|
@@ -35086,7 +35258,10 @@ function reportUnservedRoutes(contentType, entries, ctx, prefix) {
|
|
|
35086
35258
|
return;
|
|
35087
35259
|
}
|
|
35088
35260
|
const patterns = ctx.patterns.map((pattern) => withoutLocalePrefix(pattern, prefix));
|
|
35089
|
-
const unserved = entries.
|
|
35261
|
+
const unserved = entries.flatMap((entry) => {
|
|
35262
|
+
const route = entry._meta?.route;
|
|
35263
|
+
return route !== undefined && !patterns.some((pattern) => routeParams(route, pattern, []) !== null) ? [route] : [];
|
|
35264
|
+
});
|
|
35090
35265
|
if (unserved.length === 0) {
|
|
35091
35266
|
return;
|
|
35092
35267
|
}
|
|
@@ -35109,7 +35284,11 @@ function routedPaths(contentType, entries, ctx, urlPattern) {
|
|
|
35109
35284
|
example ??= route;
|
|
35110
35285
|
const params = routeParams(route, pattern, ctx.params ?? []);
|
|
35111
35286
|
if (params) {
|
|
35112
|
-
|
|
35287
|
+
const pathEntry = { ...params };
|
|
35288
|
+
if (entry._meta !== undefined) {
|
|
35289
|
+
pathEntry._meta = entry._meta;
|
|
35290
|
+
}
|
|
35291
|
+
paths.push(pathEntry);
|
|
35113
35292
|
}
|
|
35114
35293
|
}
|
|
35115
35294
|
if (paths.length === 0 && example !== undefined) {
|
|
@@ -35119,18 +35298,159 @@ function routedPaths(contentType, entries, ctx, urlPattern) {
|
|
|
35119
35298
|
return paths;
|
|
35120
35299
|
}
|
|
35121
35300
|
|
|
35301
|
+
class ContentStore extends Map {
|
|
35302
|
+
#source;
|
|
35303
|
+
#mounts;
|
|
35304
|
+
#loads = new Map;
|
|
35305
|
+
#linked = new Set;
|
|
35306
|
+
#entries = new WeakSet;
|
|
35307
|
+
#deferred = new WeakMap;
|
|
35308
|
+
#completing = new WeakMap;
|
|
35309
|
+
constructor(source) {
|
|
35310
|
+
super();
|
|
35311
|
+
this.#source = source;
|
|
35312
|
+
this.#mounts = sectionMounts(source.section, source.root, source.projectConfig);
|
|
35313
|
+
}
|
|
35314
|
+
static async whole(source) {
|
|
35315
|
+
const store = new ContentStore(source);
|
|
35316
|
+
const loaded = await loadContentSection(source.section, source.root, source.formats, source.projectConfig);
|
|
35317
|
+
for (const [name, entries] of loaded) {
|
|
35318
|
+
store.#adopt(name, entries);
|
|
35319
|
+
store.#loads.set(name, Promise.resolve());
|
|
35320
|
+
}
|
|
35321
|
+
resolveContentTypeRefs(store, source.section);
|
|
35322
|
+
for (const name of loaded.keys()) {
|
|
35323
|
+
store.#linked.add(name);
|
|
35324
|
+
}
|
|
35325
|
+
return store;
|
|
35326
|
+
}
|
|
35327
|
+
static lazy(source) {
|
|
35328
|
+
return new ContentStore(source);
|
|
35329
|
+
}
|
|
35330
|
+
async load(name) {
|
|
35331
|
+
if (!Object.hasOwn(this.#source.section, name)) {
|
|
35332
|
+
return;
|
|
35333
|
+
}
|
|
35334
|
+
const related = this.#relatedTypes(name);
|
|
35335
|
+
await Promise.all(related.map(async (type) => await this.#loadType(type)));
|
|
35336
|
+
const unlinked = related.filter((type) => !this.#linked.has(type));
|
|
35337
|
+
for (const type of unlinked) {
|
|
35338
|
+
this.#linked.add(type);
|
|
35339
|
+
}
|
|
35340
|
+
if (unlinked.length > 0) {
|
|
35341
|
+
const { section } = this.#source;
|
|
35342
|
+
resolveContentTypeRefs(this, Object.fromEntries(unlinked.flatMap((type) => section[type] ? [[type, section[type]]] : [])));
|
|
35343
|
+
}
|
|
35344
|
+
return this.get(name);
|
|
35345
|
+
}
|
|
35346
|
+
async materialize(entries) {
|
|
35347
|
+
const seen = new Set;
|
|
35348
|
+
const pending = [];
|
|
35349
|
+
const visit = (entry) => {
|
|
35350
|
+
if (seen.has(entry)) {
|
|
35351
|
+
return;
|
|
35352
|
+
}
|
|
35353
|
+
seen.add(entry);
|
|
35354
|
+
pending.push(this.#complete(entry));
|
|
35355
|
+
for (const value of Object.values(entry.data)) {
|
|
35356
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
35357
|
+
if (this.#isEntry(item)) {
|
|
35358
|
+
visit(item);
|
|
35359
|
+
}
|
|
35360
|
+
}
|
|
35361
|
+
}
|
|
35362
|
+
};
|
|
35363
|
+
for (const entry of entries) {
|
|
35364
|
+
if (entry) {
|
|
35365
|
+
visit(entry);
|
|
35366
|
+
}
|
|
35367
|
+
}
|
|
35368
|
+
await Promise.all(pending);
|
|
35369
|
+
}
|
|
35370
|
+
async complete() {
|
|
35371
|
+
const lists = await Promise.all(Object.keys(this.#source.section).map(async (name) => await this.load(name) ?? []));
|
|
35372
|
+
await this.materialize(lists.flat());
|
|
35373
|
+
}
|
|
35374
|
+
#relatedTypes(name) {
|
|
35375
|
+
const { section } = this.#source;
|
|
35376
|
+
const found = new Set([name]);
|
|
35377
|
+
const queue = [name];
|
|
35378
|
+
for (let type = queue.shift();type !== undefined; type = queue.shift()) {
|
|
35379
|
+
for (const target of referencedContentTypes(section[type])) {
|
|
35380
|
+
if (Object.hasOwn(section, target) && !found.has(target)) {
|
|
35381
|
+
found.add(target);
|
|
35382
|
+
queue.push(target);
|
|
35383
|
+
}
|
|
35384
|
+
}
|
|
35385
|
+
}
|
|
35386
|
+
return [...found];
|
|
35387
|
+
}
|
|
35388
|
+
#loadType(name) {
|
|
35389
|
+
let load = this.#loads.get(name);
|
|
35390
|
+
if (!load) {
|
|
35391
|
+
const { section, root, formats, projectConfig } = this.#source;
|
|
35392
|
+
load = loadSectionType(name, {
|
|
35393
|
+
formats,
|
|
35394
|
+
mounts: this.#mounts,
|
|
35395
|
+
onDeferred: (entry, completeBody) => {
|
|
35396
|
+
this.#deferred.set(entry, completeBody);
|
|
35397
|
+
},
|
|
35398
|
+
projectConfig,
|
|
35399
|
+
root,
|
|
35400
|
+
section
|
|
35401
|
+
}).then((entries) => {
|
|
35402
|
+
this.#adopt(name, entries);
|
|
35403
|
+
});
|
|
35404
|
+
load.catch(() => {
|
|
35405
|
+
this.#loads.delete(name);
|
|
35406
|
+
});
|
|
35407
|
+
this.#loads.set(name, load);
|
|
35408
|
+
}
|
|
35409
|
+
return load;
|
|
35410
|
+
}
|
|
35411
|
+
#adopt(name, entries) {
|
|
35412
|
+
for (const entry of entries) {
|
|
35413
|
+
this.#entries.add(entry);
|
|
35414
|
+
}
|
|
35415
|
+
this.set(name, entries);
|
|
35416
|
+
}
|
|
35417
|
+
#isEntry(value) {
|
|
35418
|
+
return isNonNullObject(value) && this.#entries.has(value);
|
|
35419
|
+
}
|
|
35420
|
+
#complete(entry) {
|
|
35421
|
+
let completing = this.#completing.get(entry);
|
|
35422
|
+
if (!completing) {
|
|
35423
|
+
const completeBody = this.#deferred.get(entry);
|
|
35424
|
+
if (!completeBody) {
|
|
35425
|
+
return Promise.resolve();
|
|
35426
|
+
}
|
|
35427
|
+
completing = completeBody().then(() => {
|
|
35428
|
+
this.#deferred.delete(entry);
|
|
35429
|
+
});
|
|
35430
|
+
completing.catch(() => {
|
|
35431
|
+
this.#completing.delete(entry);
|
|
35432
|
+
});
|
|
35433
|
+
this.#completing.set(entry, completing);
|
|
35434
|
+
}
|
|
35435
|
+
return completing;
|
|
35436
|
+
}
|
|
35437
|
+
}
|
|
35438
|
+
|
|
35122
35439
|
class Content {
|
|
35123
35440
|
static async projectData(sectionValue, ctx) {
|
|
35124
|
-
const
|
|
35125
|
-
|
|
35126
|
-
|
|
35127
|
-
|
|
35441
|
+
const source = {
|
|
35442
|
+
formats: ctx.registry.formats,
|
|
35443
|
+
projectConfig: ctx.projectConfig,
|
|
35444
|
+
root: ctx.root,
|
|
35445
|
+
section: sectionValue ?? {}
|
|
35446
|
+
};
|
|
35447
|
+
return ctx.lazy === true ? ContentStore.lazy(source) : await ContentStore.whole(source);
|
|
35128
35448
|
}
|
|
35129
35449
|
static assets(sectionValue, ctx) {
|
|
35130
35450
|
return contentAssetMounts(sectionValue ?? {}, ctx.root, ctx.projectConfig);
|
|
35131
35451
|
}
|
|
35132
35452
|
static async resolvePaths(pathsDef, ctx) {
|
|
35133
|
-
const entries = ctx.data
|
|
35453
|
+
const entries = await entriesOf(ctx.data, pathsDef.contentType);
|
|
35134
35454
|
if (!entries || entries.length === 0) {
|
|
35135
35455
|
console.warn(`Warning: $paths references content type "${pathsDef.contentType}" but it has no entries`);
|
|
35136
35456
|
return [];
|
|
@@ -35139,7 +35459,7 @@ class Content {
|
|
|
35139
35459
|
const field = pathsDef.field ?? "id";
|
|
35140
35460
|
const localized = entries.some((entry) => entry._meta?.locale !== undefined);
|
|
35141
35461
|
const wanted = ctx.locale;
|
|
35142
|
-
const scoped = localized &&
|
|
35462
|
+
const scoped = localized && isString(wanted) ? entries.filter((entry) => entry._meta?.locale === wanted) : entries;
|
|
35143
35463
|
if (pathsDef.param === undefined && pathsDef.field === undefined && ctx.urlPattern !== undefined && scoped.some((entry) => entry._meta?.route !== undefined)) {
|
|
35144
35464
|
return routedPaths(pathsDef.contentType, scoped, ctx, ctx.urlPattern);
|
|
35145
35465
|
}
|
|
@@ -35165,13 +35485,17 @@ class Content {
|
|
|
35165
35485
|
}
|
|
35166
35486
|
export {
|
|
35167
35487
|
Content,
|
|
35488
|
+
ContentStore,
|
|
35168
35489
|
LOCALE_PLACEHOLDER,
|
|
35169
35490
|
contentAssetMounts,
|
|
35170
35491
|
getContentTypeElements,
|
|
35171
35492
|
loadContentConfig,
|
|
35172
35493
|
loadContentSection,
|
|
35494
|
+
loadSectionType,
|
|
35173
35495
|
localeSource,
|
|
35174
35496
|
localesForExpansion,
|
|
35497
|
+
referencedContentTypes,
|
|
35175
35498
|
resolveContentTypeRefs,
|
|
35176
|
-
rewriteEntryAssets
|
|
35499
|
+
rewriteEntryAssets,
|
|
35500
|
+
sectionMounts
|
|
35177
35501
|
};
|