sommark 3.1.0 → 3.3.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.
@@ -3,11 +3,17 @@ import escapeHTML from "../helpers/escapeHTML.js";
3
3
  import { transpilerError } from "./errors.js";
4
4
  import { textFormat, htmlFormat, markdownFormat, mdxFormat, jsonFormat } from "./formats.js";
5
5
 
6
+ /**
7
+ * SomMark Transpiler
8
+ */
9
+
6
10
  const BODY_PLACEHOLDER = `__SOMMARK_BODY_PLACEHOLDER_${Math.random().toString(36).slice(2)}__`;
7
11
 
8
12
  // ========================================================================== //
9
- // Extracting target identifier //
13
+ // Helpers //
10
14
  // ========================================================================== //
15
+
16
+ // Finds the matching output definition for a tag identifier
11
17
  function matchedValue(outputs, targetId) {
12
18
  if (!outputs || !targetId) return undefined;
13
19
  return outputs.find(output => {
@@ -18,6 +24,24 @@ function matchedValue(outputs, targetId) {
18
24
  });
19
25
  }
20
26
 
27
+ // Recursively collects all plain text from a node and its children
28
+ function getNodeText(node) {
29
+ if (!node.body) return "";
30
+ let text = "";
31
+ for (const child of node.body) {
32
+ if (child.type === TEXT) text += child.text;
33
+ else if (child.type === INLINE) text += child.value;
34
+ else if (child.type === ATBLOCK) text += child.content;
35
+ else if (child.type === BLOCK) text += getNodeText(child);
36
+ }
37
+ return text;
38
+ }
39
+
40
+ // ========================================================================== //
41
+ // Core Rendering Logic //
42
+ // ========================================================================== //
43
+
44
+ // Processes an individual node and its body to produce formatted output
21
45
  async function generateOutput(ast, i, format, mapper_file) {
22
46
  const node = Array.isArray(ast) ? ast[i] : ast;
23
47
  let result = "";
@@ -33,12 +57,13 @@ async function generateOutput(ast, i, format, mapper_file) {
33
57
  // Always use placeholders for blocks to support wrapping //
34
58
  // ========================================================================== //
35
59
  const placeholder = format === mdxFormat && node.body.length > 0 ? `\n${BODY_PLACEHOLDER}\n` : BODY_PLACEHOLDER;
36
-
37
- result += target.render.call(mapper_file, { args: node.args, content: placeholder, ast: node });
60
+ const textContent = getNodeText(node);
61
+
62
+ result += target.render.call(mapper_file, { args: node.args, content: placeholder, textContent, ast: node });
38
63
  if (format === mdxFormat) result = "\n" + result + "\n";
39
64
 
40
65
  // ========================================================================== //
41
- // Body nodes //
66
+ // Process body nodes recursively //
42
67
  // ========================================================================== //
43
68
  for (let j = 0; j < node.body.length; j++) {
44
69
  const body_node = node.body[j];
@@ -116,13 +141,50 @@ async function generateOutput(ast, i, format, mapper_file) {
116
141
  return result.trimEnd() + "\n";
117
142
  }
118
143
 
144
+ // ========================================================================== //
145
+ // Main Transpiler Entry Point //
146
+ // ========================================================================== //
147
+
119
148
  async function transpiler({ ast, format, mapperFile, includeDocument = true }) {
120
149
  let output = "";
121
150
  for (let i = 0; i < ast.length; i++) {
122
- if (ast[i].type === BLOCK) {
123
- output += await generateOutput(ast, i, format, mapperFile);
124
- } else if (ast[i].type === COMMENT) {
125
- output += mapperFile.comment(ast[i].text);
151
+ const node = ast[i];
152
+ switch (node.type) {
153
+ case BLOCK:
154
+ output += await generateOutput(ast, i, format, mapperFile);
155
+ break;
156
+ case COMMENT:
157
+ output += mapperFile.comment(node.text);
158
+ break;
159
+ case TEXT:
160
+ const shouldEscapeText = (format === htmlFormat || format === mdxFormat);
161
+ output += shouldEscapeText ? escapeHTML(node.text) : node.text;
162
+ break;
163
+ case INLINE:
164
+ let inlineTarget = matchedValue(mapperFile.outputs, node.id);
165
+ if (!inlineTarget) inlineTarget = mapperFile.getUnknownTag(node);
166
+ if (inlineTarget) {
167
+ const shouldEscapeInline = inlineTarget.options?.escape !== false;
168
+ output += inlineTarget.render.call(mapperFile, {
169
+ args: node.args.length > 0 ? node.args : [],
170
+ content: (format === htmlFormat || format === mdxFormat) && shouldEscapeInline ? escapeHTML(node.value) : node.value,
171
+ ast: node
172
+ }) + (format === mdxFormat ? "\n" : "");
173
+ }
174
+ break;
175
+ case ATBLOCK:
176
+ let atTarget = matchedValue(mapperFile.outputs, node.id);
177
+ if (!atTarget) atTarget = mapperFile.getUnknownTag(node);
178
+ if (atTarget) {
179
+ const shouldEscapeAt = atTarget.options?.escape !== false;
180
+ let content = node.content;
181
+ if (shouldEscapeAt && (format === htmlFormat || format === mdxFormat)) {
182
+ content = escapeHTML(content);
183
+ }
184
+ const rendered = atTarget.render.call(mapperFile, { args: node.args, content, ast: node }).trimEnd() + "\n";
185
+ output = output.trim() ? output.trimEnd() + "\n" + rendered : output + rendered;
186
+ }
187
+ break;
126
188
  }
127
189
  }
128
190
  // ========================================================================== //
@@ -0,0 +1,21 @@
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/helpers/utils.js CHANGED
@@ -62,6 +62,33 @@ export function safeArg(args, index, key, type = null, setType = null, fallBack
62
62
  return fallBack;
63
63
  }
64
64
 
65
+ /**
66
+ * Calculates the Levenshtein distance between two strings.
67
+ * @param {string} a
68
+ * @param {string} b
69
+ * @returns {number}
70
+ */
71
+ export function levenshtein(a, b) {
72
+ const matrix = [];
73
+ for (let i = 0; i <= b.length; i++) matrix[i] = [i];
74
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
75
+
76
+ for (let i = 1; i <= b.length; i++) {
77
+ for (let j = 1; j <= a.length; j++) {
78
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
79
+ matrix[i][j] = matrix[i - 1][j - 1];
80
+ } else {
81
+ matrix[i][j] = Math.min(
82
+ matrix[i - 1][j - 1] + 1,
83
+ matrix[i][j - 1] + 1,
84
+ matrix[i - 1][j] + 1
85
+ );
86
+ }
87
+ }
88
+ }
89
+ return matrix[b.length][a.length];
90
+ }
91
+
65
92
  /**
66
93
  * Renders an HTML table.
67
94
  */
package/index.js CHANGED
@@ -13,7 +13,6 @@ import FORMATS, { textFormat, htmlFormat, markdownFormat, mdxFormat, jsonFormat
13
13
  import TOKEN_TYPES from "./core/tokenTypes.js";
14
14
  import * as labels from "./core/labels.js";
15
15
  import PluginManager from "./core/pluginManager.js";
16
- import QuoteEscaper from "./core/plugins/quote-escaper.js";
17
16
  import ModuleSystem from "./core/plugins/module-system.js";
18
17
  import RawContentPlugin from "./core/plugins/raw-content-plugin.js";
19
18
  import CommentRemover from "./core/plugins/comment-remover.js";
@@ -23,17 +22,18 @@ import { enableColor } from "./helpers/colorize.js";
23
22
  import { htmlTable, list, parseList, safeArg, todo } from "./helpers/utils.js";
24
23
 
25
24
 
26
- export const BUILT_IN_PLUGINS = [QuoteEscaper, ModuleSystem, RawContentPlugin, CommentRemover, RulesValidationPlugin, SomMarkFormat];
25
+ export const BUILT_IN_PLUGINS = [ModuleSystem, RawContentPlugin, CommentRemover, RulesValidationPlugin, SomMarkFormat];
27
26
 
28
27
  class SomMark {
29
- constructor({ src, format, mapperFile = null, includeDocument = true, plugins = [], excludePlugins = [], priority = [] }) {
28
+ constructor({ src, format, mapperFile = null, includeDocument = true, plugins = [], excludePlugins = [], priority = [], filename = "anonymous" }) {
30
29
  this.src = src;
31
30
  this.format = format;
32
31
  this.mapperFile = mapperFile;
33
32
  this.priority = priority;
33
+ this.filename = filename;
34
+ this.warnings = [];
34
35
 
35
36
  // 1. Identify which built-in plugins should be active by default
36
- // For now, QuoteEscaper is active, others are inactive (require manual activation)
37
37
  const inactiveByDefault = ["raw-content", "sommark-format"];
38
38
  let activeBuiltIns = BUILT_IN_PLUGINS.filter(p =>
39
39
  !inactiveByDefault.includes(p.name) && !excludePlugins.includes(p.name)
@@ -142,8 +142,12 @@ class SomMark {
142
142
  }
143
143
  }
144
144
 
145
+ reportWarning(message) {
146
+ this.warnings.push(message);
147
+ }
148
+
145
149
  async _applyScopedPreprocessors(src) {
146
- let processed = await this.pluginManager.runPreprocessor(src, "top-level");
150
+ let processed = await this.pluginManager.runPreprocessor(src, "top-level", this);
147
151
 
148
152
  // Helper for async regex replacement
149
153
  const asyncReplace = async (str, regex, scope) => {
@@ -160,7 +164,7 @@ class SomMark {
160
164
  if (scope === "content") contentToProcess = match[2];
161
165
 
162
166
  if (contentToProcess !== undefined) {
163
- const processedContent = await this.pluginManager.runPreprocessor(contentToProcess, scope);
167
+ const processedContent = await this.pluginManager.runPreprocessor(contentToProcess, scope, this);
164
168
  // Reconstruct the match with processed content
165
169
  if (scope === "arguments") return match[0].replace(match[2], processedContent);
166
170
  if (scope === "content") return match[0].replace(match[2], processedContent);
@@ -188,15 +192,20 @@ class SomMark {
188
192
  async lex(src = this.src) {
189
193
  if (src !== this.src) this.src = src;
190
194
  const processedSrc = await this._applyScopedPreprocessors(this.src);
191
- let tokens = lexer(processedSrc);
195
+ let tokens = lexer(processedSrc, this.filename);
192
196
  tokens = await this.pluginManager.runAfterLex(tokens);
193
197
  return tokens;
194
198
  }
195
199
 
196
200
  async parse(src = this.src) {
197
201
  const tokens = await this.lex(src);
198
- let ast = parser(tokens);
199
- ast = await this.pluginManager.runOnAst(ast, { mapperFile: this.mapperFile });
202
+ let ast = parser(tokens, this.filename);
203
+ ast = await this.pluginManager.runOnAst(ast, {
204
+ mapperFile: this.mapperFile,
205
+ filename: this.filename,
206
+ format: this.format,
207
+ instance: this
208
+ });
200
209
  return ast;
201
210
  }
202
211
 
@@ -254,23 +263,23 @@ class SomMark {
254
263
  }
255
264
  }
256
265
 
257
- const lex = async (src, plugins = [], excludePlugins = []) => {
258
- return await new SomMark({ src, plugins, format: htmlFormat, excludePlugins }).lex();
266
+ const lex = async (src, filename = "anonymous", plugins = [], excludePlugins = []) => {
267
+ return await new SomMark({ src, filename, plugins, format: htmlFormat, excludePlugins }).lex();
259
268
  };
260
269
 
261
- async function parse(src, plugins = [], excludePlugins = []) {
270
+ async function parse(src, filename = "anonymous", plugins = [], excludePlugins = []) {
262
271
  if (!src) {
263
272
  runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for parsing.$>{line}`]);
264
273
  }
265
- return await new SomMark({ src, plugins, format: htmlFormat, excludePlugins }).parse();
274
+ return await new SomMark({ src, filename, plugins, format: htmlFormat, excludePlugins }).parse();
266
275
  }
267
276
 
268
277
  async function transpile(options = {}) {
269
- const { src, format = htmlFormat, mapperFile = null, includeDocument = true, plugins = [], excludePlugins = [], priority = [] } = options;
278
+ const { src, format = htmlFormat, filename = "anonymous", mapperFile = null, includeDocument = true, plugins = [], excludePlugins = [], priority = [] } = options;
270
279
  if (typeof options !== "object" || options === null) {
271
280
  runtimeError([`{line}<$red:Invalid Options:$> <$yellow:The options argument must be a non-null object.$>{line}`]);
272
281
  }
273
- const knownProps = ["src", "format", "mapperFile", "includeDocument", "plugins", "excludePlugins", "priority"];
282
+ const knownProps = ["src", "format", "filename", "mapperFile", "includeDocument", "plugins", "excludePlugins", "priority"];
274
283
  Object.keys(options).forEach(key => {
275
284
  if (!knownProps.includes(key)) {
276
285
  runtimeError([
@@ -282,7 +291,7 @@ async function transpile(options = {}) {
282
291
  runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for transpilation.$>{line}`]);
283
292
  }
284
293
 
285
- const sm = new SomMark({ src, format, mapperFile, includeDocument, plugins, excludePlugins, priority });
294
+ const sm = new SomMark({ src, format, filename, mapperFile, includeDocument, plugins, excludePlugins, priority });
286
295
  return await sm.transpile();
287
296
  }
288
297
 
@@ -108,6 +108,10 @@ HTML.register(
108
108
  HTML.register(["quote", "blockquote"], function ({ content }) {
109
109
  return this.tag("blockquote").body(content);
110
110
  }, { type: "Block" });
111
+ // Raw Content Blocks
112
+ HTML.register(["raw", "mdx"], function ({ content }) {
113
+ return content;
114
+ }, { type: "Block" });
111
115
  // Bold
112
116
  HTML.register("bold", function ({ content }) {
113
117
  return this.tag("strong").body(content);
@@ -190,19 +194,10 @@ HTML_TAGS.forEach(tagName => {
190
194
 
191
195
  HTML.register(
192
196
  id,
193
- function ({ args, content }) {
197
+ function ({ args, content, textContent }) {
194
198
  const element = this.tag(id);
195
199
  let inline_style = args.style ? (args.style.endsWith(";") ? args.style : args.style + ";") : "";
196
200
 
197
- // Auto-ID for Headings
198
- if (/^h[1-6]$/i.test(id) && !args.id && content) {
199
- const idAttr = content
200
- .toString()
201
- .toLowerCase()
202
- .replace(/[^\w\s-]/g, "")
203
- .replace(/\s+/g, "-");
204
- element.attributes({ id: idAttr });
205
- }
206
201
 
207
202
  const keys = Object.keys(args).filter(arg => isNaN(arg));
208
203
  keys.forEach(key => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sommark",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "SomMark is a declarative, extensible markup language for structured content that can be converted to HTML, Markdown, MDX, JSON, and more.",
5
5
  "main": "index.js",
6
6
  "directories": {
package/v3-todo.smark CHANGED
@@ -2,74 +2,72 @@
2
2
  (SomMark v3 Goals)->(h1)
3
3
 
4
4
  # Completed Features & Fixes
5
- (done)->(todo: Full HTML5 support)
6
- (done)->(todo: Full Markdown and MDX support)
7
- (done)->(todo: Safe custom overrides for component mappers)
8
- (done)->(todo: Fix lexer infinite loops and crashes)
9
- (done)->(todo: Fix block spacing and paragraph newlines)
10
- (done)->(todo: Allow -, _, and $ in identifiers)
11
- (done)->(todo: Prevent generic HTML tags from overwriting custom components)
12
- (done)->(todo: Add 'lex' and 'parse' CLI options)
13
- (done)->(todo: Fix --text format error in CLI)
14
- (done)->(todo: Fix comma handling in inline components)
15
- (done)->(todo: Fix void element nesting in examples)
16
- (done)->(todo: CSS variable support like color: --primary)
17
- (done)->(todo: More showcase examples for docs)
18
- (done)->(todo: Auto-generate anchor IDs for headings)
19
- (done)->(todo: Fix lexer '=' bug for literal equal signs)
20
- (done)->(todo: Fix newline tokenization for multi-line blocks)
21
- (done)->(todo: Fix multi-line prefixing in Markdown output)
22
- (done)->(todo: Smart MDX mapper with JSX and style conversion)
23
- (done)->(todo: Scoped plugin system with instance isolation)
24
- (done)->(todo: Configure built-in and external plugins via 'options' prop)
25
- (done)->(todo: Consistent identifier rules for $, _, and - in lexer and parser)
26
- (done)->(todo: Reserve 'end' as a keyword)
27
- (done)->(todo: Enforce registration types: Block, Inline, AtBlock, or any)
28
- (done)->(todo: Consistent internal property names like 'is_self_closing')
29
- (done)->(todo: Multi-stage plugin pipeline: beforeLex, onAst, afterTranspile)
30
- (done)->(todo: Priority-based PluginManager)
31
- (done)->(todo: Built-in 'comment-remover' plugin using AST transforms)
32
- (done)->(todo: 'self-closing' plugin with indentation-aware detection)
33
- (done)->(todo: CLI uses unified SomMark API with plugin support)
34
- (done)->(todo: Local smark.config.js takes priority over global config)
35
- (done)->(todo: Add register\(\) and inherit\(\) methods to SomMark instance)
36
- (done)->(todo: Release SomMark Version 3)
37
- (done)->(todo: Case-insensitive target matching and BODY_PLACEHOLDER in transpiler)
38
- (done)->(todo: Mapper base class: clone, clear, extraProps, and type deprecation)
39
- (done)->(todo: Class-based HTML and Markdown mappers with type hints)
40
- (done)->(todo: Rewrite MDX mapper for better JSX and style handling)
41
- (done)->(todo: Broader JSON mapper output support)
42
- (done)->(todo: Built-in plugins: QuoteEscaper, ModuleSystem, RawContentPlugin, RulesValidationPlugin)
43
- (done)->(todo: Plugin activation with merged defaults and user plugins)
44
- (done)->(todo: validateIdentifier helper in lexer)
45
- (done)->(todo: Better parser argument trimming and quote stripping)
46
- (done)->(todo: CLI passes plugins and priority from config to SomMark)
47
- (done)->(todo: Clean up old test files)
48
- (done)->(todo: smark list plugins and smark list pipeline commands)
49
- (done)->(todo: smark show config command for configuration introspection)
50
- (done)->(todo: smark color command for CLI color management)
51
- (done)->(todo: getUnknownTag\(node\) method in Mapper system for generic tag handling)
52
- (done)->(todo: Placeholder Injection Security fix to prevent <%smark> bypass)
53
- (done)->(todo: safeArg utility in Mapper for robust argument parsing)
54
- (done)->(todo: Consistent CLI error formatting with sommarkError and cliError)
55
- (done)->(todo: Enforce semicolon in AtBlock header start)
56
- (done)->(todo: 100% API documentation coverage in docs/api)
57
- (done)->(todo: Consolidated deprecated APIs into deprecated-apis.md)
58
- (done)->(todo: Removed loadCss and unused properties from Mapper.js)
59
- (done)->(todo: Standardized HTML comment formatting to <!-- comment -->)
60
- (done)->(todo: Extracted common formatters to helpers/utils.js)
61
- (done)->(todo: Modernized all core documentation guides for v3 best practices)
62
- (done)->(todo: Created dedicated Plugin System and Built-in Plugins guides)
63
- (done)->(todo: Activated comment-remover and module-system plugins by default)
64
- (done)->(todo: Fixed escaped comma handling in Markdown table builder)
65
- (done)->(todo: Deprecated Mapper.highlightCode in favor of plugins)
66
- (done)->(todo: Deprecated Mapper.themes and currentTheme)
67
- (done)->(todo: Deprecated Mapper.enable_highlightTheme and enable_table_styles)
68
- (done)->(todo: Deprecated Mapper.env and loadCss)
69
- (done)->(todo: Deprecated Mapper.registerHighlightTheme and selectHighlightTheme)
70
- (done)->(todo: Relocated Mapper.htmlTable to helpers/utils.js)
71
- (done)->(todo: Relocated Mapper.list and parseList to helpers/utils.js)
72
- (done)->(todo: Relocated Mapper.todo to helpers/utils.js)
73
- (done)->(todo: Relocated Mapper.code to specialized mappers)
74
- (done)->(todo: Fixed todo mapper bug regarding placeholder detection and dynamic status)
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)
75
73
  [end]
@@ -1,37 +0,0 @@
1
- /**
2
- * Quote Escaper Plugin for SomMark
3
- *
4
- * Scope: arguments
5
- *
6
- * Automatically escapes special characters within double quotes in arguments.
7
- */
8
- const QuoteEscaper = {
9
- name: "quote-escaper",
10
- type: "preprocessor",
11
- author: "Adam-Elmi",
12
- description: "Automatically escapes special characters within double-quoted string arguments.",
13
- scope: "arguments",
14
- beforeLex(args) {
15
- return args.replace(/"([^"]*)"/g, (match, content) => {
16
- const options = this.options || {};
17
-
18
- const escapedContent = content
19
- .replace(/\\/g, "\\\\")
20
- .replace(/\[/g, "\\[")
21
- .replace(/\]/g, "\\]")
22
- .replace(/=/g, "\\=")
23
- .replace(/->/g, "\\->")
24
- .replace(/\(/g, "\\(")
25
- .replace(/\)/g, "\\)")
26
- .replace(/@_/g, "\\@_")
27
- .replace(/_@/g, "\\_@")
28
- .replace(/:/g, "\\:")
29
- .replace(/,/g, "\\,")
30
- .replace(/;/g, "\\;")
31
- .replace(/#/g, "\\#");
32
- return `"${escapedContent}"`;
33
- });
34
- }
35
- };
36
-
37
- export default QuoteEscaper;
package/format.js DELETED
@@ -1,23 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import SomMark from "./index.js";
3
- const rawSource = await fs.readFile("./unformatted.smark", "utf8");
4
-
5
- const smark = new SomMark({
6
- src: rawSource,
7
- format: "html",
8
- plugins: [
9
- {
10
- name: "sommark-format",
11
- options: { indentString: " " } // 2 spaces instead of default "\t"
12
- }
13
- ]
14
- });
15
-
16
- // 1. Run parse()
17
- await smark.parse();
18
-
19
- // 2. Get the formatted string from the "sommark-format" plugin
20
- const formatPlugin = smark.plugins.find(p => p.name === "sommark-format");
21
-
22
- console.log("--- Formatted Output ---");
23
- console.log(formatPlugin.formattedSource);
package/unformatted.smark DELETED
@@ -1,90 +0,0 @@
1
- [ Document = version:1.0, type: article ]
2
- # Starting the document with messy comments
3
- ( Welcome )->(Strong) to the test document!
4
- [ Section = id: intro ]
5
- [ Paragraph ]
6
- This is [Block][end] the first paragraph. It has some weird spaces and
7
- attached.[Block][end]
8
- [ end ]
9
- [ Paragraph ]
10
- Another paragraph with [ nested ] text that is not a real block, wait, [ nested ] is a block!
11
- [ end ]
12
- [end]
13
- [end]
14
- [ Section = id: content ][ List = type: bullet ][ Item ]( Apple )->(Bold)
15
- [ end ]
16
- [ Item ]
17
- Banana[ end ][ Item ]Cherry
18
- [ end ]
19
- [ end ]
20
- @_ CodeSnippet _@ : javascript;
21
- function doSomething() {
22
- let x = 10;
23
- return x * 2;
24
- }
25
- @_ end _@
26
- [ Paragraph ]
27
- Some text after the code snippet.
28
- [ end ]
29
- [ Subsection = title: "Deep Nesting" ]
30
- [ Level1 ][ Level2 ][ Level3 ]
31
- [ Level4 ]
32
- [ Level5 ]
33
- ( Deeply )->( Nested )
34
- [ end ]
35
- [ end ]
36
- [ end ]
37
- [ end ]
38
- [ end ]
39
- [ end ]
40
- [ Subsection = title: "Mix and Match" ]
41
- [ Paragraph ]
42
- ( Start )->( A ) ( Middle )->( B ) ( End )->( C )
43
- [ end ]
44
- [ end ]
45
- [ Section = id: outro ]
46
- [ Paragraph ]
47
- Thank you for reading!
48
- # A wild comment appears
49
- [ end ]
50
- @_ RawText _@ : plain ;
51
- Just some raw text.
52
- It should preserve its internal spacing.
53
-
54
- But the at-block itself will be properly indented.
55
- @_ end _@
56
- [ end ]
57
- [ Section = id: footer ]
58
- [ Paragraph ]
59
- ( Copyright )->( Small ) 2023. All rights reserved.
60
- [ end ]
61
- [ end ]
62
- [ Section = empty ]
63
- [ end ]
64
- [ Section = single_line ]
65
- Single line content.
66
- [ end ]
67
- [ Section = messy_arguments , another_arg: value ]
68
- Content here.
69
- [ end ]
70
- [ Section = id: mixed_content ]
71
- Text before block.
72
- [ Paragraph ]
73
- Inside block.
74
- [ end ]
75
- Text after block.
76
- [ end ]
77
- [ Section = id: last_one ]
78
- [ Paragraph ]
79
- ( The )->(bold)
80
- [ end ]
81
- [ end ]
82
- [ Section = id: extra ]
83
- [ Paragraph ]
84
- ( Extra )->( One )text glued to an inline!
85
- ( Extra )->( Two )
86
- [ end ]
87
- [ end ]
88
- [end]
89
- [end]
90
- [end]