sommark 4.0.3 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +304 -73
  2. package/cli/cli.mjs +1 -1
  3. package/cli/commands/build.js +3 -1
  4. package/cli/commands/help.js +2 -0
  5. package/cli/commands/init.js +25 -6
  6. package/cli/constants.js +2 -1
  7. package/cli/helpers/transpile.js +5 -2
  8. package/constants/html_props.js +1 -0
  9. package/core/evaluator.js +1061 -0
  10. package/core/formats.js +15 -7
  11. package/core/helpers/config-loader.js +16 -8
  12. package/core/helpers/lib.js +72 -0
  13. package/core/helpers/preprocessor.js +202 -0
  14. package/core/helpers/runtimeOutput.js +28 -0
  15. package/core/helpers/url.js +12 -0
  16. package/core/labels.js +9 -2
  17. package/core/lexer.js +228 -61
  18. package/core/modules.js +338 -60
  19. package/core/parser.js +275 -55
  20. package/core/tokenTypes.js +11 -0
  21. package/core/transpiler.js +352 -66
  22. package/core/validator.js +70 -7
  23. package/formatter/tag.js +31 -7
  24. package/grammar.ebnf +21 -10
  25. package/helpers/fetch-fs.js +37 -0
  26. package/helpers/safeDataParser.js +3 -3
  27. package/helpers/spinner.js +97 -0
  28. package/helpers/utils.js +46 -0
  29. package/helpers/virtual-fs.js +29 -0
  30. package/index.browser.js +87 -0
  31. package/index.js +23 -332
  32. package/index.shared.js +443 -0
  33. package/mappers/languages/html.js +50 -9
  34. package/mappers/languages/json.js +81 -38
  35. package/mappers/languages/jsonc.js +82 -0
  36. package/mappers/languages/markdown.js +88 -48
  37. package/mappers/languages/mdx.js +50 -15
  38. package/mappers/languages/text.js +67 -0
  39. package/mappers/languages/xml.js +6 -6
  40. package/mappers/mapper.js +36 -4
  41. package/mappers/shared/index.js +12 -13
  42. package/package.json +11 -2
  43. package/core/formatter.js +0 -215
package/index.js CHANGED
@@ -1,336 +1,27 @@
1
- import lexer from "./core/lexer.js";
2
- import parser from "./core/parser.js";
3
- import transpiler from "./core/transpiler.js";
4
- import Mapper from "./mappers/mapper.js";
5
- import HTML from "./mappers/languages/html.js";
6
- import MARKDOWN from "./mappers/languages/markdown.js";
7
- import MDX from "./mappers/languages/mdx.js";
8
- import Json from "./mappers/languages/json.js";
9
- import XML from "./mappers/languages/xml.js";
10
- import { runtimeError } from "./core/errors.js";
11
- import FORMATS, { textFormat, htmlFormat, markdownFormat, mdxFormat, jsonFormat, xmlFormat } from "./core/formats.js";
12
- import TOKEN_TYPES from "./core/tokenTypes.js";
13
- import * as labels from "./core/labels.js";
14
- import { resolveModules } from "./core/modules.js";
15
- import { formatAST } from "./core/formatter.js";
16
- import { validateAST } from "./core/validator.js";
17
- import { enableColor } from "./helpers/colorize.js";
18
- import { safeArg } from "./helpers/utils.js";
19
-
20
-
21
- /**
22
- * The SomMark Core Engine.
23
- * Processes SomMark code and turns it into different formats.
24
- */
25
- class SomMark {
26
- static Mapper = Mapper;
27
- /**
28
- * Creates a new SomMark engine.
29
- *
30
- * @param {Object} options - Settings for the engine.
31
- * @param {string} options.src - The SomMark code to process.
32
- * @param {string} options.format - The final format you want (like 'html' or 'markdown').
33
- * @param {Mapper|null} [options.mapperFile=null] - Custom rules for formatting.
34
- * @param {string} [options.filename="anonymous"] - The name of the file, used for errors and settings.
35
- * @param {boolean} [options.removeComments=true] - If true, comments will be removed from the final code.
36
- * @param {Object} [options.placeholders={}] - Values to use for {placeholders}.
37
- * @param {Array<string>} [options.customProps=[]] - Allowed custom HTML attributes.
38
- * @param {Array<string>} [options.importStack=[]] - Tracking for circular dependencies.
39
- */
40
- constructor({ src, format, mapperFile = null, filename = "anonymous", removeComments = true, placeholders = {}, customProps = [], importStack = [] }) {
41
- this.src = src;
42
- this.targetFormat = format;
43
- this.mapperFile = mapperFile;
44
- this.filename = filename;
45
- this.removeComments = removeComments;
46
- this.placeholders = placeholders;
47
- this.customProps = customProps;
48
- this.importStack = importStack;
49
- this.warnings = [];
50
- this._prepared = false;
51
-
52
- // Create a random token to safely wrap data
53
- this.moduleIdentityToken = `$_SM_MOD_${Math.random().toString(36).slice(2, 7)}_$`;
54
-
55
- this.Mapper = Mapper;
56
-
57
- const mapperFiles = { [htmlFormat]: HTML, [markdownFormat]: MARKDOWN, [mdxFormat]: MDX, [jsonFormat]: Json, [xmlFormat]: XML, [textFormat]: new Mapper() };
58
-
59
- if (!this.mapperFile && this.targetFormat) {
60
- const DefaultMapper = mapperFiles[this.targetFormat];
61
- if (DefaultMapper) {
62
- this.mapperFile = DefaultMapper.clone();
63
- }
64
- } else if (this.mapperFile) {
65
- this.mapperFile = this.mapperFile.clone();
66
- }
67
-
68
- if (this.mapperFile) {
69
- this.mapperFile.options.removeComments = this.removeComments;
70
- this.mapperFile.options.moduleIdentityToken = this.moduleIdentityToken;
71
- this.mapperFile.options.filename = this.filename;
72
-
73
- // Initialize custom props whitelist
74
- if (this.customProps && this.customProps.length > 0) {
75
- const props = Array.isArray(this.customProps) ? this.customProps : [this.customProps];
76
- props.forEach(prop => this.mapperFile.customProps.add(prop));
77
- }
78
- }
79
-
80
- this._initializeMappers();
81
- }
82
-
83
-
84
- /**
85
- * Adds a new rule or changes an existing one.
86
- *
87
- * @param {string} id - The name of the tag.
88
- * @param {Function} render - The function that formats the tag.
89
- * @param {Object} [options] - Extra settings for the tag.
90
- */
91
- register = (id, render, options) => {
92
- this.mapperFile.register(id, render, options);
93
- };
94
-
95
- /**
96
- * Copies rules from other mappers.
97
- *
98
- * @param {...Mapper} mappers - The mappers to copy from.
99
- */
100
- inherit = (...mappers) => {
101
- this.mapperFile.inherit(...mappers);
102
- };
103
-
104
- /**
105
- * Gets a rule by its name.
106
- *
107
- * @param {string} id - The tag name.
108
- * @returns {Object|null}
109
- */
110
- get = id => {
111
- return this.mapperFile.get(id);
112
- };
113
-
114
- removeOutput = id => {
115
- this.mapperFile.removeOutput(id);
116
- };
117
-
118
- clear = () => {
119
- this.mapperFile.clear();
120
- };
121
-
122
- _initializeMappers() {
123
- if (!this.targetFormat) {
124
- runtimeError(["{line}<$red:Undefined Format$>: <$yellow:Format argument is not defined.$>{line}"]);
125
- }
126
-
127
- if (!this.mapperFile && this.targetFormat) {
128
- runtimeError([`{line}<$red:Unknown Format$>: <$yellow:Mapper for format '${this.targetFormat}' not found.$>{line}`]);
129
- }
130
- }
131
-
132
- reportWarning(message) {
133
- this.warnings.push(message);
134
- }
135
-
136
-
137
- _ensurePrepared() {
138
- if (this._prepared) return;
139
-
140
- // Final check
141
- if (!this.mapperFile) {
142
- runtimeError([
143
- `{line}<$red:Unknown Format$>: <$yellow:No mapper found for format:$> <$green:'${this.targetFormat}'$>`,
144
- `{N}<$yellow:Make sure you have registered format mapper correctly.$>{line}`
145
- ]);
146
- }
147
-
148
- this._prepared = true;
149
- }
150
-
151
- /**
152
- * Breaks the code into small pieces called tokens.
153
- *
154
- * @param {string} [src=this.src] - The code to break apart.
155
- * @returns {Promise<Array<Object>>} - The list of tokens.
156
- */
157
- async lex(src = this.src) {
158
- this._ensurePrepared();
159
- if (src !== this.src) this.src = src;
160
- let tokens = lexer(this.src, this.filename);
161
- return tokens;
162
- }
163
-
164
- /**
165
- * Organizes the code into a tree structure.
166
- * Also handles modules and checks for errors.
167
- *
168
- * @param {string} [src=this.src] - Optional source override.
169
- * @returns {Promise<Array<Object>>} - The final code tree.
170
- */
171
- async parse(src = this.src) {
172
- const tokens = await this.lex(src);
173
- let ast = parser(tokens, this.filename, this.placeholders);
174
-
175
- ast = await resolveModules(ast, {
176
- mapperFile: this.mapperFile,
177
- filename: this.filename,
178
- format: this.targetFormat,
179
- instance: this,
180
- importStack: this.importStack
181
- });
182
-
183
- if (this.mapperFile) {
184
- validateAST(ast, this.mapperFile, this);
185
- }
186
-
187
- return ast;
188
- }
189
-
190
- parseSync(src = this.src) {
191
- this._ensurePrepared();
192
- if (src !== this.src) this.src = src;
193
- const tokens = lexer(this.src, this.filename);
194
- let ast = parser(tokens, this.filename, this.placeholders);
195
-
196
- if (this.mapperFile) {
197
- validateAST(ast, this.mapperFile, this);
198
- }
199
-
200
- return ast;
201
- }
202
-
203
- /**
204
- * Turns the SomMark code into the final format.
205
- *
206
- * @param {string} [src=this.src] - Optional source override.
207
- * @returns {Promise<string>} - The finished code.
208
- */
209
- async transpile(src = this.src) {
210
- if (src !== this.src) this.src = src;
211
- this._ensurePrepared();
212
-
213
- const ast = await this.parse(src);
214
- let result = await transpiler({ ast, format: this.targetFormat, mapperFile: this.mapperFile });
215
-
216
- return result;
217
- }
218
-
219
-
220
- async format(options = {}) {
221
- const tokens = await this.lex();
222
- const ast = parser(tokens, this.filename);
223
- return formatAST(ast, options);
224
- }
225
-
226
- formatSync(options = {}) {
227
- const tokens = lexer(this.src, this.filename);
228
- const ast = parser(tokens, this.filename);
229
- return formatAST(ast, options);
230
- }
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) {}
231
13
  }
232
-
233
- /**
234
- * A quick way to break code into tokens.
235
- * Uses HTML settings by default.
236
- *
237
- * @param {string} src - The raw SomMark source.
238
- * @param {string} [filename="anonymous"] - Filename for error context.
239
- * @returns {Promise<Array<Object>>} - The list of tokens.
240
- */
241
- const lex = async (src, filename = "anonymous") => {
242
- return await new SomMark({ src, filename, format: htmlFormat }).lex();
243
- };
244
-
245
- /**
246
- * A quick way to organize code into a tree.
247
- * Uses HTML settings by default.
248
- *
249
- * @param {string} src - The raw SomMark source.
250
- * @param {string} [filename="anonymous"] - Filename for error context.
251
- * @returns {Promise<Array<Object>>} - The final code tree.
252
- */
253
- async function parse(src, filename = "anonymous") {
254
- if (!src) {
255
- runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for parsing.$>{line}`]);
256
- }
257
- return await new SomMark({ src, filename, format: htmlFormat }).parse();
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) {}
258
24
  }
25
+ setDefaultFindAndLoadConfig(findAndLoadConfigFn);
259
26
 
260
- /**
261
- * The easiest way to process SomMark code.
262
- *
263
- * @param {Object} options - Transpilation options.
264
- * @param {string} options.src - Raw source code.
265
- * @param {string} [options.format="html"] - Target format.
266
- * @param {string} [options.filename="anonymous"] - Filename for context.
267
- * @param {Mapper|null} [options.mapperFile=null] - Custom rules for formatting.
268
- * @param {boolean} [options.removeComments=true] - Strip comments.
269
- * @param {Object} [options.placeholders={}] - Global placeholders.
270
- * @param {Array<string>} [options.customProps=[]] - Custom attribute whitelist.
271
- * @returns {Promise<string>} - Transpiled output.
272
- */
273
- async function transpile(options = {}) {
274
- const { src, format = htmlFormat, filename = "anonymous", mapperFile = null, removeComments = true, placeholders = {}, customProps = [] } = options;
275
- if (typeof options !== "object" || options === null) {
276
- runtimeError([`{line}<$red:Invalid Options:$> <$yellow:The options argument must be a non-null object.$>{line}`]);
277
- }
278
- const knownProps = ["src", "format", "filename", "mapperFile", "removeComments", "placeholders", "customProps"];
279
- Object.keys(options).forEach(key => {
280
- if (!knownProps.includes(key)) {
281
- runtimeError([
282
- `{line}<$red:Unknown Property:$> <$yellow:The property $> <$green:'${key}'$> <$yellow:is not recognized.$>{line}`
283
- ]);
284
- }
285
- });
286
- if (!src) {
287
- runtimeError([`{line}<$red:Missing Source:$> <$yellow:The 'src' argument is required for transpilation.$>{line}`]);
288
- }
289
-
290
- const sm = new SomMark({ src, format, filename, mapperFile, removeComments, placeholders, customProps });
291
- return await sm.transpile();
292
- }
293
-
294
- /**
295
- * A quick, synchronous way to get tokens.
296
- *
297
- * @param {string} src - Raw source code.
298
- * @returns {Array<Object>} - The list of tokens.
299
- */
300
- const lexSync = src => lexer(src);
301
-
302
- /**
303
- * A quick, synchronous way to get the code tree.
304
- *
305
- * @param {string} src - Raw source code.
306
- * @param {Object} [options={}] - Parsing options.
307
- * @returns {Array<Object>} - The code tree.
308
- */
309
- const parseSync = (src, options = {}) => {
310
- const { format = htmlFormat, filename = "anonymous", mapperFile = null, removeComments = true, placeholders = {}, customProps = [] } = options;
311
- return new SomMark({ src, format, filename, mapperFile, removeComments, placeholders, customProps }).parseSync();
312
- };
313
-
314
- import { findAndLoadConfig } from "./core/helpers/config-loader.js";
315
-
316
- export {
317
- HTML,
318
- MARKDOWN,
319
- MDX,
320
- Json,
321
- XML,
322
- Mapper,
323
- FORMATS,
324
- lex,
325
- parse,
326
- transpile,
327
- lexSync,
328
- parseSync,
329
- formatAST,
330
- TOKEN_TYPES,
331
- labels,
332
- enableColor,
333
- safeArg,
334
- findAndLoadConfig
335
- };
336
27
  export default SomMark;