chocola 2.0.0-next.6 → 2.0.0-next.8
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 +39 -36
- package/compiler/chalk.js +32 -32
- package/compiler/component-processor.js +180 -22
- package/compiler/dom-processor.js +3 -4
- package/compiler/index.js +35 -43
- package/compiler/runtime-generator.js +22 -5
- package/compiler/utils.js +52 -52
- package/dev/index.js +1 -1
- package/package.json +2 -2
- package/parser/component.js +129 -129
- package/parser/context.js +30 -29
- package/parser/css.js +96 -96
- package/parser/index.js +5 -5
- package/parser/template.js +120 -121
- package/parser/utils.js +16 -13
- package/runtime/index.js +312 -0
- package/utils.js +27 -2
package/README.md
CHANGED
|
@@ -1,36 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
</
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
## What is Chocola
|
|
4
|
+
|
|
5
|
+
Chocola is a new and sweeter way to build your web apps.
|
|
6
|
+
|
|
7
|
+
No bundler config. No virtual DOM. No hydration ceremony. Just `.html` files with `<template>`, `<script>`, and `<style>` — compiled to static HTML with scoped CSS, and optional runtime when you need it.
|
|
8
|
+
|
|
9
|
+
Import components. Instantiate them. Mount, update, remove. All client-side. Same file, zero overhead.
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<script>
|
|
13
|
+
let self;
|
|
14
|
+
let input;
|
|
15
|
+
|
|
16
|
+
export let title = "Hello";
|
|
17
|
+
|
|
18
|
+
function $runtime() {
|
|
19
|
+
input.focus();
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div>
|
|
25
|
+
<h1>{title}</h1>
|
|
26
|
+
<input bind:self="input" type="text" placeholder="Your name">
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
h1 { color: chocolate; }
|
|
32
|
+
</style>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Chocola 2 is in current development and will add CSR SPA and much more features!
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
38
|
+
|
|
39
|
+
https://github.com/chocolajs/chocola/wiki/Overview
|
package/compiler/chalk.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
const ansi = {
|
|
2
|
-
bold: "\x1b[1m",
|
|
3
|
-
underline: "\x1b[4m",
|
|
4
|
-
red: "\x1b[31m",
|
|
5
|
-
green: "\x1b[32m",
|
|
6
|
-
yellow: "\x1b[33m",
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const reset = "\x1b[0m";
|
|
10
|
-
|
|
11
|
-
function hexToAnsi(h) {
|
|
12
|
-
const r = parseInt(h.slice(1, 3), 16);
|
|
13
|
-
const g = parseInt(h.slice(3, 5), 16);
|
|
14
|
-
const b = parseInt(h.slice(5, 7), 16);
|
|
15
|
-
return `\x1b[38;2;${r};${g};${b}m`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function makeChalk(stack) {
|
|
19
|
-
const fn = (s) => {
|
|
20
|
-
if (stack.length === 0) return s;
|
|
21
|
-
return stack.map(n => ansi[n] || hexToAnsi(n)).join("") + s + reset;
|
|
22
|
-
};
|
|
23
|
-
return new Proxy(fn, {
|
|
24
|
-
get(_, prop) {
|
|
25
|
-
if (prop === "hex") return (h) => makeChalk([...stack, h]);
|
|
26
|
-
return makeChalk([...stack, prop]);
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const chalk = makeChalk([]);
|
|
32
|
-
export default chalk;
|
|
1
|
+
const ansi = {
|
|
2
|
+
bold: "\x1b[1m",
|
|
3
|
+
underline: "\x1b[4m",
|
|
4
|
+
red: "\x1b[31m",
|
|
5
|
+
green: "\x1b[32m",
|
|
6
|
+
yellow: "\x1b[33m",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const reset = "\x1b[0m";
|
|
10
|
+
|
|
11
|
+
function hexToAnsi(h) {
|
|
12
|
+
const r = parseInt(h.slice(1, 3), 16);
|
|
13
|
+
const g = parseInt(h.slice(3, 5), 16);
|
|
14
|
+
const b = parseInt(h.slice(5, 7), 16);
|
|
15
|
+
return `\x1b[38;2;${r};${g};${b}m`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function makeChalk(stack) {
|
|
19
|
+
const fn = (s) => {
|
|
20
|
+
if (stack.length === 0) return s;
|
|
21
|
+
return stack.map(n => ansi[n] || hexToAnsi(n)).join("") + s + reset;
|
|
22
|
+
};
|
|
23
|
+
return new Proxy(fn, {
|
|
24
|
+
get(_, prop) {
|
|
25
|
+
if (prop === "hex") return (h) => makeChalk([...stack, h]);
|
|
26
|
+
return makeChalk([...stack, prop]);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const chalk = makeChalk([]);
|
|
32
|
+
export default chalk;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { parseHTML } from "linkedom";
|
|
2
3
|
import { protectCurlyBraces } from "../utils.js";
|
|
3
4
|
import { genRandomId, incrementAlfabet, throwError, deterministicHash } from "./utils.js";
|
|
4
5
|
import {
|
|
@@ -12,7 +13,7 @@ import chalk from "./chalk.js";
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class ProcessContext {
|
|
15
|
-
constructor(loadedComponents, runtimeChunks, compIdColl, letterState, runtimeMap, cssScopes, cssScopesMap, scopedStyles, staticCtxRegistry) {
|
|
16
|
+
constructor(loadedComponents, runtimeChunks, compIdColl, letterState, runtimeMap, cssScopes, cssScopesMap, scopedStyles, staticCtxRegistry, csrClasses) {
|
|
16
17
|
this.loadedComponents = loadedComponents;
|
|
17
18
|
this.runtimeChunks = runtimeChunks;
|
|
18
19
|
this.compIdColl = compIdColl;
|
|
@@ -22,9 +23,112 @@ class ProcessContext {
|
|
|
22
23
|
this.cssScopesMap = cssScopesMap;
|
|
23
24
|
this.scopedStyles = scopedStyles;
|
|
24
25
|
this.staticCtxRegistry = staticCtxRegistry;
|
|
26
|
+
this.csrClasses = csrClasses;
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
function escapeForTemplateLiteral(str) {
|
|
31
|
+
return str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\${/g, "\\${");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseFragment(html, doc) {
|
|
35
|
+
const fragment = doc.createDocumentFragment();
|
|
36
|
+
const temp = doc.createElement("div");
|
|
37
|
+
temp.innerHTML = html;
|
|
38
|
+
const children = [...temp.childNodes];
|
|
39
|
+
for (const child of children) {
|
|
40
|
+
fragment.appendChild(child);
|
|
41
|
+
}
|
|
42
|
+
return fragment;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function generateCSRClass(compName, cx, explicitClassName) {
|
|
46
|
+
if (cx.csrClasses.has(compName)) return;
|
|
47
|
+
|
|
48
|
+
let instance = cx.loadedComponents.get(compName);
|
|
49
|
+
if (!instance) return;
|
|
50
|
+
|
|
51
|
+
instance = protectCurlyBraces(instance);
|
|
52
|
+
const dom = parseHTML(instance);
|
|
53
|
+
const doc = dom.document;
|
|
54
|
+
const script = doc.querySelector("script")?.innerHTML;
|
|
55
|
+
const template = doc.querySelector("template")?.innerHTML;
|
|
56
|
+
const styles = doc.querySelector("style")?.innerHTML;
|
|
57
|
+
|
|
58
|
+
if (!template) return;
|
|
59
|
+
|
|
60
|
+
const compProps = extractPropsDefaults(script);
|
|
61
|
+
const topFuncSrc = extractTopLevelFunctions(script || "", RUNTIME_KW);
|
|
62
|
+
let runtime = extractRuntime(script || "", compName);
|
|
63
|
+
|
|
64
|
+
if (!cx.cssScopesMap.has(compName)) {
|
|
65
|
+
cx.cssScopesMap.set(compName, deterministicHash(compName, 8));
|
|
66
|
+
}
|
|
67
|
+
const cssId = cx.cssScopesMap.get(compName);
|
|
68
|
+
|
|
69
|
+
if (styles) {
|
|
70
|
+
cx.scopedStyles.push(scopeCss(styles, cssId));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const childMappings = [];
|
|
74
|
+
const bindVarNames = new Set();
|
|
75
|
+
const templateDoc = parseFragment(template, doc);
|
|
76
|
+
const seenTags = new Set();
|
|
77
|
+
for (const el of templateDoc.querySelectorAll("*")) {
|
|
78
|
+
const tag = el.tagName.toLowerCase();
|
|
79
|
+
if (!seenTags.has(tag)) {
|
|
80
|
+
seenTags.add(tag);
|
|
81
|
+
const childCompName = tag + ".html";
|
|
82
|
+
if (cx.loadedComponents.has(childCompName)) {
|
|
83
|
+
generateCSRClass(childCompName, cx);
|
|
84
|
+
const childClassName = childCompName.replace(".html", "").replace(/^\w/, c => c.toUpperCase());
|
|
85
|
+
childMappings.push({ tag, compClass: childClassName });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const attr of el.attributes) {
|
|
89
|
+
if (attr.name.startsWith("bind:")) {
|
|
90
|
+
bindVarNames.add(attr.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let csrRuntimeSource = null;
|
|
96
|
+
if (runtime) {
|
|
97
|
+
let injectCode = "";
|
|
98
|
+
for (const { name, defaultValue } of compProps) {
|
|
99
|
+
if (defaultValue !== undefined) {
|
|
100
|
+
injectCode += `let ${name} = ctx.${name}??${defaultValue};\n`;
|
|
101
|
+
} else {
|
|
102
|
+
injectCode += `let ${name} = ctx.${name};\n`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const varName of bindVarNames) {
|
|
106
|
+
if (varName !== "self") {
|
|
107
|
+
injectCode += `let ${varName} = ctx.${varName};\n`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (topFuncSrc.length > 0) {
|
|
111
|
+
injectCode += "\n" + topFuncSrc.join("\n\n") + "\n";
|
|
112
|
+
}
|
|
113
|
+
runtime = runtime.replace(/\$runtime\([^)]*\)\s*\{/, match => match + "\n" + injectCode);
|
|
114
|
+
runtime = runtime.replace(`${RUNTIME_KW}()`, `function(self, ctx)`);
|
|
115
|
+
csrRuntimeSource = runtime.replace(/^(async\s+)?function\s+\w+/, "$1function");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const className = explicitClassName || compName.replace(".html", "").replace(/^\w/, c => c.toUpperCase());
|
|
119
|
+
const propsObj = {};
|
|
120
|
+
for (const { name, defaultValue } of compProps) {
|
|
121
|
+
propsObj[name] = defaultValue !== undefined ? defaultValue : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const childrenPart = childMappings.length > 0
|
|
125
|
+
? ",\n children: [" + childMappings.map(m => `{tag:"${m.tag}",compClass:${m.compClass}}`).join(",") + "]"
|
|
126
|
+
: "";
|
|
127
|
+
const runtimePart = csrRuntimeSource ? `,\n runtime: ${csrRuntimeSource}` : "";
|
|
128
|
+
const classDef = `class ${className} extends ChocolaComponent {\n constructor() {\n super({\n template: \`${escapeForTemplateLiteral(template)}\`,\n hash: "${cssId}",\n props: ${JSON.stringify(propsObj)}${runtimePart}${childrenPart}\n });\n }\n}`;
|
|
129
|
+
cx.csrClasses.set(compName, classDef);
|
|
130
|
+
}
|
|
131
|
+
|
|
28
132
|
|
|
29
133
|
|
|
30
134
|
export function processComponentElement(
|
|
@@ -42,8 +146,8 @@ export function processComponentElement(
|
|
|
42
146
|
if (renderChain && renderChain.includes(compName)) return false;
|
|
43
147
|
|
|
44
148
|
instance = protectCurlyBraces(instance);
|
|
45
|
-
const dom =
|
|
46
|
-
const doc = dom.
|
|
149
|
+
const dom = parseHTML(instance);
|
|
150
|
+
const doc = dom.document;
|
|
47
151
|
let script = doc.querySelector("script")?.innerHTML;
|
|
48
152
|
let template = doc.querySelector("template")?.innerHTML;
|
|
49
153
|
let styles = doc.querySelector("style")?.innerHTML;
|
|
@@ -53,6 +157,17 @@ export function processComponentElement(
|
|
|
53
157
|
return false;
|
|
54
158
|
}
|
|
55
159
|
|
|
160
|
+
if (script) {
|
|
161
|
+
const importRegex = /import\s+(\w+)\s+from\s+['"]([^'"]+)['"]\s*;?\s*/g;
|
|
162
|
+
script = script.replace(importRegex, (_, importedName, importPath) => {
|
|
163
|
+
const importedCompName = path.basename(importPath).toLowerCase();
|
|
164
|
+
if (cx.loadedComponents.has(importedCompName)) {
|
|
165
|
+
generateCSRClass(importedCompName, cx, importedName);
|
|
166
|
+
}
|
|
167
|
+
return "";
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
56
171
|
const compProps = extractPropsDefaults(script);
|
|
57
172
|
|
|
58
173
|
let ctx;
|
|
@@ -91,9 +206,9 @@ export function processComponentElement(
|
|
|
91
206
|
get(target, key) { return target[key]; }
|
|
92
207
|
});
|
|
93
208
|
|
|
94
|
-
const fragment =
|
|
209
|
+
const fragment = parseFragment(template, doc);
|
|
95
210
|
|
|
96
|
-
const slotFragment =
|
|
211
|
+
const slotFragment = parseFragment(elInnerHtml, doc);
|
|
97
212
|
if (sourceFile) {
|
|
98
213
|
validateChainStructure(slotFragment, sourceFile, sourceContent, elInnerHtml);
|
|
99
214
|
}
|
|
@@ -188,19 +303,28 @@ export function processComponentElement(
|
|
|
188
303
|
return;
|
|
189
304
|
}
|
|
190
305
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
306
|
+
child.setAttribute(
|
|
307
|
+
attribute.name,
|
|
308
|
+
attribute.value.replace(
|
|
309
|
+
/\{([^}]+)\}/g,
|
|
310
|
+
(_, expr) => {
|
|
311
|
+
try {
|
|
312
|
+
return compileExpression(expr, true)(ctxProxy);
|
|
313
|
+
} catch {
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
198
316
|
}
|
|
199
|
-
|
|
317
|
+
)
|
|
200
318
|
);
|
|
201
319
|
});
|
|
202
320
|
|
|
203
|
-
|
|
321
|
+
const condAttrs = {};
|
|
322
|
+
if (hasIf) condAttrs["if"] = child.getAttribute("if");
|
|
323
|
+
if (hasDelIf) condAttrs["mount:if"] = getDelIfAttr(child);
|
|
324
|
+
if (hasElif) condAttrs["elif"] = child.getAttribute("elif");
|
|
325
|
+
if (hasElse) condAttrs["else"] = "";
|
|
326
|
+
|
|
327
|
+
const processed = processComponentElement(
|
|
204
328
|
child,
|
|
205
329
|
cx,
|
|
206
330
|
renderChain.concat(compName),
|
|
@@ -208,11 +332,20 @@ export function processComponentElement(
|
|
|
208
332
|
template
|
|
209
333
|
);
|
|
210
334
|
|
|
211
|
-
|
|
335
|
+
let condTarget = child;
|
|
336
|
+
if (processed && processed.nodeType === 1) {
|
|
337
|
+
condTarget = processed;
|
|
338
|
+
for (const [name, value] of Object.entries(condAttrs)) {
|
|
339
|
+
condTarget.setAttribute(name, value);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
applyConditionalToElement(condTarget, ctxProxy, condChain, hasIf, hasDelIf, hasElif, hasElse);
|
|
212
344
|
|
|
213
|
-
interpolateNode(
|
|
345
|
+
interpolateNode(condTarget, ctxProxy)
|
|
214
346
|
});
|
|
215
347
|
|
|
348
|
+
let csrRuntimeSource = null;
|
|
216
349
|
const firstChild = fragment.children[0];
|
|
217
350
|
|
|
218
351
|
if (firstChild && firstChild.nodeType === 1) {
|
|
@@ -233,14 +366,14 @@ export function processComponentElement(
|
|
|
233
366
|
const ctxDefParts = [];
|
|
234
367
|
const declared = new Set();
|
|
235
368
|
for (const [key, value] of Object.entries(runtimeCtx)) {
|
|
236
|
-
ctxDefParts.push(`let ${key} = ctx.${key}
|
|
369
|
+
ctxDefParts.push(`let ${key} = ctx.${key}??${JSON.stringify(value)};\n`);
|
|
237
370
|
declared.add(key);
|
|
238
371
|
}
|
|
239
372
|
|
|
240
373
|
for (const { name, defaultValue } of compProps) {
|
|
241
374
|
if (declared.has(name)) continue;
|
|
242
375
|
if (defaultValue !== undefined) {
|
|
243
|
-
ctxDefParts.push(`let ${name} = ctx.${name}
|
|
376
|
+
ctxDefParts.push(`let ${name} = ctx.${name}??${defaultValue};\n`);
|
|
244
377
|
} else {
|
|
245
378
|
ctxDefParts.push(`let ${name} = ctx.${name};\n`);
|
|
246
379
|
}
|
|
@@ -272,6 +405,7 @@ export function processComponentElement(
|
|
|
272
405
|
runtime = runtime.replace(/\$runtime\([^)]*\)\s*\{/, match => match + "\n" + injectCode);
|
|
273
406
|
|
|
274
407
|
runtime = runtime.replace(`${RUNTIME_KW}()`, `${letter}r(self, ctx)`);
|
|
408
|
+
csrRuntimeSource = runtime.replace(/^function\s+\w+/, "function");
|
|
275
409
|
cx.runtimeChunks.push(runtime);
|
|
276
410
|
cx.runtimeMap && cx.runtimeMap.set(compName, { letter });
|
|
277
411
|
} else {
|
|
@@ -296,15 +430,38 @@ export function processComponentElement(
|
|
|
296
430
|
cx.scopedStyles.push(styles);
|
|
297
431
|
}
|
|
298
432
|
|
|
433
|
+
if (csrRuntimeSource && !cx.csrClasses.has(compName)) {
|
|
434
|
+
generateCSRClass(compName, cx);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (element.style.display === "none" && firstChild && firstChild.nodeType === 1) {
|
|
438
|
+
firstChild.style.display = "none";
|
|
439
|
+
}
|
|
440
|
+
|
|
299
441
|
element.replaceWith(fragment);
|
|
300
|
-
return true;
|
|
442
|
+
return firstChild && firstChild.nodeType === 1 ? firstChild : true;
|
|
301
443
|
}
|
|
302
444
|
|
|
303
445
|
export function processAllComponents(appElements, loadedComponents, pageSourceFile, pageSourceContent) {
|
|
304
446
|
const cx = new ProcessContext(
|
|
305
|
-
loadedComponents, [], [], { value: null }, new Map(), [], new Map(), [], new Map()
|
|
447
|
+
loadedComponents, [], [], { value: null }, new Map(), [], new Map(), [], new Map(), new Map()
|
|
306
448
|
);
|
|
307
449
|
|
|
450
|
+
for (const [compName, instance] of cx.loadedComponents) {
|
|
451
|
+
const script = instance.match(/<script>([\s\S]*?)<\/script>/i);
|
|
452
|
+
if (script) {
|
|
453
|
+
const importRegex = /import\s+(\w+)\s+from\s+['"]([^'"]+)['"]\s*;?\s*/g;
|
|
454
|
+
let match;
|
|
455
|
+
while ((match = importRegex.exec(script[1])) !== null) {
|
|
456
|
+
const importedName = match[1];
|
|
457
|
+
const importedCompName = path.basename(match[2]).toLowerCase();
|
|
458
|
+
if (cx.loadedComponents.has(importedCompName)) {
|
|
459
|
+
generateCSRClass(importedCompName, cx, importedName);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
308
465
|
appElements.forEach(el => {
|
|
309
466
|
if (!el.isConnected) return;
|
|
310
467
|
processComponentElement(el, cx, [], pageSourceFile, pageSourceContent);
|
|
@@ -312,13 +469,14 @@ export function processAllComponents(appElements, loadedComponents, pageSourceFi
|
|
|
312
469
|
const runtimeScript = cx.runtimeChunks.join("\n");
|
|
313
470
|
const hasComponents = cx.runtimeChunks.length > 0;
|
|
314
471
|
const scopesCss = cx.scopedStyles.join("\n");
|
|
472
|
+
const csrClasses = [...cx.csrClasses.values()].join("\n\n");
|
|
315
473
|
|
|
316
474
|
const hashMap = {};
|
|
317
475
|
for (const [compName, hash] of cx.cssScopesMap) {
|
|
318
476
|
hashMap[compName] = hash;
|
|
319
477
|
}
|
|
320
478
|
|
|
321
|
-
return { runtimeScript, hasComponents, scopesCss, hashMap };
|
|
479
|
+
return { runtimeScript, hasComponents, scopesCss, hashMap, csrClasses };
|
|
322
480
|
}
|
|
323
481
|
|
|
324
482
|
function getNextLetter(letterState) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseHTML } from "linkedom";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { protectCurlyBraces, restoreCurlyBraces } from "../utils.js";
|
|
@@ -7,7 +7,7 @@ import { throwError } from "./utils.js";
|
|
|
7
7
|
import { readMyFile } from "./fs.js";
|
|
8
8
|
|
|
9
9
|
export function createDOM(srcIndexContent) {
|
|
10
|
-
return
|
|
10
|
+
return parseHTML(protectCurlyBraces(srcIndexContent));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function validateAppContainer(doc) {
|
|
@@ -23,7 +23,7 @@ export function getAppElements(appContainer) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export async function serializeDOM(dom) {
|
|
26
|
-
return restoreCurlyBraces(dom.
|
|
26
|
+
return restoreCurlyBraces(dom.document.toString());
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export async function writeHTMLOutput(html, outDirPath) {
|
|
@@ -54,7 +54,6 @@ export function getScriptElements(doc) {
|
|
|
54
54
|
|
|
55
55
|
export function appendRuntimeScript(doc, filename) {
|
|
56
56
|
const runtimeScriptEl = doc.createElement("script");
|
|
57
|
-
runtimeScriptEl.type = "module";
|
|
58
57
|
runtimeScriptEl.src = "./" + filename;
|
|
59
58
|
doc.body.appendChild(runtimeScriptEl);
|
|
60
59
|
}
|
package/compiler/index.js
CHANGED
|
@@ -26,48 +26,42 @@ import {
|
|
|
26
26
|
processScript,
|
|
27
27
|
processStylesheet,
|
|
28
28
|
} from "./pipeline.js";
|
|
29
|
+
import { text } from "stream/consumers";
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
========================================================================
|
|
34
|
-
`);
|
|
31
|
+
const GOLD_COLOR = "#D87416";
|
|
32
|
+
const WHITE_COLOR = "#FAFAF8";
|
|
33
|
+
const TEXT_FAINT = "#E7EBE1";
|
|
35
34
|
|
|
36
35
|
function logBanner() {
|
|
37
|
-
console.log(chalk.bold.hex("#945e33")(`\n RUNNING CHOCOLA BUNDLER`));
|
|
38
|
-
console.log(logSeparation);
|
|
39
36
|
console.log(
|
|
40
|
-
chalk.hex(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
chalk.hex(GOLD_COLOR)(`
|
|
38
|
+
┌─────────────────────────────────────────────┐
|
|
39
|
+
│┌-------------------------------------------┐│
|
|
40
|
+
││ ││`)
|
|
41
|
+
);
|
|
42
|
+
console.log(
|
|
43
|
+
chalk.hex(GOLD_COLOR)(` ││ `) +
|
|
44
|
+
chalk.bold.hex(WHITE_COLOR)(`{`) +
|
|
45
|
+
chalk.bold.hex(GOLD_COLOR)(` C H O C O L A `) +
|
|
46
|
+
chalk.bold.hex(WHITE_COLOR)(`}`) +
|
|
47
|
+
chalk.hex(GOLD_COLOR)(` ││\n`) +
|
|
48
|
+
chalk.hex(GOLD_COLOR)(` ││ ││
|
|
49
|
+
││ `) +
|
|
50
|
+
chalk.hex(TEXT_FAINT)(`THE SWEETEST WAY TO BUILD THE WEB`) +
|
|
51
|
+
chalk.hex(GOLD_COLOR)(` ││
|
|
52
|
+
││ ││
|
|
53
|
+
│└-------------------------------------------┘│
|
|
54
|
+
└─────────────────────────────────────────────┘
|
|
55
|
+
`)
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function logSuccess(outDirPath) {
|
|
60
|
-
console.log(`
|
|
61
|
-
▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
|
|
62
|
-
██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
|
|
63
|
-
▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
|
|
64
|
-
|
|
65
|
-
`);
|
|
66
60
|
console.log(
|
|
67
61
|
chalk.bold.green(">"),
|
|
68
62
|
"Project bundled succesfully at",
|
|
69
|
-
chalk.green.underline(outDirPath)
|
|
70
|
-
|
|
63
|
+
chalk.green.underline(outDirPath));
|
|
64
|
+
console.log(chalk.bold.green(`\nJOB DONE!\n`));
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
@@ -81,9 +75,8 @@ async function loadAndDisplayComponents(srcComponentsPath) {
|
|
|
81
75
|
const foundComponents = await getComponents(srcComponentsPath);
|
|
82
76
|
const { loadedComponents, notDefComps: emptyComps, componentsLib } = foundComponents;
|
|
83
77
|
|
|
84
|
-
console.log(` LOADING COMPONENTS`);
|
|
85
78
|
console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponentsPath) + ":");
|
|
86
|
-
console.log(" ", componentsLib, "\n");
|
|
79
|
+
console.log(" ", componentsLib, "\n\n");
|
|
87
80
|
|
|
88
81
|
if (emptyComps?.length > 0) {
|
|
89
82
|
console.warn(chalk.bold.yellow("WARNING!"), "The following component files are empty:");
|
|
@@ -139,7 +132,7 @@ function processPageConditionals(parent) {
|
|
|
139
132
|
if (result) {
|
|
140
133
|
chainRendered = true;
|
|
141
134
|
} else {
|
|
142
|
-
child.
|
|
135
|
+
child.style.display = "none";
|
|
143
136
|
chainRendered = false;
|
|
144
137
|
}
|
|
145
138
|
child.removeAttribute("if");
|
|
@@ -188,7 +181,6 @@ export default async function compile(rootDir, buildConfig) {
|
|
|
188
181
|
|
|
189
182
|
const config = await loadConfig(rootDir);
|
|
190
183
|
const paths = resolvePaths(rootDir, config);
|
|
191
|
-
!isHotReload && console.log(logSeparation);
|
|
192
184
|
|
|
193
185
|
await setupOutputDirectory(paths.outDir, config.emptyOutDir);
|
|
194
186
|
|
|
@@ -197,20 +189,18 @@ export default async function compile(rootDir, buildConfig) {
|
|
|
197
189
|
const pageSourcePath = indexFiles.srcPath;
|
|
198
190
|
|
|
199
191
|
const loadedComponents = await loadAndDisplayComponents(paths.components);
|
|
200
|
-
!isHotReload && console.log(
|
|
201
|
-
!isHotReload && console.log(` BUNDLING STATIC BUILD`);
|
|
202
|
-
!isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(paths.outDir) + "\n");
|
|
203
|
-
!isHotReload && console.log(logSeparation);
|
|
192
|
+
!isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(paths.outDir));
|
|
204
193
|
|
|
205
194
|
const dom = createDOM(srcIndexContent);
|
|
206
|
-
const doc = dom.
|
|
195
|
+
const doc = dom.document;
|
|
207
196
|
const appContainer = validateAppContainer(doc);
|
|
208
197
|
|
|
209
198
|
processPageConditionals(appContainer);
|
|
210
199
|
|
|
211
200
|
const appElements = getAppElements(appContainer);
|
|
212
|
-
const { runtimeScript, scopesCss, hashMap } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
|
|
213
|
-
const
|
|
201
|
+
const { runtimeScript, scopesCss, hashMap, csrClasses } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
|
|
202
|
+
const csrSource = await fs.readFile(new URL("../runtime/index.js", import.meta.url), "utf-8");
|
|
203
|
+
const runtimeFilenames = await generateRuntimeScript(runtimeScript, paths.outDir, csrSource, csrClasses);
|
|
214
204
|
await processAssets(doc, rootDir, config.srcDir, paths.outDir);
|
|
215
205
|
|
|
216
206
|
if (scopesCss) {
|
|
@@ -219,7 +209,9 @@ export default async function compile(rootDir, buildConfig) {
|
|
|
219
209
|
appendStylesheetLink(doc, fileName);
|
|
220
210
|
};
|
|
221
211
|
|
|
222
|
-
|
|
212
|
+
for (const name of runtimeFilenames) {
|
|
213
|
+
appendRuntimeScript(doc, name);
|
|
214
|
+
}
|
|
223
215
|
const html = await serializeDOM(dom);
|
|
224
216
|
await writeHTMLOutput(html, paths.outDir);
|
|
225
217
|
|
|
@@ -2,12 +2,29 @@ import { promises as fs } from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { genRandomId } from "./utils.js";
|
|
4
4
|
|
|
5
|
-
export async function generateRuntimeScript(runtimeScript, outDirPath) {
|
|
5
|
+
export async function generateRuntimeScript(runtimeScript, outDirPath, csrSource, csrClasses) {
|
|
6
6
|
const fileIds = [];
|
|
7
|
-
const
|
|
8
|
-
const runtimeFileContents = `document.addEventListener("DOMContentLoaded", () => {${runtimeScript}})`;
|
|
7
|
+
const filenames = [];
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
if (csrSource) {
|
|
10
|
+
const name = "run-" + genRandomId(fileIds, 6) + ".js";
|
|
11
|
+
const source = csrSource.replace(/^export\s+\{?\s*[^}]+\s*}?\s*;?\s*$/m, "");
|
|
12
|
+
await fs.writeFile(path.join(outDirPath, name), source);
|
|
13
|
+
filenames.push(name);
|
|
14
|
+
}
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
if (csrClasses) {
|
|
17
|
+
const name = "run-" + genRandomId(fileIds, 6) + ".js";
|
|
18
|
+
await fs.writeFile(path.join(outDirPath, name), csrClasses);
|
|
19
|
+
filenames.push(name);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (runtimeScript) {
|
|
23
|
+
const name = "run-" + genRandomId(fileIds, 6) + ".js";
|
|
24
|
+
const content = `document.addEventListener("DOMContentLoaded", () => {${runtimeScript}})`;
|
|
25
|
+
await fs.writeFile(path.join(outDirPath, name), content);
|
|
26
|
+
filenames.push(name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return filenames;
|
|
13
30
|
}
|