@dogsbay/format-dita 0.2.0-beta.123 → 0.2.0-beta.125
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 +18 -1
- package/dist/blocks.js +110 -31
- package/dist/class-defaults-apply.d.ts +15 -0
- package/dist/class-defaults-apply.js +42 -0
- package/dist/class-defaults.d.ts +8 -0
- package/dist/class-defaults.js +625 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/inline.d.ts +12 -0
- package/dist/inline.js +41 -29
- package/dist/lines.d.ts +5 -0
- package/dist/lines.js +24 -0
- package/dist/parse.js +34 -7
- package/dist/types.d.ts +22 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ dogsbay import-dita docs/ -o content --report topics.md
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Tasks become native `:::steps`, notes become callouts, CALS and
|
|
30
|
-
simpletables become tables, `<xref href="x.dita">` becomes `[…](x.md)`,
|
|
30
|
+
simpletables (and a reference topic's `<properties>`) become tables, `<xref href="x.dita">` becomes `[…](x.md)`,
|
|
31
31
|
and `keyref` links become `key:` links. Nested or sibling topics in
|
|
32
32
|
multi-topic files are emitted inline as anchored sections.
|
|
33
33
|
|
|
@@ -38,6 +38,23 @@ emit `{% include %}` so one canonical copy of reused text survives. Use
|
|
|
38
38
|
`dogsbay import-dita --no-conref-includes` to keep conrefs on the ledger
|
|
39
39
|
instead.
|
|
40
40
|
|
|
41
|
+
**Lines.** Every conversion note carries `line`, the source line of the
|
|
42
|
+
element that raised it (or of the block being converted), so a host can point
|
|
43
|
+
at it. `{ sourceLines: true }` also puts each block's line on `meta.line`, which
|
|
44
|
+
`@dogsbay/render-html` writes as `data-line` when asked (`dataLines: true`).
|
|
45
|
+
|
|
46
|
+
**Specializations in raw source:** a file as authored has no `@class`, so
|
|
47
|
+
an element is known only by its name, and a specialization the converter
|
|
48
|
+
does not know by name (a `<troubleshooting>` topic, a hazard statement's
|
|
49
|
+
items) loses its markup. `{ classDefaults: true }` fills in each missing
|
|
50
|
+
`@class` from the DITA 1.3 DTD defaults first (612 elements, the table
|
|
51
|
+
`DITA_CLASS_DEFAULTS`, generated by `scripts/generate-class-defaults.mjs`
|
|
52
|
+
from DITA-OT's `.mod` files), as parsing against the DTDs would; the
|
|
53
|
+
element then converts as what it specializes. An existing `@class` is kept;
|
|
54
|
+
a name the DTDs do not declare (custom or DITA 2.0) stays unclassed, with
|
|
55
|
+
one note per topic. It is off by default for now because it changes the
|
|
56
|
+
output for such elements.
|
|
57
|
+
|
|
41
58
|
Everything that cannot map cleanly lands in the conversion ledger instead
|
|
42
59
|
of disappearing: filter attributes, table spans, semantic-inline downgrades,
|
|
43
60
|
`conkeyref`, conref ranges/pushes, whole-topic conrefs, unresolved targets,
|
package/dist/blocks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { inlineToDogsbayMd } from "@dogsbay/format-dogsbay-md/browser";
|
|
2
|
-
import { convertInline, elementName, idAnchor, publishImage, trimInline, textOf, inlineSafeSnippet, conrefLedgerDetail, } from "./inline.js";
|
|
2
|
+
import { convertInline, elementName, isKnownInline, idAnchor, publishImage, trimInline, textOf, inlineSafeSnippet, conrefLedgerDetail, } from "./inline.js";
|
|
3
3
|
import { publishHref, publishLabel, publishLanguage, publishString } from "./types.js";
|
|
4
4
|
import { anchorId } from "./anchors.js";
|
|
5
5
|
import { DB_QUALIFIERS, outputTokens, youtubeId } from "./qualifiers.js";
|
|
@@ -52,6 +52,13 @@ const withProvenance = (node, el) => {
|
|
|
52
52
|
}
|
|
53
53
|
return node;
|
|
54
54
|
};
|
|
55
|
+
/** A list item or step: its source line (ParseOptions.sourceLines), as convertBlock gives a block. */
|
|
56
|
+
const atLine = (node, el, ctx) => {
|
|
57
|
+
const line = ctx.lineOf?.(el);
|
|
58
|
+
if (line !== undefined && node.meta?.line === undefined)
|
|
59
|
+
node.meta = { ...node.meta, line };
|
|
60
|
+
return node;
|
|
61
|
+
};
|
|
55
62
|
const noteFilterAttrs = (el, ctx) => {
|
|
56
63
|
for (const attr of ["audience", "platform", "product", "props", "otherprops", "rev"]) {
|
|
57
64
|
if (el.attribs?.[attr]) {
|
|
@@ -77,8 +84,29 @@ const isBlockChild = (el, ctx) => {
|
|
|
77
84
|
const name = blockName(el, ctx);
|
|
78
85
|
if (ctx.publish && name === "image")
|
|
79
86
|
return el.attribs?.placement === "break";
|
|
80
|
-
return BLOCK_ELEMENTS.has(name);
|
|
87
|
+
return BLOCK_ELEMENTS.has(name) || (isUnknownElement(el, ctx) && holdsBlocks(el, ctx));
|
|
81
88
|
};
|
|
89
|
+
/**
|
|
90
|
+
* An element the converter knows by neither a block nor an inline name (a
|
|
91
|
+
* custom specialization). Both resolutions count: with @class, a
|
|
92
|
+
* specialization of <fn> or <ph> resolves to an inline name, not a block one.
|
|
93
|
+
*/
|
|
94
|
+
const isUnknownElement = (el, ctx) => !isKnownBlock(elementName(el, ctx, isKnownBlock)) && !isKnownInline(elementName(el, ctx, isKnownInline));
|
|
95
|
+
/**
|
|
96
|
+
* Whether an unknown element holds block content (a <p>, a list), directly or
|
|
97
|
+
* through other unknown wrappers. Such an element is a block wrapper: read as
|
|
98
|
+
* inline, its paragraphs and lists were flattened into one run of text, with
|
|
99
|
+
* an "unrecognized inline element" note for each of them. An image does not
|
|
100
|
+
* count: it sits in a sentence as often as not (an icon in a UI label).
|
|
101
|
+
*/
|
|
102
|
+
const holdsBlocks = (el, ctx) => el.children.some((c) => {
|
|
103
|
+
if (!isElement(c))
|
|
104
|
+
return false;
|
|
105
|
+
const name = blockName(c, ctx);
|
|
106
|
+
if (name === "image")
|
|
107
|
+
return false;
|
|
108
|
+
return BLOCK_ELEMENTS.has(name) || TRANSPARENT_CONTAINERS.has(name) || (isUnknownElement(c, ctx) && holdsBlocks(c, ctx));
|
|
109
|
+
});
|
|
82
110
|
const paragraphFrom = (nodes, ctx) => {
|
|
83
111
|
const inline = trimInline(convertInline(nodes, ctx));
|
|
84
112
|
if (inline.length === 0)
|
|
@@ -210,12 +238,18 @@ const definingItemNode = (el, ctx, type) => {
|
|
|
210
238
|
ctx.note("@conref", `<${el.name.toLowerCase()} id="${el.attribs.id}"> is reused elsewhere via "${snippet.file}" but its content is multi-line — replace it with the include manually to keep one copy`);
|
|
211
239
|
return null;
|
|
212
240
|
};
|
|
241
|
+
/**
|
|
242
|
+
* Whether an element is a `tag` by name or, with @class, by what it
|
|
243
|
+
* specializes: a hazard statement's <typeofhazard> is an <li>. Matching the
|
|
244
|
+
* name alone dropped such items, and their text, without a note.
|
|
245
|
+
*/
|
|
246
|
+
const isPart = (el, ctx, tag) => isElement(el) && elementName(el, ctx, (n) => n === tag) === tag;
|
|
213
247
|
const convertListItems = (el, ctx, itemTag) => el.children
|
|
214
|
-
.filter((c) =>
|
|
248
|
+
.filter((c) => isPart(c, ctx, itemTag))
|
|
215
249
|
.map((li) => {
|
|
216
250
|
const viaConref = itemConref(li, ctx, "list-item") ?? definingItemNode(li, ctx, "list-item");
|
|
217
251
|
if (viaConref)
|
|
218
|
-
return withProvenance(viaConref, li);
|
|
252
|
+
return atLine(withProvenance(viaConref, li), li, ctx);
|
|
219
253
|
const { inline: content, children: blocks } = mixedContent(li, { ...ctx, constrained: true });
|
|
220
254
|
const { inline, children } = withItemAnchor(content, blocks, [li.attribs?.id], ctx);
|
|
221
255
|
const item = { type: "list-item" };
|
|
@@ -223,7 +257,7 @@ const convertListItems = (el, ctx, itemTag) => el.children
|
|
|
223
257
|
item.inline = inline;
|
|
224
258
|
if (children.length > 0)
|
|
225
259
|
item.children = children;
|
|
226
|
-
return withProvenance(item, li);
|
|
260
|
+
return atLine(withProvenance(item, li), li, ctx);
|
|
227
261
|
});
|
|
228
262
|
/** Components an `@outputclass` `db-*` token selects, per element. */
|
|
229
263
|
const DB_COMPONENTS = {
|
|
@@ -529,7 +563,7 @@ const inlineLabel = (inline) => (inline ?? [])
|
|
|
529
563
|
const dlComponentEntries = (el, ctx, component, keepEmpty = false, anchorsOut) => {
|
|
530
564
|
const out = [];
|
|
531
565
|
let pending = [];
|
|
532
|
-
const entries = el.children.filter((c) =>
|
|
566
|
+
const entries = el.children.filter((c) => isPart(c, ctx, "dlentry"));
|
|
533
567
|
for (const entry of entries) {
|
|
534
568
|
const items = convertDefList({ ...el, children: [entry] }, ctx).children ?? [];
|
|
535
569
|
const terms = items.filter((n) => n.type === "dt");
|
|
@@ -637,7 +671,7 @@ const dlToCards = (el, ctx, anchors) => {
|
|
|
637
671
|
const convertDefList = (el, ctx, tags = { entry: "dlentry", term: "dt", def: "dd" }) => {
|
|
638
672
|
const children = [];
|
|
639
673
|
for (const entry of el.children.filter(isElement)) {
|
|
640
|
-
if (entry
|
|
674
|
+
if (!isPart(entry, ctx, tags.entry))
|
|
641
675
|
continue;
|
|
642
676
|
// Entry-level conref (<dlentry conref>): a term/definition pair can't
|
|
643
677
|
// splice as an include — ledger it so the loss is never silent
|
|
@@ -647,8 +681,7 @@ const convertDefList = (el, ctx, tags = { entry: "dlentry", term: "dt", def: "dd
|
|
|
647
681
|
}
|
|
648
682
|
let entryAnchored = false;
|
|
649
683
|
for (const part of entry.children.filter(isElement)) {
|
|
650
|
-
|
|
651
|
-
if (name === tags.term) {
|
|
684
|
+
if (isPart(part, ctx, tags.term)) {
|
|
652
685
|
// Term-level conref (<dt conref>/<pt conref>): single-line
|
|
653
686
|
// include or ledger — never a silent empty term
|
|
654
687
|
const viaConref = constrainedConrefInline(part, ctx);
|
|
@@ -659,7 +692,7 @@ const convertDefList = (el, ctx, tags = { entry: "dlentry", term: "dt", def: "dd
|
|
|
659
692
|
});
|
|
660
693
|
entryAnchored = true;
|
|
661
694
|
}
|
|
662
|
-
else if (
|
|
695
|
+
else if (isPart(part, ctx, tags.def)) {
|
|
663
696
|
const viaConref = constrainedConrefInline(part, ctx);
|
|
664
697
|
if (viaConref) {
|
|
665
698
|
children.push({ type: "dd", inline: viaConref });
|
|
@@ -719,12 +752,23 @@ const TABLE_PARTS = new Set([
|
|
|
719
752
|
const hasClassToken = (el, token) => ` ${el.attribs?.class ?? ""} `.includes(` ${token} `);
|
|
720
753
|
const elementChildren = (el) => el.children.filter(isElement);
|
|
721
754
|
const PROPERTY_KINDS = ["proptype", "propvalue", "propdesc"];
|
|
755
|
+
/**
|
|
756
|
+
* Whether an element is the reference domain's `local` (`properties`, `property`, …):
|
|
757
|
+
* by @class when DITA-OT has filled it in, else by name, as authored source has none.
|
|
758
|
+
*/
|
|
759
|
+
const isReference = (el, local) => hasClassToken(el, `reference/${local}`) || el.name.toLowerCase() === local;
|
|
722
760
|
/** A properties table cell's column: `proptype`, or with suffix "hd" a header cell (`proptypehd`). */
|
|
723
|
-
const propertyKind = (cell, suffix = "") => PROPERTY_KINDS.find((k) =>
|
|
761
|
+
const propertyKind = (cell, suffix = "") => PROPERTY_KINDS.find((k) => isReference(cell, `${k}${suffix}`));
|
|
762
|
+
/**
|
|
763
|
+
* A properties table's cells. Exactly the cells `propertyKind` recognizes (a
|
|
764
|
+
* published cell's @class names its kind before the stentry it specializes),
|
|
765
|
+
* so a row's converted cells and its cell elements line up index for index.
|
|
766
|
+
*/
|
|
767
|
+
const PROPERTY_CELLS = new Set([...PROPERTY_KINDS, ...PROPERTY_KINDS.map((k) => `${k}hd`)]);
|
|
724
768
|
/** A properties table's columns in use, its properties that have cells, and its header row. */
|
|
725
769
|
const propertiesColumns = (el) => {
|
|
726
|
-
const header = elementChildren(el).find((c) =>
|
|
727
|
-
const propertyRows = elementChildren(el).filter((c) =>
|
|
770
|
+
const header = elementChildren(el).find((c) => isReference(c, "prophead"));
|
|
771
|
+
const propertyRows = elementChildren(el).filter((c) => isReference(c, "property") && elementChildren(c).some((cell) => !!propertyKind(cell)));
|
|
728
772
|
const used = PROPERTY_KINDS.filter((k) => propertyRows.some((r) => elementChildren(r).some((c) => propertyKind(c) === k)) ||
|
|
729
773
|
(header ? elementChildren(header).some((c) => propertyKind(c, "hd") === k) : false));
|
|
730
774
|
return { used, propertyRows, ...(header ? { header } : {}) };
|
|
@@ -829,15 +873,24 @@ const convertTable = (el, ctx) => {
|
|
|
829
873
|
}
|
|
830
874
|
};
|
|
831
875
|
walk(el);
|
|
832
|
-
//
|
|
876
|
+
// A properties table's columns are the type, value and description columns
|
|
833
877
|
// its header or any property uses (DITA-OT's hasType, hasValue, hasDesc); each row
|
|
834
878
|
// aligns its cells to them, a property with no cells is dropped, and a header cell
|
|
835
|
-
// missing for a column in use is DITA-OT's generated label ("Type", "Value",
|
|
836
|
-
|
|
879
|
+
// missing for a column in use is DITA-OT's generated label ("Type", "Value",
|
|
880
|
+
// "Description"; localized when publishing). Authored source has no @class, so
|
|
881
|
+
// the parts are matched by name too (the walk above sees no sthead/strow then).
|
|
882
|
+
if (isReference(el, "properties")) {
|
|
837
883
|
const { used, propertyRows, header } = propertiesColumns(el);
|
|
884
|
+
// A property with no cells has no row; one that reuses a row by conref is noted, not lost
|
|
885
|
+
for (const row of elementChildren(el)) {
|
|
886
|
+
if (isReference(row, "property") && !propertyRows.includes(row) && (row.attribs?.conref || row.attribs?.conkeyref)) {
|
|
887
|
+
const ref = row.attribs.conref ?? row.attribs.conkeyref;
|
|
888
|
+
ctx.note(row.attribs.conref ? "@conref" : "@conkeyref", `<property> pulls content from "${ref}" — ${conrefLedgerDetail(row.attribs, undefined, ctx.conref !== undefined)}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
838
891
|
rows.length = 0;
|
|
839
892
|
for (const row of propertyRows) {
|
|
840
|
-
const converted = convertRow(row,
|
|
893
|
+
const converted = convertRow(row, PROPERTY_CELLS);
|
|
841
894
|
const cellEls = elementChildren(row).filter((c) => !!propertyKind(c));
|
|
842
895
|
rows.push({
|
|
843
896
|
type: "tr",
|
|
@@ -847,13 +900,10 @@ const convertTable = (el, ctx) => {
|
|
|
847
900
|
}),
|
|
848
901
|
});
|
|
849
902
|
}
|
|
850
|
-
const lang = publishLanguage(el, ctx.publish);
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
propdesc: publishString(ctx.publish, lang, "Description"),
|
|
855
|
-
};
|
|
856
|
-
const authored = header ? convertRow(header, SIMPLE_CELLS, true) : undefined;
|
|
903
|
+
const lang = ctx.publish ? publishLanguage(el, ctx.publish) : undefined;
|
|
904
|
+
const word = (w) => (ctx.publish ? publishString(ctx.publish, lang, w) : w);
|
|
905
|
+
const label = { proptype: word("Type"), propvalue: word("Value"), propdesc: word("Description") };
|
|
906
|
+
const authored = header ? convertRow(header, PROPERTY_CELLS, true) : undefined;
|
|
857
907
|
const headerCells = header ? elementChildren(header).filter((c) => !!propertyKind(c, "hd")) : [];
|
|
858
908
|
headRows.length = 0;
|
|
859
909
|
headRows.push({
|
|
@@ -940,11 +990,11 @@ export const convertStepContent = (stepEl, ctx) => {
|
|
|
940
990
|
const convertSteps = (el, ctx, type) => {
|
|
941
991
|
const stepTag = type === "steps" ? "step" : "substep";
|
|
942
992
|
const children = el.children
|
|
943
|
-
.filter((c) =>
|
|
993
|
+
.filter((c) => isPart(c, ctx, stepTag))
|
|
944
994
|
.map((stepEl) => {
|
|
945
995
|
const viaConref = itemConref(stepEl, ctx, "step") ?? definingItemNode(stepEl, ctx, "step");
|
|
946
996
|
if (viaConref)
|
|
947
|
-
return withProvenance(viaConref, stepEl);
|
|
997
|
+
return atLine(withProvenance(viaConref, stepEl), stepEl, ctx);
|
|
948
998
|
const { inline: content, children: blocks } = convertStepContent(stepEl, { ...ctx, constrained: true });
|
|
949
999
|
// Publishing: DITA-OT labels optional and required steps
|
|
950
1000
|
const importance = stepEl.attribs?.importance;
|
|
@@ -960,7 +1010,7 @@ const convertSteps = (el, ctx, type) => {
|
|
|
960
1010
|
step.inline = inline;
|
|
961
1011
|
if (children.length > 0)
|
|
962
1012
|
step.children = children;
|
|
963
|
-
return withProvenance(step, stepEl);
|
|
1013
|
+
return atLine(withProvenance(step, stepEl), stepEl, ctx);
|
|
964
1014
|
});
|
|
965
1015
|
return { type, children };
|
|
966
1016
|
};
|
|
@@ -1012,7 +1062,7 @@ const publishTaskLabel = (name, ctx, el) => ctx.publish?.taskLabels && TASK_LABE
|
|
|
1012
1062
|
/** Block-level DITA elements (used to split mixed content). */
|
|
1013
1063
|
const BLOCK_ELEMENTS = new Set([
|
|
1014
1064
|
"p", "shortdesc", "note", "codeblock", "pre", "screen", "msgblock", "ul", "ol", "sl", "dl", "parml", "table", "simpletable",
|
|
1015
|
-
"fig", "image", "section", "example", "steps", "steps-unordered", "substeps", "lq", "object",
|
|
1065
|
+
"properties", "fig", "image", "section", "example", "steps", "steps-unordered", "substeps", "lq", "object",
|
|
1016
1066
|
"prereq", "context", "result", "postreq", "tasktroubleshooting", "choices", "choicetable", "syntaxdiagram",
|
|
1017
1067
|
]);
|
|
1018
1068
|
const SYNTAX_GROUPS = new Set(["groupseq", "groupchoice", "groupcomp"]);
|
|
@@ -1119,6 +1169,27 @@ const ANCHORED_ELSEWHERE = new Set(["title", "related-links", "prolog", "data",
|
|
|
1119
1169
|
* DITA-OT's pages. (Headings carry their own id.)
|
|
1120
1170
|
*/
|
|
1121
1171
|
export function convertBlock(el, ctx) {
|
|
1172
|
+
// A note raised while converting this element takes its line
|
|
1173
|
+
const outer = ctx.position?.el;
|
|
1174
|
+
if (ctx.position)
|
|
1175
|
+
ctx.position.el = el;
|
|
1176
|
+
try {
|
|
1177
|
+
const out = convertBlockThemed(el, ctx);
|
|
1178
|
+
// Source lines: each node this element became starts at its line
|
|
1179
|
+
// (nested blocks came through here first and keep their own)
|
|
1180
|
+
const line = ctx.lineOf?.(el);
|
|
1181
|
+
if (line !== undefined)
|
|
1182
|
+
for (const node of out)
|
|
1183
|
+
if (node.meta?.line === undefined)
|
|
1184
|
+
node.meta = { ...node.meta, line };
|
|
1185
|
+
return out;
|
|
1186
|
+
}
|
|
1187
|
+
finally {
|
|
1188
|
+
if (ctx.position)
|
|
1189
|
+
ctx.position.el = outer;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
function convertBlockThemed(el, ctx) {
|
|
1122
1193
|
const out = convertBlockAnchored(el, ctx);
|
|
1123
1194
|
// A scoped theme wraps the element's whole output, its anchor included
|
|
1124
1195
|
const theme = /(^|\s)db-theme-/i.test(el.attribs?.outputclass ?? "") ? themeOf(el, ctx) : undefined;
|
|
@@ -1364,7 +1435,7 @@ function convertBlockContent(el, ctx) {
|
|
|
1364
1435
|
// as <li>: referencing items include or ledger, defining items
|
|
1365
1436
|
// hand their text to the snippet)
|
|
1366
1437
|
const items = el.children
|
|
1367
|
-
.filter((c) =>
|
|
1438
|
+
.filter((c) => isPart(c, ctx, "sli"))
|
|
1368
1439
|
.map((sli) => {
|
|
1369
1440
|
const viaConref = itemConref(sli, ctx, "list-item") ?? definingItemNode(sli, ctx, "list-item");
|
|
1370
1441
|
if (viaConref)
|
|
@@ -1400,9 +1471,17 @@ function convertBlockContent(el, ctx) {
|
|
|
1400
1471
|
return [withProvenance(convertDefList(el, ctx, { entry: "plentry", term: "pt", def: "pd" }), el)];
|
|
1401
1472
|
case "table":
|
|
1402
1473
|
case "simpletable":
|
|
1403
|
-
|
|
1404
|
-
|
|
1474
|
+
case "properties":
|
|
1475
|
+
// A properties table none of whose properties has a cell has no columns: DITA-OT drops it
|
|
1476
|
+
if (isReference(el, "properties") && propertiesColumns(el).propertyRows.length === 0) {
|
|
1477
|
+
if (!ctx.publish) {
|
|
1478
|
+
for (const row of elementChildren(el).filter((r) => r.attribs?.conref || r.attribs?.conkeyref)) {
|
|
1479
|
+
const ref = row.attribs.conref ?? row.attribs.conkeyref;
|
|
1480
|
+
ctx.note(row.attribs.conref ? "@conref" : "@conkeyref", `<${row.name.toLowerCase()}> pulls content from "${ref}" — ${conrefLedgerDetail(row.attribs, undefined, ctx.conref !== undefined)}`);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1405
1483
|
return [];
|
|
1484
|
+
}
|
|
1406
1485
|
return [withProvenance(convertTable(el, ctx), el)];
|
|
1407
1486
|
case "syntaxdiagram":
|
|
1408
1487
|
if (ctx.publish)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AnyNode, Element } from "domhandler";
|
|
2
|
+
/** An element's DITA 1.3 default @class (`inMap`: inside a map document), or undefined for a name the DTDs do not declare. */
|
|
3
|
+
export declare const classDefault: (name: string, inMap?: boolean) => string | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Fill in the @class a DTD would have defaulted, on every element that has
|
|
6
|
+
* none, as parsing against the DTDs (or DITA-OT preprocessing) does. An
|
|
7
|
+
* existing @class is kept. Returns the element names the DITA 1.3 DTDs do not
|
|
8
|
+
* declare (custom specializations, DITA 2.0), in document order, each once;
|
|
9
|
+
* those stay unclassed. Not reported: the `<dita>` wrapper of a composite
|
|
10
|
+
* topic, which has no @class by design, and foreign vocabularies (a
|
|
11
|
+
* namespace prefix, or an undeclared name inside a `<foreign>`
|
|
12
|
+
* specialization: MathML, SVG), which are not DITA elements and are left
|
|
13
|
+
* untouched. A DITA element inside one (`<mathmlref>`, `<svgref>`) is classed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function applyClassDefaults(nodes: AnyNode[], firstOf?: Map<string, Element>): string[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { isTag } from "domhandler";
|
|
2
|
+
import { DITA_CLASS_DEFAULTS, DITA_MAP_CLASS_DEFAULTS } from "./class-defaults.js";
|
|
3
|
+
const own = (table, name) => Object.prototype.hasOwnProperty.call(table, name) ? table[name] : undefined;
|
|
4
|
+
/** An element's DITA 1.3 default @class (`inMap`: inside a map document), or undefined for a name the DTDs do not declare. */
|
|
5
|
+
export const classDefault = (name, inMap = false) => (inMap ? own(DITA_MAP_CLASS_DEFAULTS, name) : undefined) ?? own(DITA_CLASS_DEFAULTS, name);
|
|
6
|
+
/**
|
|
7
|
+
* Fill in the @class a DTD would have defaulted, on every element that has
|
|
8
|
+
* none, as parsing against the DTDs (or DITA-OT preprocessing) does. An
|
|
9
|
+
* existing @class is kept. Returns the element names the DITA 1.3 DTDs do not
|
|
10
|
+
* declare (custom specializations, DITA 2.0), in document order, each once;
|
|
11
|
+
* those stay unclassed. Not reported: the `<dita>` wrapper of a composite
|
|
12
|
+
* topic, which has no @class by design, and foreign vocabularies (a
|
|
13
|
+
* namespace prefix, or an undeclared name inside a `<foreign>`
|
|
14
|
+
* specialization: MathML, SVG), which are not DITA elements and are left
|
|
15
|
+
* untouched. A DITA element inside one (`<mathmlref>`, `<svgref>`) is classed.
|
|
16
|
+
*/
|
|
17
|
+
export function applyClassDefaults(nodes, firstOf) {
|
|
18
|
+
const root = nodes.find(isTag);
|
|
19
|
+
const inMap = !!root && (["map", "bookmap", "subjectScheme"].includes(root.name) || ` ${root.attribs?.class ?? ""} `.includes(" map/map "));
|
|
20
|
+
const unknown = new Set();
|
|
21
|
+
const visit = (el, inForeign) => {
|
|
22
|
+
if (el.name.includes(":"))
|
|
23
|
+
return;
|
|
24
|
+
if (!el.attribs?.class?.trim()) {
|
|
25
|
+
const value = classDefault(el.name, inMap);
|
|
26
|
+
if (value)
|
|
27
|
+
el.attribs = { ...el.attribs, class: value };
|
|
28
|
+
else if (el.name !== "dita" && !inForeign && !unknown.has(el.name)) {
|
|
29
|
+
unknown.add(el.name);
|
|
30
|
+
firstOf?.set(el.name, el);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const foreign = inForeign || ` ${el.attribs?.class ?? ""} `.includes(" topic/foreign ");
|
|
34
|
+
for (const child of el.children)
|
|
35
|
+
if (isTag(child))
|
|
36
|
+
visit(child, foreign);
|
|
37
|
+
};
|
|
38
|
+
for (const node of nodes)
|
|
39
|
+
if (isTag(node))
|
|
40
|
+
visit(node, false);
|
|
41
|
+
return [...unknown];
|
|
42
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Each DITA 1.3 element's default @class, as its DTD declares it. GENERATED by
|
|
3
|
+
* scripts/generate-class-defaults.mjs from the .mod files under dita-ot-4.3.5's
|
|
4
|
+
* plugins/org.oasis-open.dita.v1_3/dtd/ (612 elements); do not edit.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DITA_CLASS_DEFAULTS: Readonly<Record<string, string>>;
|
|
7
|
+
/** Elements whose class differs inside a map (map.mod declares them too). */
|
|
8
|
+
export declare const DITA_MAP_CLASS_DEFAULTS: Readonly<Record<string, string>>;
|