sommark 4.5.3 → 5.0.1
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 +315 -179
- package/cli/cli.mjs +1 -1
- package/cli/commands/color.js +36 -14
- package/cli/commands/help.js +3 -0
- package/cli/commands/init.js +1 -3
- package/cli/constants.js +5 -2
- package/constants/html_props.js +0 -5
- package/core/errors.js +5 -4
- package/core/evaluator.js +1 -2
- package/core/formats.js +7 -1
- package/core/helpers/config-loader.js +2 -4
- package/core/helpers/lib.js +1 -1
- package/core/labels.js +2 -15
- package/core/lexer.js +197 -313
- package/core/modules.js +13 -13
- package/core/parser.js +226 -535
- package/core/tokenTypes.js +6 -15
- package/core/transpiler.js +129 -110
- package/core/validator.js +6 -26
- package/dist/sommark.browser.js +1781 -2172
- package/dist/sommark.browser.lite.js +1779 -2169
- package/dist/sommark.lexer.js +392 -544
- package/dist/sommark.parser.js +604 -1200
- package/formatter/mark.js +34 -0
- package/formatter/tag.js +7 -33
- package/helpers/utils.js +15 -16
- package/index.js +9 -1
- package/index.shared.js +26 -16
- package/mappers/languages/csv.js +62 -0
- package/mappers/languages/html.js +12 -66
- package/mappers/languages/json.js +74 -156
- package/mappers/languages/jsonc.js +21 -63
- package/mappers/languages/markdown.js +159 -276
- package/mappers/languages/mdx.js +7 -62
- package/mappers/languages/text.js +2 -19
- package/mappers/languages/toml.js +231 -0
- package/mappers/languages/xml.js +25 -25
- package/mappers/languages/yaml.js +323 -0
- package/mappers/mapper.js +1 -22
- package/mappers/shared/index.js +3 -16
- package/package.json +5 -2
package/mappers/languages/mdx.js
CHANGED
|
@@ -28,12 +28,11 @@ const MDX = Mapper.define({
|
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
render: (ctx) => {
|
|
31
|
-
const {
|
|
32
|
-
const element = this.tag(tagName).jsxProps(
|
|
31
|
+
const { props, content, isSelfClosing } = ctx;
|
|
32
|
+
const element = this.tag(tagName).jsxProps(props);
|
|
33
33
|
return (isSelfClosing || isVoid) ? element.selfClose() : element.body(content);
|
|
34
34
|
},
|
|
35
35
|
options: {
|
|
36
|
-
type: isVoid ? "Block" : (isCodeStyleOrScript ? ["Block", "AtBlock"] : ["Block", "Inline", "AtBlock"]),
|
|
37
36
|
escape: !isCodeStyleOrScript,
|
|
38
37
|
rules: { is_empty_body: isVoid }
|
|
39
38
|
}
|
|
@@ -50,32 +49,11 @@ const MDX = Mapper.define({
|
|
|
50
49
|
text(text, options) {
|
|
51
50
|
let out = text;
|
|
52
51
|
if (options?.escape !== false) {
|
|
53
|
-
out = this.
|
|
52
|
+
out = this.md.mdxEscaper(out);
|
|
54
53
|
}
|
|
55
54
|
return out;
|
|
56
55
|
},
|
|
57
56
|
|
|
58
|
-
/**
|
|
59
|
-
* Formats inline content before rendering, respecting explicit escape flags.
|
|
60
|
-
*/
|
|
61
|
-
inlineText(text, options) {
|
|
62
|
-
let out = text;
|
|
63
|
-
if (options?.escape !== false) {
|
|
64
|
-
out = this.escapeHTML(out);
|
|
65
|
-
}
|
|
66
|
-
return out;
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Formats the literal content inside AtBlocks.
|
|
71
|
-
*/
|
|
72
|
-
atBlockBody(text, options) {
|
|
73
|
-
let out = text;
|
|
74
|
-
if (options?.escape !== false) {
|
|
75
|
-
out = this.escapeHTML(out);
|
|
76
|
-
}
|
|
77
|
-
return out;
|
|
78
|
-
}
|
|
79
57
|
});
|
|
80
58
|
|
|
81
59
|
const { tag } = MDX;
|
|
@@ -84,47 +62,14 @@ MDX.inherit(MARKDOWN);
|
|
|
84
62
|
MDX.md = MARKDOWN.md;
|
|
85
63
|
|
|
86
64
|
["h1", "h2", "h3", "h4", "h5", "h6"].forEach(h => {
|
|
87
|
-
MDX.register(h, function ({
|
|
88
|
-
const format = this.safeArg({
|
|
65
|
+
MDX.register(h, function ({ props, content }) {
|
|
66
|
+
const format = this.safeArg({ props, key: "format", fallBack: "" });
|
|
89
67
|
if (format === "md" || format === "markdown") {
|
|
90
68
|
return this.md.heading(content, h.slice(1) || 1);
|
|
91
69
|
}
|
|
92
|
-
delete
|
|
93
|
-
return tag(h).jsxProps(
|
|
70
|
+
delete props.format;
|
|
71
|
+
return tag(h).jsxProps(props).body(content);
|
|
94
72
|
});
|
|
95
73
|
});
|
|
96
74
|
|
|
97
|
-
/**
|
|
98
|
-
* mdx AtBlock - Renders raw MDX content (ESM imports, exports, or complex JSX).
|
|
99
|
-
*/
|
|
100
|
-
MDX.register("mdx", ({ content }) => {
|
|
101
|
-
return content;
|
|
102
|
-
}, { escape: false, type: "AtBlock" });
|
|
103
|
-
|
|
104
|
-
// Inline CSS tag (Moved from shared)
|
|
105
|
-
MDX.register("css", ({ args, content }) => {
|
|
106
|
-
// Compile style from named arguments (keys that are not numeric digits)
|
|
107
|
-
const namedStyle = Object.keys(args)
|
|
108
|
-
.filter(k => isNaN(parseInt(k)))
|
|
109
|
-
.map(k => `${k}:${args[k]}`)
|
|
110
|
-
.join(";");
|
|
111
|
-
|
|
112
|
-
// Fetch positional style string (index 0) or "style" key if present
|
|
113
|
-
let positionalStyle = MDX.safeArg({ args, index: 0, key: "style", fallBack: "" });
|
|
114
|
-
|
|
115
|
-
// Filter out positional styles that are just duplicates of named arguments
|
|
116
|
-
const hasDuplicateNamed = Object.keys(args)
|
|
117
|
-
.filter(k => isNaN(parseInt(k)))
|
|
118
|
-
.some(k => args[k] === positionalStyle);
|
|
119
|
-
|
|
120
|
-
if (hasDuplicateNamed) {
|
|
121
|
-
positionalStyle = "";
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Combine both together
|
|
125
|
-
let style = [positionalStyle, namedStyle].filter(s => s.trim()).join(";");
|
|
126
|
-
|
|
127
|
-
return MDX.tag("span").jsxProps({ style }).body(content);
|
|
128
|
-
}, { type: "Inline" });
|
|
129
|
-
|
|
130
75
|
export default MDX;
|
|
@@ -36,30 +36,13 @@ const TEXT = Mapper.define({
|
|
|
36
36
|
return text;
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
* Returns inline text literally.
|
|
41
|
-
*/
|
|
42
|
-
inlineText(text) {
|
|
43
|
-
return text;
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Returns at-block body text literally.
|
|
48
|
-
*/
|
|
49
|
-
atBlockBody(text) {
|
|
50
|
-
return text;
|
|
51
|
-
},
|
|
52
|
-
|
|
53
39
|
/**
|
|
54
40
|
* Fallback for all tags - extracts inner content.
|
|
55
41
|
*/
|
|
56
|
-
getUnknownTag(
|
|
57
|
-
const isBlock = node.type === "Block" || node.type === "ForEach";
|
|
42
|
+
getUnknownTag() {
|
|
58
43
|
return {
|
|
59
44
|
render: ({ content }) => content,
|
|
60
|
-
options: {
|
|
61
|
-
type: isBlock ? "Block" : (node.type === "AtBlock" ? "AtBlock" : "Inline")
|
|
62
|
-
}
|
|
45
|
+
options: {}
|
|
63
46
|
};
|
|
64
47
|
}
|
|
65
48
|
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import Mapper from "../mapper.js";
|
|
2
|
+
import { safeArg } from "../../helpers/utils.js";
|
|
3
|
+
import { registerSharedOutputs } from "../shared/index.js";
|
|
4
|
+
import { transpilerError } from "../../core/errors.js";
|
|
5
|
+
|
|
6
|
+
const isValidInt = (v) => v !== "" && !isNaN(Number(v)) && !v.includes(".");
|
|
7
|
+
const isValidFloat = (v) => v !== "" && !isNaN(Number(v)) && v.includes(".");
|
|
8
|
+
const isValidNumber = (v) => v !== "" && !isNaN(Number(v));
|
|
9
|
+
|
|
10
|
+
// Escape a string value for use inside TOML basic strings
|
|
11
|
+
const tomlEscapeString = (str) =>
|
|
12
|
+
String(str ?? "")
|
|
13
|
+
.replace(/\\/g, "\\\\")
|
|
14
|
+
.replace(/"/g, '\\"')
|
|
15
|
+
.replace(/\x08/g, "\\b")
|
|
16
|
+
.replace(/\f/g, "\\f")
|
|
17
|
+
.replace(/\n/g, "\\n")
|
|
18
|
+
.replace(/\r/g, "\\r")
|
|
19
|
+
.replace(/\t/g, "\\t");
|
|
20
|
+
|
|
21
|
+
// Quote a bare key segment if it contains characters outside [A-Za-z0-9_-]
|
|
22
|
+
const toBareKey = (k) => /^[A-Za-z0-9_-]+$/.test(k) ? k : `"${tomlEscapeString(k)}"`;
|
|
23
|
+
|
|
24
|
+
// Handle dotted keys like "database.host" → database.host
|
|
25
|
+
const tomlKey = (key) => String(key).split(".").map(toBareKey).join(".");
|
|
26
|
+
|
|
27
|
+
const ITEM_SEP = "\x1F";
|
|
28
|
+
|
|
29
|
+
const TOML = Mapper.define({
|
|
30
|
+
comment(text) {
|
|
31
|
+
return `# ${text}`;
|
|
32
|
+
},
|
|
33
|
+
text(text) {
|
|
34
|
+
return text.trim() === "" ? "" : text;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* [str] / [string] — string key-value pair or array string value
|
|
40
|
+
*
|
|
41
|
+
* Key-value: [str = "title", "My App" !] → title = "My App"
|
|
42
|
+
* Body form: [str = "description"]Long text[end] → description = "Long text"
|
|
43
|
+
* In array: [str = "hello" !] → "hello"
|
|
44
|
+
*/
|
|
45
|
+
TOML.register(["str", "string"], ({ props, textContent, inArray = false }) => {
|
|
46
|
+
const value = inArray
|
|
47
|
+
? safeArg({ props, index: 0, key: "value", fallBack: textContent.trim() })
|
|
48
|
+
: safeArg({ props, index: 1, key: "value", fallBack: textContent.trim() });
|
|
49
|
+
const escaped = `"${tomlEscapeString(String(value))}"`;
|
|
50
|
+
if (inArray) return escaped + ITEM_SEP;
|
|
51
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
52
|
+
return `${tomlKey(key)} = ${escaped}\n`;
|
|
53
|
+
}, { handleAst: true });
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* [int] / [integer] — integer key-value pair or array integer value
|
|
57
|
+
*
|
|
58
|
+
* Key-value: [int = "port", "5432" !] → port = 5432
|
|
59
|
+
* In array: [int = "5432" !] → 5432
|
|
60
|
+
*/
|
|
61
|
+
TOML.register(["int", "integer"], ({ props, textContent, inArray = false }) => {
|
|
62
|
+
const raw = String(safeArg({ props, index: inArray ? 0 : 1, key: "value", fallBack: textContent.trim() })).trim();
|
|
63
|
+
if (!isValidInt(raw))
|
|
64
|
+
transpilerError(`<$yellow:[int]$> expects a whole number but got <$yellow:'${raw}'$>.{N}Use <$cyan:[float]$> for decimal numbers or <$cyan:[number]$> for either.`);
|
|
65
|
+
if (inArray) return raw + ITEM_SEP;
|
|
66
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
67
|
+
return `${tomlKey(key)} = ${raw}\n`;
|
|
68
|
+
}, { handleAst: true });
|
|
69
|
+
|
|
70
|
+
TOML.register("float", ({ props, textContent, inArray = false }) => {
|
|
71
|
+
const raw = String(safeArg({ props, index: inArray ? 0 : 1, key: "value", fallBack: textContent.trim() })).trim();
|
|
72
|
+
if (!isValidFloat(raw))
|
|
73
|
+
transpilerError(`<$yellow:[float]$> expects a decimal number but got <$yellow:'${raw}'$>.{N}Use <$cyan:[int]$> for whole numbers or <$cyan:[number]$> for either.`);
|
|
74
|
+
if (inArray) return raw + ITEM_SEP;
|
|
75
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
76
|
+
return `${tomlKey(key)} = ${raw}\n`;
|
|
77
|
+
}, { handleAst: true });
|
|
78
|
+
|
|
79
|
+
TOML.register("number", ({ props, textContent, inArray = false }) => {
|
|
80
|
+
const raw = String(safeArg({ props, index: inArray ? 0 : 1, key: "value", fallBack: textContent.trim() })).trim();
|
|
81
|
+
if (!isValidNumber(raw))
|
|
82
|
+
transpilerError(`<$yellow:[number]$> expects a numeric value but got <$yellow:'${raw}'$>.`);
|
|
83
|
+
if (inArray) return raw + ITEM_SEP;
|
|
84
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
85
|
+
return `${tomlKey(key)} = ${raw}\n`;
|
|
86
|
+
}, { handleAst: true });
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* [bool] / [boolean] — boolean key-value pair or array bool value
|
|
90
|
+
*
|
|
91
|
+
* Key-value: [bool = "debug", "false" !] → debug = false
|
|
92
|
+
* In array: [bool = "true" !] → true
|
|
93
|
+
*/
|
|
94
|
+
TOML.register(["bool", "boolean"], ({ props, textContent, inArray = false }) => {
|
|
95
|
+
const raw = String(
|
|
96
|
+
inArray
|
|
97
|
+
? safeArg({ props, index: 0, key: "value", fallBack: textContent.trim() })
|
|
98
|
+
: safeArg({ props, index: 1, key: "value", fallBack: textContent.trim() })
|
|
99
|
+
).trim().toLowerCase();
|
|
100
|
+
const value = (raw === "true" || raw === "1" || raw === "yes") ? "true" : "false";
|
|
101
|
+
if (inArray) return value + ITEM_SEP;
|
|
102
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
103
|
+
return `${tomlKey(key)} = ${value}\n`;
|
|
104
|
+
}, { handleAst: true });
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* [datetime] — datetime key-value pair
|
|
108
|
+
* TOML datetimes are bare (unquoted): 1979-05-27T07:32:00Z
|
|
109
|
+
*
|
|
110
|
+
* [datetime = "born", "1979-05-27T07:32:00Z" !] → born = 1979-05-27T07:32:00Z
|
|
111
|
+
*/
|
|
112
|
+
TOML.register("datetime", ({ props, textContent, inArray = false }) => {
|
|
113
|
+
const raw = String(
|
|
114
|
+
inArray
|
|
115
|
+
? safeArg({ props, index: 0, key: "value", fallBack: textContent.trim() })
|
|
116
|
+
: safeArg({ props, index: 1, key: "value", fallBack: textContent.trim() })
|
|
117
|
+
).trim();
|
|
118
|
+
if (inArray) return raw + ITEM_SEP;
|
|
119
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
120
|
+
return `${tomlKey(key)} = ${raw}\n`;
|
|
121
|
+
}, { handleAst: true });
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* [table] / [section] — renders a TOML table header + children
|
|
125
|
+
*
|
|
126
|
+
* [table = "database"]
|
|
127
|
+
* [str = "host", "localhost" !]
|
|
128
|
+
* [int = "port", "5432" !]
|
|
129
|
+
* [end]
|
|
130
|
+
*
|
|
131
|
+
* →
|
|
132
|
+
*
|
|
133
|
+
* [database]
|
|
134
|
+
* host = "localhost"
|
|
135
|
+
* port = 5432
|
|
136
|
+
*/
|
|
137
|
+
TOML.register(["table", "section"], async ({ props, ast, renderChild }) => {
|
|
138
|
+
const name = safeArg({ props, index: 0, key: "name", fallBack: "" });
|
|
139
|
+
const parts = [];
|
|
140
|
+
for (const child of ast.body) {
|
|
141
|
+
const out = await renderChild(child);
|
|
142
|
+
if (out != null && out.trim() !== "") parts.push(out);
|
|
143
|
+
}
|
|
144
|
+
return `[${tomlKey(name)}]\n${parts.join("")}\n`;
|
|
145
|
+
}, { handleAst: true });
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* [array-table] — renders a TOML array of tables entry
|
|
149
|
+
*
|
|
150
|
+
* [array-table = "servers"]
|
|
151
|
+
* [str = "name", "alpha" !]
|
|
152
|
+
* [str = "ip", "10.0.0.1" !]
|
|
153
|
+
* [end]
|
|
154
|
+
*
|
|
155
|
+
* →
|
|
156
|
+
*
|
|
157
|
+
* [[servers]]
|
|
158
|
+
* name = "alpha"
|
|
159
|
+
* ip = "10.0.0.1"
|
|
160
|
+
*/
|
|
161
|
+
TOML.register("array-table", async ({ props, ast, renderChild }) => {
|
|
162
|
+
const name = safeArg({ props, index: 0, key: "name", fallBack: "" });
|
|
163
|
+
const parts = [];
|
|
164
|
+
for (const child of ast.body) {
|
|
165
|
+
const out = await renderChild(child);
|
|
166
|
+
if (out != null && out.trim() !== "") parts.push(out);
|
|
167
|
+
}
|
|
168
|
+
return `[[${tomlKey(name)}]]\n${parts.join("")}\n`;
|
|
169
|
+
}, { handleAst: true });
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* [array] — renders a TOML inline array
|
|
173
|
+
* Children are rendered with inArray: true so they output just their value.
|
|
174
|
+
*
|
|
175
|
+
* [array = "ports"]
|
|
176
|
+
* [int = "8001" !][int = "8002" !][int = "8003" !]
|
|
177
|
+
* [end]
|
|
178
|
+
*
|
|
179
|
+
* → ports = [8001, 8002, 8003]
|
|
180
|
+
*
|
|
181
|
+
* Works with [for-each] for dynamic arrays:
|
|
182
|
+
* [array = "tags"]
|
|
183
|
+
* [for-each = ${ tags }$, as: "item"][str = ${ item }$ !][end]
|
|
184
|
+
* [end]
|
|
185
|
+
*/
|
|
186
|
+
/**
|
|
187
|
+
* Unknown tag — tag name becomes the TOML key, first positional arg is the value.
|
|
188
|
+
* Type is inferred: number → raw integer/float, true/false → boolean, everything else → quoted string.
|
|
189
|
+
*
|
|
190
|
+
* [name = "Adam" !] → name = "Adam"
|
|
191
|
+
* [port = 5432 !] → port = 5432
|
|
192
|
+
* [debug = false !] → debug = false
|
|
193
|
+
* [bio]Long text[end] → bio = "Long text"
|
|
194
|
+
*/
|
|
195
|
+
TOML.getUnknownTag = function (node) {
|
|
196
|
+
const key = node.id;
|
|
197
|
+
return {
|
|
198
|
+
render({ props, textContent, inArray = false }) {
|
|
199
|
+
const raw = String(
|
|
200
|
+
safeArg({ props, index: 0, key: "value", fallBack: textContent.trim() })
|
|
201
|
+
).trim();
|
|
202
|
+
|
|
203
|
+
let val;
|
|
204
|
+
if (raw === "true" || raw === "false") {
|
|
205
|
+
val = raw;
|
|
206
|
+
} else if (raw !== "" && !isNaN(Number(raw))) {
|
|
207
|
+
val = raw;
|
|
208
|
+
} else {
|
|
209
|
+
val = `"${tomlEscapeString(raw)}"`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (inArray) return val + ITEM_SEP;
|
|
213
|
+
return `${tomlKey(key)} = ${val}\n`;
|
|
214
|
+
},
|
|
215
|
+
options: { handleAst: true }
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
TOML.register("array", async ({ props, ast, renderChild }) => {
|
|
220
|
+
const key = safeArg({ props, index: 0, key: "key", fallBack: "" });
|
|
221
|
+
let combined = "";
|
|
222
|
+
for (const child of ast.body) {
|
|
223
|
+
combined += await renderChild(child, { inArray: true });
|
|
224
|
+
}
|
|
225
|
+
const vals = combined.split(ITEM_SEP).filter(v => v.trim() !== "");
|
|
226
|
+
return `${tomlKey(key)} = [${vals.join(", ")}]\n`;
|
|
227
|
+
}, { handleAst: true });
|
|
228
|
+
|
|
229
|
+
registerSharedOutputs(TOML);
|
|
230
|
+
|
|
231
|
+
export default TOML;
|
package/mappers/languages/xml.js
CHANGED
|
@@ -6,19 +6,19 @@ import { registerSharedOutputs } from "../shared/index.js";
|
|
|
6
6
|
* Ensures strict attribute quoting and handles self-closing tags for empty bodies.
|
|
7
7
|
*
|
|
8
8
|
* @param {string} id - The XML tag identifier (case-sensitive).
|
|
9
|
-
* @param {Object}
|
|
9
|
+
* @param {Object} props - Key-value pairs to be rendered as XML attributes.
|
|
10
10
|
* @param {string} content - The rendered inner content of the tag.
|
|
11
11
|
* @returns {string} The fully rendered XML tag string.
|
|
12
12
|
*/
|
|
13
|
-
const renderXmlTag = function (id,
|
|
13
|
+
const renderXmlTag = function (id, props, content, isSelfClosing) {
|
|
14
14
|
// XML is case-sensitive, so we use the exact id provided
|
|
15
15
|
const element = this.tag(id);
|
|
16
16
|
|
|
17
17
|
// Filter out positional indices (numeric keys) for XML attributes
|
|
18
18
|
const namedArgs = {};
|
|
19
|
-
Object.keys(
|
|
19
|
+
Object.keys(props).forEach(key => {
|
|
20
20
|
if (isNaN(parseInt(key))) {
|
|
21
|
-
namedArgs[key] =
|
|
21
|
+
namedArgs[key] = props[key];
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
@@ -55,10 +55,8 @@ const XML = Mapper.define({
|
|
|
55
55
|
getUnknownTag(node) {
|
|
56
56
|
const id = node.id;
|
|
57
57
|
return {
|
|
58
|
-
render: ({
|
|
59
|
-
options: {
|
|
60
|
-
type: "any"
|
|
61
|
-
}
|
|
58
|
+
render: ({ props, content, isSelfClosing }) => renderXmlTag.call(this, id, props, content, isSelfClosing),
|
|
59
|
+
options: {}
|
|
62
60
|
};
|
|
63
61
|
}
|
|
64
62
|
});
|
|
@@ -67,20 +65,20 @@ const XML = Mapper.define({
|
|
|
67
65
|
* Registers the XML declaration as a self-closing block.
|
|
68
66
|
* Usage: [xml = version: "1.0", encoding: "UTF-8"]
|
|
69
67
|
*/
|
|
70
|
-
XML.register("xml", ({
|
|
71
|
-
const version =
|
|
72
|
-
const encoding =
|
|
68
|
+
XML.register("xml", ({ props }) => {
|
|
69
|
+
const version = props.version || "1.0";
|
|
70
|
+
const encoding = props.encoding || "UTF-8";
|
|
73
71
|
return `<?xml version="${version}" encoding="${encoding}"?>`;
|
|
74
|
-
}, {
|
|
72
|
+
}, { rules: { is_empty_body: true } });
|
|
75
73
|
|
|
76
74
|
/**
|
|
77
75
|
* Registers the DOCTYPE declaration.
|
|
78
76
|
* Usage: [doctype = root: "note", system: "note.dtd"]
|
|
79
77
|
*/
|
|
80
|
-
XML.register(["DOCTYPE", "doctype"], ({
|
|
81
|
-
const root =
|
|
82
|
-
const system =
|
|
83
|
-
const pub =
|
|
78
|
+
XML.register(["DOCTYPE", "doctype"], ({ props }) => {
|
|
79
|
+
const root = props.root || "root";
|
|
80
|
+
const system = props.system;
|
|
81
|
+
const pub = props.public || props.fpi;
|
|
84
82
|
|
|
85
83
|
if (pub && system) {
|
|
86
84
|
return `<!DOCTYPE ${root} PUBLIC "${pub}" "${system}">`;
|
|
@@ -88,26 +86,28 @@ XML.register(["DOCTYPE", "doctype"], ({ args }) => {
|
|
|
88
86
|
return `<!DOCTYPE ${root} SYSTEM "${system}">`;
|
|
89
87
|
}
|
|
90
88
|
return `<!DOCTYPE ${root}>`;
|
|
91
|
-
}, {
|
|
89
|
+
}, { rules: { is_empty_body: true } });
|
|
92
90
|
|
|
93
91
|
/**
|
|
94
92
|
* Registers the XML stylesheet processing instruction.
|
|
95
93
|
* Usage: [xml-stylesheet = href: "style.xsl"]
|
|
96
94
|
*/
|
|
97
|
-
XML.register("xml-stylesheet", ({
|
|
98
|
-
const type =
|
|
99
|
-
const href =
|
|
95
|
+
XML.register("xml-stylesheet", ({ props }) => {
|
|
96
|
+
const type = props.type || "text/xsl";
|
|
97
|
+
const href = props.href;
|
|
100
98
|
if (!href) return "";
|
|
101
99
|
return `<?xml-stylesheet type="${type}" href="${href}"?>`;
|
|
102
|
-
}, {
|
|
100
|
+
}, { rules: { is_empty_body: true } });
|
|
103
101
|
|
|
104
102
|
/**
|
|
105
103
|
* Registers CDATA sections.
|
|
106
|
-
*
|
|
104
|
+
* Body form: [cdata]raw content[end]
|
|
105
|
+
* Self-closing: [cdata = "raw content" !] or [cdata = text: "raw content" !]
|
|
107
106
|
*/
|
|
108
|
-
XML.register("cdata", ({ content }) => {
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
XML.register("cdata", ({ props, content, isSelfClosing }) => {
|
|
108
|
+
const text = isSelfClosing ? (props[0] ?? props.text ?? "") : content;
|
|
109
|
+
return `<![CDATA[${text}]]>`;
|
|
110
|
+
});
|
|
111
111
|
|
|
112
112
|
registerSharedOutputs(XML);
|
|
113
113
|
|