chocola 2.0.0-next.2 → 2.0.0-next.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 +32 -45
- package/compiler/component-processor.js +271 -207
- package/compiler/config.js +1 -54
- package/compiler/dom-processor.js +0 -51
- package/compiler/fs.js +0 -11
- package/compiler/index.js +12 -32
- package/compiler/pipeline.js +7 -188
- package/compiler/runtime-generator.js +0 -6
- package/compiler/utils.js +0 -94
- package/package.json +1 -1
- package/utils.js +0 -5
package/README.md
CHANGED
|
@@ -1,45 +1,32 @@
|
|
|
1
|
-
# chocola
|
|
2
|
-
|
|
3
|
-
Chocola is a JavaScript library for creating web user interfaces.
|
|
4
|
-
|
|
5
|
-
`chocola` only contains the functionality to build and serve web static builds. For production you may want to implement `chocola` to your workflow with other frameworks as Vite until official workflows are provided.
|
|
6
|
-
|
|
7
|
-
## Usage
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<!--
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export default function Counter() {
|
|
35
|
-
return {
|
|
36
|
-
body,
|
|
37
|
-
styles,
|
|
38
|
-
script: RUNTIME
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Documentation
|
|
44
|
-
|
|
45
|
-
See https://github.com/sad-gabi/chocola/wiki
|
|
1
|
+
# chocola
|
|
2
|
+
|
|
3
|
+
Chocola is a JavaScript library for creating web user interfaces.
|
|
4
|
+
|
|
5
|
+
`chocola` only contains the functionality to build and serve web static builds. For production you may want to implement `chocola` to your workflow with other frameworks as Vite until official workflows are provided.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<!-- MyTitle.html -->
|
|
11
|
+
<script>
|
|
12
|
+
let self = new HTMLElement;
|
|
13
|
+
|
|
14
|
+
export let title = "Default title";
|
|
15
|
+
|
|
16
|
+
function $runtime() {
|
|
17
|
+
console.log("Component mounted:", self)
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<h1>{title}</h1>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
button { color: chocolate; }
|
|
27
|
+
</style>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
See https://github.com/sad-gabi/chocola/wiki
|
|
@@ -101,23 +101,27 @@ function scopeCss(cssString, cssId) {
|
|
|
101
101
|
return processBlock(cssString);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (el.hasAttribute("del-if") && !el.hasAttribute("del:if") && !warnedDeprecated.has(sourceLoc)) {
|
|
107
|
-
warnedDeprecated.add(sourceLoc);
|
|
108
|
-
const name = sourceLoc ? sourceLoc.split(/[\\/]/).pop() : "unknown";
|
|
109
|
-
console.warn(chalk.yellow(`[deprecation] "del-if" is deprecated in ${chalk.cyan(name)} and will be removed in Chocola 2. Use "del:if" instead.`));
|
|
110
|
-
}
|
|
111
|
-
return el.hasAttribute("del-if") || el.hasAttribute("del:if");
|
|
104
|
+
function hasDelIfAttr(el) {
|
|
105
|
+
return el.hasAttribute("del:if");
|
|
112
106
|
}
|
|
113
107
|
function getDelIfAttr(el) {
|
|
114
|
-
return el.getAttribute("del:if")
|
|
108
|
+
return el.getAttribute("del:if");
|
|
115
109
|
}
|
|
116
110
|
function removeDelIfAttr(el) {
|
|
117
|
-
el.removeAttribute("del-if");
|
|
118
111
|
el.removeAttribute("del:if");
|
|
119
112
|
}
|
|
120
113
|
|
|
114
|
+
function extractPropsDefaults(script) {
|
|
115
|
+
if (!script) return [];
|
|
116
|
+
const propsRegex = /export\s+let\s+([a-zA-Z_$][0-9a-zA-Z_$]*)\s*(?:=\s*([^;]+))?;/g;
|
|
117
|
+
let props = [];
|
|
118
|
+
let match;
|
|
119
|
+
while ((match = propsRegex.exec(script)) !== null) {
|
|
120
|
+
props.push({ name: match[1].trim(), defaultValue: match[2]?.trim() });
|
|
121
|
+
}
|
|
122
|
+
return props;
|
|
123
|
+
}
|
|
124
|
+
|
|
121
125
|
function interpolateNode(root, ctxProxy) {
|
|
122
126
|
const stack = [root];
|
|
123
127
|
while (stack.length) {
|
|
@@ -138,13 +142,28 @@ function interpolateNode(root, ctxProxy) {
|
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|
|
145
|
+
function getLineNumber(sourceContent, parentContent, idxInParent) {
|
|
146
|
+
if (sourceContent === parentContent) {
|
|
147
|
+
return parentContent.substring(0, idxInParent).split("\n").length;
|
|
148
|
+
}
|
|
149
|
+
const templateStartMatch = sourceContent.match(/<template[^>]*>/);
|
|
150
|
+
if (templateStartMatch) {
|
|
151
|
+
const contentStart = templateStartMatch.index + templateStartMatch[0].length;
|
|
152
|
+
const beforeContent = sourceContent.substring(0, contentStart);
|
|
153
|
+
const linesInBefore = beforeContent.split("\n").length;
|
|
154
|
+
const linesInTemplate = parentContent.substring(0, idxInParent).split("\n").length;
|
|
155
|
+
return linesInBefore + linesInTemplate - 1;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
141
160
|
function validateChainStructure(parent, sourceFile, sourceContent, parentContent) {
|
|
142
161
|
const children = [...parent.children];
|
|
143
162
|
let chainActive = false;
|
|
144
163
|
|
|
145
164
|
for (const child of children) {
|
|
146
165
|
const hasIf = child.hasAttribute("if");
|
|
147
|
-
const hasDelIf = hasDelIfAttr(child
|
|
166
|
+
const hasDelIf = hasDelIfAttr(child);
|
|
148
167
|
const hasElif = child.hasAttribute("elif");
|
|
149
168
|
const hasElse = child.hasAttribute("else");
|
|
150
169
|
|
|
@@ -153,10 +172,11 @@ function validateChainStructure(parent, sourceFile, sourceContent, parentContent
|
|
|
153
172
|
const tag = child.tagName.toLowerCase();
|
|
154
173
|
const attr = hasElif ? "elif" : "else";
|
|
155
174
|
let loc = sourceFile;
|
|
156
|
-
if (sourceContent &&
|
|
175
|
+
if (sourceContent && parentContent) {
|
|
157
176
|
const idx = parentContent.indexOf(child.outerHTML);
|
|
158
177
|
if (idx !== -1) {
|
|
159
|
-
|
|
178
|
+
const lineNum = getLineNumber(sourceContent, parentContent, idx);
|
|
179
|
+
if (lineNum !== null) loc = `${sourceFile}:${lineNum}`;
|
|
160
180
|
}
|
|
161
181
|
}
|
|
162
182
|
throwError(`${loc}\n <${tag}> has ${attr} without a preceding if/del-if sibling`);
|
|
@@ -176,15 +196,6 @@ function validateChainStructure(parent, sourceFile, sourceContent, parentContent
|
|
|
176
196
|
}
|
|
177
197
|
}
|
|
178
198
|
|
|
179
|
-
/**
|
|
180
|
-
* Processes a single component element and inserts it into the DOM
|
|
181
|
-
* @param {Element} element
|
|
182
|
-
* @param {Map} loadedComponents
|
|
183
|
-
* @param {Array} runtimeChunks
|
|
184
|
-
* @param {Array} compIdColl
|
|
185
|
-
* @param {object} letterState - { value: string }
|
|
186
|
-
* @returns {boolean} - true if component was processed, false if not found
|
|
187
|
-
*/
|
|
188
199
|
export function processComponentElement(
|
|
189
200
|
element,
|
|
190
201
|
loadedComponents,
|
|
@@ -201,12 +212,26 @@ export function processComponentElement(
|
|
|
201
212
|
sourceContent
|
|
202
213
|
) {
|
|
203
214
|
const tagName = element.tagName.toLowerCase();
|
|
204
|
-
const compName = tagName + ".
|
|
205
|
-
|
|
215
|
+
const compName = tagName + ".html";
|
|
216
|
+
let instance = loadedComponents.get(compName);
|
|
206
217
|
|
|
207
218
|
if (!instance || instance === undefined) return false;
|
|
208
219
|
if (renderChain && renderChain.includes(compName)) return false;
|
|
209
220
|
|
|
221
|
+
instance = protectCurlyBraces(instance);
|
|
222
|
+
const dom = new JSDOM(instance);
|
|
223
|
+
const doc = dom.window.document;
|
|
224
|
+
let script = doc.querySelector("script")?.innerHTML;
|
|
225
|
+
let template = doc.querySelector("template")?.innerHTML;
|
|
226
|
+
let styles = doc.querySelector("style")?.innerHTML;
|
|
227
|
+
|
|
228
|
+
if (!template) {
|
|
229
|
+
console.warn(chalk.yellow(`${compName} — component is missing a <template>`));
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const compProps = extractPropsDefaults(script);
|
|
234
|
+
|
|
210
235
|
let ctx;
|
|
211
236
|
if (staticCtxRegistry && staticCtxRegistry.has(element)) {
|
|
212
237
|
ctx = staticCtxRegistry.get(element);
|
|
@@ -214,245 +239,286 @@ export function processComponentElement(
|
|
|
214
239
|
ctx = extractContextFromElement(element);
|
|
215
240
|
staticCtxRegistry && staticCtxRegistry.set(element, ctx);
|
|
216
241
|
}
|
|
217
|
-
|
|
242
|
+
|
|
243
|
+
if (compProps.length > 0) {
|
|
244
|
+
compProps.forEach(({ name, defaultValue }) => {
|
|
245
|
+
if (defaultValue !== undefined && !(name in ctx)) {
|
|
246
|
+
try {
|
|
247
|
+
ctx[name] = compileExpression(defaultValue, false)();
|
|
248
|
+
} catch {
|
|
249
|
+
ctx[name] = defaultValue;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const elInnerHtml = element.innerHTML;
|
|
218
256
|
|
|
219
257
|
const ctxProxy = new Proxy(ctx, {
|
|
220
258
|
has() { return true; },
|
|
221
259
|
get(target, key) { return target[key]; }
|
|
222
260
|
});
|
|
223
261
|
|
|
224
|
-
|
|
225
|
-
|
|
262
|
+
const fragment = JSDOM.fragment(template);
|
|
263
|
+
|
|
264
|
+
const slotFragment = JSDOM.fragment(elInnerHtml);
|
|
265
|
+
if (sourceFile) {
|
|
266
|
+
validateChainStructure(slotFragment, sourceFile, sourceContent, elInnerHtml);
|
|
267
|
+
}
|
|
268
|
+
validateChainStructure(fragment, instance.__sourceFile || compName, instance, template);
|
|
269
|
+
Array.from(fragment.querySelectorAll("slot")).forEach(slot => {
|
|
270
|
+
slot.replaceWith(slotFragment);
|
|
271
|
+
});
|
|
226
272
|
|
|
227
|
-
|
|
273
|
+
const childEntries = Array.from(fragment.querySelectorAll("*")).map(el => ({
|
|
274
|
+
el,
|
|
275
|
+
parent: el.parentNode
|
|
276
|
+
}));
|
|
277
|
+
const condChains = new Map();
|
|
228
278
|
|
|
229
|
-
|
|
230
|
-
if (
|
|
231
|
-
|
|
279
|
+
childEntries.forEach(({ el: child, parent }) => {
|
|
280
|
+
if (!condChains.has(parent)) {
|
|
281
|
+
condChains.set(parent, { active: false, rendered: false });
|
|
232
282
|
}
|
|
233
|
-
|
|
234
|
-
Array.from(fragment.querySelectorAll("slot")).forEach(slot => {
|
|
235
|
-
slot.replaceWith(slotFragment);
|
|
236
|
-
});
|
|
283
|
+
const condChain = condChains.get(parent);
|
|
237
284
|
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const condChains = new Map();
|
|
285
|
+
const hasIf = child.hasAttribute("if");
|
|
286
|
+
const hasDelIf = hasDelIfAttr(child);
|
|
287
|
+
const hasElif = child.hasAttribute("elif");
|
|
288
|
+
const hasElse = child.hasAttribute("else");
|
|
243
289
|
|
|
244
|
-
|
|
245
|
-
if (!
|
|
246
|
-
|
|
290
|
+
if (hasElif || hasElse) {
|
|
291
|
+
if (!condChain.active) {
|
|
292
|
+
throwError(`${instance.__sourceFile || compName}: <${child.tagName.toLowerCase()}> has ${hasElif ? "elif" : "else"} without a preceding if/del-if sibling`);
|
|
247
293
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const hasElif = child.hasAttribute("elif");
|
|
253
|
-
const hasElse = child.hasAttribute("else");
|
|
254
|
-
|
|
255
|
-
if (hasElif || hasElse) {
|
|
256
|
-
if (!condChain.active) {
|
|
257
|
-
throwError(`${instance.__sourceFile || compName}: <${child.tagName.toLowerCase()}> has ${hasElif ? "elif" : "else"} without a preceding if/del-if sibling`);
|
|
258
|
-
}
|
|
259
|
-
if (condChain.rendered) {
|
|
260
|
-
child.remove();
|
|
261
|
-
if (hasElse) {
|
|
262
|
-
condChain.active = false;
|
|
263
|
-
}
|
|
264
|
-
return;
|
|
294
|
+
if (condChain.rendered) {
|
|
295
|
+
child.remove();
|
|
296
|
+
if (hasElse) {
|
|
297
|
+
condChain.active = false;
|
|
265
298
|
}
|
|
299
|
+
return;
|
|
266
300
|
}
|
|
301
|
+
}
|
|
267
302
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const fn = compileExpression(expr, true);
|
|
273
|
-
if (!fn(ctxProxy)) {
|
|
274
|
-
child.remove();
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
child.replaceWith(...child.children);
|
|
279
|
-
condChain.rendered = true;
|
|
280
|
-
if (hasElse) {
|
|
281
|
-
condChain.active = false;
|
|
282
|
-
}
|
|
283
|
-
} else if (hasIf || hasDelIf) {
|
|
284
|
-
const raw = hasIf ? child.getAttribute("if") : getDelIfAttr(child);
|
|
285
|
-
const expr = raw.slice(1, -1);
|
|
303
|
+
if (child.tagName.toLowerCase() === "void") {
|
|
304
|
+
if (hasElif || hasElse) {
|
|
305
|
+
if (hasElif) {
|
|
306
|
+
const expr = child.getAttribute("elif").slice(1, -1);
|
|
286
307
|
const fn = compileExpression(expr, true);
|
|
287
|
-
|
|
288
|
-
if (fn(ctxProxy)) {
|
|
289
|
-
child.replaceWith(...child.children);
|
|
290
|
-
condChain.rendered = true;
|
|
291
|
-
} else {
|
|
308
|
+
if (!fn(ctxProxy)) {
|
|
292
309
|
child.remove();
|
|
293
|
-
|
|
310
|
+
return;
|
|
294
311
|
}
|
|
295
|
-
} else {
|
|
296
|
-
child.replaceWith(...child.children);
|
|
297
|
-
condChain.active = false;
|
|
298
|
-
condChain.rendered = false;
|
|
299
312
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
Array.from(child.attributes).forEach(attribute => {
|
|
306
|
-
if (!attribute || attribute === undefined) return;
|
|
307
|
-
if (reservedAttrs.includes(attribute.name)) return;
|
|
308
|
-
attribute.value = attribute.value.replace(
|
|
309
|
-
/\{([^}]+)\}/g,
|
|
310
|
-
(_, expr) => {
|
|
311
|
-
try {
|
|
312
|
-
return compileExpression(expr, true)(ctxProxy);
|
|
313
|
-
} catch {
|
|
314
|
-
return "";
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
processComponentElement(
|
|
321
|
-
child,
|
|
322
|
-
loadedComponents,
|
|
323
|
-
runtimeChunks,
|
|
324
|
-
compIdColl,
|
|
325
|
-
letterState,
|
|
326
|
-
runtimeMap,
|
|
327
|
-
cssScopes,
|
|
328
|
-
cssScopesMap,
|
|
329
|
-
scopedStyles,
|
|
330
|
-
renderChain.concat(compName),
|
|
331
|
-
staticCtxRegistry,
|
|
332
|
-
instance.__sourceFile || compName,
|
|
333
|
-
body
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
if (hasIf) {
|
|
337
|
-
const expr = child.getAttribute("if").slice(1, -1);
|
|
338
|
-
const fn = compileExpression(expr, true);
|
|
339
|
-
condChain.active = true;
|
|
340
|
-
if (fn(ctxProxy)) {
|
|
341
|
-
condChain.rendered = true;
|
|
342
|
-
} else {
|
|
343
|
-
child.style.display = "none";
|
|
344
|
-
condChain.rendered = false;
|
|
313
|
+
child.replaceWith(...child.children);
|
|
314
|
+
condChain.rendered = true;
|
|
315
|
+
if (hasElse) {
|
|
316
|
+
condChain.active = false;
|
|
345
317
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const expr =
|
|
318
|
+
} else if (hasIf || hasDelIf) {
|
|
319
|
+
const raw = hasIf ? child.getAttribute("if") : getDelIfAttr(child);
|
|
320
|
+
const expr = raw.slice(1, -1);
|
|
349
321
|
const fn = compileExpression(expr, true);
|
|
350
322
|
condChain.active = true;
|
|
351
323
|
if (fn(ctxProxy)) {
|
|
324
|
+
child.replaceWith(...child.children);
|
|
352
325
|
condChain.rendered = true;
|
|
353
326
|
} else {
|
|
354
327
|
child.remove();
|
|
355
328
|
condChain.rendered = false;
|
|
356
329
|
}
|
|
357
|
-
removeDelIfAttr(child);
|
|
358
|
-
} else if (hasElif) {
|
|
359
|
-
const expr = child.getAttribute("elif").slice(1, -1);
|
|
360
|
-
const fn = compileExpression(expr, true);
|
|
361
|
-
if (fn(ctxProxy)) {
|
|
362
|
-
condChain.rendered = true;
|
|
363
|
-
} else {
|
|
364
|
-
child.remove();
|
|
365
|
-
}
|
|
366
|
-
child.removeAttribute("elif");
|
|
367
|
-
} else if (hasElse) {
|
|
368
|
-
condChain.rendered = true;
|
|
369
|
-
condChain.active = false;
|
|
370
|
-
child.removeAttribute("else");
|
|
371
330
|
} else {
|
|
331
|
+
child.replaceWith(...child.children);
|
|
372
332
|
condChain.active = false;
|
|
373
333
|
condChain.rendered = false;
|
|
374
334
|
}
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
375
337
|
|
|
376
|
-
|
|
338
|
+
const reservedAttrs = ["if", "del:if", "elif", "else"];
|
|
339
|
+
|
|
340
|
+
Array.from(child.attributes).forEach(attribute => {
|
|
341
|
+
if (!attribute || attribute === undefined) return;
|
|
342
|
+
if (reservedAttrs.includes(attribute.name)) return;
|
|
343
|
+
attribute.value = attribute.value.replace(
|
|
344
|
+
/\{([^}]+)\}/g,
|
|
345
|
+
(_, expr) => {
|
|
346
|
+
try {
|
|
347
|
+
return compileExpression(expr, true)(ctxProxy);
|
|
348
|
+
} catch {
|
|
349
|
+
return "";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
);
|
|
377
353
|
});
|
|
378
354
|
|
|
379
|
-
|
|
355
|
+
processComponentElement(
|
|
356
|
+
child,
|
|
357
|
+
loadedComponents,
|
|
358
|
+
runtimeChunks,
|
|
359
|
+
compIdColl,
|
|
360
|
+
letterState,
|
|
361
|
+
runtimeMap,
|
|
362
|
+
cssScopes,
|
|
363
|
+
cssScopesMap,
|
|
364
|
+
scopedStyles,
|
|
365
|
+
renderChain.concat(compName),
|
|
366
|
+
staticCtxRegistry,
|
|
367
|
+
compName,
|
|
368
|
+
template
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
if (hasIf) {
|
|
372
|
+
const expr = child.getAttribute("if").slice(1, -1);
|
|
373
|
+
const fn = compileExpression(expr, true);
|
|
374
|
+
condChain.active = true;
|
|
375
|
+
if (fn(ctxProxy)) {
|
|
376
|
+
condChain.rendered = true;
|
|
377
|
+
} else {
|
|
378
|
+
child.style.display = "none";
|
|
379
|
+
condChain.rendered = false;
|
|
380
|
+
}
|
|
381
|
+
child.removeAttribute("if");
|
|
382
|
+
} else if (hasDelIf) {
|
|
383
|
+
const expr = getDelIfAttr(child).slice(1, -1);
|
|
384
|
+
const fn = compileExpression(expr, true);
|
|
385
|
+
condChain.active = true;
|
|
386
|
+
if (fn(ctxProxy)) {
|
|
387
|
+
condChain.rendered = true;
|
|
388
|
+
} else {
|
|
389
|
+
child.remove();
|
|
390
|
+
condChain.rendered = false;
|
|
391
|
+
}
|
|
392
|
+
removeDelIfAttr(child);
|
|
393
|
+
} else if (hasElif) {
|
|
394
|
+
const expr = child.getAttribute("elif").slice(1, -1);
|
|
395
|
+
const fn = compileExpression(expr, true);
|
|
396
|
+
if (fn(ctxProxy)) {
|
|
397
|
+
condChain.rendered = true;
|
|
398
|
+
} else {
|
|
399
|
+
child.remove();
|
|
400
|
+
}
|
|
401
|
+
child.removeAttribute("elif");
|
|
402
|
+
} else if (hasElse) {
|
|
403
|
+
condChain.rendered = true;
|
|
404
|
+
condChain.active = false;
|
|
405
|
+
child.removeAttribute("else");
|
|
406
|
+
} else {
|
|
407
|
+
condChain.active = false;
|
|
408
|
+
condChain.rendered = false;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
interpolateNode(child, ctxProxy)
|
|
412
|
+
});
|
|
380
413
|
|
|
381
|
-
|
|
382
|
-
if (instance.script || instance.effects) {
|
|
383
|
-
const compId = "chid-" + genRandomId(compIdColl);
|
|
384
|
-
firstChild.setAttribute("chid", compId);
|
|
414
|
+
const firstChild = fragment.children[0];
|
|
385
415
|
|
|
386
|
-
|
|
416
|
+
if (firstChild && firstChild.nodeType === 1) {
|
|
417
|
+
if (script) {
|
|
418
|
+
const compId = "chid-" + genRandomId(compIdColl);
|
|
419
|
+
firstChild.setAttribute("chid", compId);
|
|
387
420
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
421
|
+
const ctxRegex = /ctx\s*=\s*({.*?})/;
|
|
422
|
+
const ctxMatch = script.match(ctxRegex);
|
|
423
|
+
let runtimeCtx = {};
|
|
424
|
+
if (ctxMatch) {
|
|
425
|
+
try {
|
|
426
|
+
runtimeCtx = JSON.parse(ctxMatch[1].replace(/(\w+):/g, '"$1":'));
|
|
427
|
+
} catch (e) {
|
|
428
|
+
runtimeCtx = {};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
let ctxDef = "";
|
|
432
|
+
for (const [key, value] of Object.entries(runtimeCtx)) {
|
|
433
|
+
ctxDef += `ctx.${key} = ctx.${key}||${JSON.stringify(value)};\n`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
for (const { name, defaultValue } of compProps) {
|
|
437
|
+
if (defaultValue !== undefined) {
|
|
438
|
+
ctxDef += `ctx.${name} = ctx.${name}||${defaultValue};\n`;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
script = script.replace(ctxRegex, "ctx");
|
|
443
|
+
|
|
444
|
+
function extractRuntime(script) {
|
|
445
|
+
const startRegex = /(?:async\s+)?function\s+\$runtime\(([^)]*)\)\s*\{/;
|
|
446
|
+
const match = script.match(startRegex);
|
|
447
|
+
|
|
448
|
+
if (!match) return null;
|
|
449
|
+
|
|
450
|
+
const startIndexBrace = match.index + match[0].length - 1;
|
|
451
|
+
|
|
452
|
+
let bracesCount = 0;
|
|
453
|
+
let closingBraceIndex = -1;
|
|
454
|
+
|
|
455
|
+
for (let i = startIndexBrace; i < script.length; i++) {
|
|
456
|
+
if (script[i] === "{") {
|
|
457
|
+
bracesCount++;
|
|
458
|
+
} else if (script[i] === "}") {
|
|
459
|
+
bracesCount--;
|
|
460
|
+
if (bracesCount === 0) {
|
|
461
|
+
closingBraceIndex = i;
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
396
464
|
}
|
|
397
465
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
466
|
+
|
|
467
|
+
if (closingBraceIndex === -1) {
|
|
468
|
+
throw new Error(`${compName} $runtime function has unclosed curly braces.`);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const fullMatch = script.substring(match.index, closingBraceIndex + 1);
|
|
472
|
+
|
|
473
|
+
return fullMatch;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
let runtime = extractRuntime(script);
|
|
477
|
+
|
|
478
|
+
if (runtime) {
|
|
479
|
+
for (const { name } of compProps) {
|
|
480
|
+
runtime = runtime.replace(name, `ctx.${name}`);
|
|
401
481
|
}
|
|
402
|
-
|
|
403
|
-
|
|
482
|
+
|
|
483
|
+
runtime = runtime.replace(/\$runtime\([^)]*\)\s*\{/, match => match + "\n" + ctxDef);
|
|
404
484
|
|
|
405
485
|
let letterEntry = runtimeMap && runtimeMap.get(compName);
|
|
406
486
|
let letter;
|
|
407
487
|
if (!letterEntry) {
|
|
408
488
|
letter = getNextLetter(letterState);
|
|
409
|
-
|
|
410
|
-
runtimeChunks.push(
|
|
489
|
+
runtime = runtime.replace(`${RUNTIME_KW}()`, `${letter}r(self, ctx)`);
|
|
490
|
+
runtimeChunks.push(runtime);
|
|
411
491
|
runtimeMap && runtimeMap.set(compName, { letter });
|
|
412
492
|
} else {
|
|
413
493
|
letter = letterEntry.letter;
|
|
414
494
|
}
|
|
415
495
|
|
|
416
|
-
runtimeChunks.push(`${letter}
|
|
496
|
+
runtimeChunks.push(`${letter}r(document.querySelector('[chid="${compId}"]'), ${JSON.stringify(ctx)});`);
|
|
417
497
|
}
|
|
418
498
|
}
|
|
499
|
+
}
|
|
419
500
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
cssScopesMap.set(compName, cssId);
|
|
426
|
-
}
|
|
427
|
-
if (fragment.firstChild && fragment.firstChild.nodeType === 1) {
|
|
428
|
-
fragment.firstChild.classList.add(cssId);
|
|
429
|
-
}
|
|
430
|
-
style = scopeCss(style, cssId);
|
|
431
|
-
scopedStyles.push(style);
|
|
501
|
+
if (styles) {
|
|
502
|
+
let cssId = cssScopesMap && cssScopesMap.get(compName);
|
|
503
|
+
if (!cssId) {
|
|
504
|
+
cssId = genRandomId(cssScopes, 8, true);
|
|
505
|
+
cssScopesMap.set(compName, cssId);
|
|
432
506
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
fragment.firstChild.setAttribute("data-ch-source", instance.__sourceFile || compName);
|
|
507
|
+
if (fragment.children.length === 1 && firstChild.nodeType === 1) {
|
|
508
|
+
firstChild.classList.add(cssId);
|
|
436
509
|
}
|
|
510
|
+
styles = scopeCss(styles, cssId);
|
|
511
|
+
scopedStyles.push(styles);
|
|
512
|
+
}
|
|
437
513
|
|
|
438
|
-
|
|
439
|
-
|
|
514
|
+
if (fragment.children.length === 1 && firstChild.nodeType === 1) {
|
|
515
|
+
firstChild.setAttribute("data-ch-source", compName);
|
|
440
516
|
}
|
|
441
517
|
|
|
442
|
-
|
|
443
|
-
return
|
|
518
|
+
element.replaceWith(fragment);
|
|
519
|
+
return true;
|
|
444
520
|
}
|
|
445
521
|
|
|
446
|
-
/**
|
|
447
|
-
* Processes all components in the app container
|
|
448
|
-
* @param {Element[]} appElements
|
|
449
|
-
* @param {Map} loadedComponents
|
|
450
|
-
* @returns {{
|
|
451
|
-
* runtimeScript: string,
|
|
452
|
-
* hasComponents: boolean
|
|
453
|
-
* scopesCss: CSSString
|
|
454
|
-
* }}
|
|
455
|
-
*/
|
|
456
522
|
export function processAllComponents(appElements, loadedComponents, pageSourceFile, pageSourceContent) {
|
|
457
523
|
let runtimeChunks = [];
|
|
458
524
|
let compIdColl = [];
|
|
@@ -474,11 +540,6 @@ export function processAllComponents(appElements, loadedComponents, pageSourceFi
|
|
|
474
540
|
return { runtimeScript, hasComponents, scopesCss };
|
|
475
541
|
}
|
|
476
542
|
|
|
477
|
-
/**
|
|
478
|
-
* Gets the next letter in sequence or starts with 'a'
|
|
479
|
-
* @param {object} letterState - { value: string }
|
|
480
|
-
* @returns {string}
|
|
481
|
-
*/
|
|
482
543
|
function getNextLetter(letterState) {
|
|
483
544
|
if (!letterState.value) {
|
|
484
545
|
letterState.value = "a";
|
|
@@ -487,3 +548,6 @@ function getNextLetter(letterState) {
|
|
|
487
548
|
}
|
|
488
549
|
return letterState.value;
|
|
489
550
|
}
|
|
551
|
+
|
|
552
|
+
const RUNTIME_KW = "$runtime";
|
|
553
|
+
const RUNTIME_REGEX = new RegExp(RUNTIME_KW, "g");
|