sommark 4.1.0 → 4.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 +34 -4
- package/core/evaluator.js +408 -132
- package/core/helpers/config-loader.js +8 -8
- package/core/helpers/lib.js +1 -4
- package/core/helpers/preprocessor.js +23 -6
- package/core/helpers/url.js +12 -0
- package/core/modules.js +16 -14
- package/core/transpiler.js +23 -19
- package/dist/assets/emscripten-module-B1g2L2eS.wasm +0 -0
- package/dist/assets/emscripten-module-DHbYPfAp.wasm +0 -0
- package/dist/assets/emscripten-module-ZrHFMo7O.wasm +0 -0
- package/dist/assets/emscripten-module-uFzwHH0Y.wasm +0 -0
- package/dist/emscripten-module.browser-LGpDp2J2.js +24 -0
- package/dist/ffi-7DRR-T-4.js +3 -0
- package/dist/module-ES6BEMUI-SZ556_bi.js +10 -0
- package/dist/sommark.browser.js +14532 -0
- package/helpers/fetch-fs.js +37 -0
- package/helpers/spinner.js +7 -1
- package/helpers/virtual-fs.js +29 -0
- package/index.browser.js +87 -0
- package/index.js +23 -419
- package/index.shared.js +443 -0
- package/package.json +16 -5
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class FetchFS {
|
|
2
|
+
constructor(baseURL) {
|
|
3
|
+
this.baseURL = baseURL.endsWith("/") ? baseURL : baseURL + "/";
|
|
4
|
+
this._cache = new Map();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
_resolve(p) {
|
|
8
|
+
if (p.startsWith("http://") || p.startsWith("https://")) return p;
|
|
9
|
+
return this.baseURL + p.replace(/^\//, "");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async readFile(p, encoding) {
|
|
13
|
+
const url = this._resolve(p);
|
|
14
|
+
if (this._cache.has(url)) return this._cache.get(url);
|
|
15
|
+
const res = await fetch(url);
|
|
16
|
+
if (!res.ok) throw new Error(`File not found: ${p} (${res.status})`);
|
|
17
|
+
const text = await res.text();
|
|
18
|
+
this._cache.set(url, text);
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async exists(p) {
|
|
23
|
+
try { await this.readFile(p); return true; }
|
|
24
|
+
catch { return false; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Sync versions — backed by cache, only valid after readFile has been called for that path
|
|
28
|
+
existsSync(p) {
|
|
29
|
+
return this._cache.has(this._resolve(p));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
readFileSync(p, encoding) {
|
|
33
|
+
const cached = this._cache.get(this._resolve(p));
|
|
34
|
+
if (cached !== undefined) return cached;
|
|
35
|
+
throw new Error(`Not in cache: ${p}. Call readFile first.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/helpers/spinner.js
CHANGED
|
@@ -9,7 +9,11 @@ let originalStderrWrite = null;
|
|
|
9
9
|
let redrawTimeout = null;
|
|
10
10
|
|
|
11
11
|
export function startSpinner() {
|
|
12
|
-
if (process.stdout
|
|
12
|
+
if (typeof process === "undefined" || !process.stdout?.isTTY) {
|
|
13
|
+
spinnerDepth++;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!activeSpinner) {
|
|
13
17
|
// Hide terminal cursor for a clean premium visual feel
|
|
14
18
|
process.stdout.write("\x1b[?25l");
|
|
15
19
|
|
|
@@ -84,8 +88,10 @@ export function stopSpinner() {
|
|
|
84
88
|
originalStderrWrite = null;
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
if (typeof process !== "undefined" && process.stdout) {
|
|
87
92
|
// Clear the spinner line and restore the terminal cursor
|
|
88
93
|
process.stdout.write("\r\x1b[K\x1b[?25h");
|
|
89
94
|
}
|
|
95
|
+
}
|
|
90
96
|
}
|
|
91
97
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from "pathe";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Avoids any dependency on Node.js built-in modules (like buffer, path, stream).
|
|
5
|
+
*/
|
|
6
|
+
export class VirtualFS {
|
|
7
|
+
constructor(files = {}) {
|
|
8
|
+
this.files = {};
|
|
9
|
+
for (const [key, value] of Object.entries(files)) {
|
|
10
|
+
this.files[path.normalize(key)] = value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
existsSync(p) {
|
|
15
|
+
const normalized = path.normalize(p);
|
|
16
|
+
return normalized in this.files;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
readFileSync(p, encoding) {
|
|
20
|
+
const normalized = path.normalize(p);
|
|
21
|
+
if (normalized in this.files) {
|
|
22
|
+
return this.files[normalized];
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`File not found: ${p}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async exists(p) { return this.existsSync(p); }
|
|
28
|
+
async readFile(p, encoding) { return this.readFileSync(p, encoding); }
|
|
29
|
+
}
|
package/index.browser.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import SomMark, { setDefaultFs, setDefaultCwd } from "./index.shared.js";
|
|
2
|
+
export * from "./index.shared.js";
|
|
3
|
+
|
|
4
|
+
setDefaultFs(null);
|
|
5
|
+
setDefaultCwd("/");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a relative path into a full URL using the current document location.
|
|
9
|
+
* Use this to set `baseDir` when loading .smark modules via fetch in the browser.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} relativePath - Path relative to the HTML document (e.g. "./templates/").
|
|
12
|
+
* @returns {string} Absolute URL string suitable for use as `baseDir`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import SomMark, { resolveBaseDir } from "sommark/browser";
|
|
16
|
+
* const engine = new SomMark({ src, format: "html", baseDir: resolveBaseDir("./templates/") });
|
|
17
|
+
*/
|
|
18
|
+
export function resolveBaseDir(relativePath = "./") {
|
|
19
|
+
if (typeof document === "undefined") {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"[SomMark] resolveBaseDir() can only be called in a browser environment.\n" +
|
|
22
|
+
"In Node.js, pass a file path directly as 'baseDir' instead."
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof relativePath !== "string" || relativePath.trim() === "") {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"[SomMark] resolveBaseDir() expects a non-empty string path, " +
|
|
29
|
+
`but received: ${JSON.stringify(relativePath)}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
return new URL(relativePath, document.baseURI).href;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`[SomMark] resolveBaseDir() could not resolve path '${relativePath}' ` +
|
|
38
|
+
`against document URL '${document.baseURI}'.\n${err.message}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Injects compiled HTML into a container and activates any <script> tags inside it.
|
|
45
|
+
* Browsers intentionally skip scripts inserted via innerHTML — this re-creates each
|
|
46
|
+
* one as a live DOM element so they execute normally.
|
|
47
|
+
*
|
|
48
|
+
* @param {HTMLElement} container - The element to render into.
|
|
49
|
+
* @param {string} html - The compiled HTML string.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* import SomMark, { renderCompiledHTML } from "sommark/browser";
|
|
53
|
+
* const html = await new SomMark({ src, format: "html" }).transpile();
|
|
54
|
+
* renderCompiledHTML(document.getElementById("output"), html);
|
|
55
|
+
*/
|
|
56
|
+
export function renderCompiledHTML(container, html) {
|
|
57
|
+
if (typeof document === "undefined") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"[SomMark] renderCompiledHTML() can only be called in a browser environment."
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (!(container instanceof HTMLElement)) {
|
|
63
|
+
throw new TypeError(
|
|
64
|
+
"[SomMark] renderCompiledHTML() expects an HTMLElement as the first argument, " +
|
|
65
|
+
`but received: ${Object.prototype.toString.call(container)}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (typeof html !== "string") {
|
|
69
|
+
throw new TypeError(
|
|
70
|
+
"[SomMark] renderCompiledHTML() expects a string as the second argument, " +
|
|
71
|
+
`but received: ${Object.prototype.toString.call(html)}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
container.innerHTML = html;
|
|
76
|
+
|
|
77
|
+
for (const inertScript of container.querySelectorAll("script")) {
|
|
78
|
+
const liveScript = document.createElement("script");
|
|
79
|
+
for (const { name, value } of inertScript.attributes) {
|
|
80
|
+
liveScript.setAttribute(name, value);
|
|
81
|
+
}
|
|
82
|
+
liveScript.textContent = inertScript.textContent;
|
|
83
|
+
inertScript.replaceWith(liveScript);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default SomMark;
|
package/index.js
CHANGED
|
@@ -1,423 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { runtimeError } from "./core/errors.js";
|
|
14
|
-
import FORMATS, { textFormat, htmlFormat, markdownFormat, mdxFormat, jsonFormat, jsoncFormat, xmlFormat } from "./core/formats.js";
|
|
15
|
-
import TOKEN_TYPES from "./core/tokenTypes.js";
|
|
16
|
-
import * as labels from "./core/labels.js";
|
|
17
|
-
import { resolveModules } from "./core/modules.js";
|
|
18
|
-
import { validateAST } from "./core/validator.js";
|
|
19
|
-
import { enableColor } from "./helpers/colorize.js";
|
|
20
|
-
import { safeArg } from "./helpers/utils.js";
|
|
21
|
-
import { startSpinner, stopSpinner } from "./helpers/spinner.js";
|
|
22
|
-
import { preprocessRuntimeLogic } from "./core/helpers/preprocessor.js";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* The SomMark Core Engine.
|
|
26
|
-
* Processes SomMark code and turns it into different formats.
|
|
27
|
-
*/
|
|
28
|
-
class SomMark {
|
|
29
|
-
static Mapper = Mapper;
|
|
30
|
-
/**
|
|
31
|
-
* Creates a new SomMark engine.
|
|
32
|
-
*
|
|
33
|
-
* @param {Object} options - Settings for the engine.
|
|
34
|
-
* @param {string} options.src - The SomMark code to process.
|
|
35
|
-
* @param {string} options.format - The final format you want (like 'html' or 'markdown').
|
|
36
|
-
* @param {Mapper|null} [options.mapperFile=null] - Custom rules for formatting.
|
|
37
|
-
* @param {string} [options.filename="anonymous"] - The name of the file, used for errors and settings.
|
|
38
|
-
* @param {boolean} [options.removeComments=true] - If true, comments will be removed from the final code.
|
|
39
|
-
* @param {Object} [options.placeholders={}] - Values to use for p{placeholders}.
|
|
40
|
-
* @param {Array<string>} [options.customProps=[]] - Allowed custom HTML attributes.
|
|
41
|
-
* @param {Object} [options.importAliases={}] - Custom path aliases for modules.
|
|
42
|
-
* @param {Array<string>} [options.importStack=[]] - Tracking for circular dependencies.
|
|
43
|
-
* @param {string} [options.baseDir=null] - The base directory for resolving relative paths.
|
|
44
|
-
*/
|
|
45
|
-
constructor(options = {}) {
|
|
46
|
-
const { src, ast = null, format, mapperFile = null, filename = "anonymous", removeComments = true, placeholders = {}, customProps = [], fallbackTarget = "style", outputValidator = null, importAliases = {}, importStack = [], baseDir = null, moduleCache = null, showSpinner = true, security = {}, generateRuntimeOutput = false, hideRuntimeOutput = false, moduleIdentityToken = null } = options;
|
|
47
|
-
this.rawSettings = options;
|
|
48
|
-
this.src = src;
|
|
49
|
-
this.ast = ast;
|
|
50
|
-
this.targetFormat = format;
|
|
51
|
-
this.mapperFile = mapperFile;
|
|
52
|
-
this.filename = filename;
|
|
53
|
-
this.removeComments = removeComments;
|
|
54
|
-
this.placeholders = placeholders;
|
|
55
|
-
this.customProps = customProps;
|
|
56
|
-
this.generateRuntimeOutput = generateRuntimeOutput;
|
|
57
|
-
this.hideRuntimeOutput = hideRuntimeOutput;
|
|
58
|
-
|
|
59
|
-
// Validate fallbackTarget
|
|
60
|
-
const VALID_FALLBACK_TARGETS = new Set(["style", "class", false]);
|
|
61
|
-
if (!VALID_FALLBACK_TARGETS.has(fallbackTarget)) {
|
|
62
|
-
runtimeError([
|
|
63
|
-
`{line}<$red:Invalid fallbackTarget$>: <$green:'${fallbackTarget}'$> <$yellow:is not a valid value.$>`,
|
|
64
|
-
`{N}<$yellow:Use$> <$green:'style'$><$yellow:,$> <$green:'class'$><$yellow:, or$> <$green:false$><$yellow:.$>{line}`
|
|
65
|
-
]);
|
|
66
|
-
}
|
|
67
|
-
this.fallbackTarget = fallbackTarget;
|
|
68
|
-
this.outputValidator = outputValidator;
|
|
69
|
-
this.importAliases = importAliases;
|
|
70
|
-
this.importStack = importStack;
|
|
71
|
-
this.baseDir = baseDir;
|
|
72
|
-
this.moduleCache = moduleCache || new Map();
|
|
73
|
-
this.showSpinner = showSpinner;
|
|
74
|
-
this.security = {
|
|
75
|
-
allowRaw: security?.allowRaw !== false,
|
|
76
|
-
maxDepth: security?.maxDepth ?? 5,
|
|
77
|
-
timeout: security?.timeout ?? 5000,
|
|
78
|
-
sanitize: typeof security?.sanitize === "function" ? security.sanitize : null,
|
|
79
|
-
allowFetch: security?.allowFetch !== false,
|
|
80
|
-
allowHttp: security?.allowHttp === true,
|
|
81
|
-
allowedOrigins: Array.isArray(security?.allowedOrigins) ? security.allowedOrigins.map(o => o.toLowerCase()) : null,
|
|
82
|
-
allowedExtensions: Array.isArray(security?.allowedExtensions) ? security.allowedExtensions.map(e => e.toLowerCase()) : null
|
|
83
|
-
};
|
|
84
|
-
this.warnings = [];
|
|
85
|
-
this._prepared = false;
|
|
86
|
-
|
|
87
|
-
// Create a random token to safely wrap data
|
|
88
|
-
this.moduleIdentityToken = moduleIdentityToken || `$_SM_MOD_${Math.random().toString(36).slice(2, 7)}_$`;
|
|
89
|
-
|
|
90
|
-
this.Mapper = Mapper;
|
|
91
|
-
|
|
92
|
-
const mapperFiles = { [htmlFormat]: HTML, [markdownFormat]: MARKDOWN, [mdxFormat]: MDX, [jsonFormat]: Json, [jsoncFormat]: Jsonc, [xmlFormat]: XML, [textFormat]: TEXT };
|
|
93
|
-
|
|
94
|
-
if (!this.mapperFile && this.targetFormat) {
|
|
95
|
-
const DefaultMapper = mapperFiles[this.targetFormat];
|
|
96
|
-
if (DefaultMapper) {
|
|
97
|
-
this.mapperFile = DefaultMapper.clone();
|
|
98
|
-
}
|
|
99
|
-
} else if (this.mapperFile) {
|
|
100
|
-
this.mapperFile = this.mapperFile.clone();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (this.mapperFile) {
|
|
104
|
-
this.mapperFile.options.removeComments = this.removeComments;
|
|
105
|
-
this.mapperFile.options.moduleIdentityToken = this.moduleIdentityToken;
|
|
106
|
-
this.mapperFile.options.filename = this.filename;
|
|
107
|
-
|
|
108
|
-
this.mapperFile.options.usePrivateAttributes = this.usePrivateAttributes;
|
|
109
|
-
this.mapperFile.options.fallbackTarget = this.fallbackTarget;
|
|
110
|
-
|
|
111
|
-
// Initialize custom props whitelist
|
|
112
|
-
if (this.customProps && this.customProps.length > 0) {
|
|
113
|
-
const props = Array.isArray(this.customProps) ? this.customProps : [this.customProps];
|
|
114
|
-
props.forEach(prop => this.mapperFile.customProps.add(prop));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
this._initializeMappers();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Adds a new rule or changes an existing one.
|
|
124
|
-
*
|
|
125
|
-
* @param {string} id - The name of the tag.
|
|
126
|
-
* @param {Function} render - The function that formats the tag.
|
|
127
|
-
* @param {Object} [options] - Extra settings for the tag.
|
|
128
|
-
*/
|
|
129
|
-
register = (id, render, options) => {
|
|
130
|
-
this.mapperFile.register(id, render, options);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Copies rules from other mappers.
|
|
135
|
-
*
|
|
136
|
-
* @param {...Mapper} mappers - The mappers to copy from.
|
|
137
|
-
*/
|
|
138
|
-
inherit = (...mappers) => {
|
|
139
|
-
this.mapperFile.inherit(...mappers);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Gets a rule by its name.
|
|
144
|
-
*
|
|
145
|
-
* @param {string} id - The tag name.
|
|
146
|
-
* @returns {Object|null}
|
|
147
|
-
*/
|
|
148
|
-
get = id => {
|
|
149
|
-
return this.mapperFile.get(id);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
removeOutput = id => {
|
|
153
|
-
this.mapperFile.removeOutput(id);
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
clear = () => {
|
|
157
|
-
this.mapperFile.clear();
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
_initializeMappers() {
|
|
161
|
-
if (!this.targetFormat) {
|
|
162
|
-
runtimeError(["{line}<$red:Undefined Format$>: <$yellow:Format argument is not defined.$>{line}"]);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (!this.mapperFile && this.targetFormat) {
|
|
166
|
-
runtimeError([`{line}<$red:Unknown Format$>: <$yellow:Mapper for format '${this.targetFormat}' not found.$>{line}`]);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
reportWarning(message) {
|
|
171
|
-
this.warnings.push(message);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
_ensurePrepared() {
|
|
176
|
-
if (this._prepared) return;
|
|
177
|
-
|
|
178
|
-
// Final check
|
|
179
|
-
if (!this.mapperFile) {
|
|
180
|
-
runtimeError([
|
|
181
|
-
`{line}<$red:Unknown Format$>: <$yellow:No mapper found for format:$> <$green:'${this.targetFormat}'$>`,
|
|
182
|
-
`{N}<$yellow:Make sure you have registered format mapper correctly.$>{line}`
|
|
183
|
-
]);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
this._prepared = true;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Breaks the code into small pieces called tokens.
|
|
191
|
-
*
|
|
192
|
-
* @param {string} [src=this.src] - The code to break apart.
|
|
193
|
-
* @returns {Promise<Array<Object>>} - The list of tokens.
|
|
194
|
-
*/
|
|
195
|
-
async lex(src = this.src) {
|
|
196
|
-
this._ensurePrepared();
|
|
197
|
-
if (src !== this.src) this.src = src;
|
|
198
|
-
let tokens = lexer(this.src, this.filename);
|
|
199
|
-
return tokens;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
lexSync(src = this.src) {
|
|
203
|
-
this._ensurePrepared();
|
|
204
|
-
if (src !== this.src) this.src = src;
|
|
205
|
-
let tokens = lexer(this.src, this.filename);
|
|
206
|
-
return tokens;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Organizes the code into a tree structure.
|
|
211
|
-
* Also handles modules and checks for errors.
|
|
212
|
-
*
|
|
213
|
-
* @param {string} [src=this.src] - Optional source override.
|
|
214
|
-
* @returns {Promise<Array<Object>>} - The final code tree.
|
|
215
|
-
*/
|
|
216
|
-
async parse(src = this.src) {
|
|
217
|
-
const tokens = await this.lex(src);
|
|
218
|
-
let ast = parser(tokens, this.filename, this.placeholders, {});
|
|
219
|
-
|
|
220
|
-
ast = await resolveModules(ast, {
|
|
221
|
-
mapperFile: this.mapperFile,
|
|
222
|
-
filename: this.filename,
|
|
223
|
-
format: this.targetFormat,
|
|
224
|
-
instance: this,
|
|
225
|
-
importStack: this.importStack
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
if (this.mapperFile) {
|
|
229
|
-
validateAST(ast, this.mapperFile, this);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return ast;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
parseSync(src = this.src) {
|
|
236
|
-
this._ensurePrepared();
|
|
237
|
-
if (src !== this.src) this.src = src;
|
|
238
|
-
const tokens = lexer(this.src, this.filename);
|
|
239
|
-
let ast = parser(tokens, this.filename, this.placeholders, {});
|
|
240
|
-
|
|
241
|
-
if (this.mapperFile) {
|
|
242
|
-
validateAST(ast, this.mapperFile, this);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return ast;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Turns the SomMark code into the final format.
|
|
250
|
-
*
|
|
251
|
-
* @param {string} [src=this.src] - Optional source override.
|
|
252
|
-
* @returns {Promise<string>} - The finished code.
|
|
253
|
-
*/
|
|
254
|
-
async transpile(src = this.src) {
|
|
255
|
-
if (src !== this.src) this.src = src;
|
|
256
|
-
this._ensurePrepared();
|
|
257
|
-
|
|
258
|
-
if (this.showSpinner) startSpinner();
|
|
259
|
-
try {
|
|
260
|
-
const ast = this.ast || await this.parse(src);
|
|
261
|
-
let result = await transpiler({
|
|
262
|
-
ast,
|
|
263
|
-
format: this.targetFormat,
|
|
264
|
-
mapperFile: this.mapperFile,
|
|
265
|
-
security: this.security,
|
|
266
|
-
settings: this.rawSettings,
|
|
267
|
-
generateRuntimeOutput: this.generateRuntimeOutput,
|
|
268
|
-
hideRuntimeOutput: this.hideRuntimeOutput
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
if (this.outputValidator && typeof this.outputValidator === "function") {
|
|
272
|
-
await this.outputValidator(result);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return result;
|
|
276
|
-
} finally {
|
|
277
|
-
if (this.showSpinner) stopSpinner();
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
|
|
1
|
+
import SomMark, { setDefaultFs, setDefaultCwd, setDefaultFindAndLoadConfig } from "./index.shared.js";
|
|
2
|
+
export * from "./index.shared.js";
|
|
3
|
+
|
|
4
|
+
// Node-specific filesystem import
|
|
5
|
+
let nodeFs = null;
|
|
6
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
7
|
+
try {
|
|
8
|
+
nodeFs = await import("node:fs").then(m => m.default || m);
|
|
9
|
+
// Add async interface so modules.js can use await fs.exists / await fs.readFile
|
|
10
|
+
nodeFs.exists = (p) => nodeFs.promises.access(p).then(() => true).catch(() => false);
|
|
11
|
+
nodeFs.readFile = (p, enc) => nodeFs.promises.readFile(p, enc);
|
|
12
|
+
} catch (e) {}
|
|
282
13
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (src === undefined || src === null) {
|
|
294
|
-
runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for tokenization.$>{line}`]);
|
|
295
|
-
}
|
|
296
|
-
if (typeof src !== "string") {
|
|
297
|
-
runtimeError([`{line}<$red:Invalid Source Type:$> <$yellow:The 'src' argument must be a string, received ${typeof src}.$>{line}`]);
|
|
298
|
-
}
|
|
299
|
-
return lexer(src, filename);
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* A quick way to organize code into a tree.
|
|
304
|
-
* Uses HTML settings by default.
|
|
305
|
-
*
|
|
306
|
-
* @param {string} src - The raw SomMark source.
|
|
307
|
-
* @param {string} [filename="anonymous"] - Filename for error context.
|
|
308
|
-
* @returns {Promise<Array<Object>>} - The final code tree.
|
|
309
|
-
*/
|
|
310
|
-
async function parse(src, filename = "anonymous") {
|
|
311
|
-
if (src === undefined || src === null) {
|
|
312
|
-
runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for parsing.$>{line}`]);
|
|
313
|
-
}
|
|
314
|
-
if (typeof src !== "string") {
|
|
315
|
-
runtimeError([`{line}<$red:Invalid Source Type:$> <$yellow:The 'src' argument must be a string, received ${typeof src}.$>{line}`]);
|
|
316
|
-
}
|
|
317
|
-
return await new SomMark({ src, filename, format: textFormat }).parse();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Transpiles SomMark code to a target format.
|
|
322
|
-
*
|
|
323
|
-
* @param {Object} options - Transpilation options.
|
|
324
|
-
* @param {string} options.src - Raw source code.
|
|
325
|
-
* @param {string} [options.format="html"] - Target format.
|
|
326
|
-
* @param {string} [options.filename="anonymous"] - Filename for context.
|
|
327
|
-
* @param {Mapper|null} [options.mapperFile=null] - Custom rules for formatting.
|
|
328
|
-
* @param {boolean} [options.removeComments=true] - Strip comments.
|
|
329
|
-
* @param {Object} [options.placeholders={}] - Global placeholders.
|
|
330
|
-
* @param {Array<string>} [options.customProps=[]] - Custom attribute whitelist.
|
|
331
|
-
* @param {Object} [options.importAliases={}] - Custom path aliases for modules.
|
|
332
|
-
* @returns {Promise<string>} - Transpiled output.
|
|
333
|
-
*/
|
|
334
|
-
async function transpile(options = {}) {
|
|
335
|
-
if (typeof options !== "object" || options === null) {
|
|
336
|
-
runtimeError([`{line}<$red:Invalid Options:$> <$yellow:The options argument must be a non-null object.$>{line}`]);
|
|
337
|
-
}
|
|
338
|
-
const { src, ast, format } = options;
|
|
339
|
-
|
|
340
|
-
const knownProps = ["src", "ast", "format", "filename", "mapperFile", "removeComments", "placeholders", "customProps", "importAliases", "fallbackTarget", "usePrivateAttributes", "outputValidator", "showSpinner", "security"];
|
|
341
|
-
Object.keys(options).forEach(key => {
|
|
342
|
-
if (!knownProps.includes(key)) {
|
|
343
|
-
runtimeError([
|
|
344
|
-
`{line}<$red:Unknown Property:$> <$yellow:The property $> <$green:'${key}'$> <$yellow:is not recognized.$>{line}`
|
|
345
|
-
]);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
if (format === undefined || format === null) {
|
|
350
|
-
runtimeError([`{line}<$red:Missing Target Format:$> <$yellow:The 'format' parameter is required for transpilation (e.g. 'html', 'markdown', 'xml', 'mdx', 'json', etc.).$>{line}`]);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if ((src === undefined || src === null) && (ast === undefined || ast === null)) {
|
|
354
|
-
runtimeError([`{line}<$red:Missing Input:$> <$yellow:Either 'src' or 'ast' must be provided for transpilation.$>{line}`]);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const sm = new SomMark(options);
|
|
358
|
-
return await sm.transpile();
|
|
14
|
+
setDefaultFs(nodeFs);
|
|
15
|
+
if (typeof process !== "undefined" && process.cwd) setDefaultCwd(process.cwd());
|
|
16
|
+
|
|
17
|
+
// Node-specific config-loader import
|
|
18
|
+
let findAndLoadConfigFn = async () => ({});
|
|
19
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
20
|
+
try {
|
|
21
|
+
const loader = await import("./core/helpers/config-loader.js");
|
|
22
|
+
findAndLoadConfigFn = loader.findAndLoadConfig;
|
|
23
|
+
} catch (e) {}
|
|
359
24
|
}
|
|
25
|
+
setDefaultFindAndLoadConfig(findAndLoadConfigFn);
|
|
360
26
|
|
|
361
|
-
/**
|
|
362
|
-
* A quick, synchronous way to get tokens.
|
|
363
|
-
*
|
|
364
|
-
* @param {string} src - Raw source code.
|
|
365
|
-
* @param {string} [filename="anonymous"] - Filename for error context.
|
|
366
|
-
* @returns {Array<Object>} - The list of tokens.
|
|
367
|
-
*/
|
|
368
|
-
const lexSync = (src, filename = "anonymous") => {
|
|
369
|
-
if (src === undefined || src === null) {
|
|
370
|
-
runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for tokenization.$>{line}`]);
|
|
371
|
-
}
|
|
372
|
-
if (typeof src !== "string") {
|
|
373
|
-
runtimeError([`{line}<$red:Invalid Source Type:$> <$yellow:The 'src' argument must be a string, received ${typeof src}.$>{line}`]);
|
|
374
|
-
}
|
|
375
|
-
return lexer(src, filename);
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* A quick, synchronous way to get the code tree.
|
|
380
|
-
*
|
|
381
|
-
* @param {string} src - Raw source code.
|
|
382
|
-
* @param {Object} [options={}] - Parsing options.
|
|
383
|
-
* @returns {Array<Object>} - The code tree.
|
|
384
|
-
*/
|
|
385
|
-
const parseSync = (src, filename = "anonymous") => {
|
|
386
|
-
if (src === undefined || src === null) {
|
|
387
|
-
runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for parsing.$>{line}`]);
|
|
388
|
-
}
|
|
389
|
-
if (typeof src !== "string") {
|
|
390
|
-
runtimeError([`{line}<$red:Invalid Source Type:$> <$yellow:The 'src' argument must be a string, received ${typeof src}.$>{line}`]);
|
|
391
|
-
}
|
|
392
|
-
const tokens = lexer(src, filename);
|
|
393
|
-
return parser(tokens, filename);
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
import { findAndLoadConfig } from "./core/helpers/config-loader.js";
|
|
397
|
-
|
|
398
|
-
import Evaluator from "./core/evaluator.js";
|
|
399
|
-
|
|
400
|
-
export {
|
|
401
|
-
registerSharedOutputs,
|
|
402
|
-
HTML,
|
|
403
|
-
MARKDOWN,
|
|
404
|
-
MDX,
|
|
405
|
-
Json,
|
|
406
|
-
Jsonc,
|
|
407
|
-
XML,
|
|
408
|
-
Mapper,
|
|
409
|
-
FORMATS,
|
|
410
|
-
lex,
|
|
411
|
-
parse,
|
|
412
|
-
transpile,
|
|
413
|
-
lexSync,
|
|
414
|
-
parseSync,
|
|
415
|
-
TOKEN_TYPES,
|
|
416
|
-
labels,
|
|
417
|
-
enableColor,
|
|
418
|
-
safeArg,
|
|
419
|
-
findAndLoadConfig,
|
|
420
|
-
Evaluator,
|
|
421
|
-
preprocessRuntimeLogic
|
|
422
|
-
};
|
|
423
27
|
export default SomMark;
|