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.
- package/README.md +7 -0
- package/cli/commands/build.js +2 -1
- package/cli/commands/init.js +2 -6
- package/cli/commands/list.js +17 -12
- package/cli/commands/print.js +7 -2
- package/cli/helpers/transpile.js +2 -1
- package/core/errors.js +22 -9
- package/core/labels.js +3 -0
- package/core/lexer.js +207 -590
- package/core/parser.js +201 -65
- package/core/pluginManager.js +33 -23
- package/core/plugins/comment-remover.js +3 -3
- package/core/plugins/module-system.js +163 -124
- package/core/plugins/raw-content-plugin.js +15 -9
- package/core/plugins/rules-validation-plugin.js +2 -2
- package/core/plugins/sommark-format.js +92 -72
- package/core/tokenTypes.js +2 -1
- package/core/transpiler.js +70 -8
- package/coverage_test.js +21 -0
- package/helpers/utils.js +27 -0
- package/index.js +25 -16
- package/mappers/languages/html.js +5 -10
- package/package.json +1 -1
- package/v3-todo.smark +68 -70
- package/core/plugins/quote-escaper.js +0 -37
- package/format.js +0 -23
- package/unformatted.smark +0 -90
package/core/transpiler.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
// ========================================================================== //
|
package/coverage_test.js
ADDED
|
@@ -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 = [
|
|
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, {
|
|
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
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
|
|
6
|
-
(done)->(todo: Full Markdown and MDX
|
|
7
|
-
(done)->(todo: Safe
|
|
8
|
-
(done)->(todo:
|
|
9
|
-
(done)->(todo:
|
|
10
|
-
(done)->(todo:
|
|
11
|
-
(done)->(todo:
|
|
12
|
-
(done)->(todo:
|
|
13
|
-
(done)->(todo:
|
|
14
|
-
(done)->(todo:
|
|
15
|
-
(done)->(todo:
|
|
16
|
-
(done)->(todo: CSS variable support like color: --primary)
|
|
17
|
-
(done)->(todo:
|
|
18
|
-
(done)->(todo: Auto-
|
|
19
|
-
(done)->(todo:
|
|
20
|
-
(done)->(todo:
|
|
21
|
-
(done)->(todo:
|
|
22
|
-
(done)->(todo: Smart MDX
|
|
23
|
-
(done)->(todo:
|
|
24
|
-
(done)->(todo:
|
|
25
|
-
(done)->(todo:
|
|
26
|
-
(done)->(todo:
|
|
27
|
-
(done)->(todo:
|
|
28
|
-
(done)->(todo:
|
|
29
|
-
(done)->(todo: Multi-
|
|
30
|
-
(done)->(todo:
|
|
31
|
-
(done)->(todo: Built-in
|
|
32
|
-
(done)->(todo:
|
|
33
|
-
(done)->(todo:
|
|
34
|
-
(done)->(todo:
|
|
35
|
-
(done)->(todo:
|
|
36
|
-
(done)->(todo:
|
|
37
|
-
(done)->(todo:
|
|
38
|
-
(done)->(todo:
|
|
39
|
-
(done)->(todo:
|
|
40
|
-
(done)->(todo:
|
|
41
|
-
(done)->(todo:
|
|
42
|
-
(done)->(todo: Built-in
|
|
43
|
-
(done)->(todo:
|
|
44
|
-
(done)->(todo:
|
|
45
|
-
(done)->(todo: Better
|
|
46
|
-
(done)->(todo:
|
|
47
|
-
(done)->(todo:
|
|
48
|
-
(done)->(todo:
|
|
49
|
-
(done)->(todo:
|
|
50
|
-
(done)->(todo:
|
|
51
|
-
(done)->(todo:
|
|
52
|
-
(done)->(todo:
|
|
53
|
-
(done)->(todo:
|
|
54
|
-
(done)->(todo:
|
|
55
|
-
(done)->(todo:
|
|
56
|
-
(done)->(todo:
|
|
57
|
-
(done)->(todo:
|
|
58
|
-
(done)->(todo: Removed
|
|
59
|
-
(done)->(todo: Standardized HTML comment
|
|
60
|
-
(done)->(todo:
|
|
61
|
-
(done)->(todo: Modernized all
|
|
62
|
-
(done)->(todo: Created
|
|
63
|
-
(done)->(todo:
|
|
64
|
-
(done)->(todo: Fixed
|
|
65
|
-
(done)->(todo:
|
|
66
|
-
(done)->(todo:
|
|
67
|
-
(done)->(todo:
|
|
68
|
-
(done)->(todo:
|
|
69
|
-
(done)->(todo:
|
|
70
|
-
(done)->(todo:
|
|
71
|
-
(done)->(todo:
|
|
72
|
-
(done)->(todo:
|
|
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]
|