sommark 3.3.3 → 4.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.
Files changed (61) hide show
  1. package/README.md +98 -82
  2. package/assets/logo.json +28 -0
  3. package/assets/smark.logo.png +0 -0
  4. package/assets/smark.logo.svg +21 -0
  5. package/cli/cli.mjs +8 -16
  6. package/cli/commands/build.js +24 -4
  7. package/cli/commands/color.js +22 -26
  8. package/cli/commands/help.js +10 -10
  9. package/cli/commands/init.js +19 -42
  10. package/cli/commands/print.js +20 -12
  11. package/cli/commands/show.js +4 -0
  12. package/cli/commands/version.js +6 -0
  13. package/cli/constants.js +9 -5
  14. package/cli/helpers/config.js +11 -0
  15. package/cli/helpers/file.js +17 -6
  16. package/cli/helpers/transpile.js +7 -8
  17. package/core/errors.js +49 -25
  18. package/core/formats.js +7 -3
  19. package/core/formatter.js +215 -0
  20. package/core/helpers/config-loader.js +37 -56
  21. package/core/labels.js +21 -9
  22. package/core/lexer.js +491 -212
  23. package/core/modules.js +164 -0
  24. package/core/parser.js +516 -389
  25. package/core/tokenTypes.js +36 -1
  26. package/core/transpiler.js +237 -151
  27. package/core/validator.js +79 -0
  28. package/formatter/mark.js +203 -43
  29. package/formatter/tag.js +202 -32
  30. package/grammar.ebnf +57 -50
  31. package/helpers/colorize.js +26 -13
  32. package/helpers/escapeHTML.js +13 -6
  33. package/helpers/kebabize.js +6 -0
  34. package/helpers/peek.js +9 -0
  35. package/helpers/removeChar.js +26 -13
  36. package/helpers/safeDataParser.js +114 -0
  37. package/helpers/utils.js +140 -158
  38. package/index.js +198 -188
  39. package/mappers/languages/html.js +105 -213
  40. package/mappers/languages/json.js +122 -171
  41. package/mappers/languages/markdown.js +355 -108
  42. package/mappers/languages/mdx.js +76 -114
  43. package/mappers/languages/xml.js +114 -0
  44. package/mappers/mapper.js +152 -123
  45. package/mappers/shared/index.js +22 -0
  46. package/package.json +26 -6
  47. package/SOMMARK-SPEC.md +0 -481
  48. package/cli/commands/list.js +0 -124
  49. package/constants/html_tags.js +0 -146
  50. package/core/pluginManager.js +0 -149
  51. package/core/plugins/comment-remover.js +0 -47
  52. package/core/plugins/module-system.js +0 -176
  53. package/core/plugins/raw-content-plugin.js +0 -78
  54. package/core/plugins/rules-validation-plugin.js +0 -231
  55. package/core/plugins/sommark-format.js +0 -244
  56. package/coverage_test.js +0 -21
  57. package/debug.js +0 -15
  58. package/helpers/camelize.js +0 -2
  59. package/helpers/defaultTheme.js +0 -3
  60. package/test_format_fix.js +0 -42
  61. package/v3-todo.smark +0 -73
@@ -1,231 +0,0 @@
1
- import { transpilerError } from "../errors.js";
2
-
3
- /**
4
- * Rules Validation Plugin
5
- * Validates rules defined in mapper files.
6
- * Supports rules for arguments (key, value) and content.
7
- */
8
- const RulesValidationPlugin = {
9
- name: "rules-validation",
10
- type: "on-ast",
11
- author: "Adam-Elmi",
12
- description: "Checks your document to make sure all tags and arguments follow the rules set in the mapper.",
13
- onAst(ast, { mapperFile, instance }) {
14
- if (!mapperFile) return ast;
15
-
16
- const validateNode = (node, parentTarget = null) => {
17
- if (!node) return;
18
-
19
- // ========================================================================== //
20
- // 1. TEXT nodes validation //
21
- // ========================================================================== //
22
- if (node.type === "Text" && parentTarget) {
23
- this.runValidations(node, parentTarget, null, node.text, "Text", mapperFile, instance);
24
- }
25
-
26
- // ========================================================================== //
27
- // 2. Identifier nodes validation (Block, Inline, AtBlock) //
28
- // ========================================================================== //
29
- if (node.id) {
30
- const target = mapperFile.get(node.id);
31
- if (target) {
32
- this.runValidations(node, target, node.args, this.getContent(node), node.type, mapperFile, instance);
33
- }
34
- }
35
-
36
- // ========================================================================== //
37
- // Recursive traversal //
38
- // ========================================================================== //
39
- if (node.body && Array.isArray(node.body)) {
40
- const currentTarget = node.id ? mapperFile.get(node.id) : parentTarget;
41
- node.body.forEach(child => validateNode(child, currentTarget));
42
- }
43
- };
44
-
45
- const root = Array.isArray(ast) ? ast : [ast];
46
- root.forEach(node => validateNode(node));
47
-
48
- return ast;
49
- },
50
-
51
- getContent(node) {
52
- if (node.type === "Inline") return node.value;
53
- if (node.type === "AtBlock") return node.content;
54
- return "";
55
- },
56
-
57
- runValidations(node, target, args, content, type, mapperFile, instance) {
58
- if (!target.options) return;
59
- const rules = target.options.rules || {};
60
- const id = Array.isArray(target.id) ? target.id.join(" | ") : target.id;
61
- const context = instance ? { src: instance.src, range: node.range, filename: instance.filename } : null;
62
-
63
- // ========================================================================== //
64
- // 1. Validate Args Count & Keys //
65
- // ========================================================================== //
66
- if (args && rules.args) {
67
- const { min, max, required, includes } = rules.args;
68
- const argKeys = Object.keys(args).filter(key => isNaN(parseInt(key)));
69
- const argCount = args.length;
70
-
71
- if (min !== undefined && argCount < min) {
72
- transpilerError(
73
- [
74
- "<$red:Validation Error:$> ",
75
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:requires at least$> <$green:${min}$> <$yellow:argument(s). Found$> <$red:${argCount}$>`
76
- ],
77
- context
78
- );
79
- }
80
- if (max !== undefined && argCount > max) {
81
- transpilerError(
82
- [
83
- "<$red:Validation Error:$> ",
84
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:accepts at most$> <$green:${max}$> <$yellow:argument(s). Found$> <$red:${argCount}$>`
85
- ],
86
- context
87
- );
88
- }
89
- if (required && Array.isArray(required)) {
90
- const missingKeys = required.filter(key => !Object.prototype.hasOwnProperty.call(args, key));
91
- if (missingKeys.length > 0) {
92
- transpilerError(
93
- [
94
- "<$red:Validation Error:$> ",
95
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:is missing required argument(s):$> <$red:${missingKeys.join(", ")}$>`
96
- ],
97
- context
98
- );
99
- }
100
- }
101
- if (includes && Array.isArray(includes)) {
102
- const invalidKeys = argKeys.filter(key => {
103
- return !includes.includes(key) && !mapperFile.extraProps.has(key);
104
- });
105
- if (invalidKeys.length > 0) {
106
- transpilerError(
107
- [
108
- "<$red:Validation Error:$> ",
109
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:contains invalid argument key(s):$> <$red:${invalidKeys.join(", ")}$>`,
110
- `{N}<$yellow:Allowed keys are:$> <$green:${includes.join(", ")}$>`
111
- ],
112
- context
113
- );
114
- }
115
- }
116
- }
117
-
118
- // ========================================================================== //
119
- // 2. Validation on Keys and Values //
120
- // ========================================================================== //
121
- if (args) {
122
- const argKeys = Object.keys(args).filter(key => isNaN(parseInt(key)));
123
-
124
- // Validate keys pattern
125
- if (rules.keys) {
126
- const keyPattern = rules.keys instanceof RegExp ? rules.keys : null;
127
- if (keyPattern) {
128
- const invalidKeys = argKeys.filter(key => !keyPattern.test(key));
129
- if (invalidKeys.length > 0) {
130
- transpilerError(
131
- [
132
- "<$red:Validation Error:$> ",
133
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:contains argument keys that do not match pattern $> <$green:${keyPattern.toString()}$>: <$red:${invalidKeys.join(", ")}$>`
134
- ],
135
- context
136
- );
137
- }
138
- }
139
- }
140
-
141
- // ========================================================================== //
142
- // Validate specific values //
143
- // ========================================================================== //
144
- if (rules.values) {
145
- for (const [key, value] of Object.entries(args)) {
146
- if (isNaN(parseInt(key))) {
147
- const valueRule = rules.values[key];
148
- if (valueRule) {
149
- if (valueRule instanceof RegExp && !valueRule.test(value)) {
150
- transpilerError(
151
- [
152
- "<$red:Validation Error:$> ",
153
- `<$yellow:Argument key$> <$blue:'${key}'$> <$yellow:in$> <$blue:'${id}'$> <$yellow:has invalid value:$> <$red:'${value}'$>{N}<$yellow:Expected to match pattern:$> <$green:${valueRule.toString()}$>`
154
- ],
155
- context
156
- );
157
- } else if (typeof valueRule === "function" && !valueRule(value)) {
158
- transpilerError(
159
- [
160
- "<$red:Validation Error:$> ",
161
- `<$yellow:Argument key$> <$blue:'${key}'$> <$yellow:in$> <$blue:'${id}'$> <$yellow:failed custom validation for value:$> <$red:'${value}'$>`
162
- ],
163
- context
164
- );
165
- }
166
- }
167
- }
168
- }
169
- }
170
- }
171
-
172
- // ========================================================================== //
173
- // 3. Content Validation //
174
- // ========================================================================== //
175
- if (content !== undefined && rules.content) {
176
- const { maxLength, match } = rules.content;
177
- if (maxLength && content.length > maxLength) {
178
- transpilerError(
179
- [
180
- "<$red:Validation Error:$> ",
181
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:content exceeds maximum length of$> <$green:${maxLength}$> <$yellow:characters. Found$> <$red:${content.length}$>`
182
- ],
183
- context
184
- );
185
- }
186
- if (match && match instanceof RegExp && !match.test(content)) {
187
- transpilerError(
188
- [
189
- "<$red:Validation Error:$> ",
190
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:content does not match required pattern:$> <$green:${match.toString()}$>`
191
- ],
192
- context
193
- );
194
- }
195
- }
196
-
197
- // ========================================================================== //
198
- // 4. self-closing //
199
- // ========================================================================== //
200
- if (rules.is_self_closing && (type === "Block" || content)) {
201
- if (content) {
202
- transpilerError(
203
- [
204
- "<$red:Validation Error:$> ",
205
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:is self-closing tag and is not allowed to have a content | children$>`
206
- ],
207
- context
208
- );
209
- }
210
- }
211
-
212
- // ========================================================================== //
213
- // 5. Type Validation (Block, Inline, AtBlock) //
214
- // ========================================================================== //
215
- const typeToValidate = target.options.type;
216
- if (typeToValidate && type !== "Text") {
217
- const allowedTypes = Array.isArray(typeToValidate) ? typeToValidate : [typeToValidate];
218
- if (!allowedTypes.includes("any") && !allowedTypes.includes(type)) {
219
- transpilerError(
220
- [
221
- "<$red:Validation Error:$> ",
222
- `<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:is expected to be type$> <$green:'${allowedTypes.join(" | ")}'$>{N}<$cyan:Received type: $> <$magenta:'${type}'$>`
223
- ],
224
- context
225
- );
226
- }
227
- }
228
- }
229
- };
230
-
231
- export default RulesValidationPlugin;
@@ -1,244 +0,0 @@
1
- /**
2
- * SomMark Format Plugin
3
- * Cleans up and formats your SomMark code into a neat and readable style.
4
- */
5
- export default {
6
- name: "sommark-format",
7
- type: "on-ast",
8
- author: "Adam-Elmi",
9
- description: "Cleans up and formats your SomMark code into a neat and readable style.",
10
- options: {
11
- indentString: "\t"
12
- },
13
- onAst(ast) {
14
- const indentStr = this.options?.indentString || "\t";
15
- // ========================================================================== //
16
- // 1. Escaping Helpers //
17
- // ========================================================================== //
18
-
19
- const escapeArg = (val, type) => {
20
- let escaped = String(val)
21
- .replace(/\\/g, "\\\\")
22
- .replace(/,/g, "\\,");
23
-
24
- if (type === "Block" || type === "AtBlock") {
25
- escaped = escaped.replace(/:/g, "\\:");
26
- }
27
- if (type === "AtBlock") {
28
- escaped = escaped.replace(/;/g, "\\;");
29
- }
30
- if (type === "Block" && escaped.startsWith("=")) {
31
- escaped = escaped.replace(/^=/, "\\=");
32
- }
33
- return escaped;
34
- };
35
-
36
- const escapeInlineValue = (val) => {
37
- return String(val)
38
- .replace(/\\/g, "\\\\")
39
- .replace(/\)/g, "\\)");
40
- };
41
-
42
- const escapeText = (str) => {
43
- return String(str)
44
- .replace(/\\/g, "\\\\")
45
- .replace(/\[/g, "\\[")
46
- .replace(/\]/g, "\\]")
47
- .replace(/\(/g, "\\(")
48
- .replace(/\)/g, "\\)")
49
- .replace(/->/g, "\\->")
50
- .replace(/@_/g, "\\@_")
51
- .replace(/_@/g, "\\_@")
52
- .replace(/#/g, "\\#");
53
- };
54
- // ========================================================================== //
55
- // 2. Formatting Logic //
56
- // ========================================================================== //
57
- const shouldQuote = (val) => {
58
- if (typeof val !== "string") return false;
59
- return /[ \t\n\r,:[\]()@#]/.test(val);
60
- };
61
-
62
- const formatArgs = (args, type) => {
63
- if (!args || args.length === 0) return "";
64
- let usedKeys = new Set();
65
- let formattedArgs = [];
66
-
67
- for (let i = 0; i < args.length; i++) {
68
- let val = args[i];
69
- let matchedKey = null;
70
- if (type !== "Inline") {
71
- for (let key of Object.keys(args)) {
72
- if (isNaN(parseInt(key)) && args[key] === val && !usedKeys.has(key)) {
73
- matchedKey = key;
74
- usedKeys.add(key);
75
- break;
76
- }
77
- }
78
- }
79
-
80
- let escapedVal = escapeArg(val, type);
81
- if (shouldQuote(val)) {
82
- const quotedVal = String(val)
83
- .replace(/\\/g, "\\\\")
84
- .replace(/"/g, '\\"');
85
- escapedVal = `"${quotedVal}"`;
86
- }
87
-
88
- if (matchedKey) {
89
- formattedArgs.push(`${matchedKey}: ${escapedVal}`);
90
- } else {
91
- formattedArgs.push(escapedVal);
92
- }
93
- }
94
-
95
- let res = formattedArgs.join(", ");
96
- if (!res) return "";
97
-
98
- if (type === "Block") return " = " + res;
99
- if (type === "AtBlock") return ": " + res + ";";
100
- if (type === "Inline") return ": " + res;
101
-
102
- return res;
103
- };
104
-
105
- const formatBody = (body, depth) => {
106
- if (!body || !Array.isArray(body)) return "";
107
- const innerIndentStr = depth >= 0 ? indentStr.repeat(depth) : "";
108
- let result = "";
109
- let currentText = "";
110
-
111
- const flushText = () => {
112
- if (!currentText) return;
113
- let cleanText = currentText
114
- .replace(/[ \t]+/g, " ") // collapse horizontal spaces
115
- .replace(/\n([ \t]*\n)+/g, "\n\n") // preserve max 1 empty line (paragraphs)
116
- .trim();
117
-
118
- if (cleanText) {
119
- const indentedText = cleanText.split('\n').map(line => {
120
- return line.trim() ? innerIndentStr + line.trim() : "";
121
- }).join('\n');
122
- result += `${indentedText}\n`;
123
- }
124
- currentText = "";
125
- };
126
-
127
- for (let i = 0; i < body.length; i++) {
128
- const child = body[i];
129
- if (child.type === "Text") {
130
- let textStr = escapeText(child.text);
131
-
132
- // ========================================================================== //
133
- // Separate Text from a preceding Inline statement //
134
- // ========================================================================== //
135
- if (i > 0 && body[i - 1].type === "Inline") {
136
- // Don't add space if the text starts with punctuation or already starts with whitespace
137
- if (textStr.length > 0 && !/^\s/.test(textStr) && !/^[.,!?;:\])}>"']/.test(textStr)) {
138
- textStr = " " + textStr;
139
- }
140
- }
141
- currentText += textStr;
142
- } else if (child.type === "Inline") {
143
- const argsStr = formatArgs(child.args, "Inline");
144
- const inlineVal = child.value ? String(child.value).trim() : "";
145
- const inlineStr = `(${escapeInlineValue(inlineVal)})->(${child.id}${argsStr})`;
146
-
147
- if (i > 0) {
148
- const prev = body[i - 1];
149
- if (prev.type === "Inline") {
150
- if (!/[ \t\n\r]$/.test(currentText)) currentText += " ";
151
- } else if (prev.type === "Text") {
152
- if (currentText.length > 0 && !/[ \t\n\r]$/.test(currentText)) {
153
- // Don't add space if text ends with opening punctuation/quotes
154
- if (!/[({\[<"']$/.test(currentText)) currentText += " ";
155
- }
156
- }
157
- }
158
- currentText += inlineStr;
159
- } else {
160
- // ========================================================================== //
161
- // Helper: check if a block has no meaningful body content //
162
- // ========================================================================== //
163
- const isEmptyBlock = (node) => {
164
- if (node.type !== "Block") return false;
165
- if (!node.body || node.body.length === 0) return true;
166
- // Body with only whitespace text nodes
167
- return node.body.every(
168
- n => n.type === "Text" && !n.text.trim()
169
- );
170
- };
171
-
172
- if (child.type === "Block" && isEmptyBlock(child)) {
173
- // Keep empty blocks inline with surrounding text
174
- const argsStr = formatArgs(child.args, "Block");
175
- const blockStr = `[${child.id}${argsStr}][end]`;
176
-
177
- if (i > 0) {
178
- const prev = body[i - 1];
179
- if (prev.type === "Text" || prev.type === "Inline") {
180
- if (currentText.length > 0 && !/[ \t\n\r]$/.test(currentText)) {
181
- if (!/[({\[<"']$/.test(currentText)) currentText += " ";
182
- }
183
- }
184
- }
185
- currentText += blockStr;
186
- } else {
187
- // ========================================================================== //
188
- // Process Block, AtBlock, and Comment nodes //
189
- // ========================================================================== //
190
- flushText();
191
- if (child.type === "Block") {
192
- const argsStr = formatArgs(child.args, "Block");
193
- result += `${innerIndentStr}[${child.id}${argsStr}]\n`;
194
- result += formatBody(child.body, depth + 1);
195
- result += `${innerIndentStr}[end]\n`;
196
- } else if (child.type === "AtBlock") {
197
- const argsStr = formatArgs(child.args, "AtBlock");
198
- const atHeader = argsStr ? `@_${child.id}_@${argsStr}` : `@_${child.id}_@;`;
199
- result += `${innerIndentStr}${atHeader}\n`;
200
- if (child.content) {
201
- // ========================================================================== //
202
- // Remove leading spaces from messy text block and re-indent //
203
- // ========================================================================== //
204
- const lines = child.content.replace(/\r\n/g, '\n').split('\n');
205
- while (lines.length && !lines[0].trim()) lines.shift();
206
- while (lines.length && !lines[lines.length - 1].trim()) lines.pop();
207
-
208
- let minIndent = Infinity;
209
- for (const line of lines) {
210
- if (line.trim()) {
211
- const leadingSpaces = line.match(/^[ \t]*/)[0].length;
212
- if (leadingSpaces < minIndent) minIndent = leadingSpaces;
213
- }
214
- }
215
- if (minIndent === Infinity) minIndent = 0;
216
-
217
- const indentedContent = lines.map(line => {
218
- if (!line.trim()) return "";
219
- return innerIndentStr + indentStr + line.substring(minIndent);
220
- }).join('\n');
221
-
222
- result += indentedContent + "\n";
223
- }
224
- result += `${innerIndentStr}@_end_@\n`;
225
- } else if (child.type === "Comment") {
226
- result += `${innerIndentStr}# ${child.text.replace(/^#+\s*/, "").trim()}\n`;
227
- }
228
- }
229
- }
230
- }
231
- flushText();
232
- return result;
233
- };
234
-
235
- const rootNodes = Array.isArray(ast) ? ast : [ast];
236
-
237
- // ========================================================================== //
238
- // The formatted string is available via `plugin.formattedSource` //
239
- // ========================================================================== //
240
- this.formattedSource = formatBody(rootNodes, 0).trim() + "\n";
241
-
242
- return ast; // Return original AST
243
- }
244
- };
package/coverage_test.js DELETED
@@ -1,21 +0,0 @@
1
- import lexer from "./core/lexer.js";
2
-
3
- const src = " [Block]\n content\n [end]";
4
- console.log(`Source length: ${src.length}`);
5
-
6
- const tokens = lexer(src);
7
- let totalCovered = 0;
8
-
9
- tokens.forEach((t, idx) => {
10
- if (t.type === "EOF") return;
11
- const len = t.value.length;
12
- totalCovered += len;
13
- console.log(`Token ${idx}: type=${t.type.padEnd(15)} structural=${(!!t.isStructural).toString().padEnd(6)} val=${JSON.stringify(t.value).padEnd(20)} range:(${t.range.start.line},${t.range.start.character}) - (${t.range.end.line},${t.range.end.character})`);
14
- });
15
-
16
- console.log(`Total covered length: ${totalCovered}`);
17
- if (totalCovered !== src.length) {
18
- console.log(`GAP DETECTED: missing ${src.length - totalCovered} characters.`);
19
- } else {
20
- console.log("SUCCESS: 100% Coverage.");
21
- }
package/debug.js DELETED
@@ -1,15 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import SomMark from "./index.js";
3
-
4
- const buffer = await fs.readFile("./examples/markdown/tasks.smark");
5
- const file_content = buffer.toString();
6
- let smark = new SomMark({
7
- src: file_content,
8
- format: "markdown",
9
- includeDocument: true,
10
- });
11
-
12
-
13
- // console.log(JSON.stringify(await smark.parse(), null, 2));
14
- // console.log(await smark.lex());
15
- console.log(await smark.transpile());
@@ -1,2 +0,0 @@
1
- const camelize = str => str.replace(/-./g, x => x[1].toUpperCase());
2
- export default camelize;
@@ -1,3 +0,0 @@
1
- const atomOneDark = `pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}`;
2
-
3
- export default atomOneDark;
@@ -1,42 +0,0 @@
1
- import SomMark from './index.js';
2
-
3
- async function test() {
4
- const text = '[Block = attr: "val with space"]\n Content\n[end]\n';
5
- const sm = new SomMark({
6
- src: text,
7
- format: 'html',
8
- plugins: ['sommark-format']
9
- });
10
- await sm.parse();
11
- const formatPlugin = sm.plugins.find(p => p.name === 'sommark-format');
12
- const formatted = formatPlugin.formattedSource;
13
- console.log("Original:\n", text);
14
- console.log("Formatted:\n", formatted);
15
-
16
- if (formatted.includes('"val with space"')) {
17
- console.log("PASS: Quotes preserved for space-containing value.");
18
- } else {
19
- console.error("FAIL: Quotes LOST!");
20
- process.exit(1);
21
- }
22
-
23
- const text2 = 'Text at top level\n(inline)->(bold)\n@_AtBlock_@;\n content\n@_end_@\n';
24
- const sm2 = new SomMark({
25
- src: text2,
26
- format: 'html',
27
- plugins: ['sommark-format']
28
- });
29
- await sm2.parse();
30
- const formatted2 = sm2.plugins.find(p => p.name === 'sommark-format').formattedSource;
31
- console.log("Top-level Original:\n", text2);
32
- console.log("Top-level Formatted:\n", formatted2);
33
-
34
- if (formatted2.includes('Text at top level') && formatted2.includes('(inline)->(bold)')) {
35
- console.log("PASS: Top-level content preserved.");
36
- } else {
37
- console.error("FAIL: Top-level content LOST or corrupted!");
38
- process.exit(1);
39
- }
40
- }
41
-
42
- test();
package/v3-todo.smark DELETED
@@ -1,73 +0,0 @@
1
- [Block = v3-todo-list]
2
- (SomMark v3 Goals)->(h1)
3
-
4
- # Completed Features & Fixes
5
- (done)->(todo: Full support for all HTML5 tags)
6
- (done)->(todo: Full support for Markdown and MDX)
7
- (done)->(todo: Safe way to override how components look)
8
- (done)->(todo: Fixed crashes and infinite loops)
9
- (done)->(todo: Fixed block spacing and new lines)
10
- (done)->(todo: Allowed using -, _, and $ in names)
11
- (done)->(todo: Stopped generic tags from breaking custom ones)
12
- (done)->(todo: Added 'lex' and 'parse' commands to the tool)
13
- (done)->(todo: Fixed text output errors)
14
- (done)->(todo: Fixed commas in small inline tags)
15
- (done)->(todo: Fixed nested tags in examples)
16
- (done)->(todo: Added CSS variable support like color: --primary)
17
- (done)->(todo: Added more examples to the guide)
18
- (done)->(todo: Auto-made ID links for headings)
19
- (done)->(todo: Fixed the equal sign bug in the reader)
20
- (done)->(todo: Fixed new line handling for big blocks)
21
- (done)->(todo: Fixed start-of-line marks in Markdown)
22
- (done)->(todo: Smart MDX tool for better styles and React)
23
- (done)->(todo: Better plugin system with isolated parts)
24
- (done)->(todo: Control plugins with easy 'options' settings)
25
- (done)->(todo: Better naming rules for $, _, and -)
26
- (done)->(todo: Made 'end' a special reserved word)
27
- (done)->(todo: Organized tags into types: Block, Inline, and AtBlock)
28
- (done)->(todo: Cleaned up internal code property names)
29
- (done)->(todo: Multi-step plugin pipeline: before, during, after)
30
- (done)->(todo: Sorted plugins by importance automatically)
31
- (done)->(todo: Built-in tool to remove comments)
32
- (done)->(todo: Tool to detect missing end tags)
33
- (done)->(todo: The command tool now uses the core engine for everything)
34
- (done)->(todo: Your local settings now take priority)
35
- (done)->(todo: Added easy ways to add new mappers to the system)
36
- (done)->(todo: Released Version 3)
37
- (done)->(todo: Support for custom body placement in templates)
38
- (done)->(todo: Better foundation for building new mappers)
39
- (done)->(todo: Cleaner HTML and Markdown code builders)
40
- (done)->(todo: Improved MDX support for better styles)
41
- (done)->(todo: Better JSON output support)
42
- (done)->(todo: Built-in tools: Quotes, Modules, Raw, and Validation)
43
- (done)->(todo: Better plugin loading and merged settings)
44
- (done)->(todo: Improved name checker in the reader)
45
- (done)->(todo: Better cleanup for names and quotes)
46
- (done)->(todo: Command tool now shares all settings with the core engine)
47
- (done)->(todo: Cleaned up all old test files)
48
- (done)->(todo: Added commands to see plugins and how they run)
49
- (done)->(todo: Added command to see your current settings)
50
- (done)->(todo: Added command to change tool colors)
51
- (done)->(todo: Better way to handle unknown HTML tags automatically)
52
- (done)->(todo: Security fix for template bypass tricks)
53
- (done)->(todo: Safer and more robust way to read arguments)
54
- (done)->(todo: Cleaner and more consistent error messages)
55
- (done)->(todo: Forced semicolons at the start of AtBlocks)
56
- (done)->(todo: Full guide for all tools and features)
57
- (done)->(todo: List of all old and removed features)
58
- (done)->(todo: Removed unused CSS and code pieces)
59
- (done)->(todo: Standardized HTML comment style)
60
- (done)->(todo: Moved common tools to a helper file)
61
- (done)->(todo: Modernized all user documentation)
62
- (done)->(todo: Created new guides for the plugin system)
63
- (done)->(todo: Enabled essential tools by default)
64
- (done)->(todo: Fixed commas in Markdown tables)
65
- (done)->(todo: Improved the code highlighting system)
66
- (done)->(todo: Cleaned up old and deprecated style settings)
67
- (done)->(todo: Fixed the todo list checkmark bug)
68
- (done)->(todo: Native quote support built into the engine)
69
- (done)->(todo: Optimized for speed and instant startup)
70
- (done)->(todo: Fixed structural bugs with nested blocks)
71
- (done)->(todo: SomMark core now fully supports LSP for code editors)
72
- (done)->(todo: You can now use Text, Inline tags, and AtBlocks at the top level without a Block)
73
- [end]