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.
Files changed (62) 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 +7 -17
  6. package/cli/commands/build.js +26 -6
  7. package/cli/commands/color.js +22 -26
  8. package/cli/commands/help.js +10 -10
  9. package/cli/commands/init.js +20 -31
  10. package/cli/commands/print.js +18 -16
  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 +15 -17
  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 +40 -75
  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 +238 -154
  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/dedent.js +19 -0
  33. package/helpers/escapeHTML.js +13 -6
  34. package/helpers/kebabize.js +6 -0
  35. package/helpers/peek.js +9 -0
  36. package/helpers/removeChar.js +26 -13
  37. package/helpers/safeDataParser.js +114 -0
  38. package/helpers/utils.js +140 -158
  39. package/index.js +186 -188
  40. package/mappers/languages/html.js +105 -213
  41. package/mappers/languages/json.js +122 -171
  42. package/mappers/languages/markdown.js +355 -108
  43. package/mappers/languages/mdx.js +76 -120
  44. package/mappers/languages/xml.js +114 -0
  45. package/mappers/mapper.js +152 -123
  46. package/mappers/shared/index.js +22 -0
  47. package/package.json +26 -6
  48. package/SOMMARK-SPEC.md +0 -481
  49. package/cli/commands/list.js +0 -124
  50. package/constants/html_tags.js +0 -146
  51. package/core/pluginManager.js +0 -149
  52. package/core/plugins/comment-remover.js +0 -47
  53. package/core/plugins/module-system.js +0 -176
  54. package/core/plugins/raw-content-plugin.js +0 -78
  55. package/core/plugins/rules-validation-plugin.js +0 -231
  56. package/core/plugins/sommark-format.js +0 -244
  57. package/coverage_test.js +0 -21
  58. package/debug.js +0 -15
  59. package/helpers/camelize.js +0 -2
  60. package/helpers/defaultTheme.js +0 -3
  61. package/test_format_fix.js +0 -42
  62. package/v3-todo.smark +0 -73
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Registers shared utility tags across HTML, Markdown, and MDX mappers.
3
+ * @param {Mapper} mapper - The mapper instance to register tags on.
4
+ */
5
+ export function registerSharedOutputs(mapper) {
6
+ // 1. 'raw' - AtBlock that return the raw, unparsed content.
7
+ mapper.register("raw", ({ content }) => content, {
8
+ type: "AtBlock",
9
+ escape: false
10
+ });
11
+
12
+ // 2. 'css' - An Inline tag that applies inline styles to its content.
13
+ // Usage: (text)->(css: "color: red")
14
+ mapper.register("css", ({ args, content }) => {
15
+ let style = mapper.safeArg({ args, index: 0, key: "style", fallBack: "" });
16
+ style = style.split(";").map(s => s.trim().split(":").map(s => s.trim()).join(":")).join(";");
17
+ return mapper.tag("span").attributes({ style }).body(content);
18
+ }, {
19
+ type: "Inline"
20
+ });
21
+
22
+ }
package/package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "name": "sommark",
3
- "version": "3.3.4",
4
- "description": "SomMark is a declarative, extensible markup language for structured content that can be converted to HTML, Markdown, MDX, JSON, and more.",
3
+ "version": "4.0.1",
4
+ "description": "SomMark is a declarative, extensible markup language for structured content that can be converted to HTML, Markdown, MDX, JSON, XML, and more.",
5
5
  "main": "index.js",
6
+ "files": [
7
+ "index.js",
8
+ "core/",
9
+ "mappers/",
10
+ "helpers/",
11
+ "formatter/",
12
+ "constants/",
13
+ "cli/",
14
+ "assets/",
15
+ "grammar.ebnf",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
6
19
  "directories": {
7
20
  "test": "tests"
8
21
  },
@@ -12,10 +25,9 @@
12
25
  },
13
26
  "scripts": {
14
27
  "test": "vitest",
15
- "test:run": "vitest run",
28
+ "test:run": "vitest run --pool=forks --maxWorkers=1",
16
29
  "test:watch": "vitest watch",
17
- "release": "bash scripts/release.sh",
18
- "publish": "bash scripts/publish.sh"
30
+ "test:html": "vitest run tests/html"
19
31
  },
20
32
  "bin": {
21
33
  "sommark": "cli/cli.mjs"
@@ -34,6 +46,8 @@
34
46
  "html",
35
47
  "markdown",
36
48
  "mdx",
49
+ "xml",
50
+ "json",
37
51
  "dsl",
38
52
  "mapper",
39
53
  "mapping"
@@ -45,6 +59,12 @@
45
59
  },
46
60
  "homepage": "https://github.com/Adam-Elmi/SomMark#readme",
47
61
  "devDependencies": {
62
+ "@mdx-js/mdx": "^3.1.1",
63
+ "fast-xml-parser": "^5.5.11",
64
+ "jsdom": "^29.0.2",
65
+ "remark": "^15.0.1",
66
+ "remark-gfm": "^4.0.1",
67
+ "remark-parse": "^11.0.0",
48
68
  "vitest": "^4.0.16"
49
69
  }
50
- }
70
+ }
package/SOMMARK-SPEC.md DELETED
@@ -1,481 +0,0 @@
1
- # SomMark Specification
2
-
3
- **SomMark** is a lightweight structured markup language designed to support hierarchical content, inline transformations, and raw text embedding.
4
-
5
- SomMark provides three primary constructs:
6
-
7
- 1. **Blocks** – hierarchical containers that support nesting
8
- 2. **Inline Statements** – inline value transformations
9
- 3. **AtBlocks** – raw text containers where parsing is disabled
10
-
11
- Additionally, SomMark supports **comments** and **escape sequences**.
12
-
13
- ---
14
-
15
- # 1. Identifiers
16
-
17
- Identifiers are used to name **Blocks**, **Inline Statements**, and **AtBlocks**.
18
-
19
- ## Allowed Characters
20
-
21
- Identifiers may contain:
22
-
23
- * Letters (`a-z`, `A-Z`)
24
- * Numbers (`0-9`)
25
- * Dollar sign (`$`)
26
- * Underscore (`_`)
27
- * Hyphen (`-`)
28
-
29
- Example valid identifiers:
30
-
31
- ```
32
- Block
33
- section-1
34
- user_profile
35
- $template
36
- note-2
37
- ```
38
-
39
- Example invalid identifiers:
40
-
41
- ```
42
- my block
43
- test@
44
- hello!
45
- ```
46
-
47
- Identifiers **must not contain spaces**.
48
-
49
- ---
50
-
51
- # 2. Blocks
52
-
53
-
54
- Blocks are hierarchical containers that can contain other blocks, inline statements, or plain text.
55
-
56
- Blocks are the primary structural element of SomMark.
57
-
58
- ## Block Syntax
59
-
60
- ### Block with Arguments
61
-
62
- ```
63
- [Identifier = arg1, arg2, key:value]
64
- Block body content...
65
- [end]
66
- ```
67
-
68
- Example:
69
-
70
- ```
71
- [Note = important, level:2]
72
- This is a note block.
73
- [end]
74
- ```
75
-
76
- ---
77
-
78
- ### Block without Arguments
79
-
80
- ```
81
- [Identifier]
82
- Block body content...
83
- [end]
84
- ```
85
-
86
- Example:
87
-
88
- ```
89
- [Section]
90
- This is a section.
91
- [end]
92
- ```
93
-
94
- ---
95
-
96
- ## Block Behavior
97
-
98
- * Blocks **support nesting**.
99
- * Blocks can contain:
100
-
101
- * plain text
102
- * inline statements
103
- * other blocks
104
- * Arguments are optional.
105
- * Arguments may be:
106
-
107
- * **positional values**
108
- * **key-value pairs**
109
-
110
- Example:
111
-
112
- ```
113
- [Article = author:Adam]
114
-
115
- Welcome to the article.
116
-
117
- [Section = title:Intro]
118
- This is the introduction.
119
- [end]
120
-
121
- [end]
122
- ```
123
-
124
- ---
125
-
126
- # 3. Inline Statements
127
-
128
- Inline statements transform or process a **value** within text.
129
-
130
- Inline statements **do not allow nested SomMark syntax**.
131
-
132
- ---
133
-
134
- ## Inline Syntax
135
-
136
- ### Inline with Arguments
137
-
138
- ```
139
- (Value)->(Identifier: arg1, arg2, arg3)
140
- ```
141
-
142
- Example:
143
-
144
- ```
145
- (hello world)->(uppercase)
146
- ```
147
-
148
- ---
149
-
150
- ### Inline without Arguments
151
-
152
- ```
153
- (Value)->(Identifier)
154
- ```
155
-
156
- Example:
157
-
158
- ```
159
- (hello)->(bold)
160
- ```
161
-
162
- ---
163
-
164
- ## Inline Behavior
165
-
166
- * `(Value)` is the **input value**.
167
- * `(Identifier)` specifies the operation.
168
- * Inline arguments **cannot use key:value syntax**.
169
- * Inline statements **cannot contain nested syntax**.
170
-
171
- Example:
172
-
173
- ```
174
- (This text)->(highlight: yellow)
175
- ```
176
-
177
- ---
178
-
179
- # 4. AtBlocks (Raw Text Blocks)
180
-
181
- AtBlocks are used when content must be treated as **plain text**.
182
-
183
- No SomMark syntax inside an AtBlock is parsed.
184
-
185
- ---
186
-
187
- ## AtBlock Syntax
188
-
189
- ### AtBlock with Arguments
190
-
191
- ```
192
- @_Identifier_@: arg1, arg2, key:value;
193
- Raw content here.
194
- Everything is treated as plain text.
195
- @_end_@
196
- ```
197
-
198
- Example:
199
-
200
- ```
201
- @_Code_@: lang:js;
202
-
203
- function hello(){
204
- console.log("hello");
205
- }
206
-
207
- @_end_@
208
- ```
209
-
210
- ---
211
-
212
- ### AtBlock without Arguments
213
-
214
- ```
215
- @_Identifier_@;
216
- content...
217
- @_end_@
218
- ```
219
-
220
- Example:
221
-
222
- ```
223
- @_Raw_@;
224
- [This will not be parsed]
225
- (Value)->(test)
226
- @_end_@
227
- ```
228
-
229
- ---
230
-
231
- ## AtBlock Behavior
232
-
233
- * Content inside AtBlocks is **not parsed**.
234
- * All characters inside are treated as **plain text**.
235
- * Nested SomMark syntax is ignored.
236
- * The AtBlock header **MUST end with a semicolon (`;`)**, even if no arguments are provided.
237
-
238
- Example:
239
-
240
- ```
241
- @_Template_@: engine:handlebars;
242
-
243
- {{ user.name }}
244
-
245
- @_end_@
246
- ```
247
-
248
- ---
249
-
250
- # 5. Arguments
251
-
252
- Arguments are used in **Blocks** and **AtBlocks**.
253
-
254
- ## Argument Types
255
-
256
- ### Positional Arguments
257
-
258
- ```
259
- arg1, arg2, arg3
260
- ```
261
-
262
- Example:
263
-
264
- ```
265
- [Block = first, second]
266
- ```
267
-
268
- ---
269
-
270
- ### Key-Value Arguments
271
-
272
- ```
273
- key:value
274
- ```
275
-
276
- Example:
277
-
278
- ```
279
- [Block = title:Intro, level:2]
280
- ```
281
-
282
- ---
283
-
284
- ## Argument Restrictions
285
-
286
- Arguments may contain most characters including:
287
-
288
- ```
289
- [
290
- ]
291
- (
292
- )
293
- ```
294
-
295
- However the following characters are restricted:
296
-
297
- ### Colon (`:`)
298
-
299
- Colon is used to separate **key-value pairs**.
300
-
301
- Example:
302
-
303
- ```
304
- key:value
305
- ```
306
-
307
- If a colon must appear in a value, it must be escaped.
308
-
309
- Example:
310
-
311
- ```
312
- x\:y
313
- ```
314
-
315
- Example block:
316
-
317
- ```
318
- [Block = path:x\:y]
319
- content
320
- [end]
321
- ```
322
-
323
- ---
324
-
325
- ### Semicolon (`;`)
326
-
327
- Semicolon is used **only in AtBlock arguments**.
328
-
329
- It indicates the **end of the argument list**.
330
-
331
- Example:
332
-
333
- ```
334
- @_Code_@: lang:js, theme:dark;
335
- ```
336
-
337
- Semicolons **have no special meaning** in Blocks or Inline Statements.
338
-
339
- ---
340
-
341
- # 6. Escape Character
342
-
343
- SomMark uses the **backslash (`\`)** as the escape character.
344
-
345
- Escaping works similarly to JavaScript string escaping.
346
-
347
- Example:
348
-
349
- ```
350
- \[
351
- ```
352
-
353
- This prevents a block from being parsed.
354
-
355
- Example:
356
-
357
- ```
358
- \[Not a block\]
359
- ```
360
-
361
- Escaping can also be used inside arguments.
362
-
363
- Example:
364
-
365
- ```
366
- [Block = x\:y]
367
- ```
368
-
369
- ---
370
-
371
- # 7. Comments
372
-
373
- SomMark supports **single-line comments**.
374
-
375
- ## Syntax
376
-
377
- ```
378
- # this is a comment
379
- ```
380
-
381
- Everything following `#` until the end of the line is ignored by the parser.
382
-
383
- ---
384
-
385
- ## Comment Rules
386
-
387
- Comments may appear:
388
-
389
- * on their own line
390
- * between blocks
391
- * after statements
392
-
393
- Example:
394
-
395
- ```
396
- # Article metadata
397
-
398
- [Article = author:Adam]
399
-
400
- # introduction
401
- [Section = title:Intro]
402
- Welcome to SomMark.
403
- [end]
404
-
405
- [end]
406
- ```
407
-
408
- ---
409
-
410
- ## Comments Inside AtBlocks
411
-
412
- Inside AtBlocks, comments are treated as **plain text**.
413
-
414
- Example:
415
-
416
- ```
417
- @_Code_@: lang:js;
418
-
419
- # This line is not treated as a comment
420
- console.log("Hello")
421
-
422
- @_end_@
423
- ```
424
-
425
- ---
426
-
427
- # 8. Non-Empty Content Rules
428
-
429
- The following elements **must not be empty**:
430
-
431
- * Block body
432
- * AtBlock body
433
- * Inline value
434
-
435
- Invalid examples:
436
-
437
- ```
438
- ()->(Identifier)
439
- ```
440
-
441
- ---
442
-
443
- # 9. Flexible Formatting
444
-
445
- SomMark allows flexible formatting and whitespace.
446
-
447
- The following are equivalent.
448
-
449
- ## Compact Format
450
-
451
- ```
452
- [Block]Hello World[end]
453
- ```
454
-
455
- ## Expanded Format
456
-
457
- ```
458
- [
459
- Block
460
- ]
461
-
462
- Hello World
463
-
464
- [
465
- end
466
- ]
467
- ```
468
-
469
- Inline and AtBlocks follow similar whitespace flexibility.
470
-
471
- ---
472
-
473
- # 10. Construct Summary
474
-
475
- | Construct | Nesting | Arguments | Key-Value Args | Parsing |
476
- | ---------------- | ------- | --------- | -------------- | ---------- |
477
- | Block | Yes | Optional | Yes | Parsed |
478
- | Inline Statement | No | Optional | No | Parsed |
479
- | AtBlock | No | Optional | Yes | Not parsed (Header ends with `;`) |
480
- | Comment | No | No | No | Parsed but ignored by built-in "comment-remover" plugin during AST |
481
-
@@ -1,124 +0,0 @@
1
- import SomMark, { BUILT_IN_PLUGINS } from "../../index.js";
2
- import { loadConfig } from "../helpers/config.js";
3
- import { formatMessage } from "../../core/errors.js";
4
-
5
- /**
6
- * List Plugins Command
7
- * smark list plugins
8
- * smark list --internal plugins
9
- * smark list --external plugins
10
- */
11
- export async function runListPlugins(args) {
12
- const config = await loadConfig();
13
-
14
- const sm = new SomMark({
15
- src: "",
16
- format: "html",
17
- plugins: config.plugins,
18
- excludePlugins: config.excludePlugins,
19
- priority: config.priority
20
- });
21
-
22
- const enabledPlugins = sm.plugins;
23
-
24
- // Filter flags
25
- const showInternal = args.includes("--internal") || args.includes("-i");
26
- const showExternal = args.includes("--external") || args.includes("-e");
27
- const showAll = (!showInternal && !showExternal) || (args.length === 1 && args[0] === "plugins");
28
-
29
- console.log(formatMessage(`{N}<$yellow:SomMark Enabled Plugins:$>{N}`));
30
-
31
- let found = false;
32
-
33
- // 1. Internal Plugins
34
- if (showInternal || showAll) {
35
- const internal = enabledPlugins.filter(p => {
36
- return BUILT_IN_PLUGINS.some(bp => bp.name === p.name);
37
- });
38
-
39
- if (internal.length > 0) {
40
- console.log(formatMessage(` <$magenta:Internal Plugins (Built-in):$>{N}`));
41
- internal.forEach(p => {
42
- const pluginObj = BUILT_IN_PLUGINS.find(bp => bp.name === p.name);
43
- printPluginInfo(p.name, pluginObj);
44
- });
45
- found = true;
46
- }
47
- }
48
-
49
- // 2. External Plugins
50
- if (showExternal || showAll) {
51
- const external = enabledPlugins.filter(p => {
52
- return !BUILT_IN_PLUGINS.some(bp => bp.name === p.name);
53
- });
54
-
55
- if (external.length > 0) {
56
- console.log(formatMessage(`${found ? "{N}" : ""} <$magenta:External Plugins (User-defined):$>{N}`));
57
- external.forEach(p => {
58
- printPluginInfo(p.name || "Unknown", p);
59
- });
60
- found = true;
61
- }
62
- }
63
-
64
- if (!found) {
65
- console.log(formatMessage(` <$red:No plugins enabled or matching the filter.$>{N}`));
66
- } else {
67
- console.log("");
68
- }
69
- }
70
-
71
- /**
72
- * List Pipeline Command
73
- * smark list pipeline
74
- */
75
- export async function runListPipeline() {
76
- const config = await loadConfig();
77
- const sm = new SomMark({
78
- src: "",
79
- format: "html", // Default format
80
- plugins: config.plugins,
81
- priority: config.priority
82
- });
83
-
84
- const pipeline = sm.pluginManager.plugins;
85
-
86
- console.log(formatMessage(`{N}<$yellow:SomMark Execution Pipeline:$>{N}`));
87
-
88
- const phases = [
89
- { name: "1. Preprocessors (Global)", type: "preprocessor", scope: "top-level" },
90
- { name: "2. Preprocessors (Scoped)", type: "preprocessor", scope: "arguments" },
91
- { name: "3. After Lexer (Tokens)", type: ["lexer", "after-lexer"] },
92
- { name: "4. AST Handlers (Parser Hooks)", type: ["parser", "on-ast"] },
93
- { name: "5. Mapper Extensions (Rules)", type: "mapper" },
94
- { name: "6. Output Transformers (Final)", type: ["transform", "postprocessor"] }
95
- ];
96
-
97
- phases.forEach((phase, index) => {
98
- const types = Array.isArray(phase.type) ? phase.type : [phase.type];
99
- const matched = pipeline.filter(p => {
100
- const pTypes = Array.isArray(p.type) ? p.type : [p.type];
101
- const typeMatch = pTypes.some(t => types.includes(t));
102
- const scopeMatch = !phase.scope || p.scope === phase.scope;
103
- return typeMatch && scopeMatch;
104
- });
105
-
106
- console.log(formatMessage(` <$magenta:${phase.name}$>`));
107
- if (matched.length > 0) {
108
- matched.forEach(p => {
109
- console.log(formatMessage(` <$green:└── ${p.name}$> <$cyan:[${Array.isArray(p.type) ? p.type.join(", ") : p.type}]$>`));
110
- });
111
- } else {
112
- console.log(formatMessage(` <$yellow: (None registered)$>`));
113
- }
114
- if (index < phases.length - 1) console.log("");
115
- });
116
- console.log("");
117
- }
118
-
119
- function printPluginInfo(name, pluginObj) {
120
- const author = pluginObj?.author || "Unknown";
121
- const desc = pluginObj?.description || "No description provided.";
122
- console.log(formatMessage(` <$green:└── ${name}$> - <$cyan:by ${author}$>`));
123
- console.log(formatMessage(` <$yellow:${desc}$>`));
124
- }