mancha 0.9.0 → 0.9.4
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 +17 -45
- package/dist/browser.d.ts +6 -5
- package/dist/browser.js +9 -38
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -1
- package/dist/core.d.ts +6 -24
- package/dist/core.js +8 -44
- package/dist/css_gen_basic.d.ts +2 -1
- package/dist/css_gen_basic.js +20 -2
- package/dist/dome.d.ts +26 -24
- package/dist/dome.js +60 -69
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -6
- package/dist/interfaces.d.ts +1 -2
- package/dist/mancha.d.ts +3 -0
- package/dist/mancha.js +88 -1
- package/dist/plugins.js +79 -117
- package/dist/safe_browser.d.ts +7 -0
- package/dist/safe_browser.js +19 -0
- package/dist/store.d.ts +7 -9
- package/dist/store.js +39 -19
- package/dist/test_utils.d.ts +2 -0
- package/dist/test_utils.js +14 -0
- package/dist/worker.d.ts +3 -1
- package/dist/worker.js +7 -0
- package/gulpfile.js +1 -1
- package/package.json +8 -9
- package/tsconfig.json +19 -13
- package/tsec_exemptions.json +4 -0
- package/webpack.config.js +6 -4
- package/dist/css_raw_basic.css +0 -1
- package/dist/gulp_plugin.d.ts +0 -15
- package/dist/gulp_plugin.js +0 -68
- package/yarn-error.log +0 -4103
package/dist/plugins.js
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isRelativePath } from "./
|
|
1
|
+
import { safeAnchorEl, safeAreaEl } from "safevalues/dom";
|
|
2
|
+
import { appendChild, attributeNameToCamelCase, cloneAttribute, ellipsize, firstElementChild, getAttribute, insertBefore, isRelativePath, nodeToString, removeAttribute, removeChild, replaceChildren, replaceWith, setAttribute, traverse, } from "./dome.js";
|
|
3
3
|
import { Iterator } from "./iterator.js";
|
|
4
|
-
import { makeAsyncEvalFunction } from "./store.js";
|
|
5
|
-
const KW_ATTRIBUTES = new Set([
|
|
6
|
-
":bind",
|
|
7
|
-
":bind-events",
|
|
8
|
-
":data",
|
|
9
|
-
":for",
|
|
10
|
-
":show",
|
|
11
|
-
"@watch",
|
|
12
|
-
"$html",
|
|
13
|
-
]);
|
|
14
4
|
/** @internal */
|
|
15
5
|
export var RendererPlugins;
|
|
16
6
|
(function (RendererPlugins) {
|
|
@@ -77,52 +67,38 @@ export var RendererPlugins;
|
|
|
77
67
|
// We have to retrieve the attribute, because the node property is always an absolute path.
|
|
78
68
|
const src = getAttribute(elem, "src");
|
|
79
69
|
const href = getAttribute(elem, "href");
|
|
80
|
-
const data = getAttribute(elem, "data");
|
|
81
70
|
// Early exit: if there is no element attribute to rebase, we can skip this step.
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
setAttribute(elem, "src", `${params.dirpath}/${src}`);
|
|
114
|
-
}
|
|
115
|
-
else if (tagName === "object" && data && isRelativePath(data)) {
|
|
116
|
-
setAttribute(elem, "data", `${params.dirpath}/${data}`);
|
|
117
|
-
}
|
|
118
|
-
else if (tagName === "input" && src && isRelativePath(src)) {
|
|
119
|
-
setAttribute(elem, "src", `${params.dirpath}/${src}`);
|
|
120
|
-
}
|
|
121
|
-
else if (tagName === "area" && href && isRelativePath(href)) {
|
|
122
|
-
setAttribute(elem, "href", `${params.dirpath}/${href}`);
|
|
123
|
-
}
|
|
124
|
-
else if (tagName === "base" && href && isRelativePath(href)) {
|
|
125
|
-
setAttribute(elem, "href", `${params.dirpath}/${href}`);
|
|
71
|
+
const pathref = src || href;
|
|
72
|
+
if (pathref && isRelativePath(pathref)) {
|
|
73
|
+
const relpath = `${params.dirpath}/${pathref}`;
|
|
74
|
+
this.log("Rebasing relative path as:", relpath);
|
|
75
|
+
if (tagName === "img") {
|
|
76
|
+
elem.src = relpath;
|
|
77
|
+
}
|
|
78
|
+
else if (tagName === "a") {
|
|
79
|
+
safeAnchorEl.setHref(elem, relpath);
|
|
80
|
+
}
|
|
81
|
+
else if (tagName === "source") {
|
|
82
|
+
elem.src = relpath;
|
|
83
|
+
}
|
|
84
|
+
else if (tagName === "audio") {
|
|
85
|
+
elem.src = relpath;
|
|
86
|
+
}
|
|
87
|
+
else if (tagName === "video") {
|
|
88
|
+
elem.src = relpath;
|
|
89
|
+
}
|
|
90
|
+
else if (tagName === "track") {
|
|
91
|
+
elem.src = relpath;
|
|
92
|
+
}
|
|
93
|
+
else if (tagName === "input") {
|
|
94
|
+
elem.src = relpath;
|
|
95
|
+
}
|
|
96
|
+
else if (tagName === "area") {
|
|
97
|
+
safeAreaEl.setHref(elem, relpath);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.log("Unable to rebase relative path for element:", tagName);
|
|
101
|
+
}
|
|
126
102
|
}
|
|
127
103
|
};
|
|
128
104
|
RendererPlugins.registerCustomElements = async function (node, params) {
|
|
@@ -160,7 +136,7 @@ export var RendererPlugins;
|
|
|
160
136
|
}
|
|
161
137
|
};
|
|
162
138
|
RendererPlugins.resolveTextNodeExpressions = async function (node, params) {
|
|
163
|
-
const content =
|
|
139
|
+
const content = node.nodeValue || "";
|
|
164
140
|
if (node.nodeType !== 3 || !content?.trim())
|
|
165
141
|
return;
|
|
166
142
|
this.log(`Processing node content value:\n`, ellipsize(content, 128));
|
|
@@ -175,7 +151,7 @@ export var RendererPlugins;
|
|
|
175
151
|
const result = this.eval(expr, { $elem: node });
|
|
176
152
|
updatedContent = updatedContent.replace(`{{ ${expr} }}`, String(result));
|
|
177
153
|
}
|
|
178
|
-
|
|
154
|
+
node.nodeValue = updatedContent;
|
|
179
155
|
});
|
|
180
156
|
};
|
|
181
157
|
RendererPlugins.resolveDataAttribute = async function (node, params) {
|
|
@@ -190,9 +166,9 @@ export var RendererPlugins;
|
|
|
190
166
|
// Create a subrenderer and process the tag, unless it's the root node.
|
|
191
167
|
const subrenderer = params?.rootNode === node ? this : this.clone();
|
|
192
168
|
node.renderer = subrenderer;
|
|
193
|
-
//
|
|
194
|
-
const
|
|
195
|
-
|
|
169
|
+
// Evaluate the expression.
|
|
170
|
+
const result = subrenderer.eval(dataAttr, { $elem: node });
|
|
171
|
+
// Await any promises in the result object.
|
|
196
172
|
await Promise.all(Object.entries(result).map(([key, value]) => subrenderer.set(key, value)));
|
|
197
173
|
// Skip all the children of the current node, if it's a subrenderer.
|
|
198
174
|
if (subrenderer !== this) {
|
|
@@ -204,18 +180,21 @@ export var RendererPlugins;
|
|
|
204
180
|
await subrenderer.mount(node, params);
|
|
205
181
|
}
|
|
206
182
|
};
|
|
207
|
-
RendererPlugins.
|
|
183
|
+
RendererPlugins.resolveClassAttribute = async function (node, params) {
|
|
208
184
|
if (this._skipNodes.has(node))
|
|
209
185
|
return;
|
|
210
186
|
const elem = node;
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
this.log("
|
|
187
|
+
const classAttr = getAttribute(elem, ":class");
|
|
188
|
+
if (classAttr) {
|
|
189
|
+
this.log(":class attribute found in:\n", nodeToString(node, 128));
|
|
214
190
|
// Remove the attribute from the node.
|
|
215
|
-
removeAttribute(elem, "
|
|
191
|
+
removeAttribute(elem, ":class");
|
|
192
|
+
// Store the original class attribute, if any.
|
|
193
|
+
const originalClass = getAttribute(elem, "class") || "";
|
|
216
194
|
// Compute the function's result.
|
|
217
|
-
|
|
218
|
-
|
|
195
|
+
return this.effect(function () {
|
|
196
|
+
const result = this.eval(classAttr, { $elem: node });
|
|
197
|
+
setAttribute(elem, "class", (result ? `${originalClass} ${result}` : originalClass).trim());
|
|
219
198
|
});
|
|
220
199
|
}
|
|
221
200
|
};
|
|
@@ -223,14 +202,15 @@ export var RendererPlugins;
|
|
|
223
202
|
if (this._skipNodes.has(node))
|
|
224
203
|
return;
|
|
225
204
|
const elem = node;
|
|
226
|
-
const textAttr = getAttribute(elem, "
|
|
205
|
+
const textAttr = getAttribute(elem, ":text");
|
|
227
206
|
if (textAttr) {
|
|
228
|
-
this.log("
|
|
207
|
+
this.log(":text attribute found in:\n", nodeToString(node, 128));
|
|
229
208
|
// Remove the attribute from the node.
|
|
230
|
-
removeAttribute(elem, "
|
|
209
|
+
removeAttribute(elem, ":text");
|
|
231
210
|
// Compute the function's result and track dependencies.
|
|
211
|
+
const setTextContent = (content) => this.textContent(node, content);
|
|
232
212
|
return this.effect(function () {
|
|
233
|
-
setTextContent(
|
|
213
|
+
setTextContent(this.eval(textAttr, { $elem: node }));
|
|
234
214
|
});
|
|
235
215
|
}
|
|
236
216
|
};
|
|
@@ -238,11 +218,11 @@ export var RendererPlugins;
|
|
|
238
218
|
if (this._skipNodes.has(node))
|
|
239
219
|
return;
|
|
240
220
|
const elem = node;
|
|
241
|
-
const htmlAttr = getAttribute(elem, "
|
|
221
|
+
const htmlAttr = getAttribute(elem, ":html");
|
|
242
222
|
if (htmlAttr) {
|
|
243
|
-
this.log("
|
|
223
|
+
this.log(":html attribute found in:\n", nodeToString(node, 128));
|
|
244
224
|
// Remove the attribute from the node.
|
|
245
|
-
removeAttribute(elem, "
|
|
225
|
+
removeAttribute(elem, ":html");
|
|
246
226
|
// Compute the function's result and track dependencies.
|
|
247
227
|
return this.effect(function () {
|
|
248
228
|
const result = this.eval(htmlAttr, { $elem: node });
|
|
@@ -255,50 +235,16 @@ export var RendererPlugins;
|
|
|
255
235
|
});
|
|
256
236
|
}
|
|
257
237
|
};
|
|
258
|
-
RendererPlugins.resolvePropAttributes = async function (node, params) {
|
|
259
|
-
if (this._skipNodes.has(node))
|
|
260
|
-
return;
|
|
261
|
-
const elem = node;
|
|
262
|
-
for (const attr of Array.from(elem.attributes || [])) {
|
|
263
|
-
if (attr.name.startsWith("$") && !KW_ATTRIBUTES.has(attr.name)) {
|
|
264
|
-
this.log(attr.name, "attribute found in:\n", nodeToString(node, 128));
|
|
265
|
-
// Remove the attribute from the node.
|
|
266
|
-
removeAttribute(elem, attr.name);
|
|
267
|
-
// Compute the function's result and track dependencies.
|
|
268
|
-
const propName = attributeNameToCamelCase(attr.name.slice(1));
|
|
269
|
-
await this.effect(function () {
|
|
270
|
-
node[propName] = this.eval(attr.value, { $elem: node });
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
RendererPlugins.resolveAttrAttributes = async function (node, params) {
|
|
276
|
-
if (this._skipNodes.has(node))
|
|
277
|
-
return;
|
|
278
|
-
const elem = node;
|
|
279
|
-
for (const attr of Array.from(elem.attributes || [])) {
|
|
280
|
-
if (attr.name.startsWith(":") && !KW_ATTRIBUTES.has(attr.name)) {
|
|
281
|
-
this.log(attr.name, "attribute found in:\n", nodeToString(node, 128));
|
|
282
|
-
// Remove the processed attributes from node.
|
|
283
|
-
removeAttribute(elem, attr.name);
|
|
284
|
-
// Compute the function's result and track dependencies.
|
|
285
|
-
const attrName = attr.name.slice(1);
|
|
286
|
-
this.effect(function () {
|
|
287
|
-
setAttribute(elem, attrName, this.eval(attr.value, { $elem: node }));
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
238
|
RendererPlugins.resolveEventAttributes = async function (node, params) {
|
|
293
239
|
if (this._skipNodes.has(node))
|
|
294
240
|
return;
|
|
295
241
|
const elem = node;
|
|
296
242
|
for (const attr of Array.from(elem.attributes || [])) {
|
|
297
|
-
if (attr.name.startsWith("
|
|
243
|
+
if (attr.name.startsWith(":on:")) {
|
|
298
244
|
this.log(attr.name, "attribute found in:\n", nodeToString(node, 128));
|
|
299
245
|
// Remove the processed attributes from node.
|
|
300
246
|
removeAttribute(elem, attr.name);
|
|
301
|
-
node.addEventListener?.(attr.name.substring(
|
|
247
|
+
node.addEventListener?.(attr.name.substring(4), (event) => {
|
|
302
248
|
return this.eval(attr.value, { $elem: node, $event: event });
|
|
303
249
|
});
|
|
304
250
|
}
|
|
@@ -319,7 +265,7 @@ export var RendererPlugins;
|
|
|
319
265
|
}
|
|
320
266
|
// Place the template node into a template element.
|
|
321
267
|
const parent = node.parentNode;
|
|
322
|
-
const template = createElement("template", node.ownerDocument);
|
|
268
|
+
const template = this.createElement("template", node.ownerDocument);
|
|
323
269
|
insertBefore(parent, template, node);
|
|
324
270
|
removeChild(parent, node);
|
|
325
271
|
appendChild(template, node);
|
|
@@ -380,16 +326,15 @@ export var RendererPlugins;
|
|
|
380
326
|
this.log(":bind attribute found in:\n", nodeToString(node, 128));
|
|
381
327
|
// The change events we listen for can be overriden by user.
|
|
382
328
|
const defaultEvents = ["change", "input"];
|
|
383
|
-
const updateEvents = getAttribute(elem, ":bind
|
|
329
|
+
const updateEvents = getAttribute(elem, ":bind:on")?.split(",") || defaultEvents;
|
|
384
330
|
// Remove the processed attributes from node.
|
|
385
331
|
removeAttribute(elem, ":bind");
|
|
386
|
-
removeAttribute(elem, ":bind
|
|
332
|
+
removeAttribute(elem, ":bind:on");
|
|
387
333
|
// If the element is of type checkbox, we bind to the "checked" property.
|
|
388
334
|
const prop = getAttribute(elem, "type") === "checkbox" ? "checked" : "value";
|
|
389
335
|
// Watch for updates in the store and bind our property ==> node value.
|
|
390
|
-
const propExpr = `$elem.${prop} = ${bindExpr}`;
|
|
391
336
|
this.effect(function () {
|
|
392
|
-
const result = this.eval(
|
|
337
|
+
const result = this.eval(bindExpr, { $elem: node });
|
|
393
338
|
elem[prop] = result;
|
|
394
339
|
});
|
|
395
340
|
// Bind node value ==> our property.
|
|
@@ -432,4 +377,21 @@ export var RendererPlugins;
|
|
|
432
377
|
});
|
|
433
378
|
}
|
|
434
379
|
};
|
|
380
|
+
RendererPlugins.resolveCustomAttribute = async function (node, params) {
|
|
381
|
+
if (this._skipNodes.has(node))
|
|
382
|
+
return;
|
|
383
|
+
const elem = node;
|
|
384
|
+
for (const attr of Array.from(elem.attributes || [])) {
|
|
385
|
+
if (attr.name.startsWith(":")) {
|
|
386
|
+
this.log(attr.name, "attribute found in:\n", nodeToString(node, 128));
|
|
387
|
+
// Remove the processed attributes from node.
|
|
388
|
+
removeAttribute(elem, attr.name);
|
|
389
|
+
const propName = attributeNameToCamelCase(attr.name.substring(1));
|
|
390
|
+
this.effect(function () {
|
|
391
|
+
const propValue = this.eval(attr.value, { $elem: node });
|
|
392
|
+
elem[propName] = propValue;
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
};
|
|
435
397
|
})(RendererPlugins || (RendererPlugins = {}));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Renderer as BrowserRenderer } from "./browser.js";
|
|
2
|
+
import { ParserParams } from "./interfaces.js";
|
|
3
|
+
export declare class Renderer extends BrowserRenderer {
|
|
4
|
+
protected readonly dirpath: string;
|
|
5
|
+
parseHTML(content: string, params?: ParserParams): Document | DocumentFragment;
|
|
6
|
+
}
|
|
7
|
+
export declare const Mancha: Renderer;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sanitizeHtml } from "safevalues";
|
|
2
|
+
import { safeRange, safeDomParser } from "safevalues/dom";
|
|
3
|
+
import { Renderer as BrowserRenderer } from "./browser.js";
|
|
4
|
+
import { dirname } from "./dome.js";
|
|
5
|
+
export class Renderer extends BrowserRenderer {
|
|
6
|
+
dirpath = dirname(self.location.href);
|
|
7
|
+
parseHTML(content, params = { rootDocument: false }) {
|
|
8
|
+
if (params.rootDocument) {
|
|
9
|
+
const parser = new DOMParser();
|
|
10
|
+
return safeDomParser.parseFromString(parser, sanitizeHtml(content), "text/html");
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const range = document.createRange();
|
|
14
|
+
range.selectNodeContents(document.body);
|
|
15
|
+
return safeRange.createContextualFragment(range, sanitizeHtml(content));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export const Mancha = new Renderer();
|
package/dist/store.d.ts
CHANGED
|
@@ -8,14 +8,6 @@ declare abstract class IDebouncer {
|
|
|
8
8
|
}
|
|
9
9
|
/** Default debouncer time in millis. */
|
|
10
10
|
export declare const REACTIVE_DEBOUNCE_MILLIS = 10;
|
|
11
|
-
/**
|
|
12
|
-
* Creates an evaluation function based on the provided code and arguments.
|
|
13
|
-
* @param code The code to be evaluated.
|
|
14
|
-
* @param args The arguments to be passed to the evaluation function. Default is an empty array.
|
|
15
|
-
* @returns The evaluation function.
|
|
16
|
-
*/
|
|
17
|
-
export declare function makeEvalFunction(code: string, args?: string[]): Function;
|
|
18
|
-
export declare function makeAsyncEvalFunction(code: string, args?: string[]): Function;
|
|
19
11
|
export declare class SignalStore extends IDebouncer {
|
|
20
12
|
protected readonly evalkeys: string[];
|
|
21
13
|
protected readonly expressionCache: Map<string, Function>;
|
|
@@ -38,7 +30,13 @@ export declare class SignalStore extends IDebouncer {
|
|
|
38
30
|
private proxify;
|
|
39
31
|
get $(): SignalStoreProxy;
|
|
40
32
|
/**
|
|
41
|
-
*
|
|
33
|
+
* Creates an evaluation function for the provided expression.
|
|
34
|
+
* @param expr The expression to be evaluated.
|
|
35
|
+
* @returns The evaluation function.
|
|
36
|
+
*/
|
|
37
|
+
private makeEvalFunction;
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves or creates a cached expression function for the provided expression.
|
|
42
40
|
* @param expr - The expression to retrieve or create a cached function for.
|
|
43
41
|
* @returns The cached expression function.
|
|
44
42
|
*/
|
package/dist/store.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as jexpr from "jexpr";
|
|
1
2
|
class IDebouncer {
|
|
2
3
|
timeouts = new Map();
|
|
3
4
|
debounce(millis, callback) {
|
|
@@ -19,18 +20,8 @@ class IDebouncer {
|
|
|
19
20
|
}
|
|
20
21
|
/** Default debouncer time in millis. */
|
|
21
22
|
export const REACTIVE_DEBOUNCE_MILLIS = 10;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
* @param code The code to be evaluated.
|
|
25
|
-
* @param args The arguments to be passed to the evaluation function. Default is an empty array.
|
|
26
|
-
* @returns The evaluation function.
|
|
27
|
-
*/
|
|
28
|
-
export function makeEvalFunction(code, args = []) {
|
|
29
|
-
return new Function(...args, `with (this) { return (${code}); }`);
|
|
30
|
-
}
|
|
31
|
-
export function makeAsyncEvalFunction(code, args = []) {
|
|
32
|
-
return new Function(...args, `with (this) { return (async () => (${code}))(); }`);
|
|
33
|
-
}
|
|
23
|
+
/** Shared AST factory. */
|
|
24
|
+
const AST_FACTORY = new jexpr.EvalAstFactory();
|
|
34
25
|
function isProxified(object) {
|
|
35
26
|
return object instanceof SignalStore || object["__is_proxy__"];
|
|
36
27
|
}
|
|
@@ -152,13 +143,46 @@ export class SignalStore extends IDebouncer {
|
|
|
152
143
|
return this.proxify();
|
|
153
144
|
}
|
|
154
145
|
/**
|
|
155
|
-
*
|
|
146
|
+
* Creates an evaluation function for the provided expression.
|
|
147
|
+
* @param expr The expression to be evaluated.
|
|
148
|
+
* @returns The evaluation function.
|
|
149
|
+
*/
|
|
150
|
+
makeEvalFunction(expr) {
|
|
151
|
+
// Throw an error if the expression is not a simple one-liner.
|
|
152
|
+
if (expr.includes(";")) {
|
|
153
|
+
throw new Error("Complex expressions are not supported.");
|
|
154
|
+
}
|
|
155
|
+
// If the expression includes assignment, save the left-hand side for later.
|
|
156
|
+
let assignResult = null;
|
|
157
|
+
if (expr.includes("=")) {
|
|
158
|
+
const [lhs, rhs] = expr.split("=");
|
|
159
|
+
assignResult = lhs.trim();
|
|
160
|
+
expr = rhs.trim();
|
|
161
|
+
}
|
|
162
|
+
// Otherwise, just return the simple expression function.
|
|
163
|
+
return (thisArg, args) => {
|
|
164
|
+
const ast = jexpr.parse(expr, AST_FACTORY);
|
|
165
|
+
const ctx = ast
|
|
166
|
+
?.getIds([])
|
|
167
|
+
?.map((id) => [id, args[id] ?? thisArg[id] ?? globalThis[id]]);
|
|
168
|
+
const res = ast?.evaluate(Object.fromEntries(ctx || []));
|
|
169
|
+
if (assignResult) {
|
|
170
|
+
thisArg[assignResult] = res;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
return res;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Retrieves or creates a cached expression function for the provided expression.
|
|
156
179
|
* @param expr - The expression to retrieve or create a cached function for.
|
|
157
180
|
* @returns The cached expression function.
|
|
158
181
|
*/
|
|
159
182
|
cachedExpressionFunction(expr) {
|
|
183
|
+
expr = expr.trim();
|
|
160
184
|
if (!this.expressionCache.has(expr)) {
|
|
161
|
-
this.expressionCache.set(expr, makeEvalFunction(expr
|
|
185
|
+
this.expressionCache.set(expr, this.makeEvalFunction(expr));
|
|
162
186
|
}
|
|
163
187
|
return this.expressionCache.get(expr);
|
|
164
188
|
}
|
|
@@ -172,11 +196,7 @@ export class SignalStore extends IDebouncer {
|
|
|
172
196
|
else {
|
|
173
197
|
// Otherwise, perform the expression evaluation.
|
|
174
198
|
const fn = this.cachedExpressionFunction(expr);
|
|
175
|
-
|
|
176
|
-
if (Object.keys(args).some((key) => !this.evalkeys.includes(key))) {
|
|
177
|
-
throw new Error(`Invalid argument key, must be one of: ${this.evalkeys.join(", ")}`);
|
|
178
|
-
}
|
|
179
|
-
return fn.call(thisArg, ...argvals);
|
|
199
|
+
return fn(thisArg, args);
|
|
180
200
|
}
|
|
181
201
|
}
|
|
182
202
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DomUtils } from "htmlparser2";
|
|
2
|
+
import { hasProperty } from "./dome.js";
|
|
3
|
+
export function innerHTML(elem) {
|
|
4
|
+
if (hasProperty(elem, "innerHTML"))
|
|
5
|
+
return elem.innerHTML;
|
|
6
|
+
else
|
|
7
|
+
return DomUtils.getInnerHTML(elem);
|
|
8
|
+
}
|
|
9
|
+
export function getTextContent(elem) {
|
|
10
|
+
if (hasProperty(elem, "textContent"))
|
|
11
|
+
return elem.textContent;
|
|
12
|
+
else
|
|
13
|
+
return DomUtils.textContent(elem);
|
|
14
|
+
}
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { IRenderer } from "./core.js";
|
|
2
2
|
import { ParserParams } from "./interfaces.js";
|
|
3
3
|
export declare class Renderer extends IRenderer {
|
|
4
|
-
parseHTML(content: string, params?: ParserParams): DocumentFragment;
|
|
4
|
+
parseHTML(content: string, params?: ParserParams): Document | DocumentFragment;
|
|
5
5
|
serializeHTML(root: Node | DocumentFragment | Document): string;
|
|
6
|
+
createElement(tag: string, owner?: Document | null): globalThis.Element;
|
|
7
|
+
textContent(node: Node, content: string): void;
|
|
6
8
|
}
|
|
7
9
|
export declare const Mancha: Renderer;
|
package/dist/worker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as htmlparser2 from "htmlparser2";
|
|
2
|
+
import { Element, Text } from "domhandler";
|
|
2
3
|
import { render as renderDOM } from "dom-serializer";
|
|
3
4
|
import { IRenderer } from "./core.js";
|
|
4
5
|
export class Renderer extends IRenderer {
|
|
@@ -8,6 +9,12 @@ export class Renderer extends IRenderer {
|
|
|
8
9
|
serializeHTML(root) {
|
|
9
10
|
return renderDOM(root);
|
|
10
11
|
}
|
|
12
|
+
createElement(tag, owner) {
|
|
13
|
+
return new Element(tag, {});
|
|
14
|
+
}
|
|
15
|
+
textContent(node, content) {
|
|
16
|
+
node.children = [new Text(content)];
|
|
17
|
+
}
|
|
11
18
|
}
|
|
12
19
|
// Export the renderer instance directly.
|
|
13
20
|
export const Mancha = new Renderer();
|
package/gulpfile.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mancha",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Javscript HTML rendering engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"clean": "rm -rf dist/",
|
|
11
11
|
"build": "gulp build && webpack",
|
|
12
|
-
"test": "
|
|
12
|
+
"test": "node --test dist/*.test.js",
|
|
13
13
|
"webpack": "webpack",
|
|
14
14
|
"cli": "node dist/cli.js"
|
|
15
15
|
},
|
|
@@ -31,17 +31,14 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"dom-serializer": "^2.0.0",
|
|
33
33
|
"htmlparser2": "^9.1.0",
|
|
34
|
+
"jexpr": "^1.0.0-pre.9",
|
|
34
35
|
"jsdom": "^24.0.0",
|
|
35
|
-
"
|
|
36
|
-
"yargs": "^17.7.2"
|
|
36
|
+
"safevalues": "^0.6.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/gulp": "^4.0.8",
|
|
40
39
|
"@types/jsdom": "^21.1.6",
|
|
41
|
-
"@types/mocha": "^10.0.3",
|
|
42
40
|
"@types/node": "^20.12.11",
|
|
43
41
|
"@types/path-browserify": "^1.0.1",
|
|
44
|
-
"@types/through2": "^2.0.36",
|
|
45
42
|
"@types/yargs": "^17.0.29",
|
|
46
43
|
"css-loader": "^7.1.2",
|
|
47
44
|
"csso": "^5.0.5",
|
|
@@ -49,11 +46,13 @@
|
|
|
49
46
|
"gulp-csso": "^4.0.1",
|
|
50
47
|
"gulp-run": "^1.7.1",
|
|
51
48
|
"gulp-typescript": "^6.0.0-alpha.1",
|
|
52
|
-
"mocha": "^10.2.0",
|
|
53
49
|
"static-server": "^3.0.0",
|
|
50
|
+
"terser-webpack-plugin": "^5.3.10",
|
|
54
51
|
"ts-node": "^10.9.2",
|
|
52
|
+
"tsec": "^0.2.8",
|
|
55
53
|
"typescript": "5.4.5",
|
|
56
54
|
"webpack": "5.91.0",
|
|
57
|
-
"webpack-cli": "^5.1.4"
|
|
55
|
+
"webpack-cli": "^5.1.4",
|
|
56
|
+
"yargs": "^17.7.2"
|
|
58
57
|
}
|
|
59
58
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"noImplicitAny": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"stripInternal": true,
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"plugins": [
|
|
14
|
+
{
|
|
15
|
+
"name": "tsec",
|
|
16
|
+
"exemptionConfig": "./tsec_exemptions.json"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
}
|
package/webpack.config.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
+
import TerserPlugin from "terser-webpack-plugin";
|
|
1
2
|
|
|
2
3
|
export default {
|
|
3
4
|
target: "web",
|
|
4
5
|
mode: "production",
|
|
5
6
|
entry: {
|
|
6
|
-
mancha: "./dist/
|
|
7
|
+
mancha: "./dist/mancha.js",
|
|
7
8
|
},
|
|
8
9
|
output: {
|
|
9
10
|
filename: "[name].js",
|
|
10
11
|
},
|
|
11
|
-
externals: {
|
|
12
|
-
htmlparser2: "window.htmlparser2",
|
|
13
|
-
},
|
|
14
12
|
module: {
|
|
15
13
|
rules: [
|
|
16
14
|
{
|
|
@@ -19,4 +17,8 @@ export default {
|
|
|
19
17
|
},
|
|
20
18
|
],
|
|
21
19
|
},
|
|
20
|
+
optimization: {
|
|
21
|
+
minimize: true,
|
|
22
|
+
minimizer: [new TerserPlugin({ extractComments: false })],
|
|
23
|
+
},
|
|
22
24
|
};
|
package/dist/css_raw_basic.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
html{max-width:70ch;padding:2em 1em;margin:auto;line-height:1.75;font-size:1.25em;font-family:sans-serif}h1,h2,h3,h4,h5,h6{margin:1em 0 .5em}ol,p,ul{margin-bottom:1em;color:#1d1d1d}
|
package/dist/gulp_plugin.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import * as stream from "stream";
|
|
3
|
-
/**
|
|
4
|
-
* Main entrypoint to be used in Gulp. Usage:
|
|
5
|
-
*
|
|
6
|
-
* var mancha = require('mancha/dist/gulp')
|
|
7
|
-
* gulp.src(...).pipe(mancha({myvar: myval})).pipe(...)
|
|
8
|
-
*
|
|
9
|
-
* @param context <key, value> pairs of literal string replacements. `key` will become `{{ key }}`
|
|
10
|
-
* before replacing it with `value` in the processed files.
|
|
11
|
-
*/
|
|
12
|
-
declare function mancha(context?: {
|
|
13
|
-
[key: string]: string;
|
|
14
|
-
}): stream.Transform;
|
|
15
|
-
export default mancha;
|