@heroiclands/package-build 23.12.8 → 23.13.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/CHANGELOG.md +48 -0
- package/README.md +1 -1
- package/assets/preview/gitea.ini +2 -2
- package/assets/styles/content-base.css +143 -23
- package/bin/content-commands.mjs +15 -4
- package/bin/package-build-preview.mjs +2 -2
- package/docs/api.md +18 -18
- package/docs/authoring/links-and-markup.md +156 -22
- package/docs/commands.md +22 -7
- package/docs/configuration.md +1 -1
- package/docs/content-format.md +3 -3
- package/docs/diagnostics.md +42 -0
- package/docs/reference/format-details.md +99 -22
- package/docs/reference/note-types.md +8 -1
- package/docs/reference/theme-pages.md +51 -0
- package/docs/source-preview.md +1 -1
- package/engine/anchors.mjs +2 -2
- package/engine/code-fences.mjs +3 -3
- package/engine/content-blocks.mjs +3 -3
- package/engine/content-figures.mjs +5 -5
- package/engine/content-index.mjs +1 -1
- package/engine/content-scripts.mjs +55 -21
- package/engine/content-spans.mjs +2 -2
- package/engine/content-verse.mjs +750 -0
- package/engine/dictionary-rows.mjs +15 -4
- package/engine/field-reference.mjs +4 -4
- package/engine/first-use.mjs +17 -27
- package/engine/key-case-collisions.mjs +136 -0
- package/engine/language-data.mjs +203 -6
- package/engine/language-terms.mjs +74 -5
- package/engine/map-lexicon.mjs +42 -23
- package/engine/names.mjs +36 -9
- package/engine/note-matter.mjs +66 -0
- package/engine/pdf-build.mjs +1 -0
- package/engine/pdf-render.mjs +107 -18
- package/engine/read-stdin.mjs +29 -0
- package/engine/run-rows.mjs +2 -2
- package/engine/run-words.mjs +4 -111
- package/engine/site-build.mjs +4 -4
- package/engine/site-preview.mjs +14 -14
- package/engine/sql-tables.mjs +20 -3
- package/engine/web-wikilinks.mjs +1 -1
- package/engine/word-forms.mjs +349 -0
- package/engine/word-terms.mjs +9 -0
- package/hugo-theme/static/css/style.css +92 -14
- package/package.json +2 -2
- package/types/engine/code-fences.d.mts +3 -3
- package/types/engine/content-scripts.d.mts +8 -4
- package/types/engine/content-verse.d.mts +240 -0
- package/types/engine/dictionary-rows.d.mts +10 -0
- package/types/engine/field-reference.d.mts +1 -1
- package/types/engine/key-case-collisions.d.mts +28 -0
- package/types/engine/language-data.d.mts +0 -2
- package/types/engine/map-lexicon.d.mts +19 -6
- package/types/engine/names.d.mts +34 -3
- package/types/engine/note-matter.d.mts +48 -0
- package/types/engine/read-stdin.d.mts +11 -0
- package/types/engine/run-rows.d.mts +2 -2
- package/types/engine/run-words.d.mts +0 -15
- package/types/engine/sql-tables.d.mts +4 -1
- package/types/engine/web-wikilinks.d.mts +1 -1
- package/types/engine/word-forms.d.mts +82 -0
- package/engine/content-poetry.mjs +0 -240
- package/types/engine/content-poetry.d.mts +0 -58
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* **Text marked with a language, a font and a mapping.** Three keys on the
|
|
16
|
-
* attribute grammar spans, divs and
|
|
16
|
+
* attribute grammar spans, divs and verse fences already share, each optional
|
|
17
17
|
* and independent of the others:
|
|
18
18
|
*
|
|
19
19
|
* ```markdown
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* Zaron tholmeza.
|
|
25
25
|
* :::
|
|
26
26
|
*
|
|
27
|
-
* ```
|
|
27
|
+
* ```verse {form=epic lang=vyl font=semaron-kuriale map=vyl-semaron}
|
|
28
28
|
* ```
|
|
29
29
|
*
|
|
30
30
|
* - **`lang=`** gives the language tag and changes nothing else. It is a name
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* form. Any other
|
|
46
46
|
* word is a finding at its first character, once per run; see
|
|
47
47
|
* {@link module:engine/run-words}.
|
|
48
|
-
* - **A run inside a div or a
|
|
48
|
+
* - **A run inside a div or a verse fence inherits its keys**, and its own
|
|
49
49
|
* keys override them.
|
|
50
50
|
* - **Text with a font, a map or a declared language is plain.** A span or a
|
|
51
51
|
* verse holds no emphasis, link, image, footnote, wikilink, code or HTML,
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
*
|
|
60
60
|
* {@link scanRuns} reads the markup through {@link module:engine/content-spans.scanSpans},
|
|
61
61
|
* {@link module:engine/content-blocks.scanBlocks} and
|
|
62
|
-
* {@link module:engine/content-
|
|
62
|
+
* {@link module:engine/content-verse.scanVerse}, so what counts as a span, a
|
|
63
63
|
* div or a fence is theirs to say.
|
|
64
64
|
*
|
|
65
65
|
* @module
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
import MarkdownIt from "markdown-it";
|
|
69
69
|
|
|
70
70
|
import { scanBlocks, BLOCK_CONTAINERS } from "./content-blocks.mjs";
|
|
71
|
-
import {
|
|
71
|
+
import { expandVerse, expandVerseOrigins, scanVerse, sourcePosition } from "./content-verse.mjs";
|
|
72
72
|
import { scanSpans } from "./content-spans.mjs";
|
|
73
73
|
import { isLanguageTag } from "./language-tag.mjs";
|
|
74
74
|
import { distance, nearest } from "./near-miss.mjs";
|
|
@@ -116,12 +116,12 @@ const CONFIG = "package-build.config.yaml";
|
|
|
116
116
|
*/
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* One span, div or
|
|
119
|
+
* One span, div or verse fence carrying `lang=`, `font=` or `map=`. Its
|
|
120
120
|
* `lang`, `font` and `map` are the ones it is set with: its own, else the
|
|
121
121
|
* innermost div or fence around it that states one.
|
|
122
122
|
*
|
|
123
123
|
* @typedef {object} Run
|
|
124
|
-
* @property {"span"|"block"|"
|
|
124
|
+
* @property {"span"|"block"|"verse"} kind - Which construct.
|
|
125
125
|
* @property {string|undefined} lang - The language as written.
|
|
126
126
|
* @property {string|undefined} tag - The BCP 47 tag it resolves to; `undefined`
|
|
127
127
|
* when it resolves to none.
|
|
@@ -205,7 +205,7 @@ function withinOneEdit(a, b) {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
/** The word each construct is called in a finding. */
|
|
208
|
-
const CONSTRUCT = Object.freeze({ span: "span", block: "div",
|
|
208
|
+
const CONSTRUCT = Object.freeze({ span: "span", block: "div", verse: "verse fence" });
|
|
209
209
|
|
|
210
210
|
/** Line starts of a source, for offsets to positions. */
|
|
211
211
|
function lineStarts(text) {
|
|
@@ -645,7 +645,7 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
645
645
|
for (const key of Object.keys(keys))
|
|
646
646
|
report(
|
|
647
647
|
at(key, "key"),
|
|
648
|
-
`\`${key}=\` is read on a span, a div or a
|
|
648
|
+
`\`${key}=\` is read on a span, a div or a verse fence, not on a ${block.name} block`,
|
|
649
649
|
);
|
|
650
650
|
} else {
|
|
651
651
|
divs.set(first, block);
|
|
@@ -679,13 +679,13 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
679
679
|
collect(0, text);
|
|
680
680
|
|
|
681
681
|
const poems = new Map();
|
|
682
|
-
for (const poem of
|
|
682
|
+
for (const poem of scanVerse(text).blocks) {
|
|
683
683
|
poems.set(poem.start, poem);
|
|
684
684
|
const keys = runKeys(poem.attributes);
|
|
685
685
|
if (!Object.keys(keys).length) continue;
|
|
686
686
|
const opening = lines[poem.start];
|
|
687
687
|
constructs.push({
|
|
688
|
-
kind: "
|
|
688
|
+
kind: "verse",
|
|
689
689
|
keys,
|
|
690
690
|
node: poem,
|
|
691
691
|
line0: poem.start,
|
|
@@ -914,7 +914,11 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
914
914
|
const runs = [];
|
|
915
915
|
const details = [];
|
|
916
916
|
for (const construct of constructs) {
|
|
917
|
+
const unchecked = errors.length;
|
|
917
918
|
checkOwn(construct);
|
|
919
|
+
// A key that names nothing is the finding; the text it would have set
|
|
920
|
+
// is not then checked against a face or a language it cannot reach.
|
|
921
|
+
const named = errors.length === unchecked;
|
|
918
922
|
const keys = Object.assign({}, ...around(construct).map((c) => c.keys), construct.keys);
|
|
919
923
|
const { lang, font, map } = keys;
|
|
920
924
|
// A face the run's map is not for, once the face and the map are each
|
|
@@ -973,7 +977,7 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
973
977
|
const span = construct.node;
|
|
974
978
|
const inline = text.slice(span.start + 1, span.contentEnd);
|
|
975
979
|
plainInline(inline, span.start + 1, marking);
|
|
976
|
-
if (errors.length === before) {
|
|
980
|
+
if (named && errors.length === before) {
|
|
977
981
|
lexical([[span.start + 1, span.contentEnd]], detail);
|
|
978
982
|
capitals([[span.start + 1, span.contentEnd]], detail);
|
|
979
983
|
covered([[span.start + 1, span.contentEnd]], detail);
|
|
@@ -1017,8 +1021,8 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
1017
1021
|
if (token.type === "fence")
|
|
1018
1022
|
said(
|
|
1019
1023
|
top,
|
|
1020
|
-
token.info.trim().split(/\s/)[0] === "
|
|
1021
|
-
"a
|
|
1024
|
+
token.info.trim().split(/\s/)[0] === "verse" ?
|
|
1025
|
+
"a verse fence"
|
|
1022
1026
|
: "a code fence",
|
|
1023
1027
|
);
|
|
1024
1028
|
else if (Object.hasOwn(BLOCK_WHAT, token.type)) said(top, BLOCK_WHAT[token.type]);
|
|
@@ -1027,7 +1031,7 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
1027
1031
|
for (let line = first; line < last; line++)
|
|
1028
1032
|
plainInline(lines[line], starts[line], marking, detail.holes);
|
|
1029
1033
|
}
|
|
1030
|
-
if (errors.length === before) {
|
|
1034
|
+
if (named && errors.length === before) {
|
|
1031
1035
|
lexical(outsideHoles(from, to, detail.holes), detail);
|
|
1032
1036
|
capitals(outsideHoles(from, to, detail.holes), detail);
|
|
1033
1037
|
covered(outsideHoles(from, to, detail.holes), detail);
|
|
@@ -1043,7 +1047,7 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
1043
1047
|
}
|
|
1044
1048
|
|
|
1045
1049
|
/**
|
|
1046
|
-
* Find the spans, divs and
|
|
1050
|
+
* Find the spans, divs and verse fences marked with `lang=`, `font=` or
|
|
1047
1051
|
* `map=`, and every fault in their markup and their text.
|
|
1048
1052
|
*
|
|
1049
1053
|
* @param {string} source - Authored Markdown: a note body, or a whole file
|
|
@@ -1059,8 +1063,32 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
1059
1063
|
* declared name.
|
|
1060
1064
|
*/
|
|
1061
1065
|
export function scanRuns(source, ctx = {}, syntax) {
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1066
|
+
const text = String(source ?? "");
|
|
1067
|
+
const { markdown, origins } = expandVerseOrigins(text);
|
|
1068
|
+
const authored = analyseRuns(text, ctx, syntax);
|
|
1069
|
+
if (markdown === text) return authored;
|
|
1070
|
+
// A verse in columns is checked as the spans each entry sets, which are
|
|
1071
|
+
// written only in the expansion; every finding is placed back where the
|
|
1072
|
+
// note wrote the text or the entry it comes from, once.
|
|
1073
|
+
const expanded = markdown.split("\n");
|
|
1074
|
+
const checked = analyseRuns(markdown, ctx);
|
|
1075
|
+
const placed = (findings) => {
|
|
1076
|
+
const seen = new Set();
|
|
1077
|
+
return findings
|
|
1078
|
+
.map((finding) => ({ ...finding, ...sourcePosition(finding, expanded, origins) }))
|
|
1079
|
+
.filter((finding) => {
|
|
1080
|
+
const key = `${finding.line}:${finding.column}:${finding.message}`;
|
|
1081
|
+
if (seen.has(key)) return false;
|
|
1082
|
+
seen.add(key);
|
|
1083
|
+
return true;
|
|
1084
|
+
})
|
|
1085
|
+
.sort((a, b) => a.line - b.line || a.column - b.column);
|
|
1086
|
+
};
|
|
1087
|
+
return {
|
|
1088
|
+
runs: authored.runs,
|
|
1089
|
+
errors: placed(checked.errors),
|
|
1090
|
+
warnings: placed(checked.warnings),
|
|
1091
|
+
};
|
|
1064
1092
|
}
|
|
1065
1093
|
|
|
1066
1094
|
/**
|
|
@@ -1113,7 +1141,7 @@ function rewrittenAttributes(detail, search) {
|
|
|
1113
1141
|
const changes =
|
|
1114
1142
|
run.font !== undefined ||
|
|
1115
1143
|
run.map !== undefined ||
|
|
1116
|
-
(run.tag !== undefined && (keys.lang !== run.tag || run.kind === "
|
|
1144
|
+
(run.tag !== undefined && (keys.lang !== run.tag || run.kind === "verse"));
|
|
1117
1145
|
if (!changes) return undefined;
|
|
1118
1146
|
const classes = [...own.classes];
|
|
1119
1147
|
const values = [...own.values];
|
|
@@ -1130,8 +1158,12 @@ function rewrittenAttributes(detail, search) {
|
|
|
1130
1158
|
|
|
1131
1159
|
/**
|
|
1132
1160
|
* Rewrite text marked with `lang=`, `font=` and `map=` into the plain span,
|
|
1133
|
-
* div and
|
|
1161
|
+
* div and verse grammar the rest of each chain renders, one line for one line.
|
|
1134
1162
|
*
|
|
1163
|
+
* - **A verse in columns is expanded first** into one span per column entry
|
|
1164
|
+
* per verse line, by {@link module:engine/content-verse.expandVerse}, so
|
|
1165
|
+
* each entry is mapped and set as a span with its keys is. That is the one
|
|
1166
|
+
* rewrite that changes the number of lines.
|
|
1135
1167
|
* - **The map, if any, is applied first.** Characters Markdown would read are
|
|
1136
1168
|
* then backslash-escaped, so they reach every later pass as characters.
|
|
1137
1169
|
* - **The construct carries** the language's tag as `lang`, the face's class
|
|
@@ -1156,7 +1188,9 @@ function rewrittenAttributes(detail, search) {
|
|
|
1156
1188
|
*/
|
|
1157
1189
|
export function renderRuns(source, ctx = {}, syntax, options = {}) {
|
|
1158
1190
|
const search = options.target === "site";
|
|
1159
|
-
const
|
|
1191
|
+
const authored = String(source ?? "");
|
|
1192
|
+
const text = expandVerse(authored);
|
|
1193
|
+
if (text !== authored) syntax = undefined;
|
|
1160
1194
|
if (!/\b(?:lang|font|map)=/.test(text)) return { markdown: text, errors: [] };
|
|
1161
1195
|
const { details, errors } = analyseRuns(text, ctx, syntax);
|
|
1162
1196
|
if (!details.length) return { markdown: text, errors };
|
package/engine/content-spans.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import MarkdownIt from "markdown-it";
|
|
15
15
|
import { alertMarkdownPlugin } from "./content-alerts.mjs";
|
|
16
16
|
import { slugify } from "./content-slug.mjs";
|
|
17
|
-
import {
|
|
17
|
+
import { scanVerse } from "./content-verse.mjs";
|
|
18
18
|
import footnotePlugin from "markdown-it-footnote";
|
|
19
19
|
import { parseExtensionAttributes, refusedAttributes } from "./extension-attributes.mjs";
|
|
20
20
|
import { inlineText } from "./name-markup.mjs";
|
|
@@ -263,7 +263,7 @@ export function scanSpans(source) {
|
|
|
263
263
|
}
|
|
264
264
|
// Verse fences preserve inline Markdown even though the block parser
|
|
265
265
|
// normally treats every code fence as literal text.
|
|
266
|
-
for (const poem of
|
|
266
|
+
for (const poem of scanVerse(text).blocks) {
|
|
267
267
|
for (let line = poem.start + 1; line < poem.end; line++) {
|
|
268
268
|
const children = [];
|
|
269
269
|
parser.inline.parse(lines[line], parser, env, children);
|