@hraness/wordcell 0.22.2 → 0.22.4
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 +4 -4
- package/dist/cli.js +4 -4
- package/dist/{index-3agn8scn.js → index-23m6bbjt.js} +47 -11
- package/dist/{index-bw2r37z9.js → index-9h7zh0wt.js} +1 -1
- package/dist/{index-hmw17zaa.js → index-pnt9wr59.js} +516 -230
- package/dist/index.js +2 -2
- package/dist/publish-reader/reader.js +48 -12
- package/dist/publish-search.js +1 -1
- package/dist/publish.js +2 -2
- package/dist/{support-p452p9c4.js → support-m3t917ev.js} +3 -3
- package/package.json +3 -3
- package/skills/wordcell/SKILL.md +1 -1
- package/src/publish-index.ts +3 -2
- package/src/publish-markdown.ts +400 -122
- package/src/publish-model.ts +2 -2
- package/src/publish-reader/reader.ts +3 -1
- package/src/publish-search.ts +44 -13
- package/src/publish.ts +4 -1
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
publishNormalize,
|
|
33
33
|
publishShardName
|
|
34
|
-
} from "./index-
|
|
34
|
+
} from "./index-23m6bbjt.js";
|
|
35
35
|
import {
|
|
36
36
|
parseLocalAttachmentReferences,
|
|
37
37
|
validateMarkdownAttachments
|
|
@@ -49,133 +49,6 @@ import { canonicalJson as canonicalJson2, canonicalSha256 } from "@hraness/oh";
|
|
|
49
49
|
|
|
50
50
|
// src/publish-index.ts
|
|
51
51
|
import { canonicalJson } from "@hraness/oh";
|
|
52
|
-
var TERM_PATTERN = /[\p{L}\p{N}][\p{L}\p{N}._/-]*/gu;
|
|
53
|
-
function bounded(value, maximumBytes) {
|
|
54
|
-
if (Buffer.byteLength(value, "utf8") <= maximumBytes)
|
|
55
|
-
return { text: value, truncated: false };
|
|
56
|
-
const bytes = Buffer.from(value, "utf8");
|
|
57
|
-
let end = maximumBytes;
|
|
58
|
-
while (end > 0 && ((bytes[end] ?? 0) & 192) === 128)
|
|
59
|
-
end -= 1;
|
|
60
|
-
return { text: bytes.subarray(0, end).toString("utf8"), truncated: true };
|
|
61
|
-
}
|
|
62
|
-
function previewText(note) {
|
|
63
|
-
const basis = note.summary !== "" ? note.summary : note.searchableText;
|
|
64
|
-
const collapsed = basis.replace(/\s+/gu, " ").trim();
|
|
65
|
-
return bounded(collapsed, WORDCELL_SITE_LIMITS_V1.docPreviewBytes).text;
|
|
66
|
-
}
|
|
67
|
-
function indexableTerms(text) {
|
|
68
|
-
const terms = new Set;
|
|
69
|
-
for (const match of text.matchAll(TERM_PATTERN))
|
|
70
|
-
terms.add(match[0]);
|
|
71
|
-
return [...terms];
|
|
72
|
-
}
|
|
73
|
-
function buildSiteIndex(notes, slugById, options = {}) {
|
|
74
|
-
const indexContent = options.indexContent !== false;
|
|
75
|
-
const fields = [];
|
|
76
|
-
const inlineTexts = [];
|
|
77
|
-
let textTruncated = false;
|
|
78
|
-
let inlineTotal = 0;
|
|
79
|
-
for (const note of notes) {
|
|
80
|
-
const title = bounded(publishNormalize(note.title), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
81
|
-
const aliases = bounded(note.aliases.map(publishNormalize).join(`
|
|
82
|
-
`), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
83
|
-
const path = bounded(`${publishNormalize(note.path)}
|
|
84
|
-
${publishNormalize(note.id)}`, WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
85
|
-
const tags = bounded(note.tags.map(publishNormalize).join(`
|
|
86
|
-
`), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
87
|
-
const metadata = bounded(publishNormalize(canonicalJson(note.metadata)), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
88
|
-
textTruncated ||= title.truncated || aliases.truncated || path.truncated || tags.truncated || metadata.truncated;
|
|
89
|
-
fields.push({ t: title.text, a: aliases.text, p: path.text, g: tags.text, m: metadata.text });
|
|
90
|
-
if (indexContent) {
|
|
91
|
-
const inline = bounded(publishNormalize(note.searchableText), WORDCELL_SITE_LIMITS_V1.inlineTextBytes);
|
|
92
|
-
textTruncated ||= inline.truncated;
|
|
93
|
-
inlineTexts.push(inline.text);
|
|
94
|
-
inlineTotal += Buffer.byteLength(inline.text, "utf8");
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const mode = !indexContent ? "none" : inlineTotal <= WORDCELL_SITE_LIMITS_V1.inlineTotalBytes ? "inline" : "shards";
|
|
98
|
-
const allTerms = new Set;
|
|
99
|
-
const contentPostings = new Map;
|
|
100
|
-
const docs = [];
|
|
101
|
-
for (const [index, note] of notes.entries()) {
|
|
102
|
-
const slug = slugById.get(note.id);
|
|
103
|
-
if (slug === undefined)
|
|
104
|
-
throw new Error(`Missing published slug for ${note.id}.`);
|
|
105
|
-
const field = fields[index];
|
|
106
|
-
if (field === undefined)
|
|
107
|
-
throw new Error(`Missing indexed fields for ${note.id}.`);
|
|
108
|
-
const inline = mode === "inline" ? inlineTexts[index] : undefined;
|
|
109
|
-
const fieldTerms = indexableTerms(`${field.t}
|
|
110
|
-
${field.a}
|
|
111
|
-
${field.p}
|
|
112
|
-
${field.g}
|
|
113
|
-
${field.m}`);
|
|
114
|
-
for (const term of fieldTerms)
|
|
115
|
-
allTerms.add(term);
|
|
116
|
-
if (mode !== "none") {
|
|
117
|
-
const content = inlineTexts[index] ?? "";
|
|
118
|
-
for (const term of indexableTerms(content)) {
|
|
119
|
-
allTerms.add(term);
|
|
120
|
-
if (mode === "shards") {
|
|
121
|
-
const list = contentPostings.get(term) ?? [];
|
|
122
|
-
list.push(index);
|
|
123
|
-
contentPostings.set(term, list);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
docs.push({
|
|
128
|
-
i: index,
|
|
129
|
-
s: slug,
|
|
130
|
-
t: note.title,
|
|
131
|
-
p: previewText(note),
|
|
132
|
-
f: field,
|
|
133
|
-
...inline === undefined ? {} : { x: inline }
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
let termsTruncated = false;
|
|
137
|
-
let sortedTerms = [...allTerms].toSorted((left, right) => left.localeCompare(right));
|
|
138
|
-
if (sortedTerms.length > WORDCELL_SITE_LIMITS_V1.indexTerms) {
|
|
139
|
-
sortedTerms = sortedTerms.slice(0, WORDCELL_SITE_LIMITS_V1.indexTerms);
|
|
140
|
-
termsTruncated = true;
|
|
141
|
-
}
|
|
142
|
-
const admitted = new Set(sortedTerms);
|
|
143
|
-
const postings = new Map;
|
|
144
|
-
if (mode === "shards") {
|
|
145
|
-
const grouped = new Map;
|
|
146
|
-
for (const [term, docIds] of [...contentPostings.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
147
|
-
if (!admitted.has(term))
|
|
148
|
-
continue;
|
|
149
|
-
const shard = publishShardName(term);
|
|
150
|
-
const bucket = grouped.get(shard) ?? Object.create(null);
|
|
151
|
-
bucket[term] = docIds;
|
|
152
|
-
grouped.set(shard, bucket);
|
|
153
|
-
}
|
|
154
|
-
for (const [shard, bucket] of [...grouped.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
155
|
-
postings.set(shard, {
|
|
156
|
-
format: WORDCELL_SITE_POSTINGS_FORMAT_V1,
|
|
157
|
-
shard,
|
|
158
|
-
postings: Object.freeze(bucket)
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
docs: {
|
|
164
|
-
format: WORDCELL_SITE_DOCS_FORMAT_V1,
|
|
165
|
-
content: mode,
|
|
166
|
-
docs
|
|
167
|
-
},
|
|
168
|
-
terms: {
|
|
169
|
-
format: WORDCELL_SITE_TERMS_FORMAT_V1,
|
|
170
|
-
terms: sortedTerms
|
|
171
|
-
},
|
|
172
|
-
postings,
|
|
173
|
-
termsCount: sortedTerms.length,
|
|
174
|
-
termsTruncated,
|
|
175
|
-
textTruncated,
|
|
176
|
-
mode
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
52
|
|
|
180
53
|
// src/publish-markdown.ts
|
|
181
54
|
var VOID_SLUG = /[^\p{L}\p{N}._~-]+/gu;
|
|
@@ -187,9 +60,15 @@ function escapeAttribute(value) {
|
|
|
187
60
|
}
|
|
188
61
|
function anchorId(text, used) {
|
|
189
62
|
const base = text.normalize("NFC").toLocaleLowerCase("en-US").replace(VOID_SLUG, "-").replace(/^[-._~]+|[-._~]+$/gu, "") || "section";
|
|
190
|
-
|
|
63
|
+
let seen = used.get(base) ?? 0;
|
|
64
|
+
let id = seen === 0 ? base : `${base}-${seen + 1}`;
|
|
65
|
+
while (used.has(id)) {
|
|
66
|
+
seen += 1;
|
|
67
|
+
id = `${base}-${seen + 1}`;
|
|
68
|
+
}
|
|
191
69
|
used.set(base, seen + 1);
|
|
192
|
-
|
|
70
|
+
used.set(id, 1);
|
|
71
|
+
return id;
|
|
193
72
|
}
|
|
194
73
|
function isExternalUrl(value) {
|
|
195
74
|
return /^(?:https?|mailto):/iu.test(value);
|
|
@@ -221,7 +100,41 @@ function wikiDisplay(raw) {
|
|
|
221
100
|
label: alias ?? (fragment === "" ? target : `${target} > ${fragment}`)
|
|
222
101
|
};
|
|
223
102
|
}
|
|
224
|
-
|
|
103
|
+
var FOOTNOTE_LIMITS = {
|
|
104
|
+
labelBytes: 128,
|
|
105
|
+
definitions: 256,
|
|
106
|
+
bodyBytes: 8192,
|
|
107
|
+
bodyLines: 32,
|
|
108
|
+
references: 2048
|
|
109
|
+
};
|
|
110
|
+
function footnoteLabel(raw) {
|
|
111
|
+
const label = raw.normalize("NFC");
|
|
112
|
+
if (label.length > FOOTNOTE_LIMITS.labelBytes || !/^[\p{L}\p{M}\p{N}._:-]+$/u.test(label))
|
|
113
|
+
return;
|
|
114
|
+
return new TextEncoder().encode(label).byteLength <= FOOTNOTE_LIMITS.labelBytes ? label : undefined;
|
|
115
|
+
}
|
|
116
|
+
function footnoteNumber(note, footnotes) {
|
|
117
|
+
if (note.number === undefined) {
|
|
118
|
+
note.number = footnotes.numbered.length + 1;
|
|
119
|
+
footnotes.numbered.push(note);
|
|
120
|
+
}
|
|
121
|
+
return note.number;
|
|
122
|
+
}
|
|
123
|
+
function renderFootnoteReference(raw, label, footnotes) {
|
|
124
|
+
const key = footnoteLabel(label);
|
|
125
|
+
const note = key === undefined ? undefined : footnotes.byLabel.get(key);
|
|
126
|
+
if (footnotes.disabled || note === undefined || note === null || note.invalid || footnotes.references >= FOOTNOTE_LIMITS.references) {
|
|
127
|
+
return `<span class="unresolved footnote-reference" aria-label="Unresolved footnote">${escapeHtml(raw)}</span>`;
|
|
128
|
+
}
|
|
129
|
+
const number = footnoteNumber(note, footnotes);
|
|
130
|
+
const id = `wordcell:footnote-ref:${number}:${note.references.length + 1}`;
|
|
131
|
+
note.references.push(id);
|
|
132
|
+
footnotes.references += 1;
|
|
133
|
+
if (footnotes.plain)
|
|
134
|
+
return "";
|
|
135
|
+
return `<sup><a id="${id}" href="#wordcell:footnote:${number}" role="doc-noteref" aria-label="Footnote ${number}">${number}</a></sup>`;
|
|
136
|
+
}
|
|
137
|
+
function renderInline(text, ctx, footnotes) {
|
|
225
138
|
let output = "";
|
|
226
139
|
let cursor = 0;
|
|
227
140
|
const length = text.length;
|
|
@@ -236,7 +149,7 @@ function renderInline(text, ctx) {
|
|
|
236
149
|
if (closing !== -1) {
|
|
237
150
|
const code = rest.slice(ticks.length, closing).replace(/\s+/gu, " ");
|
|
238
151
|
output += `<code>${escapeHtml(code)}</code>`;
|
|
239
|
-
cursor +=
|
|
152
|
+
cursor += closing + ticks.length;
|
|
240
153
|
continue;
|
|
241
154
|
}
|
|
242
155
|
pushText("`");
|
|
@@ -348,6 +261,14 @@ function renderInline(text, ctx) {
|
|
|
348
261
|
continue;
|
|
349
262
|
}
|
|
350
263
|
}
|
|
264
|
+
if (footnotes !== undefined && rest.startsWith("[^")) {
|
|
265
|
+
const end = rest.indexOf("]", 2);
|
|
266
|
+
if (end !== -1) {
|
|
267
|
+
output += renderFootnoteReference(rest.slice(0, end + 1), rest.slice(2, end), footnotes);
|
|
268
|
+
cursor += end + 1;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
351
272
|
pushText("[");
|
|
352
273
|
cursor += 1;
|
|
353
274
|
continue;
|
|
@@ -373,7 +294,7 @@ function renderInline(text, ctx) {
|
|
|
373
294
|
if (emphasis !== undefined && emphasis !== "_") {
|
|
374
295
|
const closing = rest.indexOf(emphasis, emphasis.length);
|
|
375
296
|
if (closing > emphasis.length) {
|
|
376
|
-
const inner = renderInline(rest.slice(emphasis.length, closing), ctx);
|
|
297
|
+
const inner = renderInline(rest.slice(emphasis.length, closing), ctx, footnotes);
|
|
377
298
|
const tag = emphasis === "**" || emphasis === "__" ? "strong" : emphasis === "~~" ? "del" : emphasis === "==" ? "mark" : "em";
|
|
378
299
|
output += `<${tag}>${inner}</${tag}>`;
|
|
379
300
|
cursor += closing + emphasis.length;
|
|
@@ -383,7 +304,7 @@ function renderInline(text, ctx) {
|
|
|
383
304
|
if (emphasis === "_") {
|
|
384
305
|
const closing = rest.indexOf("_", 1);
|
|
385
306
|
if (closing > 1) {
|
|
386
|
-
output += `<em>${renderInline(rest.slice(1, closing), ctx)}</em>`;
|
|
307
|
+
output += `<em>${renderInline(rest.slice(1, closing), ctx, footnotes)}</em>`;
|
|
387
308
|
cursor += closing + 1;
|
|
388
309
|
continue;
|
|
389
310
|
}
|
|
@@ -509,44 +430,125 @@ function tableCells(line) {
|
|
|
509
430
|
cells.push(current);
|
|
510
431
|
return cells.map((cell) => cell.trim());
|
|
511
432
|
}
|
|
512
|
-
function
|
|
433
|
+
function withoutComments(line, comments, continuesCode) {
|
|
434
|
+
let text = "";
|
|
435
|
+
let cursor = 0;
|
|
436
|
+
while (cursor < line.length) {
|
|
437
|
+
if (comments.code !== undefined) {
|
|
438
|
+
const end = line.indexOf(comments.code, cursor);
|
|
439
|
+
if (end === -1)
|
|
440
|
+
return text + line.slice(cursor);
|
|
441
|
+
const next = end + comments.code.length;
|
|
442
|
+
text += line.slice(cursor, next);
|
|
443
|
+
cursor = next;
|
|
444
|
+
delete comments.code;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (comments.closing !== undefined) {
|
|
448
|
+
const end = line.indexOf(comments.closing, cursor);
|
|
449
|
+
if (end === -1)
|
|
450
|
+
return text;
|
|
451
|
+
cursor = end + comments.closing.length;
|
|
452
|
+
delete comments.closing;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
const rest = line.slice(cursor);
|
|
456
|
+
if (rest[0] === "\\" && rest.length > 1) {
|
|
457
|
+
text += rest.slice(0, 2);
|
|
458
|
+
cursor += 2;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (rest[0] === "`") {
|
|
462
|
+
const ticks = /^`+/u.exec(rest)?.[0] ?? "`";
|
|
463
|
+
const closing = rest.indexOf(ticks, ticks.length);
|
|
464
|
+
if (closing !== -1) {
|
|
465
|
+
const end = closing + ticks.length;
|
|
466
|
+
text += rest.slice(0, end);
|
|
467
|
+
cursor += end;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (continuesCode?.(ticks) === true) {
|
|
471
|
+
comments.code = ticks;
|
|
472
|
+
return text + rest;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (rest.startsWith("<!--") || rest.startsWith("%%")) {
|
|
476
|
+
comments.closing = rest.startsWith("<!--") ? "-->" : "%%";
|
|
477
|
+
cursor += comments.closing === "-->" ? 4 : 2;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
text += rest[0] ?? "";
|
|
481
|
+
cursor += 1;
|
|
482
|
+
}
|
|
483
|
+
return text;
|
|
484
|
+
}
|
|
485
|
+
function paragraphBoundary(line, next) {
|
|
486
|
+
return line.trim() === "" || /^(?: {4}|\t|#{1,6}\s|\s{0,3}>|\s{0,3}(?:`{3,}|~{3,}))/u.test(line) || listMarker(line) !== undefined || line.includes("|") && isTableDivider(next) || /^\s{0,3}(?:(?:\*\s*){3,}|(?:-\s*){3,}|(?:_\s*){3,})$/u.test(line);
|
|
487
|
+
}
|
|
488
|
+
function hasContinuedInlineCode(lines, start, ticks) {
|
|
489
|
+
for (let index = start;index < lines.length; index += 1) {
|
|
490
|
+
const line = lines[index] ?? "";
|
|
491
|
+
if (paragraphBoundary(line, lines[index + 1] ?? ""))
|
|
492
|
+
return false;
|
|
493
|
+
if (line.includes(ticks))
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
function admitFootnote(rawLabel, body, authored, footnotes) {
|
|
499
|
+
const text = body.join(`
|
|
500
|
+
`);
|
|
501
|
+
const label = footnoteLabel(rawLabel);
|
|
502
|
+
const note = {
|
|
503
|
+
body: text,
|
|
504
|
+
authored: authored.join(`
|
|
505
|
+
`),
|
|
506
|
+
invalid: label === undefined || body.length > FOOTNOTE_LIMITS.bodyLines || text.length > FOOTNOTE_LIMITS.bodyBytes || new TextEncoder().encode(text).byteLength > FOOTNOTE_LIMITS.bodyBytes || text.trim() === "",
|
|
507
|
+
references: []
|
|
508
|
+
};
|
|
509
|
+
footnotes.definitions += 1;
|
|
510
|
+
if (footnotes.definitions > FOOTNOTE_LIMITS.definitions) {
|
|
511
|
+
footnotes.disabled = true;
|
|
512
|
+
footnotes.byLabel.clear();
|
|
513
|
+
}
|
|
514
|
+
if (!footnotes.disabled && label !== undefined) {
|
|
515
|
+
if (footnotes.byLabel.has(label)) {
|
|
516
|
+
const prior = footnotes.byLabel.get(label);
|
|
517
|
+
if (prior !== undefined && prior !== null)
|
|
518
|
+
prior.invalid = true;
|
|
519
|
+
note.invalid = true;
|
|
520
|
+
footnotes.byLabel.set(label, null);
|
|
521
|
+
} else {
|
|
522
|
+
footnotes.byLabel.set(label, note.invalid ? null : note);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return note;
|
|
526
|
+
}
|
|
527
|
+
function parseMarkdownBlocks(content, footnotes, topLevel) {
|
|
513
528
|
const lines = content.split(`
|
|
514
529
|
`);
|
|
515
|
-
let
|
|
516
|
-
if (lines[0]?.trim() === "---") {
|
|
517
|
-
for (let
|
|
518
|
-
if (lines[
|
|
519
|
-
|
|
530
|
+
let index = 0;
|
|
531
|
+
if (topLevel && lines[0]?.trim() === "---") {
|
|
532
|
+
for (let cursor = 1;cursor < lines.length; cursor += 1) {
|
|
533
|
+
if (lines[cursor]?.trim() === "---" || lines[cursor]?.trim() === "...") {
|
|
534
|
+
index = cursor + 1;
|
|
520
535
|
break;
|
|
521
536
|
}
|
|
522
537
|
}
|
|
523
538
|
}
|
|
524
|
-
const
|
|
525
|
-
const
|
|
526
|
-
let index = start;
|
|
527
|
-
let inComment = false;
|
|
539
|
+
const blocks = [];
|
|
540
|
+
const comments = {};
|
|
528
541
|
const paragraph = [];
|
|
529
542
|
const flushParagraph = () => {
|
|
530
543
|
if (paragraph.length === 0)
|
|
531
544
|
return;
|
|
532
|
-
|
|
533
|
-
`).replace(/\n/gu, " "), ctx)}</p>`);
|
|
545
|
+
blocks.push({ type: "paragraph", text: paragraph.join(" ") });
|
|
534
546
|
paragraph.length = 0;
|
|
535
547
|
};
|
|
536
548
|
while (index < lines.length) {
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
index += 1;
|
|
541
|
-
if (close !== -1)
|
|
542
|
-
inComment = false;
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
if (line.trim().startsWith("%%") && line.trim().endsWith("%%") && line.trim().length > 3) {
|
|
546
|
-
index += 1;
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
const fence = /^\s{0,3}(`{3,}|~{3,})([^`]*)$/u.exec(line);
|
|
549
|
+
const raw = lines[index] ?? "";
|
|
550
|
+
const inCode = comments.code !== undefined;
|
|
551
|
+
const fence = comments.closing === undefined && !inCode ? /^\s{0,3}(`{3,}|~{3,})([^`]*)$/u.exec(raw) : null;
|
|
550
552
|
if (fence !== null) {
|
|
551
553
|
flushParagraph();
|
|
552
554
|
const marker2 = fence[1] ?? "```";
|
|
@@ -556,16 +558,58 @@ function renderMarkdownToHtml(content, ctx) {
|
|
|
556
558
|
while (index < lines.length) {
|
|
557
559
|
const candidate = lines[index] ?? "";
|
|
558
560
|
const close = /^\s{0,3}(`{3,}|~{3,})\s*$/u.exec(candidate);
|
|
559
|
-
if (close !== null && (close[1] ?? "")[0] === marker2[0] && (close[1] ?? "").length >= marker2.length)
|
|
561
|
+
if (close !== null && (close[1] ?? "")[0] === marker2[0] && (close[1] ?? "").length >= marker2.length)
|
|
560
562
|
break;
|
|
561
|
-
}
|
|
562
563
|
body.push(candidate);
|
|
563
564
|
index += 1;
|
|
564
565
|
}
|
|
565
566
|
index += 1;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
567
|
+
blocks.push({ type: "code", language, text: body.join(`
|
|
568
|
+
`) });
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (comments.closing === undefined && !inCode && /^(?: {4}|\t)/u.test(raw)) {
|
|
572
|
+
flushParagraph();
|
|
573
|
+
const body = [];
|
|
574
|
+
while (index < lines.length && /^(?: {4}|\t| *$)/u.test(lines[index] ?? "")) {
|
|
575
|
+
body.push((lines[index] ?? "").replace(/^(?: {4}|\t)/u, ""));
|
|
576
|
+
index += 1;
|
|
577
|
+
}
|
|
578
|
+
blocks.push({ type: "code", language: "", text: body.join(`
|
|
579
|
+
`).replace(/\n+$/u, "") });
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
const rawDefinition = comments.closing === undefined && !inCode ? /^ {0,3}\[\^([^\]\n]*)\]:/u.exec(raw) : null;
|
|
583
|
+
if (rawDefinition !== null) {
|
|
584
|
+
flushParagraph();
|
|
585
|
+
const authored = [raw];
|
|
586
|
+
index += 1;
|
|
587
|
+
while (index < lines.length) {
|
|
588
|
+
const next = lines[index] ?? "";
|
|
589
|
+
if (/^(?: {4}|\t)/u.test(next) || next.trim() === "" && /^(?: {4}|\t)/u.test(lines[index + 1] ?? "")) {
|
|
590
|
+
authored.push(next);
|
|
591
|
+
index += 1;
|
|
592
|
+
} else {
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const clean = withoutComments(authored.join(`
|
|
597
|
+
`), comments).split(`
|
|
598
|
+
`);
|
|
599
|
+
const definition = /^ {0,3}\[\^([^\]\n]*)\]:[ \t]*(.*)$/u.exec(clean[0] ?? "");
|
|
600
|
+
if (!topLevel || definition === null) {
|
|
601
|
+
blocks.push({ type: "literal", text: clean.join(`
|
|
602
|
+
`) });
|
|
603
|
+
} else {
|
|
604
|
+
const body = [definition[2] ?? "", ...clean.slice(1).map((line2) => line2.replace(/^(?: {4}|\t)/u, ""))];
|
|
605
|
+
blocks.push({ type: "footnote", note: admitFootnote(rawDefinition[1] ?? "", body, clean, footnotes) });
|
|
606
|
+
}
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const line = comments.closing === undefined && !inCode && /^\s{0,3}>/u.test(raw) ? raw : withoutComments(raw, comments, paragraphBoundary(raw, lines[index + 1] ?? "") ? undefined : (ticks) => hasContinuedInlineCode(lines, index + 1, ticks));
|
|
610
|
+
if (inCode) {
|
|
611
|
+
paragraph.push(line);
|
|
612
|
+
index += 1;
|
|
569
613
|
continue;
|
|
570
614
|
}
|
|
571
615
|
if (line.trim() === "") {
|
|
@@ -576,118 +620,231 @@ function renderMarkdownToHtml(content, ctx) {
|
|
|
576
620
|
const heading = /^(#{1,6})\s+(.*)$/u.exec(line);
|
|
577
621
|
if (heading !== null) {
|
|
578
622
|
flushParagraph();
|
|
579
|
-
|
|
580
|
-
const body = renderInline((heading[2] ?? "").replace(/\s+#+$/u, ""), ctx);
|
|
581
|
-
const id = anchorId(stripMarkup(body), anchors);
|
|
582
|
-
html.push(`<h${level} id="${escapeAttribute(id)}">${body}</h${level}>`);
|
|
623
|
+
blocks.push({ type: "heading", level: (heading[1] ?? "#").length, text: (heading[2] ?? "").replace(/\s+#+$/u, "") });
|
|
583
624
|
index += 1;
|
|
584
625
|
continue;
|
|
585
626
|
}
|
|
586
627
|
if (/^\s{0,3}(?:\*\s*){3,}$/u.test(line) || /^\s{0,3}(?:-\s*){3,}$/u.test(line) || /^\s{0,3}(?:_\s*){3,}$/u.test(line)) {
|
|
587
628
|
flushParagraph();
|
|
588
|
-
|
|
629
|
+
blocks.push({ type: "break" });
|
|
589
630
|
index += 1;
|
|
590
631
|
continue;
|
|
591
632
|
}
|
|
592
633
|
if (/^\s{0,3}>/u.test(line)) {
|
|
593
634
|
flushParagraph();
|
|
594
|
-
const quote = [];
|
|
635
|
+
const quote = [line.replace(/^\s{0,3}>\s?/u, "")];
|
|
636
|
+
index += 1;
|
|
595
637
|
while (index < lines.length && /^\s{0,3}>/u.test(lines[index] ?? "")) {
|
|
596
638
|
quote.push((lines[index] ?? "").replace(/^\s{0,3}>\s?/u, ""));
|
|
597
639
|
index += 1;
|
|
598
640
|
}
|
|
599
|
-
|
|
600
|
-
`),
|
|
641
|
+
blocks.push({ type: "quote", blocks: parseMarkdownBlocks(quote.join(`
|
|
642
|
+
`), footnotes, false) });
|
|
601
643
|
continue;
|
|
602
644
|
}
|
|
603
645
|
if (line.includes("|") && index + 1 < lines.length && isTableDivider(lines[index + 1] ?? "")) {
|
|
604
646
|
flushParagraph();
|
|
605
647
|
const header = tableCells(line);
|
|
606
|
-
index += 2;
|
|
607
648
|
const rows = [];
|
|
649
|
+
index += 2;
|
|
608
650
|
while (index < lines.length && (lines[index] ?? "").includes("|") && (lines[index] ?? "").trim() !== "") {
|
|
609
|
-
|
|
651
|
+
const row = withoutComments(lines[index] ?? "", comments);
|
|
652
|
+
if (row.trim() !== "")
|
|
653
|
+
rows.push(tableCells(row));
|
|
610
654
|
index += 1;
|
|
611
655
|
}
|
|
612
|
-
|
|
613
|
-
const bodyRows = rows.map((row) => `<tr>${tableCells(row).map((cell) => `<td>${renderInline(cell, ctx)}</td>`).join("")}</tr>`).join("");
|
|
614
|
-
html.push(`<table><thead><tr>${head}</tr></thead><tbody>${bodyRows}</tbody></table>`);
|
|
656
|
+
blocks.push({ type: "table", header, rows });
|
|
615
657
|
continue;
|
|
616
658
|
}
|
|
617
659
|
const marker = listMarker(line);
|
|
618
660
|
if (marker !== undefined) {
|
|
619
661
|
flushParagraph();
|
|
620
|
-
const
|
|
621
|
-
let ordered = marker.ordered;
|
|
662
|
+
const parts = [];
|
|
622
663
|
const baseIndent = marker.indent;
|
|
623
664
|
const stack = [];
|
|
665
|
+
let currentLine = line;
|
|
624
666
|
while (index < lines.length) {
|
|
625
|
-
const current = listMarker(
|
|
626
|
-
const raw = lines[index] ?? "";
|
|
667
|
+
const current = listMarker(currentLine);
|
|
627
668
|
if (current === undefined) {
|
|
628
|
-
if (
|
|
669
|
+
if (currentLine.trim() === "") {
|
|
629
670
|
index += 1;
|
|
630
671
|
break;
|
|
631
672
|
}
|
|
632
|
-
const continuationIndent =
|
|
633
|
-
if (
|
|
634
|
-
|
|
635
|
-
${renderInline(raw.trim(), ctx)}`);
|
|
673
|
+
const continuationIndent = currentLine.length - currentLine.trimStart().length;
|
|
674
|
+
if (parts.length > 0 && continuationIndent > baseIndent) {
|
|
675
|
+
parts.push({ type: "continuation", text: currentLine.trim() });
|
|
636
676
|
index += 1;
|
|
637
|
-
|
|
677
|
+
} else {
|
|
678
|
+
break;
|
|
638
679
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
stack.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
items.push(`</${popped?.ordered === true ? "ol" : "ul"}>`);
|
|
680
|
+
} else {
|
|
681
|
+
const body = currentLine.slice(currentLine.search(/\S/u));
|
|
682
|
+
const itemBody = body.replace(/^(?:[-+*]|\d{1,9}[.)])\s+/u, "");
|
|
683
|
+
const task = /^\[([ xX])\]\s+/u.exec(itemBody);
|
|
684
|
+
const text = task === null ? itemBody : itemBody.slice((task[0] ?? "").length);
|
|
685
|
+
if (current.indent > (stack[stack.length - 1]?.indent ?? baseIndent) && stack.length < 8) {
|
|
686
|
+
stack.push({ indent: current.indent, ordered: current.ordered });
|
|
687
|
+
parts.push({ type: "open", ordered: current.ordered });
|
|
688
|
+
}
|
|
689
|
+
while (stack.length > 0 && current.indent < (stack[stack.length - 1]?.indent ?? baseIndent)) {
|
|
690
|
+
parts.push({ type: "close", ordered: stack.pop()?.ordered === true });
|
|
691
|
+
}
|
|
692
|
+
parts.push({ type: "item", text, ...task === null ? {} : { checked: (task[1] ?? " ").toLowerCase() === "x" } });
|
|
693
|
+
index += 1;
|
|
654
694
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const popped = stack.pop();
|
|
660
|
-
items.push(`</${popped?.ordered === true ? "ol" : "ul"}>`);
|
|
661
|
-
}
|
|
662
|
-
html.push(`<${ordered ? "ol" : "ul"}>${items.join("")}</${ordered ? "ol" : "ul"}>`);
|
|
663
|
-
continue;
|
|
664
|
-
}
|
|
665
|
-
const commentStart = line.indexOf("<!--");
|
|
666
|
-
if (commentStart !== -1) {
|
|
667
|
-
const close = line.indexOf("-->", commentStart + 4);
|
|
668
|
-
if (close === -1)
|
|
669
|
-
inComment = true;
|
|
670
|
-
index += 1;
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
if (/^(?: {4}|\t)/u.test(line)) {
|
|
674
|
-
flushParagraph();
|
|
675
|
-
const body = [];
|
|
676
|
-
while (index < lines.length && /^(?: {4}|\t| *$)/u.test(lines[index] ?? "")) {
|
|
677
|
-
body.push((lines[index] ?? "").replace(/^(?: {4}|\t)/u, ""));
|
|
678
|
-
index += 1;
|
|
695
|
+
const next = lines[index] ?? "";
|
|
696
|
+
if (listMarker(next) === undefined && next.trim() !== "" && next.length - next.trimStart().length <= baseIndent)
|
|
697
|
+
break;
|
|
698
|
+
currentLine = withoutComments(next, comments);
|
|
679
699
|
}
|
|
680
|
-
|
|
681
|
-
|
|
700
|
+
while (stack.length > 0)
|
|
701
|
+
parts.push({ type: "close", ordered: stack.pop()?.ordered === true });
|
|
702
|
+
blocks.push({ type: "list", ordered: marker.ordered, parts });
|
|
682
703
|
continue;
|
|
683
704
|
}
|
|
684
705
|
paragraph.push(line);
|
|
685
706
|
index += 1;
|
|
686
707
|
}
|
|
687
708
|
flushParagraph();
|
|
688
|
-
return
|
|
709
|
+
return blocks;
|
|
710
|
+
}
|
|
711
|
+
function renderBlocks(blocks, ctx, footnotes, anchors) {
|
|
712
|
+
return blocks.map((block) => {
|
|
713
|
+
switch (block.type) {
|
|
714
|
+
case "paragraph":
|
|
715
|
+
return `<p>${renderInline(block.text, ctx, footnotes)}</p>`;
|
|
716
|
+
case "literal":
|
|
717
|
+
return `<p>${escapeHtml(block.text).replaceAll(`
|
|
718
|
+
`, "<br>")}</p>`;
|
|
719
|
+
case "heading": {
|
|
720
|
+
const body = renderInline(block.text, ctx, footnotes);
|
|
721
|
+
return `<h${block.level} id="${escapeAttribute(anchorId(stripMarkup(body), anchors))}">${body}</h${block.level}>`;
|
|
722
|
+
}
|
|
723
|
+
case "code":
|
|
724
|
+
return `<pre><code${block.language === "" ? "" : ` class="language-${escapeAttribute(block.language)}"`}>${escapeHtml(block.text)}</code></pre>`;
|
|
725
|
+
case "break":
|
|
726
|
+
return "<hr>";
|
|
727
|
+
case "quote":
|
|
728
|
+
return `<blockquote>${renderBlocks(block.blocks, ctx, footnotes, anchors)}</blockquote>`;
|
|
729
|
+
case "table": {
|
|
730
|
+
const header = block.header.map((cell) => `<th>${renderInline(cell, ctx, footnotes)}</th>`).join("");
|
|
731
|
+
const rows = block.rows.map((row) => `<tr>${row.map((cell) => `<td>${renderInline(cell, ctx, footnotes)}</td>`).join("")}</tr>`).join("");
|
|
732
|
+
return `<table><thead><tr>${header}</tr></thead><tbody>${rows}</tbody></table>`;
|
|
733
|
+
}
|
|
734
|
+
case "list": {
|
|
735
|
+
const parts = block.parts.map((part) => {
|
|
736
|
+
switch (part.type) {
|
|
737
|
+
case "open":
|
|
738
|
+
return `<${part.ordered ? "ol" : "ul"}>`;
|
|
739
|
+
case "close":
|
|
740
|
+
return `</${part.ordered ? "ol" : "ul"}>`;
|
|
741
|
+
case "continuation":
|
|
742
|
+
return `
|
|
743
|
+
${renderInline(part.text, ctx, footnotes)}`;
|
|
744
|
+
case "item":
|
|
745
|
+
return `<li>${part.checked === undefined ? "" : `<input type="checkbox" disabled${part.checked ? " checked" : ""}> `}${renderInline(part.text, ctx, footnotes)}</li>`;
|
|
746
|
+
}
|
|
747
|
+
}).join("");
|
|
748
|
+
return `<${block.ordered ? "ol" : "ul"}>${parts}</${block.ordered ? "ol" : "ul"}>`;
|
|
749
|
+
}
|
|
750
|
+
case "footnote":
|
|
751
|
+
return footnotes.disabled || block.note.invalid ? `<p class="unresolved footnote-definition">${escapeHtml(block.note.authored).replaceAll(`
|
|
752
|
+
`, "<br>")}</p>` : "";
|
|
753
|
+
}
|
|
754
|
+
}).filter((html) => html !== "").join(`
|
|
689
755
|
`);
|
|
690
756
|
}
|
|
757
|
+
function renderMarkdownToHtml(content, ctx) {
|
|
758
|
+
const footnotes = { byLabel: new Map, numbered: [], definitions: 0, references: 0, disabled: false };
|
|
759
|
+
const blocks = parseMarkdownBlocks(content, footnotes, true);
|
|
760
|
+
const body = renderBlocks(blocks, ctx, footnotes, new Map);
|
|
761
|
+
if (footnotes.disabled)
|
|
762
|
+
return body;
|
|
763
|
+
for (const note of footnotes.byLabel.values()) {
|
|
764
|
+
if (note !== null && !note.invalid)
|
|
765
|
+
footnoteNumber(note, footnotes);
|
|
766
|
+
}
|
|
767
|
+
if (footnotes.numbered.length === 0)
|
|
768
|
+
return body;
|
|
769
|
+
const notes = footnotes.numbered.map((note) => {
|
|
770
|
+
const number = note.number;
|
|
771
|
+
const backlinks = note.references.map((id, index) => `<a href="#${id}" role="doc-backlink" aria-label="Back to reference ${index + 1} for footnote ${number}">\u21A9 ${index + 1}</a>`).join(" ");
|
|
772
|
+
const text = renderInline(note.body.replaceAll(`
|
|
773
|
+
`, " "), ctx);
|
|
774
|
+
return `<li id="wordcell:footnote:${number}" tabindex="-1">${text}${backlinks === "" ? "" : ` ${backlinks}`}</li>`;
|
|
775
|
+
}).join(`
|
|
776
|
+
`);
|
|
777
|
+
return `${body}${body === "" ? "" : `
|
|
778
|
+
`}<section class="footnotes" aria-label="Footnotes"><ol>
|
|
779
|
+
${notes}
|
|
780
|
+
</ol></section>`;
|
|
781
|
+
}
|
|
782
|
+
function projectMarkdownText(content, description) {
|
|
783
|
+
const footnotes = { byLabel: new Map, numbered: [], definitions: 0, references: 0, disabled: false, plain: true };
|
|
784
|
+
const blocks = parseMarkdownBlocks(content, footnotes, true);
|
|
785
|
+
const context = {
|
|
786
|
+
source: "",
|
|
787
|
+
resolveNote: () => {
|
|
788
|
+
return;
|
|
789
|
+
},
|
|
790
|
+
resolveAsset: () => {
|
|
791
|
+
return;
|
|
792
|
+
},
|
|
793
|
+
noteHref: () => ""
|
|
794
|
+
};
|
|
795
|
+
const inline = (text2, state) => stripMarkup(renderInline(text2, context, state));
|
|
796
|
+
const parts = (items, state) => items.map((block) => {
|
|
797
|
+
switch (block.type) {
|
|
798
|
+
case "paragraph":
|
|
799
|
+
case "heading":
|
|
800
|
+
return inline(block.text, state);
|
|
801
|
+
case "literal":
|
|
802
|
+
case "code":
|
|
803
|
+
return block.text;
|
|
804
|
+
case "break":
|
|
805
|
+
return "";
|
|
806
|
+
case "quote":
|
|
807
|
+
return parts(block.blocks, state).join(`
|
|
808
|
+
|
|
809
|
+
`);
|
|
810
|
+
case "table":
|
|
811
|
+
return [block.header, ...block.rows].map((row) => row.map((cell) => inline(cell, state)).join(" ")).join(`
|
|
812
|
+
`);
|
|
813
|
+
case "list":
|
|
814
|
+
return block.parts.map((part) => part.type === "item" || part.type === "continuation" ? inline(part.text, state) : "").filter(Boolean).join(`
|
|
815
|
+
`);
|
|
816
|
+
case "footnote":
|
|
817
|
+
return state.disabled || block.note.invalid ? block.note.authored : "";
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
const rendered = parts(blocks, footnotes);
|
|
821
|
+
if (!footnotes.disabled)
|
|
822
|
+
for (const note of footnotes.byLabel.values()) {
|
|
823
|
+
if (note !== null && !note.invalid)
|
|
824
|
+
footnoteNumber(note, footnotes);
|
|
825
|
+
}
|
|
826
|
+
const text = [...rendered, ...footnotes.numbered.map((note) => inline(note.body.replaceAll(`
|
|
827
|
+
`, " ")))].filter(Boolean).join(`
|
|
828
|
+
|
|
829
|
+
`);
|
|
830
|
+
let preview = rendered.find((value, index) => blocks[index]?.type === "paragraph" && value.trim() !== "") ?? text;
|
|
831
|
+
if (description !== undefined && description.trim() !== "") {
|
|
832
|
+
const state = {
|
|
833
|
+
byLabel: new Map([...footnotes.byLabel].map(([label, note]) => [label, note === null ? null : { body: note.body, authored: note.authored, invalid: note.invalid, references: [] }])),
|
|
834
|
+
numbered: [],
|
|
835
|
+
definitions: footnotes.definitions,
|
|
836
|
+
references: 0,
|
|
837
|
+
disabled: footnotes.disabled,
|
|
838
|
+
plain: true
|
|
839
|
+
};
|
|
840
|
+
const authored = parts(parseMarkdownBlocks(description, state, false), state).filter(Boolean).join(`
|
|
841
|
+
|
|
842
|
+
`);
|
|
843
|
+
if (authored.trim() !== "")
|
|
844
|
+
preview = authored;
|
|
845
|
+
}
|
|
846
|
+
return { text, preview };
|
|
847
|
+
}
|
|
691
848
|
function stripMarkup(html) {
|
|
692
849
|
let stripped = "";
|
|
693
850
|
let inTag = false;
|
|
@@ -702,6 +859,135 @@ function stripMarkup(html) {
|
|
|
702
859
|
return stripped.replaceAll("<", "<").replaceAll(">", ">").replaceAll(""", '"').replaceAll("'", "'").replaceAll("&", "&");
|
|
703
860
|
}
|
|
704
861
|
|
|
862
|
+
// src/publish-index.ts
|
|
863
|
+
var TERM_PATTERN = /[\p{L}\p{N}][\p{L}\p{N}._/-]*/gu;
|
|
864
|
+
function bounded(value, maximumBytes) {
|
|
865
|
+
if (Buffer.byteLength(value, "utf8") <= maximumBytes)
|
|
866
|
+
return { text: value, truncated: false };
|
|
867
|
+
const bytes = Buffer.from(value, "utf8");
|
|
868
|
+
let end = maximumBytes;
|
|
869
|
+
while (end > 0 && ((bytes[end] ?? 0) & 192) === 128)
|
|
870
|
+
end -= 1;
|
|
871
|
+
return { text: bytes.subarray(0, end).toString("utf8"), truncated: true };
|
|
872
|
+
}
|
|
873
|
+
function previewText(note) {
|
|
874
|
+
const display = projectMarkdownText(note.content, note.properties["description"]);
|
|
875
|
+
const collapsed = display.preview.replace(/\s+/gu, " ").trim();
|
|
876
|
+
return bounded(collapsed, WORDCELL_SITE_LIMITS_V1.docPreviewBytes).text;
|
|
877
|
+
}
|
|
878
|
+
function indexableTerms(text) {
|
|
879
|
+
const terms = new Set;
|
|
880
|
+
for (const match of text.matchAll(TERM_PATTERN))
|
|
881
|
+
terms.add(match[0]);
|
|
882
|
+
return [...terms];
|
|
883
|
+
}
|
|
884
|
+
function buildSiteIndex(notes, slugById, options = {}) {
|
|
885
|
+
const indexContent = options.indexContent !== false;
|
|
886
|
+
const fields = [];
|
|
887
|
+
const inlineTexts = [];
|
|
888
|
+
let textTruncated = false;
|
|
889
|
+
let inlineTotal = 0;
|
|
890
|
+
for (const note of notes) {
|
|
891
|
+
const title = bounded(publishNormalize(note.title), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
892
|
+
const aliases = bounded(note.aliases.map(publishNormalize).join(`
|
|
893
|
+
`), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
894
|
+
const path = bounded(`${publishNormalize(note.path)}
|
|
895
|
+
${publishNormalize(note.id)}`, WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
896
|
+
const tags = bounded(note.tags.map(publishNormalize).join(`
|
|
897
|
+
`), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
898
|
+
const metadata = bounded(publishNormalize(canonicalJson(note.metadata)), WORDCELL_SITE_LIMITS_V1.fieldTextBytes);
|
|
899
|
+
textTruncated ||= title.truncated || aliases.truncated || path.truncated || tags.truncated || metadata.truncated;
|
|
900
|
+
fields.push({ t: title.text, a: aliases.text, p: path.text, g: tags.text, m: metadata.text });
|
|
901
|
+
if (indexContent) {
|
|
902
|
+
const inline = bounded(publishNormalize(note.searchableText), WORDCELL_SITE_LIMITS_V1.inlineTextBytes);
|
|
903
|
+
textTruncated ||= inline.truncated;
|
|
904
|
+
inlineTexts.push(inline.text);
|
|
905
|
+
inlineTotal += Buffer.byteLength(inline.text, "utf8");
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const mode = !indexContent ? "none" : inlineTotal <= WORDCELL_SITE_LIMITS_V1.inlineTotalBytes ? "inline" : "shards";
|
|
909
|
+
const allTerms = new Set;
|
|
910
|
+
const contentPostings = new Map;
|
|
911
|
+
const docs = [];
|
|
912
|
+
for (const [index, note] of notes.entries()) {
|
|
913
|
+
const slug = slugById.get(note.id);
|
|
914
|
+
if (slug === undefined)
|
|
915
|
+
throw new Error(`Missing published slug for ${note.id}.`);
|
|
916
|
+
const field = fields[index];
|
|
917
|
+
if (field === undefined)
|
|
918
|
+
throw new Error(`Missing indexed fields for ${note.id}.`);
|
|
919
|
+
const inline = mode === "inline" ? inlineTexts[index] : undefined;
|
|
920
|
+
const fieldTerms = indexableTerms(`${field.t}
|
|
921
|
+
${field.a}
|
|
922
|
+
${field.p}
|
|
923
|
+
${field.g}
|
|
924
|
+
${field.m}`);
|
|
925
|
+
for (const term of fieldTerms)
|
|
926
|
+
allTerms.add(term);
|
|
927
|
+
if (mode !== "none") {
|
|
928
|
+
const content = inlineTexts[index] ?? "";
|
|
929
|
+
for (const term of indexableTerms(content)) {
|
|
930
|
+
allTerms.add(term);
|
|
931
|
+
if (mode === "shards") {
|
|
932
|
+
const list = contentPostings.get(term) ?? [];
|
|
933
|
+
list.push(index);
|
|
934
|
+
contentPostings.set(term, list);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
docs.push({
|
|
939
|
+
i: index,
|
|
940
|
+
s: slug,
|
|
941
|
+
t: note.title,
|
|
942
|
+
p: previewText(note),
|
|
943
|
+
f: field,
|
|
944
|
+
...inline === undefined ? {} : { x: inline }
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
let termsTruncated = false;
|
|
948
|
+
let sortedTerms = [...allTerms].toSorted((left, right) => left.localeCompare(right));
|
|
949
|
+
if (sortedTerms.length > WORDCELL_SITE_LIMITS_V1.indexTerms) {
|
|
950
|
+
sortedTerms = sortedTerms.slice(0, WORDCELL_SITE_LIMITS_V1.indexTerms);
|
|
951
|
+
termsTruncated = true;
|
|
952
|
+
}
|
|
953
|
+
const admitted = new Set(sortedTerms);
|
|
954
|
+
const postings = new Map;
|
|
955
|
+
if (mode === "shards") {
|
|
956
|
+
const grouped = new Map;
|
|
957
|
+
for (const [term, docIds] of [...contentPostings.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
958
|
+
if (!admitted.has(term))
|
|
959
|
+
continue;
|
|
960
|
+
const shard = publishShardName(term);
|
|
961
|
+
const bucket = grouped.get(shard) ?? Object.create(null);
|
|
962
|
+
bucket[term] = docIds;
|
|
963
|
+
grouped.set(shard, bucket);
|
|
964
|
+
}
|
|
965
|
+
for (const [shard, bucket] of [...grouped.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
966
|
+
postings.set(shard, {
|
|
967
|
+
format: WORDCELL_SITE_POSTINGS_FORMAT_V1,
|
|
968
|
+
shard,
|
|
969
|
+
postings: Object.freeze(bucket)
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
docs: {
|
|
975
|
+
format: WORDCELL_SITE_DOCS_FORMAT_V1,
|
|
976
|
+
content: mode,
|
|
977
|
+
docs
|
|
978
|
+
},
|
|
979
|
+
terms: {
|
|
980
|
+
format: WORDCELL_SITE_TERMS_FORMAT_V1,
|
|
981
|
+
terms: sortedTerms
|
|
982
|
+
},
|
|
983
|
+
postings,
|
|
984
|
+
termsCount: sortedTerms.length,
|
|
985
|
+
termsTruncated,
|
|
986
|
+
textTruncated,
|
|
987
|
+
mode
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
705
991
|
// src/publish-nav.ts
|
|
706
992
|
var SITE_NAV_GROUP_LIMIT = 96;
|
|
707
993
|
var SITE_NAV_NODE_BUDGET = 768;
|
|
@@ -1493,7 +1779,7 @@ async function projectVault(snapshot, options, io) {
|
|
|
1493
1779
|
let anyTextTruncated = false;
|
|
1494
1780
|
for (const note of selection.notes) {
|
|
1495
1781
|
const slug = slugFor(note.id);
|
|
1496
|
-
const text = boundedNormalized(note.
|
|
1782
|
+
const text = boundedNormalized(projectMarkdownText(note.content).text, WORDCELL_SITE_LIMITS_V1.noteTextBytes);
|
|
1497
1783
|
anyTextTruncated ||= text.truncated;
|
|
1498
1784
|
const payload = {
|
|
1499
1785
|
format: WORDCELL_SITE_NOTE_FORMAT_V1,
|