sommark 4.5.3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +314 -178
- 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 +0 -2
- package/cli/constants.js +5 -2
- package/core/errors.js +5 -4
- package/core/evaluator.js +1 -2
- package/core/formats.js +7 -1
- package/core/helpers/config-loader.js +1 -3
- 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 +1777 -2163
- package/dist/sommark.browser.lite.js +1775 -2160
- 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 +22 -12
- 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
|
@@ -1,185 +1,103 @@
|
|
|
1
1
|
import Mapper from "../mapper.js";
|
|
2
|
-
import { getPositionalArgs,
|
|
2
|
+
import { getPositionalArgs, safeArg } from "../../helpers/utils.js";
|
|
3
|
+
import { transpilerError } from "../../core/errors.js";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
* JSON Mapper - Creates JSON output.
|
|
6
|
-
* It manages the structure manually using 'handleAst: true'.
|
|
7
|
-
*/
|
|
5
|
+
const ITEM_SEP = "\x1F";
|
|
8
6
|
|
|
9
|
-
/**
|
|
10
|
-
* Returns a string representing the specified indentation level.
|
|
11
|
-
*/
|
|
12
7
|
export function getIndent(depth) {
|
|
13
8
|
return " ".repeat(depth);
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
/**
|
|
17
|
-
* Escapes a string for use in a JSON property or value.
|
|
18
|
-
* @param {string} str - The string to escape.
|
|
19
|
-
* @param {boolean} [trim=false] - Whether to trim the string.
|
|
20
|
-
*/
|
|
21
11
|
export function escapeString(str, trim = false) {
|
|
22
12
|
let out = String(str);
|
|
23
13
|
if (trim) out = out.trim();
|
|
24
14
|
return JSON.stringify(out);
|
|
25
15
|
}
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*/
|
|
33
|
-
async function getNodeText(node) {
|
|
34
|
-
if (!node.body) return "";
|
|
35
|
-
let text = "";
|
|
36
|
-
for (const child of node.body) {
|
|
37
|
-
if (child.type === "Text") text += child.text || "";
|
|
38
|
-
else if (child.type === "StaticLogic") {
|
|
39
|
-
try {
|
|
40
|
-
const result = await evaluator.execute(child.code);
|
|
41
|
-
if (result !== undefined && typeof result !== "object") {
|
|
42
|
-
text += String(result);
|
|
43
|
-
}
|
|
44
|
-
} catch (err) {
|
|
45
|
-
console.error(`\x1b[31mLogic Error in JSON mapper:\x1b[0m ${err.message}`);
|
|
46
|
-
console.error(`\x1b[33mCode:\x1b[0m \x1b[34m${child.code}\x1b[0m`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else if (child.type === "Block") text += await getNodeText(child);
|
|
50
|
-
}
|
|
51
|
-
return text;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Resolves the key-value pairing for a JSON member.
|
|
56
|
-
*/
|
|
57
|
-
export function renderMember(args, value, inArray = false) {
|
|
58
|
-
if (inArray) return value;
|
|
59
|
-
|
|
60
|
-
const posArgs = getPositionalArgs(args);
|
|
61
|
-
const key = args.key || posArgs[0]; // The 'key' rule determines the member name
|
|
62
|
-
|
|
63
|
-
if (key) {
|
|
64
|
-
return `${escapeString(key)}: ${value}`;
|
|
65
|
-
}
|
|
17
|
+
export function renderMember(props, value, inArray = false) {
|
|
18
|
+
if (inArray) return value + ITEM_SEP;
|
|
19
|
+
const posArgs = getPositionalArgs(props);
|
|
20
|
+
const key = props.key || posArgs[0];
|
|
21
|
+
if (key) return `${escapeString(key)}: ${value}` + ITEM_SEP;
|
|
66
22
|
return value;
|
|
67
23
|
}
|
|
68
24
|
|
|
69
|
-
|
|
70
|
-
* Formats a given node and tracks its indentation.
|
|
71
|
-
*/
|
|
72
|
-
export async function renderNode(node, mapper, depth = 0, inArray = false) {
|
|
73
|
-
const target = matchedValue(mapper.outputs, node.id) || mapper.getUnknownTag(node);
|
|
74
|
-
if (!target) return "";
|
|
75
|
-
|
|
76
|
-
evaluator.pushScope();
|
|
77
|
-
const textContent = await getNodeText(node);
|
|
78
|
-
const output = await target.render.call(mapper, {
|
|
79
|
-
nodeType: node.type,
|
|
80
|
-
args: node.args,
|
|
81
|
-
content: "",
|
|
82
|
-
textContent,
|
|
83
|
-
ast: node,
|
|
84
|
-
depth,
|
|
85
|
-
inArray
|
|
86
|
-
});
|
|
87
|
-
await evaluator.popScope();
|
|
88
|
-
return output;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Formats the children of a node into a neat list.
|
|
93
|
-
*/
|
|
94
|
-
export async function renderChildren(node, mapper, depth = 0, inArray = false) {
|
|
95
|
-
let results = [];
|
|
96
|
-
const childIndent = getIndent(depth + 1);
|
|
25
|
+
const Json = Mapper.define({});
|
|
97
26
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
27
|
+
Json.register(["Object", "object"], async function ({ props, ast, depth = 0, inArray = false, renderChild }) {
|
|
28
|
+
let combined = "";
|
|
29
|
+
for (const child of ast.body) {
|
|
30
|
+
const out = await renderChild(child, { depth: depth + 1, inArray: false });
|
|
31
|
+
if (out) combined += out;
|
|
105
32
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (hasNextBlock) finalOutput += ",";
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (i < results.length - 1) {
|
|
125
|
-
finalOutput += "\n";
|
|
126
|
-
}
|
|
33
|
+
const parts = combined.split(ITEM_SEP).map(v => v.trim()).filter(v => v !== "");
|
|
34
|
+
const value = parts.length === 0
|
|
35
|
+
? "{}"
|
|
36
|
+
: `{\n${parts.map(p => getIndent(depth + 1) + p).join(",\n")}\n${getIndent(depth)}}`;
|
|
37
|
+
return renderMember(props, value, inArray);
|
|
38
|
+
}, { handleAst: true });
|
|
39
|
+
|
|
40
|
+
Json.register(["Array", "array"], async function ({ props, ast, depth = 0, inArray = false, renderChild }) {
|
|
41
|
+
let combined = "";
|
|
42
|
+
for (const child of ast.body) {
|
|
43
|
+
const out = await renderChild(child, { depth: depth + 1, inArray: true });
|
|
44
|
+
if (out) combined += out;
|
|
127
45
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Json.register(["Object", "object"], async ({ args, ast, depth = 0, inArray = false }) => {
|
|
137
|
-
if (ast.body.length === 0) return renderMember(args, "{}", inArray);
|
|
138
|
-
const content = await renderChildren(ast, Json, depth, false);
|
|
139
|
-
const val = `{\n${content}\n${getIndent(depth)}}`;
|
|
140
|
-
return renderMember(args, val, inArray);
|
|
141
|
-
}, { type: "Block", handleAst: true });
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* The JSON array node rule.
|
|
145
|
-
*/
|
|
146
|
-
Json.register(["Array", "array"], async ({ args, ast, depth = 0, inArray = false }) => {
|
|
147
|
-
if (ast.body.length === 0) return renderMember(args, "[]", inArray);
|
|
148
|
-
const content = await renderChildren(ast, Json, depth, true);
|
|
149
|
-
const val = `[\n${content}\n${getIndent(depth)}]`;
|
|
150
|
-
return renderMember(args, val, inArray);
|
|
151
|
-
}, { type: "Block", handleAst: true });
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* JSON Primitives
|
|
155
|
-
*/
|
|
156
|
-
Json.register("string", ({ args, textContent, inArray }) => {
|
|
46
|
+
const parts = combined.split(ITEM_SEP).map(v => v.trim()).filter(v => v !== "");
|
|
47
|
+
const value = parts.length === 0
|
|
48
|
+
? "[]"
|
|
49
|
+
: `[\n${parts.map(p => getIndent(depth + 1) + p).join(",\n")}\n${getIndent(depth)}]`;
|
|
50
|
+
return renderMember(props, value, inArray);
|
|
51
|
+
}, { handleAst: true });
|
|
52
|
+
|
|
53
|
+
Json.register(["string", "str"], ({ props, textContent, inArray }) => {
|
|
157
54
|
const trim = safeArg({
|
|
158
|
-
|
|
55
|
+
props,
|
|
159
56
|
key: "trim",
|
|
160
57
|
type: "boolean",
|
|
161
58
|
setType: v => v === "true" || v === true,
|
|
162
59
|
fallBack: false
|
|
163
60
|
});
|
|
164
|
-
const raw = safeArg({
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}, { type: "Block", handleAst: true });
|
|
61
|
+
const raw = safeArg({ props, index: inArray ? 0 : undefined, key: "value", fallBack: textContent });
|
|
62
|
+
return renderMember(props, escapeString(raw, trim), inArray);
|
|
63
|
+
}, { handleAst: true });
|
|
168
64
|
|
|
169
|
-
Json.register("number", ({
|
|
170
|
-
const raw = String(safeArg({
|
|
65
|
+
Json.register("number", ({ props, textContent, inArray }) => {
|
|
66
|
+
const raw = String(safeArg({ props, index: inArray ? 0 : undefined, key: "value", fallBack: textContent })).trim();
|
|
171
67
|
const val = (isNaN(Number(raw)) || raw === "") ? "0" : raw;
|
|
172
|
-
return renderMember(
|
|
173
|
-
}, {
|
|
174
|
-
|
|
175
|
-
Json.register("bool", ({
|
|
176
|
-
const raw = String(safeArg({
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
68
|
+
return renderMember(props, val, inArray);
|
|
69
|
+
}, { handleAst: true });
|
|
70
|
+
|
|
71
|
+
Json.register("bool", ({ props, textContent, inArray }) => {
|
|
72
|
+
const raw = String(safeArg({ props, index: inArray ? 0 : undefined, key: "value", fallBack: textContent })).trim().toLowerCase();
|
|
73
|
+
return renderMember(props, (raw === "true" || raw === "1") ? "true" : "false", inArray);
|
|
74
|
+
}, { handleAst: true });
|
|
75
|
+
|
|
76
|
+
Json.register("null", ({ props, inArray }) => {
|
|
77
|
+
return renderMember(props, "null", inArray);
|
|
78
|
+
}, { handleAst: true });
|
|
79
|
+
|
|
80
|
+
Json.getUnknownTag = function (node) {
|
|
81
|
+
const key = node.id;
|
|
82
|
+
return {
|
|
83
|
+
render({ props, textContent, inArray = false }) {
|
|
84
|
+
if (inArray) {
|
|
85
|
+
transpilerError(
|
|
86
|
+
`Unknown tag '<$yellow:[${key}]$>' cannot be used inside <$yellow:[Array]$>.{N}Use <$cyan:[string]$>, <$cyan:[number]$>, <$cyan:[bool]$>, or <$cyan:[null]$> instead.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const raw = String(
|
|
90
|
+
safeArg({ props, index: 0, key: "value", fallBack: textContent.trim() })
|
|
91
|
+
).trim();
|
|
92
|
+
let val;
|
|
93
|
+
if (raw === "null") val = "null";
|
|
94
|
+
else if (raw === "true" || raw === "false") val = raw;
|
|
95
|
+
else if (raw !== "" && !isNaN(Number(raw))) val = raw;
|
|
96
|
+
else val = escapeString(raw);
|
|
97
|
+
return `${escapeString(key)}: ${val}` + ITEM_SEP;
|
|
98
|
+
},
|
|
99
|
+
options: { handleAst: true }
|
|
100
|
+
};
|
|
101
|
+
};
|
|
184
102
|
|
|
185
103
|
export default Json;
|
|
@@ -1,54 +1,6 @@
|
|
|
1
|
-
import Json, {
|
|
1
|
+
import Json, { getIndent, renderMember } from "./json.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* JSONC Mapper - Creates JSON output with comments.
|
|
5
|
-
* It inherits from the standard JSON mapper and adds comment support.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
async function renderChildren(node, mapper, depth = 0, inArray = false) {
|
|
9
|
-
let results = [];
|
|
10
|
-
const childIndent = getIndent(depth + 1);
|
|
11
|
-
|
|
12
|
-
for (const child of node.body) {
|
|
13
|
-
if (child.type === "Block") {
|
|
14
|
-
const output = await renderNode(child, mapper, depth + 1, inArray);
|
|
15
|
-
if (output) {
|
|
16
|
-
results.push({ type: "Block", value: childIndent + output });
|
|
17
|
-
}
|
|
18
|
-
} else if (child.type === "Comment") {
|
|
19
|
-
if (!mapper.options?.removeComments) {
|
|
20
|
-
results.push({ type: "Comment", value: childIndent + mapper.comment(child.text) });
|
|
21
|
-
}
|
|
22
|
-
} else if (child.type === "CommentBlock") {
|
|
23
|
-
if (!mapper.options?.removeComments) {
|
|
24
|
-
results.push({ type: "CommentBlock", value: childIndent + mapper.commentBlock(child.text, childIndent) });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let finalOutput = "";
|
|
30
|
-
for (let i = 0; i < results.length; i++) {
|
|
31
|
-
const current = results[i];
|
|
32
|
-
finalOutput += current.value;
|
|
33
|
-
|
|
34
|
-
if (current.type === "Block") {
|
|
35
|
-
// Add comma if there is another Block later
|
|
36
|
-
let hasNextBlock = false;
|
|
37
|
-
for (let j = i + 1; j < results.length; j++) {
|
|
38
|
-
if (results[j].type === "Block") {
|
|
39
|
-
hasNextBlock = true;
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (hasNextBlock) finalOutput += ",";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (i < results.length - 1) {
|
|
47
|
-
finalOutput += "\n";
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return finalOutput;
|
|
51
|
-
}
|
|
3
|
+
const ITEM_SEP = "\x1F";
|
|
52
4
|
|
|
53
5
|
const Jsonc = Json.clone();
|
|
54
6
|
|
|
@@ -64,19 +16,25 @@ Jsonc.commentBlock = function (text, indent = " ") {
|
|
|
64
16
|
return `/* ${text} */`;
|
|
65
17
|
};
|
|
66
18
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
19
|
+
async function renderStructure(props, ast, depth, inArray, childInArray, openBracket, closeBracket, renderChild) {
|
|
20
|
+
let combined = "";
|
|
21
|
+
for (const child of ast.body) {
|
|
22
|
+
const out = await renderChild(child, { depth: depth + 1, inArray: childInArray });
|
|
23
|
+
if (out) combined += out;
|
|
24
|
+
}
|
|
25
|
+
const parts = combined.split(ITEM_SEP).map(v => v.trim()).filter(v => v !== "");
|
|
26
|
+
const value = parts.length === 0
|
|
27
|
+
? `${openBracket}${closeBracket}`
|
|
28
|
+
: `${openBracket}\n${parts.map(p => getIndent(depth + 1) + p).join(",\n")}\n${getIndent(depth)}${closeBracket}`;
|
|
29
|
+
return renderMember(props, value, inArray);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Jsonc.register(["Object", "object"], async function ({ props, ast, depth = 0, inArray = false, renderChild }) {
|
|
33
|
+
return renderStructure(props, ast, depth, inArray, false, "{", "}", renderChild);
|
|
34
|
+
}, { handleAst: true });
|
|
74
35
|
|
|
75
|
-
Jsonc.register(["Array", "array"], async function ({
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const val = `[\n${content}\n${getIndent(depth)}]`;
|
|
79
|
-
return renderMember(args, val, inArray);
|
|
80
|
-
}, { type: "Block", handleAst: true });
|
|
36
|
+
Jsonc.register(["Array", "array"], async function ({ props, ast, depth = 0, inArray = false, renderChild }) {
|
|
37
|
+
return renderStructure(props, ast, depth, inArray, true, "[", "]", renderChild);
|
|
38
|
+
}, { handleAst: true });
|
|
81
39
|
|
|
82
40
|
export default Jsonc;
|