mancha 0.4.0 → 0.5.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/.vscode/extensions.json +3 -0
- package/README.md +11 -11
- package/dist/attributes.d.ts +13 -0
- package/dist/attributes.js +27 -0
- package/dist/browser.d.ts +8 -1
- package/dist/browser.js +32 -10
- package/dist/cli.js +3 -3
- package/dist/core.d.ts +44 -0
- package/dist/core.js +472 -0
- package/dist/gulp.d.ts +4 -4
- package/dist/gulp.js +19 -15
- package/dist/index.d.ts +8 -8
- package/dist/index.js +22 -23
- package/dist/mancha.js +1 -1
- package/dist/reactive.d.ts +52 -0
- package/dist/reactive.js +251 -0
- package/dist/worker.d.ts +8 -0
- package/dist/worker.js +35 -0
- package/package.json +6 -2
- package/webpack.config.js +0 -1
- package/dist/web.d.ts +0 -22
- package/dist/web.js +0 -172
- package/yarn-error.log +0 -3935
package/dist/core.js
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.IRenderer = exports.safeEval = exports.extractTextNodeKeys = exports.resolvePath = exports.folderPath = exports.traverse = void 0;
|
|
13
|
+
const path = require("path-browserify");
|
|
14
|
+
const reactive_1 = require("./reactive");
|
|
15
|
+
const attributes_1 = require("./attributes");
|
|
16
|
+
const KW_ATTRIBUTES = new Set([":bind", ":bind-events", ":data", ":for", ":show", "@watch"]);
|
|
17
|
+
const ATTR_SHORTHANDS = {
|
|
18
|
+
// ":class": ":class-name",
|
|
19
|
+
$text: "$text-content",
|
|
20
|
+
$html: "$inner-HTML",
|
|
21
|
+
};
|
|
22
|
+
function* traverse(root, skip = new Set()) {
|
|
23
|
+
const explored = new Set();
|
|
24
|
+
const frontier = Array.from(root.childNodes).filter((node) => !skip.has(node));
|
|
25
|
+
// Also yield the root node.
|
|
26
|
+
yield root;
|
|
27
|
+
while (frontier.length) {
|
|
28
|
+
const node = frontier.pop();
|
|
29
|
+
if (!explored.has(node)) {
|
|
30
|
+
explored.add(node);
|
|
31
|
+
yield node;
|
|
32
|
+
}
|
|
33
|
+
if (node.childNodes) {
|
|
34
|
+
Array.from(node.childNodes)
|
|
35
|
+
.filter((node) => !skip.has(node))
|
|
36
|
+
.forEach((node) => frontier.push(node));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.traverse = traverse;
|
|
41
|
+
function folderPath(fpath) {
|
|
42
|
+
if (fpath.endsWith("/")) {
|
|
43
|
+
return fpath.slice(0, -1);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return path.dirname(fpath);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.folderPath = folderPath;
|
|
50
|
+
function resolvePath(fpath) {
|
|
51
|
+
if (fpath.includes("://")) {
|
|
52
|
+
const [scheme, remotePath] = fpath.split("://", 2);
|
|
53
|
+
return `${scheme}://${resolvePath("/" + remotePath).substring(1)}`;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return path.resolve(fpath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.resolvePath = resolvePath;
|
|
60
|
+
function extractTextNodeKeys(content) {
|
|
61
|
+
const matcher = new RegExp(/{{ ([\w\.]+) }}/gm);
|
|
62
|
+
return Array.from(content.matchAll(matcher)).map((match) => {
|
|
63
|
+
const orig = match[0];
|
|
64
|
+
let key = match[1];
|
|
65
|
+
const props = [];
|
|
66
|
+
if (key.includes(".")) {
|
|
67
|
+
const parts = key.split(".");
|
|
68
|
+
key = parts[0];
|
|
69
|
+
props.push(...parts.slice(1));
|
|
70
|
+
}
|
|
71
|
+
return [orig, key, props];
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
exports.extractTextNodeKeys = extractTextNodeKeys;
|
|
75
|
+
function safeEval(code, context, args = {}) {
|
|
76
|
+
const inner = `with (this) { return (async () => (${code}))(); }`;
|
|
77
|
+
return new Function(...Object.keys(args), inner).call(context, ...Object.values(args));
|
|
78
|
+
}
|
|
79
|
+
exports.safeEval = safeEval;
|
|
80
|
+
const _DEFAULT_RENDERER_PARAMS = { maxdepth: 10 };
|
|
81
|
+
class IRenderer extends reactive_1.ReactiveProxyStore {
|
|
82
|
+
constructor() {
|
|
83
|
+
super(...arguments);
|
|
84
|
+
this.fsroot = ".";
|
|
85
|
+
this.skipNodes = new Set();
|
|
86
|
+
}
|
|
87
|
+
clone() {
|
|
88
|
+
return new this.constructor(Object.fromEntries(this.store.entries()));
|
|
89
|
+
}
|
|
90
|
+
log(params, ...args) {
|
|
91
|
+
if (params === null || params === void 0 ? void 0 : params.debug)
|
|
92
|
+
console.debug(...args);
|
|
93
|
+
}
|
|
94
|
+
eval(expr_1) {
|
|
95
|
+
return __awaiter(this, arguments, void 0, function* (expr, args = {}, params) {
|
|
96
|
+
const proxy = (0, reactive_1.proxify)(this);
|
|
97
|
+
const result = yield safeEval(expr, proxy, Object.assign({}, args));
|
|
98
|
+
this.log(params, `eval \`${expr}\` => `, result);
|
|
99
|
+
return result;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
resolveIncludes(root, params) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
params = Object.assign({ fsroot: this.fsroot }, _DEFAULT_RENDERER_PARAMS, params);
|
|
105
|
+
const includes = Array.from(traverse(root, this.skipNodes))
|
|
106
|
+
.map((node) => node)
|
|
107
|
+
.filter((node) => { var _a; return ((_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === "include"; })
|
|
108
|
+
.map((node) => __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
var _a, _b;
|
|
110
|
+
const src = (_a = node.getAttribute) === null || _a === void 0 ? void 0 : _a.call(node, "src");
|
|
111
|
+
const dataset = Object.assign({}, node.dataset);
|
|
112
|
+
// Add all the data-* attributes as properties to current context.
|
|
113
|
+
// NOTE: this will propagate to all subsequent render calls, including nested calls.
|
|
114
|
+
Object.entries(dataset).forEach(([key, attr]) => this.set(key, (0, attributes_1.decodeHtmlAttrib)(attr)));
|
|
115
|
+
// Early exit: <include> tags must have a src attribute.
|
|
116
|
+
if (!src) {
|
|
117
|
+
throw new Error(`"src" attribute missing from ${node}.`);
|
|
118
|
+
}
|
|
119
|
+
// The included file will replace this tag.
|
|
120
|
+
const handler = (fragment) => {
|
|
121
|
+
node.replaceWith(...Array.from(fragment.childNodes));
|
|
122
|
+
};
|
|
123
|
+
// Decrement the maxdepth param.
|
|
124
|
+
params.maxdepth--;
|
|
125
|
+
// Case 1: Absolute remote path.
|
|
126
|
+
if (src.indexOf("://") !== -1) {
|
|
127
|
+
yield this.renderRemotePath(src, Object.assign(Object.assign({}, params), { isRoot: false })).then(handler);
|
|
128
|
+
// Case 2: Relative remote path.
|
|
129
|
+
}
|
|
130
|
+
else if (((_b = params.fsroot) === null || _b === void 0 ? void 0 : _b.indexOf("://")) !== -1) {
|
|
131
|
+
const relpath = `${params.fsroot}/${src}`;
|
|
132
|
+
yield this.renderRemotePath(relpath, Object.assign(Object.assign({}, params), { isRoot: false })).then(handler);
|
|
133
|
+
// Case 3: Local absolute path.
|
|
134
|
+
}
|
|
135
|
+
else if (src.charAt(0) === "/") {
|
|
136
|
+
yield this.renderLocalPath(src, Object.assign(Object.assign({}, params), { isRoot: false })).then(handler);
|
|
137
|
+
// Case 4: Local relative path.
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const relpath = path.join(params.fsroot, src);
|
|
141
|
+
yield this.renderLocalPath(relpath, Object.assign(Object.assign({}, params), { isRoot: false })).then(handler);
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
// Wait for all the rendering operations to complete.
|
|
145
|
+
yield Promise.all(includes);
|
|
146
|
+
// Re-render until no changes are made.
|
|
147
|
+
if (includes.length === 0) {
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
else if (params.maxdepth === 0) {
|
|
151
|
+
throw new Error("Maximum recursion depth reached.");
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
return this.resolveIncludes(root, {
|
|
155
|
+
fsroot: params.fsroot,
|
|
156
|
+
maxdepth: params.maxdepth - 1,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
resolveTextNode(node, params) {
|
|
162
|
+
if (node.nodeType !== 3)
|
|
163
|
+
return [];
|
|
164
|
+
const content = node.nodeValue || "";
|
|
165
|
+
// Identify all the context variables found in the content.
|
|
166
|
+
const keys = extractTextNodeKeys(content).filter(([, key]) => this.store.has(key));
|
|
167
|
+
// Early exit: no keys found in content.
|
|
168
|
+
if (keys.length === 0)
|
|
169
|
+
return [];
|
|
170
|
+
this.log(params, keys, "keys found in node:", node);
|
|
171
|
+
// Apply the context variables to the content, iteratively.
|
|
172
|
+
const updateNode = () => {
|
|
173
|
+
let updatedContent = content;
|
|
174
|
+
keys.forEach(([match, key, props]) => {
|
|
175
|
+
var _a;
|
|
176
|
+
updatedContent = updatedContent.replace(match, String((_a = this.get(key, ...props)) !== null && _a !== void 0 ? _a : ""));
|
|
177
|
+
});
|
|
178
|
+
node.nodeValue = updatedContent;
|
|
179
|
+
};
|
|
180
|
+
// Update the content now, and set up the listeners for future updates.
|
|
181
|
+
updateNode();
|
|
182
|
+
this.watch(keys.map(([, key]) => key), updateNode);
|
|
183
|
+
// Return all the proxies found in the content.
|
|
184
|
+
return keys.map(([, key]) => this.store.get(key));
|
|
185
|
+
}
|
|
186
|
+
resolveDataAttribute(node, params) {
|
|
187
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
+
var _a;
|
|
189
|
+
if (this.skipNodes.has(node))
|
|
190
|
+
return;
|
|
191
|
+
const elem = node;
|
|
192
|
+
const dataAttr = (_a = elem.getAttribute) === null || _a === void 0 ? void 0 : _a.call(elem, ":data");
|
|
193
|
+
if (dataAttr) {
|
|
194
|
+
this.log(params, ":data attribute found in:\n", node);
|
|
195
|
+
elem.removeAttribute(":data");
|
|
196
|
+
const result = yield this.eval(dataAttr, { $elem: node }, params);
|
|
197
|
+
this.log(params, ":data", dataAttr, "=>", result);
|
|
198
|
+
yield this.update(result);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
resolveWatchAttribute(node, params) {
|
|
203
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
+
var _a;
|
|
205
|
+
if (this.skipNodes.has(node))
|
|
206
|
+
return;
|
|
207
|
+
const elem = node;
|
|
208
|
+
const watchAttr = (_a = elem.getAttribute) === null || _a === void 0 ? void 0 : _a.call(elem, "@watch");
|
|
209
|
+
if (watchAttr) {
|
|
210
|
+
this.log(params, "@watch attribute found in:\n", node);
|
|
211
|
+
// Remove the attribute from the node.
|
|
212
|
+
elem.removeAttribute("@watch");
|
|
213
|
+
// Compute the function's result and trace dependencies.
|
|
214
|
+
const fn = () => this.eval(watchAttr, { $elem: node }, params);
|
|
215
|
+
const [result, dependencies] = yield this.trace(fn);
|
|
216
|
+
this.log(params, "@watch", watchAttr, "=>", result);
|
|
217
|
+
// Watch for updates, and re-execute function if needed.
|
|
218
|
+
this.watch(dependencies, fn);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
resolvePropAttributes(node, params) {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
if (this.skipNodes.has(node))
|
|
225
|
+
return;
|
|
226
|
+
const elem = node;
|
|
227
|
+
for (const attr of Array.from(elem.attributes || [])) {
|
|
228
|
+
if (attr.name.startsWith("$") && !KW_ATTRIBUTES.has(attr.name)) {
|
|
229
|
+
this.log(params, attr.name, "attribute found in:\n", node);
|
|
230
|
+
// Remove the attribute from the node.
|
|
231
|
+
elem.removeAttribute(attr.name);
|
|
232
|
+
// Apply any shorthand conversions if necessary.
|
|
233
|
+
const propName = (ATTR_SHORTHANDS[attr.name] || attr.name).slice(1);
|
|
234
|
+
// Compute the function's result and trace dependencies.
|
|
235
|
+
const fn = () => this.eval(attr.value, { $elem: node }, params);
|
|
236
|
+
const [result, dependencies] = yield this.trace(fn);
|
|
237
|
+
this.log(params, attr.name, attr.value, "=>", result, `[${dependencies}]`);
|
|
238
|
+
// Set the requested property value on the original node, and watch for updates.
|
|
239
|
+
const prop = (0, attributes_1.attributeNameToCamelCase)(propName);
|
|
240
|
+
this.watch(dependencies, () => __awaiter(this, void 0, void 0, function* () { return (node[prop] = yield fn()); }));
|
|
241
|
+
node[prop] = result;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
resolveAttrAttributes(node, params) {
|
|
247
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
248
|
+
if (this.skipNodes.has(node))
|
|
249
|
+
return;
|
|
250
|
+
const elem = node;
|
|
251
|
+
for (const attr of Array.from(elem.attributes || [])) {
|
|
252
|
+
if (attr.name.startsWith(":") && !KW_ATTRIBUTES.has(attr.name)) {
|
|
253
|
+
this.log(params, attr.name, "attribute found in:\n", node);
|
|
254
|
+
// Remove the processed attributes from node.
|
|
255
|
+
elem.removeAttribute(attr.name);
|
|
256
|
+
// Apply any shorthand conversions if necessary.
|
|
257
|
+
const attrName = (ATTR_SHORTHANDS[attr.name] || attr.name).slice(1);
|
|
258
|
+
// Compute the function's result and trace dependencies.
|
|
259
|
+
const fn = () => this.eval(attr.value, { $elem: node }, params);
|
|
260
|
+
const [result, dependencies] = yield this.trace(fn);
|
|
261
|
+
this.log(params, attr.name, attr.value, "=>", result, `[${dependencies}]`);
|
|
262
|
+
// Set the requested property value on the original node, and watch for updates.
|
|
263
|
+
this.watch(dependencies, () => __awaiter(this, void 0, void 0, function* () { return elem.setAttribute(attrName, yield fn()); }));
|
|
264
|
+
elem.setAttribute(attrName, result);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
resolveEventAttributes(node, params) {
|
|
270
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
271
|
+
var _a;
|
|
272
|
+
if (this.skipNodes.has(node))
|
|
273
|
+
return;
|
|
274
|
+
const elem = node;
|
|
275
|
+
for (const attr of Array.from(elem.attributes || [])) {
|
|
276
|
+
if (attr.name.startsWith("@") && !KW_ATTRIBUTES.has(attr.name)) {
|
|
277
|
+
this.log(params, attr.name, "attribute found in:\n", node);
|
|
278
|
+
// Remove the processed attributes from node.
|
|
279
|
+
elem.removeAttribute(attr.name);
|
|
280
|
+
(_a = node.addEventListener) === null || _a === void 0 ? void 0 : _a.call(node, attr.name.substring(1), (event) => this.eval(attr.value, { $elem: node, $event: event }, params));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
resolveForAttribute(node, params) {
|
|
286
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
287
|
+
var _a;
|
|
288
|
+
if (this.skipNodes.has(node))
|
|
289
|
+
return;
|
|
290
|
+
const elem = node;
|
|
291
|
+
const forAttr = (_a = elem.getAttribute) === null || _a === void 0 ? void 0 : _a.call(elem, ":for");
|
|
292
|
+
if (forAttr) {
|
|
293
|
+
this.log(params, ":for attribute found in:\n", node);
|
|
294
|
+
// Remove the processed attributes from node.
|
|
295
|
+
elem.removeAttribute(":for");
|
|
296
|
+
// Ensure the node and its children are not processed by subsequent steps.
|
|
297
|
+
for (const child of traverse(node, this.skipNodes)) {
|
|
298
|
+
this.skipNodes.add(child);
|
|
299
|
+
}
|
|
300
|
+
// Place the template node into a template element.
|
|
301
|
+
const parent = node.parentNode;
|
|
302
|
+
const template = node.ownerDocument.createElement("template");
|
|
303
|
+
parent.insertBefore(template, node);
|
|
304
|
+
template.append(node);
|
|
305
|
+
this.log(params, ":for template:\n", template);
|
|
306
|
+
// Tokenize the input by splitting it based on the format "{key} in {expression}".
|
|
307
|
+
const tokens = forAttr.split(" in ", 2);
|
|
308
|
+
if (tokens.length !== 2) {
|
|
309
|
+
throw new Error(`Invalid :for format: \`${forAttr}\`. Expected "{key} in {expression}".`);
|
|
310
|
+
}
|
|
311
|
+
// Compute the container expression and trace dependencies.
|
|
312
|
+
let items = [];
|
|
313
|
+
let deps = [];
|
|
314
|
+
const [loopKey, itemsExpr] = tokens;
|
|
315
|
+
try {
|
|
316
|
+
[items, deps] = yield this.trace(() => this.eval(itemsExpr, { $elem: node }, params));
|
|
317
|
+
this.log(params, itemsExpr, "=>", items, `[${deps}]`);
|
|
318
|
+
}
|
|
319
|
+
catch (exc) {
|
|
320
|
+
console.error(exc);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// Keep track of all the child nodes added.
|
|
324
|
+
const children = [];
|
|
325
|
+
// Define the function that will update the DOM.
|
|
326
|
+
const fn = (items) => __awaiter(this, void 0, void 0, function* () {
|
|
327
|
+
this.log(params, ":for list items:", items);
|
|
328
|
+
// Validate that the expression returns a list of items.
|
|
329
|
+
if (!Array.isArray(items)) {
|
|
330
|
+
console.error(`Expression did not yield a list: \`${itemsExpr}\` => \`${items}\``);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
// Acquire the lock atomically.
|
|
334
|
+
this.lock = this.lock.then(() => new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
335
|
+
// Remove all the previously added children, if any.
|
|
336
|
+
children.splice(0, children.length).forEach((child) => {
|
|
337
|
+
parent.removeChild(child);
|
|
338
|
+
// this.skipNodes.delete(child);
|
|
339
|
+
});
|
|
340
|
+
// Loop through the container items in reverse, because we insert from back to front.
|
|
341
|
+
for (const item of items.slice(0).reverse()) {
|
|
342
|
+
// Create a subrenderer that will hold the loop item and all node descendants.
|
|
343
|
+
const subrenderer = this.clone();
|
|
344
|
+
yield subrenderer.set(loopKey, item);
|
|
345
|
+
// Create a new HTML element for each item and add them to parent node.
|
|
346
|
+
const copy = node.cloneNode(true);
|
|
347
|
+
parent.insertBefore(copy, template.nextSibling);
|
|
348
|
+
// Also add the new element to the store.
|
|
349
|
+
children.push(copy);
|
|
350
|
+
// Since the element will be handled by a subrenderer, skip it in the source.
|
|
351
|
+
this.skipNodes.add(copy);
|
|
352
|
+
// Render the element using the subrenderer.
|
|
353
|
+
yield subrenderer.mount(copy, params);
|
|
354
|
+
this.log(params, "Rendered list child:\n", copy);
|
|
355
|
+
}
|
|
356
|
+
// Release the lock.
|
|
357
|
+
resolve();
|
|
358
|
+
})));
|
|
359
|
+
// Return the lock so the whole operation can be awaited.
|
|
360
|
+
return this.lock;
|
|
361
|
+
});
|
|
362
|
+
// Apply changes, and watch for updates in the dependencies.
|
|
363
|
+
this.watch(deps, () => __awaiter(this, void 0, void 0, function* () { return fn(yield this.eval(itemsExpr, { $elem: node }, params)); }));
|
|
364
|
+
return fn(items);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
resolveBindAttribute(node, params) {
|
|
369
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
370
|
+
var _a, _b, _c;
|
|
371
|
+
if (this.skipNodes.has(node))
|
|
372
|
+
return;
|
|
373
|
+
const elem = node;
|
|
374
|
+
const bindKey = (_a = elem.getAttribute) === null || _a === void 0 ? void 0 : _a.call(elem, ":bind");
|
|
375
|
+
if (bindKey) {
|
|
376
|
+
this.log(params, ":bind attribute found in:\n", node);
|
|
377
|
+
// The change events we listen for can be overriden by user.
|
|
378
|
+
const defaultEvents = ["change", "input"];
|
|
379
|
+
const updateEvents = ((_c = (_b = elem.getAttribute) === null || _b === void 0 ? void 0 : _b.call(elem, ":bind-events")) === null || _c === void 0 ? void 0 : _c.split(",")) || defaultEvents;
|
|
380
|
+
// If the element is of type checkbox, we bind to the "checked" property.
|
|
381
|
+
const prop = elem.getAttribute("type") === "checkbox" ? "checked" : "value";
|
|
382
|
+
// If the key is not found in our store, create it and initialize it with the node's value.
|
|
383
|
+
if (!this.store.has(bindKey))
|
|
384
|
+
yield this.set(bindKey, elem[prop]);
|
|
385
|
+
// Set the node's value to our current value.
|
|
386
|
+
elem[prop] = this.get(bindKey);
|
|
387
|
+
// Watch for updates in the node's value.
|
|
388
|
+
for (const event of updateEvents) {
|
|
389
|
+
node.addEventListener(event, () => this.set(bindKey, elem[prop]));
|
|
390
|
+
}
|
|
391
|
+
// Watch for updates in the store.
|
|
392
|
+
this.watch([bindKey], () => (elem[prop] = this.get(bindKey)));
|
|
393
|
+
// Remove the processed attributes from node.
|
|
394
|
+
elem.removeAttribute(":bind");
|
|
395
|
+
elem.removeAttribute(":bind-events");
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
resolveShowAttribute(node, params) {
|
|
400
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
401
|
+
var _a, _b;
|
|
402
|
+
if (this.skipNodes.has(node))
|
|
403
|
+
return;
|
|
404
|
+
const elem = node;
|
|
405
|
+
const showExpr = (_b = (_a = node).getAttribute) === null || _b === void 0 ? void 0 : _b.call(_a, ":show");
|
|
406
|
+
if (showExpr) {
|
|
407
|
+
// Compute the function's result and trace dependencies.
|
|
408
|
+
const fn = () => this.eval(showExpr, { $elem: node }, params);
|
|
409
|
+
const [result, dependencies] = yield this.trace(fn);
|
|
410
|
+
this.log(params, ":show", showExpr, "=>", result, `[${dependencies}]`);
|
|
411
|
+
// If the result is false, remove the element from the DOM.
|
|
412
|
+
const parent = node.parentNode;
|
|
413
|
+
if (!result)
|
|
414
|
+
parent.removeChild(node);
|
|
415
|
+
// Watch the dependencies, and re-evaluate the expression.
|
|
416
|
+
this.watch(dependencies, () => __awaiter(this, void 0, void 0, function* () {
|
|
417
|
+
if ((yield fn()) && node.parentNode !== parent)
|
|
418
|
+
parent.append(node);
|
|
419
|
+
else if (Array.from(parent.childNodes).includes(node))
|
|
420
|
+
node.remove();
|
|
421
|
+
}));
|
|
422
|
+
// Remove the processed attributes from node.
|
|
423
|
+
elem.removeAttribute(":show");
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
mount(root, params) {
|
|
428
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
429
|
+
// Resolve all the includes recursively first.
|
|
430
|
+
yield this.resolveIncludes(root, params);
|
|
431
|
+
// Iterate over all the nodes and apply appropriate handlers.
|
|
432
|
+
// Do these steps one at a time to avoid any potential race conditions.
|
|
433
|
+
for (const node of traverse(root, this.skipNodes)) {
|
|
434
|
+
this.log(params, "Processing node:\n", node);
|
|
435
|
+
// Resolve the :show attribute in the node.
|
|
436
|
+
yield this.resolveShowAttribute(node, params);
|
|
437
|
+
// Resolve the :data attribute in the node.
|
|
438
|
+
yield this.resolveDataAttribute(node, params);
|
|
439
|
+
// Resolve the @watch attribute in the node.
|
|
440
|
+
yield this.resolveWatchAttribute(node, params);
|
|
441
|
+
// Resolve the :for attribute in the node.
|
|
442
|
+
yield this.resolveForAttribute(node, params);
|
|
443
|
+
// Resolve the :bind attribute in the node.
|
|
444
|
+
yield this.resolveBindAttribute(node, params);
|
|
445
|
+
// Resolve all $attributes in the node.
|
|
446
|
+
yield this.resolvePropAttributes(node, params);
|
|
447
|
+
// Resolve all :attributes in the node.
|
|
448
|
+
yield this.resolveAttrAttributes(node, params);
|
|
449
|
+
// Resolve all @attributes in the node.
|
|
450
|
+
yield this.resolveEventAttributes(node, params);
|
|
451
|
+
// Replace all the {{ variables }} in the text.
|
|
452
|
+
this.resolveTextNode(node, params);
|
|
453
|
+
}
|
|
454
|
+
return this;
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
renderString(content, params) {
|
|
458
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
459
|
+
const fragment = this.parseHTML(content, params);
|
|
460
|
+
yield this.mount(fragment, params);
|
|
461
|
+
return fragment;
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
renderRemotePath(fpath, params) {
|
|
465
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
466
|
+
const cache = (params === null || params === void 0 ? void 0 : params.cache) || "default";
|
|
467
|
+
const content = yield fetch(fpath, { cache }).then((res) => res.text());
|
|
468
|
+
return this.renderString(content, Object.assign(Object.assign({}, params), { fsroot: folderPath(fpath), isRoot: (params === null || params === void 0 ? void 0 : params.isRoot) || !fpath.endsWith(".tpl.html") }));
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
exports.IRenderer = IRenderer;
|
package/dist/gulp.d.ts
CHANGED
|
@@ -3,13 +3,13 @@ import * as stream from "stream";
|
|
|
3
3
|
/**
|
|
4
4
|
* Main entrypoint to be used in Gulp. Usage:
|
|
5
5
|
*
|
|
6
|
-
* var mancha = require('gulp
|
|
6
|
+
* var mancha = require('mancha/dist/gulp')
|
|
7
7
|
* gulp.src(...).pipe(mancha({myvar: myval})).pipe(...)
|
|
8
8
|
*
|
|
9
|
-
* @param
|
|
10
|
-
* replacing it with `value` in the processed files.
|
|
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
11
|
*/
|
|
12
|
-
declare function mancha(
|
|
12
|
+
declare function mancha(context?: {
|
|
13
13
|
[key: string]: string;
|
|
14
14
|
}, wwwroot?: string): stream.Transform;
|
|
15
15
|
export default mancha;
|
package/dist/gulp.js
CHANGED
|
@@ -3,52 +3,56 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const stream = require("stream");
|
|
5
5
|
const through = require("through2");
|
|
6
|
-
const
|
|
6
|
+
const index_1 = require("./index");
|
|
7
7
|
/**
|
|
8
8
|
* Main entrypoint to be used in Gulp. Usage:
|
|
9
9
|
*
|
|
10
|
-
* var mancha = require('gulp
|
|
10
|
+
* var mancha = require('mancha/dist/gulp')
|
|
11
11
|
* gulp.src(...).pipe(mancha({myvar: myval})).pipe(...)
|
|
12
12
|
*
|
|
13
|
-
* @param
|
|
14
|
-
* replacing it with `value` in the processed files.
|
|
13
|
+
* @param context <key, value> pairs of literal string replacements. `key` will become `{{ key }}`
|
|
14
|
+
* before replacing it with `value` in the processed files.
|
|
15
15
|
*/
|
|
16
|
-
function mancha(
|
|
16
|
+
function mancha(context = {}, wwwroot = process.cwd()) {
|
|
17
|
+
const renderer = new index_1.RendererImpl(context);
|
|
18
|
+
// Mancha.update(context);
|
|
17
19
|
return through.obj(function (file, encoding, callback) {
|
|
18
20
|
const catcher = (err) => {
|
|
19
21
|
console.log(err);
|
|
20
22
|
callback(err, file);
|
|
21
23
|
};
|
|
22
24
|
const fsroot = path.dirname(file.path);
|
|
23
|
-
const relpath = path.relative(file.path, wwwroot) || ".";
|
|
24
|
-
const newvars = Object.assign(vars, { wwwroot: relpath });
|
|
25
25
|
if (file.isNull()) {
|
|
26
26
|
callback(null, file);
|
|
27
27
|
}
|
|
28
28
|
else {
|
|
29
29
|
if (file.isBuffer()) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
.
|
|
30
|
+
const chunk = file.contents.toString(encoding);
|
|
31
|
+
renderer
|
|
32
|
+
.renderString(chunk, { fsroot, isRoot: !file.path.endsWith(".tpl.html") })
|
|
33
|
+
.then((fragment) => {
|
|
34
|
+
const content = renderer.serializeHTML(fragment);
|
|
33
35
|
file.contents = Buffer.from(content, encoding);
|
|
34
36
|
callback(null, file);
|
|
35
37
|
})
|
|
36
38
|
.catch(catcher);
|
|
37
39
|
}
|
|
38
40
|
else if (file.isStream()) {
|
|
39
|
-
let
|
|
41
|
+
let docstr = "";
|
|
40
42
|
file.contents
|
|
41
43
|
.on("data", (chunk) => {
|
|
42
44
|
if (Buffer.isBuffer(chunk)) {
|
|
43
|
-
|
|
45
|
+
docstr += chunk.toString(encoding);
|
|
44
46
|
}
|
|
45
47
|
else {
|
|
46
|
-
|
|
48
|
+
docstr += chunk.toString();
|
|
47
49
|
}
|
|
48
50
|
})
|
|
49
51
|
.on("end", () => {
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
+
renderer
|
|
53
|
+
.renderString(docstr, { fsroot, isRoot: !file.path.endsWith(".tpl.html") })
|
|
54
|
+
.then((document) => {
|
|
55
|
+
const content = renderer.serializeHTML(document);
|
|
52
56
|
const readable = new stream.Readable();
|
|
53
57
|
readable._read = function () { };
|
|
54
58
|
readable.push(content);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
1
|
+
import { ParserParams, RendererParams } from "./core";
|
|
2
|
+
import { RendererImpl as WorkerRendererImpl } from "./worker";
|
|
3
|
+
/** The Node Mancha renderer is just like the worker renderer, but it also uses the filesystem. */
|
|
4
|
+
export declare class RendererImpl extends WorkerRendererImpl {
|
|
5
|
+
renderLocalPath(fpath: string, params?: RendererParams & ParserParams): Promise<DocumentFragment>;
|
|
6
|
+
}
|
|
7
|
+
export { folderPath, resolvePath } from "./core";
|
|
8
|
+
export declare const Mancha: RendererImpl;
|
package/dist/index.js
CHANGED
|
@@ -9,28 +9,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.Mancha = exports.resolvePath = exports.folderPath = exports.RendererImpl = void 0;
|
|
13
13
|
const fs = require("fs/promises");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
const core_1 = require("./core");
|
|
15
|
+
const worker_1 = require("./worker");
|
|
16
|
+
/** The Node Mancha renderer is just like the worker renderer, but it also uses the filesystem. */
|
|
17
|
+
class RendererImpl extends worker_1.RendererImpl {
|
|
18
|
+
renderLocalPath(fpath_1) {
|
|
19
|
+
return __awaiter(this, arguments, void 0, function* (fpath, params = { encoding: "utf8" }) {
|
|
20
|
+
const content = yield fs.readFile(fpath, { encoding: params.encoding });
|
|
21
|
+
return this.renderString(content.toString(), {
|
|
22
|
+
fsroot: (0, core_1.folderPath)(fpath),
|
|
23
|
+
// Determine whether a root node is needed based on filename.
|
|
24
|
+
isRoot: params.isRoot || !fpath.endsWith(".tpl.html"),
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
27
28
|
}
|
|
28
|
-
exports.
|
|
29
|
-
// Re-exports from
|
|
30
|
-
var
|
|
31
|
-
Object.defineProperty(exports, "
|
|
32
|
-
Object.defineProperty(exports, "
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Object.defineProperty(exports, "encodeHtmlAttrib", { enumerable: true, get: function () { return web_2.encodeHtmlAttrib; } });
|
|
36
|
-
Object.defineProperty(exports, "decodeHtmlAttrib", { enumerable: true, get: function () { return web_2.decodeHtmlAttrib; } });
|
|
29
|
+
exports.RendererImpl = RendererImpl;
|
|
30
|
+
// Re-exports from core.
|
|
31
|
+
var core_2 = require("./core");
|
|
32
|
+
Object.defineProperty(exports, "folderPath", { enumerable: true, get: function () { return core_2.folderPath; } });
|
|
33
|
+
Object.defineProperty(exports, "resolvePath", { enumerable: true, get: function () { return core_2.resolvePath; } });
|
|
34
|
+
// Export the renderer instance directly.
|
|
35
|
+
exports.Mancha = new RendererImpl();
|