sommark 1.0.0 → 1.1.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 +31 -0
- package/cli/cli.mjs +1 -1
- package/core/parser.js +9 -2
- package/core/transpiler.js +36 -21
- package/formatter/tag.js +4 -3
- package/helpers/loadStyle.js +25 -0
- package/index.js +3 -2
- package/lib/highlight.js +173 -2
- package/mappers/default_mode/smark.html.js +8 -5
- package/mappers/default_mode/smark.md.js +4 -5
- package/mappers/mapper.js +20 -3
- package/package.json +15 -15
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 (2026-01-04)
|
|
4
|
+
- Initial release
|
|
5
|
+
- Supports HTML, MD, MDX output
|
|
6
|
+
- CLI ready
|
|
7
|
+
- Lightweight 61 KB package
|
|
8
|
+
|
|
9
|
+
## 1.1.0 (2026-01-08)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- **Highlight.js Integration**:
|
|
14
|
+
- Added support for highlight.js themes.
|
|
15
|
+
- Added `codeThemes` and `selectedTheme` to Mapper configuration.
|
|
16
|
+
- Default theme for HTML output is now `atom-one-dark`.
|
|
17
|
+
- Added `helpers/loadStyle.js` to dynamically load theme CSS (isomorphic: supports both Node.js via `fs` and browser via `fetch`).
|
|
18
|
+
- Automatically injects selected theme CSS into HTML output when code blocks are present.
|
|
19
|
+
|
|
20
|
+
- **Mappers**:
|
|
21
|
+
- Added `includesId(id)` helper method to `Mapper` class for checking output mapping existence.
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
- **Core/Parser**: Fixed a critical issue where global state variables (`block_stack`, `line`, etc.) were not reset between parse calls, causing errors on subsequent runs.
|
|
26
|
+
- **Core/Transpiler**: Removed leftover debug `console.log` calls.
|
|
27
|
+
- **Mappers/Markdown**: Fixed `Heading` block ignoring inner content (text/comments) in MD/MDX output. Now appends nested content after the heading.
|
|
28
|
+
- **Security**: Refactored HTML escaping architecture.
|
|
29
|
+
- **Transpiler**: `AtBlock` content is now **escaped by default** in the transpiler to prevent XSS.
|
|
30
|
+
- **Mapper**: Added `options` to `Mapper.create` (e.g., `{ escape: false }`) to allow specific blocks (like `Code`, `List`, `Table`) to opt-out of automatic escaping when they handle raw content safely or require it for parsing.
|
|
31
|
+
- **Parser**: Removed manual escaping from Parser to support the new transpiler-based architecture.
|
package/cli/cli.mjs
CHANGED
|
@@ -22,7 +22,7 @@ function getHelp(unknown_option = true) {
|
|
|
22
22
|
"{N} <$green:-v or --version$> <$cyan: show version information$>",
|
|
23
23
|
"{N} <$green:--html$> <$cyan: transpile to html$>",
|
|
24
24
|
"{N} <$green:--md$> <$cyan: transpile to markdown$>",
|
|
25
|
-
"{N} <$green:--mdx
|
|
25
|
+
"{N} <$green:--mdx$> <$cyan: transpile to mdx$>"
|
|
26
26
|
].join("");
|
|
27
27
|
const help_msg = formatMessage(msg);
|
|
28
28
|
if (!options.includes(process.argv[2]) && unknown_option) {
|
package/core/parser.js
CHANGED
|
@@ -78,7 +78,7 @@ let block_stack = [];
|
|
|
78
78
|
let end_stack = [];
|
|
79
79
|
let tokens_stack = [];
|
|
80
80
|
let line = 1,
|
|
81
|
-
start = 1,
|
|
81
|
+
start = 1,
|
|
82
82
|
end = 1,
|
|
83
83
|
value = "";
|
|
84
84
|
|
|
@@ -430,6 +430,13 @@ function parseNode(tokens, i) {
|
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
function parser(tokens) {
|
|
433
|
+
block_stack = [];
|
|
434
|
+
end_stack = [];
|
|
435
|
+
tokens_stack = [];
|
|
436
|
+
line = 1;
|
|
437
|
+
start = 1;
|
|
438
|
+
end = 1;
|
|
439
|
+
value = "";
|
|
433
440
|
let ast = [];
|
|
434
441
|
for (let i = 0; i < tokens.length; i++) {
|
|
435
442
|
let [nodes, nextIndex] = parseNode(tokens, i);
|
|
@@ -455,4 +462,4 @@ function parser(tokens) {
|
|
|
455
462
|
return ast;
|
|
456
463
|
}
|
|
457
464
|
|
|
458
|
-
export default parser;
|
|
465
|
+
export default parser;
|
package/core/transpiler.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import PREDEFINED_IDS from "./ids.js";
|
|
2
2
|
import { BLOCK, TEXT, INLINE, ATBLOCK, COMMENT, NEWLINE } from "./names.js";
|
|
3
|
+
import escapeHTML from "../helpers/escapeHTML.js";
|
|
3
4
|
import { transpilerError } from "./validator.js";
|
|
4
5
|
|
|
5
|
-
const formats = {
|
|
6
|
-
const {
|
|
6
|
+
const formats = { htmlFormat: "html", markdownFormat: "md", mdxFormat: "mdx" };
|
|
7
|
+
const { htmlFormat, markdownFormat, mdxFormat } = formats;
|
|
7
8
|
|
|
8
9
|
// Extracting target identifier
|
|
9
10
|
function matchedValue(outputs, targetId) {
|
|
@@ -29,15 +30,15 @@ function matchedValue(outputs, targetId) {
|
|
|
29
30
|
function generateOutput(ast, i, format, file) {
|
|
30
31
|
const node = Array.isArray(ast) ? ast[i] : ast;
|
|
31
32
|
let result = "";
|
|
33
|
+
let context = "";
|
|
32
34
|
let target = matchedValue(file.outputs, node.id);
|
|
33
35
|
if (target) {
|
|
34
36
|
result +=
|
|
35
|
-
format ===
|
|
37
|
+
format === htmlFormat || format === mdxFormat
|
|
36
38
|
? (node.depth > 1 ? " ".repeat(node.depth) : "") +
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
target.render({ args: node.args, content: "\n<%smark>" + (node.depth > 1 ? " ".repeat(node.depth) : "") }) +
|
|
40
|
+
"\n"
|
|
39
41
|
: target.render({ args: node.args, content: "" });
|
|
40
|
-
let context = "";
|
|
41
42
|
for (const body_node of node.body) {
|
|
42
43
|
switch (body_node.type) {
|
|
43
44
|
case TEXT:
|
|
@@ -48,11 +49,7 @@ function generateOutput(ast, i, format, file) {
|
|
|
48
49
|
body_node.text = body_node.text.slice(1, body_node.text.length - 1);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
context += " ".repeat(body_node.depth) + `<p>${body_node.text}</p>`;
|
|
53
|
-
} else {
|
|
54
|
-
context += body_node.text;
|
|
55
|
-
}
|
|
52
|
+
context += (format === htmlFormat || format === mdxFormat) ? escapeHTML(body_node.text) : body_node.text;
|
|
56
53
|
break;
|
|
57
54
|
case INLINE:
|
|
58
55
|
target = matchedValue(file.outputs, body_node.id);
|
|
@@ -64,15 +61,15 @@ function generateOutput(ast, i, format, file) {
|
|
|
64
61
|
metadata.push(body_node.data);
|
|
65
62
|
}
|
|
66
63
|
if (body_node.hasOwnProperty("title")) {
|
|
67
|
-
if (format ===
|
|
64
|
+
if (format === htmlFormat) body_node.title = body_node.title.replaceAll('"', "");
|
|
68
65
|
metadata.push(body_node.title);
|
|
69
66
|
}
|
|
70
67
|
break;
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
context +=
|
|
74
|
-
(format ===
|
|
75
|
-
target.render({ args: metadata.length > 0 ? metadata : "", content: body_node.value });
|
|
71
|
+
(format === htmlFormat || format === mdxFormat ? "\n" : "") +
|
|
72
|
+
target.render({ args: metadata.length > 0 ? metadata : "", content: (format === htmlFormat || format === mdxFormat) ? escapeHTML(body_node.value) : body_node.value });
|
|
76
73
|
}
|
|
77
74
|
break;
|
|
78
75
|
case NEWLINE:
|
|
@@ -81,17 +78,24 @@ function generateOutput(ast, i, format, file) {
|
|
|
81
78
|
case ATBLOCK:
|
|
82
79
|
target = matchedValue(file.outputs, body_node.id);
|
|
83
80
|
if (target) {
|
|
81
|
+
const shouldEscape = target.options?.escape ?? true;
|
|
84
82
|
let content = "";
|
|
85
83
|
for (let v = 0; v < body_node.content.length; v++) {
|
|
86
84
|
let value = body_node.content[v];
|
|
85
|
+
if (shouldEscape) {
|
|
86
|
+
value = escapeHTML(value);
|
|
87
|
+
}
|
|
87
88
|
content += v === 0 ? value : "\n" + value;
|
|
88
89
|
}
|
|
89
90
|
context += target.render({ args: body_node.args, content });
|
|
90
91
|
}
|
|
91
92
|
break;
|
|
92
93
|
case COMMENT:
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
if (format === htmlFormat || format === markdownFormat) {
|
|
95
|
+
context += " ".repeat(body_node.depth) + `<!--${body_node.text.replace("#", "")}-->`;
|
|
96
|
+
} else if (format === mdxFormat) {
|
|
97
|
+
context += " ".repeat(body_node.depth) + `{/*${body_node.text.replace("#", "")} */}`;
|
|
98
|
+
}
|
|
95
99
|
break;
|
|
96
100
|
case BLOCK:
|
|
97
101
|
target = matchedValue(file.outputs, body_node.id);
|
|
@@ -99,7 +103,7 @@ function generateOutput(ast, i, format, file) {
|
|
|
99
103
|
break;
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
|
-
if (format ===
|
|
106
|
+
if (format === htmlFormat || format === mdxFormat) {
|
|
103
107
|
result = result.replace("<%smark>", context);
|
|
104
108
|
} else {
|
|
105
109
|
result += context;
|
|
@@ -113,17 +117,28 @@ function generateOutput(ast, i, format, file) {
|
|
|
113
117
|
return result;
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
function transpiler({ast, format, mapperFile, includeDocument = true }) {
|
|
120
|
+
function transpiler({ ast, format, mapperFile, includeDocument = true }) {
|
|
117
121
|
let output = "";
|
|
118
122
|
for (let i = 0; i < ast.length; i++) {
|
|
119
123
|
if (ast[i].type === BLOCK) {
|
|
120
124
|
output += generateOutput(ast, i, format, mapperFile);
|
|
121
125
|
} else if (ast[i].type === COMMENT) {
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
if (format === htmlFormat || format === markdownFormat) {
|
|
127
|
+
output += `<!--${ast[i].text.replace("#", "")}-->\n`;
|
|
128
|
+
} else if (format === mdxFormat) {
|
|
129
|
+
output += `{/*${ast[i].text.replace("#", "")} */}\n`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (format === htmlFormat && (output.includes("<code>") || output.includes("<pre>"))) {
|
|
134
|
+
if (mapperFile.enableLoadStyle) {
|
|
135
|
+
mapperFile.setHeader([mapperFile.loadStyle(mapperFile.env, mapperFile.selectedTheme)]);
|
|
136
|
+
}
|
|
137
|
+
if (mapperFile.enableLinkStyle) {
|
|
138
|
+
mapperFile.setHeader([mapperFile.cssTheme(mapperFile.selectedTheme)]);
|
|
124
139
|
}
|
|
125
140
|
}
|
|
126
|
-
if (includeDocument && format ===
|
|
141
|
+
if (includeDocument && format === htmlFormat) {
|
|
127
142
|
const document = "<!DOCTYPE html>\n" + "<html>\n" + mapperFile.header + "<body>\n" + output + "</body>\n" + "</html>\n";
|
|
128
143
|
return document;
|
|
129
144
|
}
|
package/formatter/tag.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import escapeHTML from "../helpers/escapeHTML.js";
|
|
1
2
|
class TagBuilder {
|
|
2
3
|
#children;
|
|
3
4
|
#attr;
|
|
@@ -11,7 +12,7 @@ class TagBuilder {
|
|
|
11
12
|
attributes(obj, ...arr) {
|
|
12
13
|
if (obj && obj instanceof Object) {
|
|
13
14
|
Object.entries(obj).forEach(([key, value]) => {
|
|
14
|
-
this.#attr.push(`${key}="${
|
|
15
|
+
this.#attr.push(`${key}="${escapeHTML(value ?? "")}"`);
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
18
|
if (arr && Array.isArray(arr)) {
|
|
@@ -32,10 +33,10 @@ class TagBuilder {
|
|
|
32
33
|
if (prop && type) {
|
|
33
34
|
switch (type) {
|
|
34
35
|
case "string":
|
|
35
|
-
this.#attr.push(`${key2}="${value}"`);
|
|
36
|
+
this.#attr.push(`${key2}="${escapeHTML(value)}"`);
|
|
36
37
|
break;
|
|
37
38
|
case "other":
|
|
38
|
-
this.#attr.push(`${key2}={${value}}`);
|
|
39
|
+
this.#attr.push(`${key2}={${value}}`); // React/MDX props might need different handling, but usually raw JS expression?
|
|
39
40
|
break;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export async function loadStyle(env = "node", theme = 'atom-one-dark.min', targetPath = "./node_modules/highlight.js/styles/") {
|
|
2
|
+
try {
|
|
3
|
+
if (env === "node" || env === "browser") {
|
|
4
|
+
if (env === "node") {
|
|
5
|
+
const fs = await import("fs/promises");
|
|
6
|
+
const path = await import("path");
|
|
7
|
+
return await fs.readFile(path.join(process.cwd(), targetPath, theme + ".css"), "utf-8");
|
|
8
|
+
} else if (env === "browser") {
|
|
9
|
+
const url = new URL("../" + targetPath + theme + ".css", import.meta.url);
|
|
10
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
11
|
+
const fs = await import("fs/promises");
|
|
12
|
+
return await fs.readFile(url, "utf-8");
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(url);
|
|
15
|
+
return await response.text();
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
throw new Error("Invalid environment");
|
|
19
|
+
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error("Error loading style:", error);
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export default loadStyle;
|
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import mdx from "./mappers/default_mode/smark.mdx.js";
|
|
|
8
8
|
import TagBuilder from "./formatter/tag.js";
|
|
9
9
|
import MarkdownBuilder from "./formatter/mark.js";
|
|
10
10
|
import { runtimeError } from "./core/validator.js";
|
|
11
|
+
import { loadStyle } from "./helpers/loadStyle.js";
|
|
11
12
|
|
|
12
13
|
class SomMark {
|
|
13
14
|
constructor({ src, format, mapperFile = null, mode = "default", includeDocument = true }) {
|
|
@@ -57,7 +58,7 @@ class SomMark {
|
|
|
57
58
|
const lex = src => lexer(src);
|
|
58
59
|
|
|
59
60
|
function parse(src) {
|
|
60
|
-
if(!src) {
|
|
61
|
+
if (!src) {
|
|
61
62
|
runtimeError([
|
|
62
63
|
`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for parsing.$>{line}`
|
|
63
64
|
]);
|
|
@@ -100,5 +101,5 @@ function transpile(options = {}) {
|
|
|
100
101
|
return transpiler({ ast, format, mapperFile, includeDocument });
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
export { html, markdown, mdx, Mapper, TagBuilder, MarkdownBuilder, lex, parse, transpile };
|
|
104
|
+
export { html, markdown, mdx, Mapper, TagBuilder, MarkdownBuilder, lex, parse, transpile, loadStyle };
|
|
104
105
|
export default SomMark;
|
package/lib/highlight.js
CHANGED
|
@@ -1,12 +1,183 @@
|
|
|
1
1
|
import hljs from "highlight.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function highlightCode(code, language = "plaintext") {
|
|
4
4
|
if (!hljs.getLanguage(language)) {
|
|
5
5
|
language = "plaintext";
|
|
6
6
|
}
|
|
7
7
|
return hljs.highlight(code, { language }).value;
|
|
8
8
|
}
|
|
9
|
+
const themes = [
|
|
10
|
+
"gradient-dark",
|
|
11
|
+
"qtcreator-light",
|
|
12
|
+
"sunburst",
|
|
13
|
+
"intellij-light",
|
|
14
|
+
"rose-pine-moon.min",
|
|
15
|
+
"default.min",
|
|
16
|
+
"stackoverflow-light",
|
|
17
|
+
"rose-pine-moon",
|
|
18
|
+
"stackoverflow-light.min",
|
|
19
|
+
"magula.min",
|
|
20
|
+
"qtcreator-light.min",
|
|
21
|
+
"srcery.min",
|
|
22
|
+
"nord",
|
|
23
|
+
"purebasic",
|
|
24
|
+
"atom-one-dark-reasonable.min",
|
|
25
|
+
"a11y-light",
|
|
26
|
+
"monokai-sublime.min",
|
|
27
|
+
"cybertopia-dimmer.min",
|
|
28
|
+
"arduino-light.min",
|
|
29
|
+
"routeros",
|
|
30
|
+
"shades-of-purple.min",
|
|
31
|
+
"lioshi",
|
|
32
|
+
"isbl-editor-dark",
|
|
33
|
+
"monokai-sublime",
|
|
34
|
+
"docco",
|
|
35
|
+
"xcode",
|
|
36
|
+
"lioshi.min",
|
|
37
|
+
"foundation",
|
|
38
|
+
"github-dark-dimmed",
|
|
39
|
+
"far.min",
|
|
40
|
+
"night-owl.min",
|
|
41
|
+
"routeros.min",
|
|
42
|
+
"atom-one-light",
|
|
43
|
+
"1c-light.min",
|
|
44
|
+
"kimbie-light",
|
|
45
|
+
"ascetic.min",
|
|
46
|
+
"tokyo-night-dark.min",
|
|
47
|
+
"nnfx-dark",
|
|
48
|
+
"school-book.min",
|
|
49
|
+
"agate",
|
|
50
|
+
"lightfair.min",
|
|
51
|
+
"mono-blue.min",
|
|
52
|
+
"an-old-hope.min",
|
|
53
|
+
"a11y-dark.min",
|
|
54
|
+
"panda-syntax-dark",
|
|
55
|
+
"mono-blue",
|
|
56
|
+
"obsidian.min",
|
|
57
|
+
"gradient-light",
|
|
58
|
+
"tomorrow-night-bright",
|
|
59
|
+
"qtcreator-dark",
|
|
60
|
+
"gml.min",
|
|
61
|
+
"grayscale.min",
|
|
62
|
+
"panda-syntax-light",
|
|
63
|
+
"atom-one-dark-reasonable",
|
|
64
|
+
"xt256", "monokai",
|
|
65
|
+
"sunburst.min",
|
|
66
|
+
"gradient-light.min",
|
|
67
|
+
"arta",
|
|
68
|
+
"felipec.min",
|
|
69
|
+
"srcery",
|
|
70
|
+
"stackoverflow-dark.min",
|
|
71
|
+
"codepen-embed.min",
|
|
72
|
+
"docco.min",
|
|
73
|
+
"shades-of-purple",
|
|
74
|
+
"github.min",
|
|
75
|
+
"cybertopia-icecap",
|
|
76
|
+
"felipec",
|
|
77
|
+
"paraiso-dark.min",
|
|
78
|
+
"arta.min",
|
|
79
|
+
"gml",
|
|
80
|
+
"vs2015.min",
|
|
81
|
+
"a11y-dark",
|
|
82
|
+
"github-dark-dimmed.min",
|
|
83
|
+
"tokyo-night-dark",
|
|
84
|
+
"vs2015",
|
|
85
|
+
"rose-pine-dawn",
|
|
86
|
+
"ir-black.min",
|
|
87
|
+
"googlecode.min",
|
|
88
|
+
"cybertopia-saturated.min",
|
|
89
|
+
"color-brewer.min",
|
|
90
|
+
"night-owl",
|
|
91
|
+
"rose-pine",
|
|
92
|
+
"tomorrow-night-bright.min",
|
|
93
|
+
"a11y-light.min",
|
|
94
|
+
"brown-paper.min",
|
|
95
|
+
"panda-syntax-dark.min",
|
|
96
|
+
"dark.min",
|
|
97
|
+
"school-book",
|
|
98
|
+
"obsidian",
|
|
99
|
+
"stackoverflow-dark",
|
|
100
|
+
"1c-light",
|
|
101
|
+
"nord.min",
|
|
102
|
+
"kimbie-dark",
|
|
103
|
+
"brown-paper",
|
|
104
|
+
"nnfx-light.min",
|
|
105
|
+
"rainbow",
|
|
106
|
+
"cybertopia-icecap.min",
|
|
107
|
+
"gradient-dark.min",
|
|
108
|
+
"isbl-editor-light",
|
|
109
|
+
"paraiso-light.min",
|
|
110
|
+
"cybertopia-cherry",
|
|
111
|
+
"foundation.min",
|
|
112
|
+
"purebasic.min",
|
|
113
|
+
"xt256.min",
|
|
114
|
+
"pojoaque.min",
|
|
115
|
+
"lightfair",
|
|
116
|
+
"nnfx-dark.min",
|
|
117
|
+
"isbl-editor-light.min",
|
|
118
|
+
"tomorrow-night-blue",
|
|
119
|
+
"rainbow.min",
|
|
120
|
+
"ir-black",
|
|
121
|
+
"androidstudio",
|
|
122
|
+
"github",
|
|
123
|
+
"googlecode",
|
|
124
|
+
"paraiso-dark",
|
|
125
|
+
"codepen-embed",
|
|
126
|
+
"vs",
|
|
127
|
+
"cybertopia-dimmer",
|
|
128
|
+
"devibeans",
|
|
129
|
+
"tokyo-night-light",
|
|
130
|
+
"isbl-editor-dark.min",
|
|
131
|
+
"idea",
|
|
132
|
+
"github-dark",
|
|
133
|
+
"hybrid.min",
|
|
134
|
+
"idea.min",
|
|
135
|
+
"dark",
|
|
136
|
+
"atom-one-light.min",
|
|
137
|
+
"an-old-hope",
|
|
138
|
+
"atom-one-dark.min",
|
|
139
|
+
"agate.min",
|
|
140
|
+
"github-dark.min",
|
|
141
|
+
"qtcreator-dark.min",
|
|
142
|
+
"devibeans.min",
|
|
143
|
+
"magula",
|
|
144
|
+
"rose-pine-dawn.min",
|
|
145
|
+
"ascetic",
|
|
146
|
+
"vs.min",
|
|
147
|
+
"far",
|
|
148
|
+
"rose-pine.min",
|
|
149
|
+
"hybrid",
|
|
150
|
+
"nnfx-light",
|
|
151
|
+
"atom-one-dark",
|
|
152
|
+
"paraiso-light",
|
|
153
|
+
"intellij-light.min",
|
|
154
|
+
"grayscale",
|
|
155
|
+
"xcode.min",
|
|
156
|
+
"arduino-light",
|
|
157
|
+
"panda-syntax-light.min",
|
|
158
|
+
"tomorrow-night-blue.min",
|
|
159
|
+
"tokyo-night-light.min",
|
|
160
|
+
"kimbie-light.min",
|
|
161
|
+
"cybertopia-cherry.min",
|
|
162
|
+
"androidstudio.min",
|
|
163
|
+
"default",
|
|
164
|
+
"color-brewer",
|
|
165
|
+
"monokai.min",
|
|
166
|
+
"pojoaque",
|
|
167
|
+
"kimbie-dark.min",
|
|
168
|
+
"cybertopia-saturated"];
|
|
9
169
|
|
|
10
|
-
|
|
170
|
+
let selectedTheme = "atom-one-dark.min";
|
|
171
|
+
function cssTheme(theme = selectedTheme) {
|
|
172
|
+
if (!themes.includes(theme)) {
|
|
173
|
+
theme = "atom-one-dark.min";
|
|
174
|
+
}
|
|
11
175
|
return `<link rel="stylesheet" href="node_modules/highlight.js/styles/${theme}.css">`;
|
|
12
176
|
}
|
|
177
|
+
|
|
178
|
+
export {
|
|
179
|
+
highlightCode,
|
|
180
|
+
cssTheme,
|
|
181
|
+
selectedTheme,
|
|
182
|
+
themes,
|
|
183
|
+
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import Mapper from "../mapper.js";
|
|
2
2
|
const html = new Mapper();
|
|
3
|
-
const { tag,
|
|
3
|
+
const { tag, code, list } = html;
|
|
4
|
+
|
|
5
|
+
html.selectedTheme = "paraiso-dark";
|
|
6
|
+
html.env = "browser";
|
|
4
7
|
|
|
5
8
|
// Block
|
|
6
9
|
html.create("Block", ({ content }) => {
|
|
@@ -58,18 +61,18 @@ html.create("image", ({ args, content }) => {
|
|
|
58
61
|
// Code
|
|
59
62
|
html.create("code", ({ args, content }) => {
|
|
60
63
|
return code(args, content);
|
|
61
|
-
});
|
|
64
|
+
}, { escape: false });
|
|
62
65
|
// List
|
|
63
66
|
html.create(["list", "List"], ({ content }) => {
|
|
64
67
|
return list(content);
|
|
65
|
-
});
|
|
68
|
+
}, { escape: false });
|
|
66
69
|
// Table
|
|
67
70
|
html.create("table", ({ content, args }) => {
|
|
68
71
|
return html.htmlTable(content.split(/\n/), args);
|
|
69
|
-
});
|
|
72
|
+
}, { escape: false });
|
|
70
73
|
// Horizontal Rule
|
|
71
74
|
html.create("hr", () => {
|
|
72
75
|
return tag("hr").selfClose();
|
|
73
76
|
});
|
|
74
|
-
|
|
77
|
+
|
|
75
78
|
export default html;
|
|
@@ -7,8 +7,7 @@ markdown.create("Block", ({ content }) => {
|
|
|
7
7
|
});
|
|
8
8
|
// Headings
|
|
9
9
|
markdown.create("Heading", ({ args, content }) => {
|
|
10
|
-
|
|
11
|
-
return md.heading(args[1], args[0]);
|
|
10
|
+
return md.heading(args[1], args[0]) + content;
|
|
12
11
|
});
|
|
13
12
|
// Inline Headings
|
|
14
13
|
markdown.create("h1", ({ content }) => {
|
|
@@ -44,7 +43,7 @@ markdown.create(["emphasis", "e"], ({ content }) => {
|
|
|
44
43
|
// Code Blocks
|
|
45
44
|
markdown.create(["code", "Code", "codeBlock", "CodeBlock"], ({ args, content }) => {
|
|
46
45
|
return md.codeBlock(content, args[0]);
|
|
47
|
-
});
|
|
46
|
+
}, { escape: false });
|
|
48
47
|
// Link
|
|
49
48
|
markdown.create("link", ({ args, content }) => {
|
|
50
49
|
return md.url("link", content, args[0], args[1]);
|
|
@@ -64,10 +63,10 @@ markdown.create(["escape", "s"], ({ content }) => {
|
|
|
64
63
|
// Table
|
|
65
64
|
markdown.create("table", ({ args, content }) => {
|
|
66
65
|
return md.table(args, content.split("\n"));
|
|
67
|
-
});
|
|
66
|
+
}, { escape: false });
|
|
68
67
|
// List
|
|
69
68
|
markdown.create(["list", "List"], ({ content }) => {
|
|
70
69
|
return content;
|
|
71
|
-
});
|
|
70
|
+
}, { escape: false });
|
|
72
71
|
export default markdown;
|
|
73
72
|
|
package/mappers/mapper.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import TagBuilder from "../formatter/tag.js";
|
|
2
2
|
import MarkdownBuilder from "../formatter/mark.js";
|
|
3
|
-
import { highlightCode, cssTheme } from "../lib/highlight.js";
|
|
3
|
+
import { highlightCode, cssTheme, selectedTheme, themes } from "../lib/highlight.js";
|
|
4
4
|
import escapeHTML from "../helpers/escapeHTML.js";
|
|
5
|
+
import loadStyle from "../helpers/loadStyle.js";
|
|
5
6
|
class Mapper {
|
|
6
7
|
#predefinedHeaderData;
|
|
7
8
|
#customElements;
|
|
@@ -22,9 +23,15 @@ class Mapper {
|
|
|
22
23
|
this.#customElements = "";
|
|
23
24
|
this.highlightCode = highlightCode;
|
|
24
25
|
this.cssTheme = cssTheme;
|
|
26
|
+
this.selectedTheme = selectedTheme;
|
|
27
|
+
this.codeThemes = themes;
|
|
25
28
|
this.escapeHTML = escapeHTML;
|
|
29
|
+
this.enableLoadStyle = false;
|
|
30
|
+
this.enableLinkStyle = true;
|
|
31
|
+
this.loadStyle = loadStyle;
|
|
32
|
+
this.env = "node";
|
|
26
33
|
}
|
|
27
|
-
create(id, renderOutput) {
|
|
34
|
+
create(id, renderOutput, options = { escape: true }) {
|
|
28
35
|
if (!id || !renderOutput) {
|
|
29
36
|
throw new Error("Expected arguments are not defined");
|
|
30
37
|
}
|
|
@@ -48,7 +55,7 @@ class Mapper {
|
|
|
48
55
|
return renderOutput(data);
|
|
49
56
|
};
|
|
50
57
|
|
|
51
|
-
this.outputs.push({ id, render });
|
|
58
|
+
this.outputs.push({ id, render, options });
|
|
52
59
|
}
|
|
53
60
|
removeOutput(id) {
|
|
54
61
|
this.outputs = this.outputs.filter(output => {
|
|
@@ -188,5 +195,15 @@ class Mapper {
|
|
|
188
195
|
|
|
189
196
|
return renderItems(nodes);
|
|
190
197
|
};
|
|
198
|
+
includesId = id => {
|
|
199
|
+
if (Array.isArray(id)) {
|
|
200
|
+
return this.outputs.some(output => {
|
|
201
|
+
return id.some(singleId => singleId === output.id);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return this.outputs.some(output => {
|
|
205
|
+
return output.id === id;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
191
208
|
}
|
|
192
209
|
export default Mapper;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sommark",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "SomMark is a structural markup language for writing structured documents and converting them into HTML or Markdown or MDX(only ready components).",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"url": "git+https://github.com/Adam-Elmi/SomMark.git"
|
|
24
24
|
},
|
|
25
25
|
"keywords": [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
"language",
|
|
27
|
+
"markup",
|
|
28
|
+
"markup-lanuage",
|
|
29
|
+
"documentation",
|
|
30
|
+
"parser",
|
|
31
|
+
"transpiler",
|
|
32
|
+
"html",
|
|
33
|
+
"markdown",
|
|
34
|
+
"mdx",
|
|
35
|
+
"dsl",
|
|
36
|
+
"mapper",
|
|
37
|
+
"mapping"
|
|
38
|
+
],
|
|
39
39
|
"author": "Adam Elmi",
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"bugs": {
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"vitest": "^4.0.16"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|