chocola 2.0.0-next.1 → 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 +292 -249
- package/compiler/config.js +1 -54
- package/compiler/dom-processor.js +3 -53
- package/compiler/fs.js +0 -11
- package/compiler/index.js +16 -36
- package/compiler/pipeline.js +7 -188
- package/compiler/runtime-generator.js +0 -6
- package/compiler/utils.js +17 -99
- package/dev/index.js +1 -7
- package/package.json +2 -3
- package/utils.js +0 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JSDOM } from "jsdom";
|
|
2
2
|
import { extractContextFromElement } from "./dom-processor.js";
|
|
3
|
-
import { genRandomId, incrementAlfabet, throwError, protectCurlyBraces } from "./utils.js";
|
|
3
|
+
import { genRandomId, incrementAlfabet, throwError, protectCurlyBraces, compileExpression } from "./utils.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import beautify from "js-beautify";
|
|
6
6
|
|
|
@@ -101,45 +101,69 @@ 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
|
|
|
121
|
-
function
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
|
|
125
|
+
function interpolateNode(root, ctxProxy) {
|
|
126
|
+
const stack = [root];
|
|
127
|
+
while (stack.length) {
|
|
128
|
+
const node = stack.pop();
|
|
129
|
+
if (node.nodeType === 3) {
|
|
130
|
+
node.textContent = node.textContent.replace(/\{([^}]+)\}/g, (_, expr) => {
|
|
131
|
+
try {
|
|
132
|
+
return compileExpression(expr, true)(ctxProxy);
|
|
133
|
+
} catch {
|
|
134
|
+
return "";
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
} else {
|
|
138
|
+
for (let i = node.childNodes.length - 1; i >= 0; i--) {
|
|
139
|
+
stack.push(node.childNodes[i]);
|
|
128
140
|
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
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;
|
|
132
156
|
}
|
|
157
|
+
return null;
|
|
133
158
|
}
|
|
134
159
|
|
|
135
160
|
function validateChainStructure(parent, sourceFile, sourceContent, parentContent) {
|
|
136
161
|
const children = [...parent.children];
|
|
137
162
|
let chainActive = false;
|
|
138
|
-
const outerCounts = {};
|
|
139
163
|
|
|
140
164
|
for (const child of children) {
|
|
141
165
|
const hasIf = child.hasAttribute("if");
|
|
142
|
-
const hasDelIf = hasDelIfAttr(child
|
|
166
|
+
const hasDelIf = hasDelIfAttr(child);
|
|
143
167
|
const hasElif = child.hasAttribute("elif");
|
|
144
168
|
const hasElse = child.hasAttribute("else");
|
|
145
169
|
|
|
@@ -149,36 +173,10 @@ function validateChainStructure(parent, sourceFile, sourceContent, parentContent
|
|
|
149
173
|
const attr = hasElif ? "elif" : "else";
|
|
150
174
|
let loc = sourceFile;
|
|
151
175
|
if (sourceContent && parentContent) {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
while ((searchIdx = parentContent.indexOf(outer, searchIdx)) !== -1) {
|
|
157
|
-
hits++;
|
|
158
|
-
if (hits === outerCounts[outer]) {
|
|
159
|
-
let line = parentContent.substring(0, searchIdx).split("\n").length;
|
|
160
|
-
if (sourceContent !== parentContent) {
|
|
161
|
-
const anchor = parentContent.trim().slice(0, 80);
|
|
162
|
-
const vars = [
|
|
163
|
-
anchor,
|
|
164
|
-
anchor.replace(/=""/g, ' '),
|
|
165
|
-
anchor.replace(/=""/g, ''),
|
|
166
|
-
anchor.replace(/="/g, "='").replace(/"/g, "'"),
|
|
167
|
-
];
|
|
168
|
-
let anchorIdx = -1;
|
|
169
|
-
for (const v of vars) {
|
|
170
|
-
anchorIdx = sourceContent.indexOf(v);
|
|
171
|
-
if (anchorIdx !== -1) break;
|
|
172
|
-
}
|
|
173
|
-
if (anchorIdx !== -1) {
|
|
174
|
-
const offset = sourceContent.substring(0, anchorIdx).split("\n").length;
|
|
175
|
-
line = offset + line - 1;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
loc = `${sourceFile}:${line}`;
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
searchIdx += outer.length;
|
|
176
|
+
const idx = parentContent.indexOf(child.outerHTML);
|
|
177
|
+
if (idx !== -1) {
|
|
178
|
+
const lineNum = getLineNumber(sourceContent, parentContent, idx);
|
|
179
|
+
if (lineNum !== null) loc = `${sourceFile}:${lineNum}`;
|
|
182
180
|
}
|
|
183
181
|
}
|
|
184
182
|
throwError(`${loc}\n <${tag}> has ${attr} without a preceding if/del-if sibling`);
|
|
@@ -198,15 +196,6 @@ function validateChainStructure(parent, sourceFile, sourceContent, parentContent
|
|
|
198
196
|
}
|
|
199
197
|
}
|
|
200
198
|
|
|
201
|
-
/**
|
|
202
|
-
* Processes a single component element and inserts it into the DOM
|
|
203
|
-
* @param {Element} element
|
|
204
|
-
* @param {Map} loadedComponents
|
|
205
|
-
* @param {Array} runtimeChunks
|
|
206
|
-
* @param {Array} compIdColl
|
|
207
|
-
* @param {object} letterState - { value: string }
|
|
208
|
-
* @returns {boolean} - true if component was processed, false if not found
|
|
209
|
-
*/
|
|
210
199
|
export function processComponentElement(
|
|
211
200
|
element,
|
|
212
201
|
loadedComponents,
|
|
@@ -223,12 +212,26 @@ export function processComponentElement(
|
|
|
223
212
|
sourceContent
|
|
224
213
|
) {
|
|
225
214
|
const tagName = element.tagName.toLowerCase();
|
|
226
|
-
const compName = tagName + ".
|
|
227
|
-
|
|
215
|
+
const compName = tagName + ".html";
|
|
216
|
+
let instance = loadedComponents.get(compName);
|
|
228
217
|
|
|
229
218
|
if (!instance || instance === undefined) return false;
|
|
230
219
|
if (renderChain && renderChain.includes(compName)) return false;
|
|
231
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
|
+
|
|
232
235
|
let ctx;
|
|
233
236
|
if (staticCtxRegistry && staticCtxRegistry.has(element)) {
|
|
234
237
|
ctx = staticCtxRegistry.get(element);
|
|
@@ -236,245 +239,286 @@ export function processComponentElement(
|
|
|
236
239
|
ctx = extractContextFromElement(element);
|
|
237
240
|
staticCtxRegistry && staticCtxRegistry.set(element, ctx);
|
|
238
241
|
}
|
|
239
|
-
|
|
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;
|
|
240
256
|
|
|
241
257
|
const ctxProxy = new Proxy(ctx, {
|
|
242
258
|
has() { return true; },
|
|
243
259
|
get(target, key) { return target[key]; }
|
|
244
260
|
});
|
|
245
261
|
|
|
246
|
-
|
|
247
|
-
let body = protectCurlyBraces(instance.body);
|
|
262
|
+
const fragment = JSDOM.fragment(template);
|
|
248
263
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
slot.replaceWith(slotFragment);
|
|
258
|
-
});
|
|
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
|
+
});
|
|
259
272
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
const childEntries = Array.from(fragment.querySelectorAll("*")).map(el => ({
|
|
274
|
+
el,
|
|
275
|
+
parent: el.parentNode
|
|
276
|
+
}));
|
|
277
|
+
const condChains = new Map();
|
|
265
278
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
279
|
+
childEntries.forEach(({ el: child, parent }) => {
|
|
280
|
+
if (!condChains.has(parent)) {
|
|
281
|
+
condChains.set(parent, { active: false, rendered: false });
|
|
282
|
+
}
|
|
283
|
+
const condChain = condChains.get(parent);
|
|
271
284
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
285
|
+
const hasIf = child.hasAttribute("if");
|
|
286
|
+
const hasDelIf = hasDelIfAttr(child);
|
|
287
|
+
const hasElif = child.hasAttribute("elif");
|
|
288
|
+
const hasElse = child.hasAttribute("else");
|
|
276
289
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
if (condChain.rendered) {
|
|
282
|
-
child.remove();
|
|
283
|
-
if (hasElse) {
|
|
284
|
-
condChain.active = false;
|
|
285
|
-
}
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
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`);
|
|
288
293
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (
|
|
292
|
-
if (hasElif) {
|
|
293
|
-
const expr = child.getAttribute("elif").slice(1, -1);
|
|
294
|
-
const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
|
|
295
|
-
if (!fn(ctxProxy)) {
|
|
296
|
-
child.remove();
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
child.replaceWith(...child.children);
|
|
301
|
-
condChain.rendered = true;
|
|
302
|
-
if (hasElse) {
|
|
303
|
-
condChain.active = false;
|
|
304
|
-
}
|
|
305
|
-
} else if (hasIf || hasDelIf) {
|
|
306
|
-
const raw = hasIf ? child.getAttribute("if") : getDelIfAttr(child);
|
|
307
|
-
const expr = raw.slice(1, -1);
|
|
308
|
-
const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
|
|
309
|
-
condChain.active = true;
|
|
310
|
-
if (fn(ctxProxy)) {
|
|
311
|
-
child.replaceWith(...child.children);
|
|
312
|
-
condChain.rendered = true;
|
|
313
|
-
} else {
|
|
314
|
-
child.remove();
|
|
315
|
-
condChain.rendered = false;
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
child.replaceWith(...child.children);
|
|
294
|
+
if (condChain.rendered) {
|
|
295
|
+
child.remove();
|
|
296
|
+
if (hasElse) {
|
|
319
297
|
condChain.active = false;
|
|
320
|
-
condChain.rendered = false;
|
|
321
298
|
}
|
|
322
299
|
return;
|
|
323
300
|
}
|
|
301
|
+
}
|
|
324
302
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
try {
|
|
334
|
-
return Function("ctx", `with(ctx) { return (${expr}); }`)(ctxProxy);
|
|
335
|
-
} catch {
|
|
336
|
-
return "";
|
|
337
|
-
}
|
|
303
|
+
if (child.tagName.toLowerCase() === "void") {
|
|
304
|
+
if (hasElif || hasElse) {
|
|
305
|
+
if (hasElif) {
|
|
306
|
+
const expr = child.getAttribute("elif").slice(1, -1);
|
|
307
|
+
const fn = compileExpression(expr, true);
|
|
308
|
+
if (!fn(ctxProxy)) {
|
|
309
|
+
child.remove();
|
|
310
|
+
return;
|
|
338
311
|
}
|
|
339
|
-
);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
processComponentElement(
|
|
343
|
-
child,
|
|
344
|
-
loadedComponents,
|
|
345
|
-
runtimeChunks,
|
|
346
|
-
compIdColl,
|
|
347
|
-
letterState,
|
|
348
|
-
runtimeMap,
|
|
349
|
-
cssScopes,
|
|
350
|
-
cssScopesMap,
|
|
351
|
-
scopedStyles,
|
|
352
|
-
renderChain.concat(compName),
|
|
353
|
-
staticCtxRegistry,
|
|
354
|
-
instance.__sourceFile || compName,
|
|
355
|
-
body
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
if (hasIf) {
|
|
359
|
-
const expr = child.getAttribute("if").slice(1, -1);
|
|
360
|
-
const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
|
|
361
|
-
condChain.active = true;
|
|
362
|
-
if (fn(ctxProxy)) {
|
|
363
|
-
condChain.rendered = true;
|
|
364
|
-
} else {
|
|
365
|
-
child.style.display = "none";
|
|
366
|
-
condChain.rendered = false;
|
|
367
312
|
}
|
|
368
|
-
child.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
313
|
+
child.replaceWith(...child.children);
|
|
314
|
+
condChain.rendered = true;
|
|
315
|
+
if (hasElse) {
|
|
316
|
+
condChain.active = false;
|
|
317
|
+
}
|
|
318
|
+
} else if (hasIf || hasDelIf) {
|
|
319
|
+
const raw = hasIf ? child.getAttribute("if") : getDelIfAttr(child);
|
|
320
|
+
const expr = raw.slice(1, -1);
|
|
321
|
+
const fn = compileExpression(expr, true);
|
|
372
322
|
condChain.active = true;
|
|
373
323
|
if (fn(ctxProxy)) {
|
|
324
|
+
child.replaceWith(...child.children);
|
|
374
325
|
condChain.rendered = true;
|
|
375
326
|
} else {
|
|
376
327
|
child.remove();
|
|
377
328
|
condChain.rendered = false;
|
|
378
329
|
}
|
|
379
|
-
removeDelIfAttr(child);
|
|
380
|
-
} else if (hasElif) {
|
|
381
|
-
const expr = child.getAttribute("elif").slice(1, -1);
|
|
382
|
-
const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
|
|
383
|
-
if (fn(ctxProxy)) {
|
|
384
|
-
condChain.rendered = true;
|
|
385
|
-
} else {
|
|
386
|
-
child.remove();
|
|
387
|
-
}
|
|
388
|
-
child.removeAttribute("elif");
|
|
389
|
-
} else if (hasElse) {
|
|
390
|
-
condChain.rendered = true;
|
|
391
|
-
condChain.active = false;
|
|
392
|
-
child.removeAttribute("else");
|
|
393
330
|
} else {
|
|
331
|
+
child.replaceWith(...child.children);
|
|
394
332
|
condChain.active = false;
|
|
395
333
|
condChain.rendered = false;
|
|
396
334
|
}
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const reservedAttrs = ["if", "del:if", "elif", "else"];
|
|
397
339
|
|
|
398
|
-
|
|
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
|
+
);
|
|
399
353
|
});
|
|
400
354
|
|
|
401
|
-
|
|
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
|
+
});
|
|
413
|
+
|
|
414
|
+
const firstChild = fragment.children[0];
|
|
415
|
+
|
|
416
|
+
if (firstChild && firstChild.nodeType === 1) {
|
|
417
|
+
if (script) {
|
|
418
|
+
const compId = "chid-" + genRandomId(compIdColl);
|
|
419
|
+
firstChild.setAttribute("chid", compId);
|
|
420
|
+
|
|
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
|
+
}
|
|
402
441
|
|
|
403
|
-
|
|
404
|
-
if (instance.script || instance.effects) {
|
|
405
|
-
const compId = "chid-" + genRandomId(compIdColl);
|
|
406
|
-
firstChild.setAttribute("chid", compId);
|
|
442
|
+
script = script.replace(ctxRegex, "ctx");
|
|
407
443
|
|
|
408
|
-
|
|
444
|
+
function extractRuntime(script) {
|
|
445
|
+
const startRegex = /(?:async\s+)?function\s+\$runtime\(([^)]*)\)\s*\{/;
|
|
446
|
+
const match = script.match(startRegex);
|
|
409
447
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
+
}
|
|
418
464
|
}
|
|
419
465
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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}`);
|
|
423
481
|
}
|
|
424
|
-
|
|
425
|
-
|
|
482
|
+
|
|
483
|
+
runtime = runtime.replace(/\$runtime\([^)]*\)\s*\{/, match => match + "\n" + ctxDef);
|
|
426
484
|
|
|
427
485
|
let letterEntry = runtimeMap && runtimeMap.get(compName);
|
|
428
486
|
let letter;
|
|
429
487
|
if (!letterEntry) {
|
|
430
488
|
letter = getNextLetter(letterState);
|
|
431
|
-
|
|
432
|
-
runtimeChunks.push(
|
|
489
|
+
runtime = runtime.replace(`${RUNTIME_KW}()`, `${letter}r(self, ctx)`);
|
|
490
|
+
runtimeChunks.push(runtime);
|
|
433
491
|
runtimeMap && runtimeMap.set(compName, { letter });
|
|
434
492
|
} else {
|
|
435
493
|
letter = letterEntry.letter;
|
|
436
494
|
}
|
|
437
495
|
|
|
438
|
-
runtimeChunks.push(`${letter}
|
|
496
|
+
runtimeChunks.push(`${letter}r(document.querySelector('[chid="${compId}"]'), ${JSON.stringify(ctx)});`);
|
|
439
497
|
}
|
|
440
498
|
}
|
|
499
|
+
}
|
|
441
500
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
cssScopesMap.set(compName, cssId);
|
|
448
|
-
}
|
|
449
|
-
if (fragment.firstChild && fragment.firstChild.nodeType === 1) {
|
|
450
|
-
fragment.firstChild.classList.add(cssId);
|
|
451
|
-
}
|
|
452
|
-
style = scopeCss(style, cssId);
|
|
453
|
-
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);
|
|
454
506
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
fragment.firstChild.setAttribute("data-ch-source", instance.__sourceFile || compName);
|
|
507
|
+
if (fragment.children.length === 1 && firstChild.nodeType === 1) {
|
|
508
|
+
firstChild.classList.add(cssId);
|
|
458
509
|
}
|
|
510
|
+
styles = scopeCss(styles, cssId);
|
|
511
|
+
scopedStyles.push(styles);
|
|
512
|
+
}
|
|
459
513
|
|
|
460
|
-
|
|
461
|
-
|
|
514
|
+
if (fragment.children.length === 1 && firstChild.nodeType === 1) {
|
|
515
|
+
firstChild.setAttribute("data-ch-source", compName);
|
|
462
516
|
}
|
|
463
517
|
|
|
464
|
-
|
|
465
|
-
return
|
|
518
|
+
element.replaceWith(fragment);
|
|
519
|
+
return true;
|
|
466
520
|
}
|
|
467
521
|
|
|
468
|
-
/**
|
|
469
|
-
* Processes all components in the app container
|
|
470
|
-
* @param {Element[]} appElements
|
|
471
|
-
* @param {Map} loadedComponents
|
|
472
|
-
* @returns {{
|
|
473
|
-
* runtimeScript: string,
|
|
474
|
-
* hasComponents: boolean
|
|
475
|
-
* scopesCss: CSSString
|
|
476
|
-
* }}
|
|
477
|
-
*/
|
|
478
522
|
export function processAllComponents(appElements, loadedComponents, pageSourceFile, pageSourceContent) {
|
|
479
523
|
let runtimeChunks = [];
|
|
480
524
|
let compIdColl = [];
|
|
@@ -486,6 +530,7 @@ export function processAllComponents(appElements, loadedComponents, pageSourceFi
|
|
|
486
530
|
let staticCtxRegistry = new Map();
|
|
487
531
|
|
|
488
532
|
appElements.forEach(el => {
|
|
533
|
+
if (!el.isConnected) return;
|
|
489
534
|
processComponentElement(el, loadedComponents, runtimeChunks, compIdColl, letterState, runtimeMap, cssScopes, cssScopesMap, scopedStyles, [], staticCtxRegistry, pageSourceFile, pageSourceContent);
|
|
490
535
|
});
|
|
491
536
|
const runtimeScript = runtimeChunks.join("\n");
|
|
@@ -495,11 +540,6 @@ export function processAllComponents(appElements, loadedComponents, pageSourceFi
|
|
|
495
540
|
return { runtimeScript, hasComponents, scopesCss };
|
|
496
541
|
}
|
|
497
542
|
|
|
498
|
-
/**
|
|
499
|
-
* Gets the next letter in sequence or starts with 'a'
|
|
500
|
-
* @param {object} letterState - { value: string }
|
|
501
|
-
* @returns {string}
|
|
502
|
-
*/
|
|
503
543
|
function getNextLetter(letterState) {
|
|
504
544
|
if (!letterState.value) {
|
|
505
545
|
letterState.value = "a";
|
|
@@ -508,3 +548,6 @@ function getNextLetter(letterState) {
|
|
|
508
548
|
}
|
|
509
549
|
return letterState.value;
|
|
510
550
|
}
|
|
551
|
+
|
|
552
|
+
const RUNTIME_KW = "$runtime";
|
|
553
|
+
const RUNTIME_REGEX = new RegExp(RUNTIME_KW, "g");
|