sommark 3.3.4 → 4.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 +98 -82
- package/assets/logo.json +28 -0
- package/assets/smark.logo.png +0 -0
- package/assets/smark.logo.svg +21 -0
- package/cli/cli.mjs +7 -17
- package/cli/commands/build.js +26 -6
- package/cli/commands/color.js +22 -26
- package/cli/commands/help.js +10 -10
- package/cli/commands/init.js +20 -31
- package/cli/commands/print.js +18 -16
- package/cli/commands/show.js +4 -0
- package/cli/commands/version.js +6 -0
- package/cli/constants.js +9 -5
- package/cli/helpers/config.js +11 -0
- package/cli/helpers/file.js +17 -6
- package/cli/helpers/transpile.js +15 -17
- package/core/errors.js +49 -25
- package/core/formats.js +7 -3
- package/core/formatter.js +215 -0
- package/core/helpers/config-loader.js +40 -75
- package/core/labels.js +21 -9
- package/core/lexer.js +491 -212
- package/core/modules.js +164 -0
- package/core/parser.js +516 -389
- package/core/tokenTypes.js +36 -1
- package/core/transpiler.js +238 -154
- package/core/validator.js +79 -0
- package/formatter/mark.js +203 -43
- package/formatter/tag.js +202 -32
- package/grammar.ebnf +57 -50
- package/helpers/colorize.js +26 -13
- package/helpers/dedent.js +19 -0
- package/helpers/escapeHTML.js +13 -6
- package/helpers/kebabize.js +6 -0
- package/helpers/peek.js +9 -0
- package/helpers/removeChar.js +26 -13
- package/helpers/safeDataParser.js +114 -0
- package/helpers/utils.js +140 -158
- package/index.js +186 -188
- package/mappers/languages/html.js +105 -213
- package/mappers/languages/json.js +122 -171
- package/mappers/languages/markdown.js +355 -108
- package/mappers/languages/mdx.js +76 -120
- package/mappers/languages/xml.js +114 -0
- package/mappers/mapper.js +152 -123
- package/mappers/shared/index.js +22 -0
- package/package.json +26 -6
- package/SOMMARK-SPEC.md +0 -481
- package/cli/commands/list.js +0 -124
- package/constants/html_tags.js +0 -146
- package/core/pluginManager.js +0 -149
- package/core/plugins/comment-remover.js +0 -47
- package/core/plugins/module-system.js +0 -176
- package/core/plugins/raw-content-plugin.js +0 -78
- package/core/plugins/rules-validation-plugin.js +0 -231
- package/core/plugins/sommark-format.js +0 -244
- package/coverage_test.js +0 -21
- package/debug.js +0 -15
- package/helpers/camelize.js +0 -2
- package/helpers/defaultTheme.js +0 -3
- package/test_format_fix.js +0 -42
- package/v3-todo.smark +0 -73
package/grammar.ebnf
CHANGED
|
@@ -1,79 +1,86 @@
|
|
|
1
|
-
(* SomMark EBNF Grammar
|
|
1
|
+
(* SomMark V4 EBNF Grammar Specification *)
|
|
2
|
+
|
|
3
|
+
(* ========================================== *)
|
|
4
|
+
(* Basic Lexical Atoms *)
|
|
5
|
+
(* ========================================== *)
|
|
2
6
|
|
|
3
|
-
(* Basic Tokens *)
|
|
4
7
|
WhiteSpace = " " | "\t" | "\n" | "\r";
|
|
5
8
|
Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
6
9
|
Letter = "A" | ... | "Z" | "a" | ... | "z";
|
|
7
|
-
SpecialChar = "[" | "]" | "(" | ")" | ":" | "-" | ">" | "@" | "_" | "=" | "," | ";";
|
|
8
|
-
Identifier = (Letter | Digit | "_" | "$" | "-"), { Letter | Digit | "_" | "$" | "-" };
|
|
9
|
-
EscapeChar = "\";
|
|
10
10
|
|
|
11
|
-
(*
|
|
12
|
-
(
|
|
13
|
-
|
|
14
|
-
(*
|
|
11
|
+
(* Standard Identifiers (A-Z, 0-9, _, -, $) *)
|
|
12
|
+
SimpleID = (Letter | Digit | "_" | "$" | "-"), { Letter | Digit | "_" | "$" | "-" };
|
|
13
|
+
|
|
14
|
+
(* Block Identifiers (Adds support for Colons :) *)
|
|
15
|
+
BlockID = (Letter | Digit | "_" | "$" | "-" | ":"), { Letter | Digit | "_" | "$" | "-" | ":" };
|
|
16
|
+
|
|
17
|
+
EscapeChar = "\", ? any character except WhiteSpace ?;
|
|
18
|
+
|
|
19
|
+
(* "Junk" refers to whitespace and comments which the parser skips in headers *)
|
|
20
|
+
Comment = "#", { ? any character except "\n" ? }, "\n";
|
|
21
|
+
Junk = { WhiteSpace | Comment };
|
|
15
22
|
|
|
16
|
-
(*
|
|
17
|
-
|
|
23
|
+
(* Prefix Layers for Dynamic Data *)
|
|
24
|
+
PrefixJS = "js{", { ? any character ? }, "}";
|
|
25
|
+
PrefixP = "p{", { ? any character ? }, "}";
|
|
26
|
+
PrefixLayer = PrefixJS | PrefixP;
|
|
18
27
|
|
|
19
28
|
(* ========================================== *)
|
|
20
|
-
(* Block Syntax
|
|
21
|
-
(* Acts as container and allows nesting *)
|
|
29
|
+
(* Block Syntax (Containers) *)
|
|
22
30
|
(* ========================================== *)
|
|
23
31
|
|
|
24
|
-
Block =
|
|
25
|
-
BlockStart = "[", Identifier, [ "=", BlockArgs ], "]";
|
|
26
|
-
BlockEnd = "[", "end", "]";
|
|
27
|
-
BlockBody = { Block | InlineStatement | AtBlock | TextContent };
|
|
32
|
+
Block = BlockOpen, BlockBody, BlockEnd;
|
|
28
33
|
|
|
29
|
-
(*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
(* Value accepts anything except restricted chars unless escaped *)
|
|
34
|
-
Value = { ? any char except ",", "]" ? | EscapedChar };
|
|
35
|
-
EscapedChar = EscapeChar, ? any character ?;
|
|
34
|
+
(* Flexible Header with Junk-Skipping. Blocks allow colons in ID. *)
|
|
35
|
+
BlockOpen = "[", Junk, BlockID, Junk, [ "=", Junk, BlockArgs, Junk ], "]";
|
|
36
|
+
BlockEnd = "[", Junk, "end", Junk, "]";
|
|
37
|
+
BlockBody = { Block | InlineStatement | AtBlock | TextContent | Comment | WhiteSpace };
|
|
36
38
|
|
|
37
|
-
(*
|
|
38
|
-
|
|
39
|
+
(* Block Metadata (Arguments). Block keys allow colons. *)
|
|
40
|
+
BlockArgs = BlockArg, { Junk, ",", Junk, BlockArg };
|
|
41
|
+
BlockArg = [ BlockKey, Junk, ":", Junk ], Value;
|
|
39
42
|
|
|
43
|
+
BlockKey = BlockID | QuotedString;
|
|
44
|
+
Value = QuotedString | PrefixLayer | RawValue;
|
|
45
|
+
|
|
46
|
+
QuotedString = ("'" | '"'), { ? any character ? | EscapeChar }, ("'" | '"');
|
|
47
|
+
RawValue = { ? any char except ",", Junk, "]" ? | EscapeChar };
|
|
40
48
|
|
|
41
49
|
(* ========================================== *)
|
|
42
50
|
(* Inline Statement Syntax *)
|
|
43
|
-
(* Does not allow nesting *)
|
|
44
51
|
(* ========================================== *)
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
InlineIdentifier= Identifier;
|
|
49
|
-
|
|
50
|
-
(* Inline Arguments: Value only, NO Key-Value support *)
|
|
51
|
-
InlineArgs = InlineArg, { ",", InlineArg };
|
|
52
|
-
InlineArg = { ? any char except ",", ")" ? | EscapedChar };
|
|
53
|
+
(* Inlines use SimpleID (NO COLONS allowed in ID) *)
|
|
54
|
+
InlineStatement = "(", InlineContent, ")", Junk, "->", Junk, "(", SimpleID, [ Junk, ":", Junk, InlineArgs ], ")";
|
|
53
55
|
|
|
54
|
-
(*
|
|
55
|
-
|
|
56
|
+
(* Content allows balanced parentheses and placeholders *)
|
|
57
|
+
InlineContent = { ? any char except ")" ? | BalancedParen | EscapeChar | PrefixP };
|
|
58
|
+
BalancedParen = "(", InlineContent, ")";
|
|
56
59
|
|
|
60
|
+
(* Inline Arguments: Positional Only *)
|
|
61
|
+
InlineArgs = Value, { Junk, ",", Junk, Value };
|
|
57
62
|
|
|
58
63
|
(* ========================================== *)
|
|
59
|
-
(*
|
|
60
|
-
(* Content is plain text, no nesting *)
|
|
64
|
+
(* At-Block Syntax (Raw Containers) *)
|
|
61
65
|
(* ========================================== *)
|
|
62
66
|
|
|
63
|
-
AtBlock =
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
AtBlock = AtHeader, AtBody, AtEnd;
|
|
68
|
+
|
|
69
|
+
(* At-Blocks use SimpleID (NO COLONS allowed in ID or Metadata Keys) *)
|
|
70
|
+
AtHeader = "@_", Junk, SimpleID, Junk, "_@", Junk, [ ":", Junk, AtBlockArgs, Junk ], ";";
|
|
71
|
+
AtEnd = "@_", Junk, "end", Junk, "_@";
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
AtBlockValue = { ? any char except ",", ";" ? | EscapedChar };
|
|
73
|
+
AtBlockArgs = AtBlockArg, { Junk, ",", Junk, AtBlockArg };
|
|
74
|
+
AtBlockArg = [ SimpleID, Junk, ":", Junk ], (QuotedString | RawAtValue);
|
|
75
|
+
RawAtValue = { ? any char except ",", ";" ? | EscapeChar };
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
AtBody = { ? any character ? | EscapeChar };
|
|
73
78
|
|
|
74
|
-
(*
|
|
75
|
-
(*
|
|
79
|
+
(* ========================================== *)
|
|
80
|
+
(* Document Body *)
|
|
81
|
+
(* ========================================== *)
|
|
76
82
|
|
|
83
|
+
Document = { Block | InlineStatement | AtBlock | TextContent | Comment | WhiteSpace };
|
|
77
84
|
|
|
78
|
-
(* Text Content *)
|
|
79
|
-
TextContent = { ? any char except "[", "@_", "(", "#" ? |
|
|
85
|
+
(* Text Content: Plain text with fallback behavior for symbols *)
|
|
86
|
+
TextContent = { ? any char except "[", "@_", "(", "#" ? | EscapeChar | PrefixP };
|
package/helpers/colorize.js
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
const colors = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
red: "\x1b[31m",
|
|
3
|
+
green: "\x1b[32m",
|
|
4
|
+
yellow: "\x1b[33m",
|
|
5
|
+
blue: "\x1b[34m",
|
|
6
|
+
magenta: "\x1b[35m",
|
|
7
|
+
cyan: "\x1b[36m",
|
|
8
|
+
reset: "\x1b[0m"
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
/** @type {boolean} If true, the CLI will show colors. */
|
|
11
12
|
export let useColor = false;
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Turns colors on or off globally.
|
|
16
|
+
* @param {boolean} [enabled=true] - Set to true to see colors, false to hide them.
|
|
17
|
+
*/
|
|
13
18
|
export function enableColor(enabled = true) {
|
|
14
|
-
|
|
19
|
+
useColor = enabled;
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Wraps your text in a color if colors are turned on.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} color - The color to use (red, green, yellow, blue, magenta, or cyan).
|
|
26
|
+
* @param {string} text - The text you want to color.
|
|
27
|
+
* @returns {string} - The colored text, or plain text if colors are off.
|
|
28
|
+
* @throws {Error} - Fails if you forget to provide the text.
|
|
29
|
+
*/
|
|
17
30
|
export default function colorize(color, text) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
if (!text) throw new Error("argument 'text' is not defined.");
|
|
32
|
+
if (useColor && color && colors[color]) {
|
|
33
|
+
return colors[color] + text + colors["reset"];
|
|
34
|
+
}
|
|
35
|
+
return text;
|
|
23
36
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedents a string by a given amount of characters.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} str - The string to dedent.
|
|
5
|
+
* @param {number} amount - The number of characters to remove from the start of each line.
|
|
6
|
+
* @returns {string} - The dedented string.
|
|
7
|
+
*/
|
|
8
|
+
export function dedentBy(str, amount) {
|
|
9
|
+
if (!str || amount <= 0) return str;
|
|
10
|
+
const lines = str.split("\n");
|
|
11
|
+
const dedentedLines = lines.map((line) => {
|
|
12
|
+
let count = 0;
|
|
13
|
+
while (count < amount && (line[count] === " " || line[count] === "\t")) {
|
|
14
|
+
count++;
|
|
15
|
+
}
|
|
16
|
+
return line.slice(count);
|
|
17
|
+
});
|
|
18
|
+
return dedentedLines.join("\n");
|
|
19
|
+
}
|
package/helpers/escapeHTML.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes a string safe to display in HTML by replacing special characters.
|
|
3
|
+
* This helps prevent security issues and formatting errors.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} str - The raw string to escape.
|
|
6
|
+
* @returns {string} - The HTML-safe string.
|
|
7
|
+
*/
|
|
1
8
|
export default function escapeHTML(str) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
return String(str)
|
|
10
|
+
.replace(/&/g, "&")
|
|
11
|
+
.replace(/</g, "<")
|
|
12
|
+
.replace(/>/g, ">")
|
|
13
|
+
.replace(/"/g, """)
|
|
14
|
+
.replace(/'/g, "'");
|
|
8
15
|
}
|
package/helpers/kebabize.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Changes CamelCase or PascalCase names into kebab-case.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} str - The string to convert.
|
|
5
|
+
* @returns {string} - The kebab-cased string.
|
|
6
|
+
*/
|
|
1
7
|
const kebabize = str => str.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
|
|
2
8
|
export default kebabize;
|
package/helpers/peek.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Looks at an item in a list or string without moving your current position.
|
|
3
|
+
* You can look ahead or behind by using a positive or negative offset.
|
|
4
|
+
*
|
|
5
|
+
* @param {Array|string} input - The list or string to check.
|
|
6
|
+
* @param {number} index - Your current spot in the list.
|
|
7
|
+
* @param {number} offset - How many spots to look ahead or behind.
|
|
8
|
+
* @returns {any|null} - The item you found, or null if it is out of range.
|
|
9
|
+
*/
|
|
1
10
|
function peek(input, index, offset) {
|
|
2
11
|
if (input === null || index < 0 || offset < -index) {
|
|
3
12
|
return null;
|
package/helpers/removeChar.js
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes or collapses whitespace in a string based on the specified mode.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} text - The source string.
|
|
5
|
+
* @param {'all'|'trim'|'collapse'} [mode="all"] - The whitespace removal strategy.
|
|
6
|
+
* @returns {string} - The processed string.
|
|
7
|
+
*/
|
|
1
8
|
function removeWhiteSpaces(text, mode = "all") {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
if (text == null) return "";
|
|
10
|
+
text = String(text);
|
|
11
|
+
switch (mode) {
|
|
12
|
+
case "trim":
|
|
13
|
+
return text.trim();
|
|
14
|
+
case "collapse":
|
|
15
|
+
return text.replace(/\s+/g, " ").trim();
|
|
16
|
+
case "all":
|
|
17
|
+
default:
|
|
18
|
+
return text.replace(/\s+/g, "");
|
|
19
|
+
}
|
|
13
20
|
}
|
|
14
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Removes all newline characters (\r, \n) from a string.
|
|
24
|
+
* @param {string} text - The source string.
|
|
25
|
+
* @returns {string} - The processed string without newlines.
|
|
26
|
+
*/
|
|
15
27
|
function removeNewline(text) {
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
if (text == null) return "";
|
|
29
|
+
return String(text).replace(/[\r\n]+/g, "");
|
|
18
30
|
}
|
|
31
|
+
|
|
19
32
|
export { removeWhiteSpaces, removeNewline };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe parser that turns Javascript-like strings into real objects and arrays.
|
|
3
|
+
* It is built to handle data structures without running any dangerous code or
|
|
4
|
+
* accessing other parts of your project.
|
|
5
|
+
*
|
|
6
|
+
* It supports:
|
|
7
|
+
* - Standard JSON: {"key": "val"}
|
|
8
|
+
* - Javascript-style: { key: 'val' }
|
|
9
|
+
* - Basic data: true, false, null, numbers, and strings
|
|
10
|
+
*/
|
|
11
|
+
export function safeDataParse(str) {
|
|
12
|
+
if (typeof str !== "string") return str;
|
|
13
|
+
const s = str.trim();
|
|
14
|
+
if (!s) return null;
|
|
15
|
+
|
|
16
|
+
let index = 0;
|
|
17
|
+
|
|
18
|
+
function skipWhitespace() {
|
|
19
|
+
while (index < s.length && /\s/.test(s[index])) {
|
|
20
|
+
index++;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseValue() {
|
|
25
|
+
skipWhitespace();
|
|
26
|
+
const char = s[index];
|
|
27
|
+
|
|
28
|
+
if (char === '{') return parseObject();
|
|
29
|
+
if (char === '[') return parseArray();
|
|
30
|
+
if (char === '"' || char === "'") return parseString();
|
|
31
|
+
|
|
32
|
+
// Primitives or Unquoted identifiers
|
|
33
|
+
return parsePrimitiveOrIdentifier();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseString() {
|
|
37
|
+
const quote = s[index++];
|
|
38
|
+
let result = "";
|
|
39
|
+
while (index < s.length && s[index] !== quote) {
|
|
40
|
+
if (s[index] === '\\') index++; // Skip escape
|
|
41
|
+
result += s[index++];
|
|
42
|
+
}
|
|
43
|
+
index++; // Skip closing quote
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseObject() {
|
|
48
|
+
index++; // Skip {
|
|
49
|
+
const obj = {};
|
|
50
|
+
skipWhitespace();
|
|
51
|
+
|
|
52
|
+
while (index < s.length && s[index] !== '}') {
|
|
53
|
+
skipWhitespace();
|
|
54
|
+
// Key can be unquoted, quoted "key", or quoted 'key'
|
|
55
|
+
let key;
|
|
56
|
+
if (s[index] === '"' || s[index] === "'") {
|
|
57
|
+
key = parseString();
|
|
58
|
+
} else {
|
|
59
|
+
let keyMatch = s.slice(index).match(/^[a-zA-Z_$][a-zA-Z0-9_$]*/);
|
|
60
|
+
if (!keyMatch) break;
|
|
61
|
+
key = keyMatch[0];
|
|
62
|
+
index += key.length;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
skipWhitespace();
|
|
66
|
+
if (s[index] !== ':') break;
|
|
67
|
+
index++; // Skip :
|
|
68
|
+
|
|
69
|
+
obj[key] = parseValue();
|
|
70
|
+
|
|
71
|
+
skipWhitespace();
|
|
72
|
+
if (s[index] === ',') index++; // Skip optional comma
|
|
73
|
+
skipWhitespace();
|
|
74
|
+
}
|
|
75
|
+
index++; // Skip }
|
|
76
|
+
return obj;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function parseArray() {
|
|
80
|
+
index++; // Skip [
|
|
81
|
+
const arr = [];
|
|
82
|
+
skipWhitespace();
|
|
83
|
+
|
|
84
|
+
while (index < s.length && s[index] !== ']') {
|
|
85
|
+
arr.push(parseValue());
|
|
86
|
+
skipWhitespace();
|
|
87
|
+
if (s[index] === ',') index++; // Skip optional comma
|
|
88
|
+
skipWhitespace();
|
|
89
|
+
}
|
|
90
|
+
index++; // Skip ]
|
|
91
|
+
return arr;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parsePrimitiveOrIdentifier() {
|
|
95
|
+
const start = index;
|
|
96
|
+
while (index < s.length && /[a-zA-Z0-9_$+\-.]/.test(s[index])) {
|
|
97
|
+
index++;
|
|
98
|
+
}
|
|
99
|
+
const token = s.slice(start, index);
|
|
100
|
+
|
|
101
|
+
if (token === "true") return true;
|
|
102
|
+
if (token === "false") return false;
|
|
103
|
+
if (token === "null") return null;
|
|
104
|
+
if (!isNaN(Number(token))) return Number(token);
|
|
105
|
+
|
|
106
|
+
return token; // Fallback to string if it looks like an identifier
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
return parseValue();
|
|
111
|
+
} catch (e) {
|
|
112
|
+
return str; // Fallback to raw string if parsing fails
|
|
113
|
+
}
|
|
114
|
+
}
|