chocola 1.6.0 → 2.0.0-next.2
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/compiler/component-processor.js +28 -49
- package/compiler/dom-processor.js +3 -2
- package/compiler/index.js +277 -254
- package/compiler/utils.js +17 -5
- package/dev/index.js +24 -7
- package/index.js +0 -31
- package/package.json +3 -3
|
@@ -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
|
|
|
@@ -118,24 +118,29 @@ function removeDelIfAttr(el) {
|
|
|
118
118
|
el.removeAttribute("del:if");
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function interpolateNode(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
121
|
+
function interpolateNode(root, ctxProxy) {
|
|
122
|
+
const stack = [root];
|
|
123
|
+
while (stack.length) {
|
|
124
|
+
const node = stack.pop();
|
|
125
|
+
if (node.nodeType === 3) {
|
|
126
|
+
node.textContent = node.textContent.replace(/\{([^}]+)\}/g, (_, expr) => {
|
|
127
|
+
try {
|
|
128
|
+
return compileExpression(expr, true)(ctxProxy);
|
|
129
|
+
} catch {
|
|
130
|
+
return "";
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
for (let i = node.childNodes.length - 1; i >= 0; i--) {
|
|
135
|
+
stack.push(node.childNodes[i]);
|
|
128
136
|
}
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
node.childNodes.forEach(child => interpolateNode(child, ctxProxy));
|
|
137
|
+
}
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
|
|
135
141
|
function validateChainStructure(parent, sourceFile, sourceContent, parentContent) {
|
|
136
142
|
const children = [...parent.children];
|
|
137
143
|
let chainActive = false;
|
|
138
|
-
const outerCounts = {};
|
|
139
144
|
|
|
140
145
|
for (const child of children) {
|
|
141
146
|
const hasIf = child.hasAttribute("if");
|
|
@@ -148,37 +153,10 @@ function validateChainStructure(parent, sourceFile, sourceContent, parentContent
|
|
|
148
153
|
const tag = child.tagName.toLowerCase();
|
|
149
154
|
const attr = hasElif ? "elif" : "else";
|
|
150
155
|
let loc = sourceFile;
|
|
151
|
-
if (sourceContent && parentContent) {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
let hits = 0;
|
|
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;
|
|
156
|
+
if (sourceContent && sourceContent === parentContent) {
|
|
157
|
+
const idx = parentContent.indexOf(child.outerHTML);
|
|
158
|
+
if (idx !== -1) {
|
|
159
|
+
loc = `${sourceFile}:${parentContent.substring(0, idx).split("\n").length}`;
|
|
182
160
|
}
|
|
183
161
|
}
|
|
184
162
|
throwError(`${loc}\n <${tag}> has ${attr} without a preceding if/del-if sibling`);
|
|
@@ -291,7 +269,7 @@ export function processComponentElement(
|
|
|
291
269
|
if (hasElif || hasElse) {
|
|
292
270
|
if (hasElif) {
|
|
293
271
|
const expr = child.getAttribute("elif").slice(1, -1);
|
|
294
|
-
const fn =
|
|
272
|
+
const fn = compileExpression(expr, true);
|
|
295
273
|
if (!fn(ctxProxy)) {
|
|
296
274
|
child.remove();
|
|
297
275
|
return;
|
|
@@ -305,7 +283,7 @@ export function processComponentElement(
|
|
|
305
283
|
} else if (hasIf || hasDelIf) {
|
|
306
284
|
const raw = hasIf ? child.getAttribute("if") : getDelIfAttr(child);
|
|
307
285
|
const expr = raw.slice(1, -1);
|
|
308
|
-
const fn =
|
|
286
|
+
const fn = compileExpression(expr, true);
|
|
309
287
|
condChain.active = true;
|
|
310
288
|
if (fn(ctxProxy)) {
|
|
311
289
|
child.replaceWith(...child.children);
|
|
@@ -331,7 +309,7 @@ export function processComponentElement(
|
|
|
331
309
|
/\{([^}]+)\}/g,
|
|
332
310
|
(_, expr) => {
|
|
333
311
|
try {
|
|
334
|
-
return
|
|
312
|
+
return compileExpression(expr, true)(ctxProxy);
|
|
335
313
|
} catch {
|
|
336
314
|
return "";
|
|
337
315
|
}
|
|
@@ -357,7 +335,7 @@ export function processComponentElement(
|
|
|
357
335
|
|
|
358
336
|
if (hasIf) {
|
|
359
337
|
const expr = child.getAttribute("if").slice(1, -1);
|
|
360
|
-
const fn =
|
|
338
|
+
const fn = compileExpression(expr, true);
|
|
361
339
|
condChain.active = true;
|
|
362
340
|
if (fn(ctxProxy)) {
|
|
363
341
|
condChain.rendered = true;
|
|
@@ -368,7 +346,7 @@ export function processComponentElement(
|
|
|
368
346
|
child.removeAttribute("if");
|
|
369
347
|
} else if (hasDelIf) {
|
|
370
348
|
const expr = getDelIfAttr(child).slice(1, -1);
|
|
371
|
-
const fn =
|
|
349
|
+
const fn = compileExpression(expr, true);
|
|
372
350
|
condChain.active = true;
|
|
373
351
|
if (fn(ctxProxy)) {
|
|
374
352
|
condChain.rendered = true;
|
|
@@ -379,7 +357,7 @@ export function processComponentElement(
|
|
|
379
357
|
removeDelIfAttr(child);
|
|
380
358
|
} else if (hasElif) {
|
|
381
359
|
const expr = child.getAttribute("elif").slice(1, -1);
|
|
382
|
-
const fn =
|
|
360
|
+
const fn = compileExpression(expr, true);
|
|
383
361
|
if (fn(ctxProxy)) {
|
|
384
362
|
condChain.rendered = true;
|
|
385
363
|
} else {
|
|
@@ -486,6 +464,7 @@ export function processAllComponents(appElements, loadedComponents, pageSourceFi
|
|
|
486
464
|
let staticCtxRegistry = new Map();
|
|
487
465
|
|
|
488
466
|
appElements.forEach(el => {
|
|
467
|
+
if (!el.isConnected) return;
|
|
489
468
|
processComponentElement(el, loadedComponents, runtimeChunks, compIdColl, letterState, runtimeMap, cssScopes, cssScopesMap, scopedStyles, [], staticCtxRegistry, pageSourceFile, pageSourceContent);
|
|
490
469
|
});
|
|
491
470
|
const runtimeScript = runtimeChunks.join("\n");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JSDOM } from "jsdom";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { throwError, protectCurlyBraces, restoreCurlyBraces } from "./utils.js";
|
|
4
|
+
import { throwError, protectCurlyBraces, restoreCurlyBraces, compileExpression } from "./utils.js";
|
|
5
5
|
import { readMyFile } from "./fs.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -45,10 +45,11 @@ export function extractContextFromElement(element) {
|
|
|
45
45
|
for (const attr of element.attributes) {
|
|
46
46
|
const key = attr.name;
|
|
47
47
|
const val = attr.value;
|
|
48
|
+
if (!val.includes("{")) { ctx[key] = val; continue; }
|
|
48
49
|
const matches = [...val.matchAll(/\{([^}]+)\}/g)];
|
|
49
50
|
if (matches.length === 1 && matches[0][0] === val) {
|
|
50
51
|
try {
|
|
51
|
-
ctx[key] =
|
|
52
|
+
ctx[key] = compileExpression(matches[0][1], false)();
|
|
52
53
|
continue;
|
|
53
54
|
} catch {}
|
|
54
55
|
}
|
package/compiler/index.js
CHANGED
|
@@ -1,254 +1,277 @@
|
|
|
1
|
-
import { promises as fs } from "fs";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { loadConfig, resolvePaths } from "./config.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "./dom-processor.js";
|
|
15
|
-
import { processAllComponents } from "./component-processor.js";
|
|
16
|
-
import { generateRuntimeScript } from "./runtime-generator.js";
|
|
17
|
-
import { genRandomId, throwError } from "./utils.js";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from "./pipeline.js";
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const logSeparation = chalk.yellow(`
|
|
29
|
-
________________________________________________________________________
|
|
30
|
-
========================================================================
|
|
31
|
-
`);
|
|
32
|
-
|
|
33
|
-
function logBanner() {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
▄████▄ ██░ ██ ▒█████ ▄████▄ ▒█████ ██▓ ▄▄▄
|
|
41
|
-
▒██▀ ▀█ ▓██░ ██▒▒██▒ ██▒▒██▀ ▀█ ▒██▒ ██▒▓██▒ ▒████▄
|
|
42
|
-
▒▓█ ▄ ▒██▀▀██░▒██░ ██▒▒▓█ ▄ ▒██░ ██▒▒██░ ▒██ ▀█▄
|
|
43
|
-
▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
|
|
44
|
-
▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
|
|
45
|
-
░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
|
|
46
|
-
░ ▒ ▒ ░▒░ ░ ░ ░ ▒ ▒░ ░ ▒ ░ ░ ▒░ ░ ░ ▒ ░ ░ ▒▒ ░
|
|
47
|
-
░ ░ ░░ ░░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
|
|
48
|
-
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
|
49
|
-
░ ░
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`)
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function logSuccess(outDirPath) {
|
|
57
|
-
|
|
58
|
-
▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
|
|
59
|
-
██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
|
|
60
|
-
▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
|
|
61
|
-
|
|
62
|
-
`);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function loadAndDisplayComponents(srcComponentsPath) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Compiles a static build of your Chocola project from the directory provided.
|
|
111
|
-
* @param {import("fs").PathLike} rootDir
|
|
112
|
-
* @param {Object<{
|
|
113
|
-
* isHotReload?: boolean
|
|
114
|
-
* }
|
|
115
|
-
* >} config
|
|
116
|
-
*/
|
|
117
|
-
export default async function runtime(rootDir, buildConfig) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { loadConfig, resolvePaths } from "./config.js";
|
|
4
|
+
import {
|
|
5
|
+
createDOM,
|
|
6
|
+
validateAppContainer,
|
|
7
|
+
getAppElements,
|
|
8
|
+
getAssetLinks,
|
|
9
|
+
appendRuntimeScript,
|
|
10
|
+
appendStylesheetLink,
|
|
11
|
+
serializeDOM,
|
|
12
|
+
writeHTMLOutput,
|
|
13
|
+
writeCSSOutput,
|
|
14
|
+
} from "./dom-processor.js";
|
|
15
|
+
import { processAllComponents } from "./component-processor.js";
|
|
16
|
+
import { generateRuntimeScript } from "./runtime-generator.js";
|
|
17
|
+
import { genRandomId, throwError, compileExpression } from "./utils.js";
|
|
18
|
+
import {
|
|
19
|
+
copyResources,
|
|
20
|
+
copyStaticDir,
|
|
21
|
+
getComponents,
|
|
22
|
+
getSrcIndex,
|
|
23
|
+
processIcons,
|
|
24
|
+
processStylesheet,
|
|
25
|
+
} from "./pipeline.js";
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const logSeparation = chalk.yellow(`
|
|
29
|
+
________________________________________________________________________
|
|
30
|
+
========================================================================
|
|
31
|
+
`);
|
|
32
|
+
|
|
33
|
+
function logBanner() {
|
|
34
|
+
console.log(chalk.bold.hex("#945e33")(`\n RUNNING CHOCOLA BUNDLER`));
|
|
35
|
+
console.log(logSeparation);
|
|
36
|
+
console.log(
|
|
37
|
+
chalk.hex("#945e33")(`
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
▄████▄ ██░ ██ ▒█████ ▄████▄ ▒█████ ██▓ ▄▄▄
|
|
41
|
+
▒██▀ ▀█ ▓██░ ██▒▒██▒ ██▒▒██▀ ▀█ ▒██▒ ██▒▓██▒ ▒████▄
|
|
42
|
+
▒▓█ ▄ ▒██▀▀██░▒██░ ██▒▒▓█ ▄ ▒██░ ██▒▒██░ ▒██ ▀█▄
|
|
43
|
+
▒▓▓▄ ▄██▒░▓█ ░██ ▒██ ██░▒▓▓▄ ▄██▒▒██ ██░▒██░ ░██▄▄▄▄██
|
|
44
|
+
▒ ▓███▀ ░░▓█▒░██▓░ ████▓▒░▒ ▓███▀ ░░ ████▓▒░░██████▒▓█ ▓██▒
|
|
45
|
+
░ ░▒ ▒ ░ ▒ ░░▒░▒░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒▒ ▓▒█░
|
|
46
|
+
░ ▒ ▒ ░▒░ ░ ░ ░ ▒ ▒░ ░ ▒ ░ ░ ▒░ ░ ░ ▒ ░ ░ ▒▒ ░
|
|
47
|
+
░ ░ ░░ ░░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ▒
|
|
48
|
+
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
|
49
|
+
░ ░
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
`)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function logSuccess(outDirPath) {
|
|
57
|
+
console.log(`
|
|
58
|
+
▄▄ ▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄ ▄▄▄▄▄ ██
|
|
59
|
+
██ ██▀██ ██▄██ ██▀██ ██▀██ ███▄██ ██▄▄ ██
|
|
60
|
+
▄▄█▀ ▀███▀ ██▄█▀ ████▀ ▀███▀ ██ ▀██ ██▄▄▄ ▄▄
|
|
61
|
+
|
|
62
|
+
`);
|
|
63
|
+
console.log(
|
|
64
|
+
chalk.bold.green(">"),
|
|
65
|
+
"Project bundled succesfully at",
|
|
66
|
+
chalk.green.underline(outDirPath) + "\n\n"
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function setupOutputDirectory(outDirPath, emptyOutDir) {
|
|
71
|
+
if (emptyOutDir) {
|
|
72
|
+
await fs.rm(outDirPath, { recursive: true, force: true });
|
|
73
|
+
await fs.mkdir(outDirPath);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function loadAndDisplayComponents(srcComponentsPath) {
|
|
78
|
+
const foundComponents = await getComponents(srcComponentsPath);
|
|
79
|
+
const { loadedComponents, notDefComps, componentsLib } = foundComponents;
|
|
80
|
+
|
|
81
|
+
console.log(` LOADING COMPONENTS`);
|
|
82
|
+
console.log(chalk.bold.green(">"), "Components found in", chalk.green.underline(srcComponentsPath) + ":");
|
|
83
|
+
console.log(" ", componentsLib, "\n");
|
|
84
|
+
|
|
85
|
+
if (notDefComps.length > 0) {
|
|
86
|
+
console.warn(chalk.bold.yellow("WARNING!"), "The following components don't include a default export:");
|
|
87
|
+
console.log(" ", notDefComps);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return loadedComponents;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
94
|
+
const { stylesheets, icons } = getAssetLinks(doc);
|
|
95
|
+
const fileIds = [];
|
|
96
|
+
let cssContents = [];
|
|
97
|
+
|
|
98
|
+
for (const link of stylesheets) {
|
|
99
|
+
const css = await processStylesheet(link, rootDir, srcDir, outDirPath, fileIds);
|
|
100
|
+
cssContents.push(css);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const link of icons) {
|
|
104
|
+
await processIcons(link, rootDir, srcDir, outDirPath);
|
|
105
|
+
}
|
|
106
|
+
return cssContents
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Compiles a static build of your Chocola project from the directory provided.
|
|
111
|
+
* @param {import("fs").PathLike} rootDir
|
|
112
|
+
* @param {Object<{
|
|
113
|
+
* isHotReload?: boolean
|
|
114
|
+
* }
|
|
115
|
+
* >} config
|
|
116
|
+
*/
|
|
117
|
+
export default async function runtime(rootDir, buildConfig) {
|
|
118
|
+
const isHotReload = buildConfig?.isHotReload || null;
|
|
119
|
+
!isHotReload && logBanner();
|
|
120
|
+
|
|
121
|
+
const config = await loadConfig(rootDir);
|
|
122
|
+
const paths = resolvePaths(rootDir, config);
|
|
123
|
+
!isHotReload && console.log(logSeparation);
|
|
124
|
+
|
|
125
|
+
await setupOutputDirectory(paths.outDir, config.emptyOutDir);
|
|
126
|
+
|
|
127
|
+
const indexFiles = await getSrcIndex(paths.src);
|
|
128
|
+
const srcIndexContent = indexFiles.srcHtmlFile;
|
|
129
|
+
const pageSourcePath = indexFiles.srcPath;
|
|
130
|
+
|
|
131
|
+
const loadedComponents = await loadAndDisplayComponents(paths.components);
|
|
132
|
+
!isHotReload && console.log(logSeparation);
|
|
133
|
+
!isHotReload && console.log(` BUNDLING STATIC BUILD`);
|
|
134
|
+
!isHotReload && console.log(chalk.bold.green(">"), "Creating Chocola static build in directory", chalk.green.underline(paths.outDir) + "\n");
|
|
135
|
+
!isHotReload && console.log(logSeparation);
|
|
136
|
+
|
|
137
|
+
const dom = createDOM(srcIndexContent);
|
|
138
|
+
const doc = dom.window.document;
|
|
139
|
+
const appContainer = validateAppContainer(doc);
|
|
140
|
+
|
|
141
|
+
const warnedDeprecated = new Set();
|
|
142
|
+
function hasDelIfAttr(el, sourceLoc) {
|
|
143
|
+
if (el.hasAttribute("del-if") && !el.hasAttribute("del:if") && !warnedDeprecated.has(sourceLoc)) {
|
|
144
|
+
warnedDeprecated.add(sourceLoc);
|
|
145
|
+
const name = sourceLoc ? sourceLoc.split(/[\\/]/).pop() : "unknown";
|
|
146
|
+
console.warn(chalk.yellow(`[deprecation] "del-if" is deprecated in ${chalk.cyan(name)} and will be removed in Chocola 2. Use "del:if" instead.`));
|
|
147
|
+
}
|
|
148
|
+
return el.hasAttribute("del-if") || el.hasAttribute("del:if");
|
|
149
|
+
}
|
|
150
|
+
function getDelIfAttr(el) {
|
|
151
|
+
return el.getAttribute("del:if") ?? el.getAttribute("del-if");
|
|
152
|
+
}
|
|
153
|
+
function removeDelIfAttr(el) {
|
|
154
|
+
el.removeAttribute("del-if");
|
|
155
|
+
el.removeAttribute("del:if");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function processPageConditionals(parent) {
|
|
159
|
+
const children = [...parent.children];
|
|
160
|
+
let chainActive = false;
|
|
161
|
+
let chainRendered = false;
|
|
162
|
+
|
|
163
|
+
for (const child of children) {
|
|
164
|
+
const hasIf = child.hasAttribute("if");
|
|
165
|
+
const hasDelIf = hasDelIfAttr(child, pageSourcePath);
|
|
166
|
+
const hasElif = child.hasAttribute("elif");
|
|
167
|
+
const hasElse = child.hasAttribute("else");
|
|
168
|
+
|
|
169
|
+
if (hasElif || hasElse) {
|
|
170
|
+
if (!chainActive) continue;
|
|
171
|
+
if (chainRendered) { child.remove(); continue; }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (hasIf) {
|
|
175
|
+
const raw = child.getAttribute("if");
|
|
176
|
+
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
177
|
+
const fn = compileExpression(expr, false);
|
|
178
|
+
const result = fn();
|
|
179
|
+
chainActive = true;
|
|
180
|
+
if (result) {
|
|
181
|
+
chainRendered = true;
|
|
182
|
+
} else {
|
|
183
|
+
child.remove();
|
|
184
|
+
chainRendered = false;
|
|
185
|
+
}
|
|
186
|
+
child.removeAttribute("if");
|
|
187
|
+
} else if (hasDelIf) {
|
|
188
|
+
const raw = getDelIfAttr(child);
|
|
189
|
+
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
190
|
+
const fn = compileExpression(expr, false);
|
|
191
|
+
const result = fn();
|
|
192
|
+
chainActive = true;
|
|
193
|
+
if (result) {
|
|
194
|
+
chainRendered = true;
|
|
195
|
+
} else {
|
|
196
|
+
child.remove();
|
|
197
|
+
chainRendered = false;
|
|
198
|
+
}
|
|
199
|
+
removeDelIfAttr(child);
|
|
200
|
+
} else if (hasElif) {
|
|
201
|
+
const raw = child.getAttribute("elif");
|
|
202
|
+
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
203
|
+
const fn = compileExpression(expr, false);
|
|
204
|
+
const result = fn();
|
|
205
|
+
if (result) {
|
|
206
|
+
chainRendered = true;
|
|
207
|
+
} else {
|
|
208
|
+
child.remove();
|
|
209
|
+
}
|
|
210
|
+
child.removeAttribute("elif");
|
|
211
|
+
} else if (hasElse) {
|
|
212
|
+
chainRendered = true;
|
|
213
|
+
chainActive = false;
|
|
214
|
+
child.removeAttribute("else");
|
|
215
|
+
} else {
|
|
216
|
+
chainActive = false;
|
|
217
|
+
chainRendered = false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (child.parentNode) {
|
|
221
|
+
processPageConditionals(child);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
processPageConditionals(appContainer);
|
|
226
|
+
|
|
227
|
+
const appElements = getAppElements(appContainer);
|
|
228
|
+
const { runtimeScript, scopesCss } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
|
|
229
|
+
const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
|
|
230
|
+
const globalCss = (await processAssets(doc, rootDir, config.srcDir, paths.outDir)).join("\n");
|
|
231
|
+
|
|
232
|
+
if (scopesCss) {
|
|
233
|
+
const fileName = "sc-" + genRandomId(null, 6) + ".css";
|
|
234
|
+
await writeCSSOutput(scopesCss, paths.outDir, fileName);
|
|
235
|
+
appendStylesheetLink(doc, fileName);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
appendRuntimeScript(doc, runtimeFilename);
|
|
239
|
+
const html = await serializeDOM(dom);
|
|
240
|
+
await writeHTMLOutput(html, paths.outDir);
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
if (config.assetImport === "static") {
|
|
244
|
+
await copyStaticDir(paths.src, paths.outDir);
|
|
245
|
+
} else {
|
|
246
|
+
await copyResources(rootDir, scopesCss, globalCss, config.srcDir, paths.outDir);
|
|
247
|
+
}
|
|
248
|
+
} catch (err) {
|
|
249
|
+
throwError(err.message || err);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
!isHotReload && logSuccess(paths.outDir);
|
|
253
|
+
isHotReload && console.log("Dev server updated");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* An intrinsic object that contains the Chocola App methods.
|
|
258
|
+
*/
|
|
259
|
+
export const app = {
|
|
260
|
+
/**
|
|
261
|
+
* Initializes your Chocola App using a root directory.
|
|
262
|
+
*
|
|
263
|
+
* ```js
|
|
264
|
+
* import { app } from "chocola/compiler"
|
|
265
|
+
import path from "path";
|
|
266
|
+
import { fileURLToPath } from "url";
|
|
267
|
+
|
|
268
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
269
|
+
const __dirname = path.dirname(__filename);
|
|
270
|
+
|
|
271
|
+
app.build(__dirname);
|
|
272
|
+
```
|
|
273
|
+
* @example
|
|
274
|
+
* @param {PathLike} __rootdir the directory where your Chocola App is
|
|
275
|
+
*/
|
|
276
|
+
async build(__rootdir) { return runtime(__rootdir) }
|
|
277
|
+
};
|
package/compiler/utils.js
CHANGED
|
@@ -80,8 +80,7 @@ export async function loadWithAssets(filePath) {
|
|
|
80
80
|
export function genRandomId(collection = null, length = 10, lettersOnly = false) {
|
|
81
81
|
let id;
|
|
82
82
|
if (lettersOnly) {
|
|
83
|
-
|
|
84
|
-
id = Array.from({ length }, () => letters[Math.floor(Math.random() * letters.length)]).join("");
|
|
83
|
+
id = Array.from({ length }, () => ID_LETTERS[Math.floor(Math.random() * ID_LETTERS.length)]).join("");
|
|
85
84
|
} else {
|
|
86
85
|
id = Math.random().toString(36).substring(2, length + 2);
|
|
87
86
|
}
|
|
@@ -103,14 +102,13 @@ export function genRandomId(collection = null, length = 10, lettersOnly = false)
|
|
|
103
102
|
* incrementAlfabet("z") // "aa"
|
|
104
103
|
*/
|
|
105
104
|
export function incrementAlfabet(letters) {
|
|
106
|
-
const alfabet = "abcdefghijklmnopqrstuvwxyz";
|
|
107
105
|
let arr = letters.split("");
|
|
108
106
|
let i = arr.length - 1;
|
|
109
107
|
|
|
110
108
|
while (i >= 0) {
|
|
111
|
-
let pos =
|
|
109
|
+
let pos = arr[i].charCodeAt(0) - 97;
|
|
112
110
|
if (pos < 25) {
|
|
113
|
-
arr[i] =
|
|
111
|
+
arr[i] = String.fromCharCode(pos + 97 + 1);
|
|
114
112
|
return arr.join("");
|
|
115
113
|
} else {
|
|
116
114
|
arr[i] = "a";
|
|
@@ -161,3 +159,17 @@ export function restoreCurlyBraces(html) {
|
|
|
161
159
|
.replace(/_%%CHOCOLA-LBRACE%%_/g, "{")
|
|
162
160
|
.replace(/_%%CHOCOLA-RBRACE%%_/g, "}");
|
|
163
161
|
}
|
|
162
|
+
|
|
163
|
+
const ID_LETTERS = "abcdefghijklmnopqrstuvwxyz";
|
|
164
|
+
const compiledCache = new Map();
|
|
165
|
+
export function compileExpression(expr, useCtx) {
|
|
166
|
+
const key = useCtx ? `ctx:${expr}` : `raw:${expr}`;
|
|
167
|
+
let fn = compiledCache.get(key);
|
|
168
|
+
if (!fn) {
|
|
169
|
+
fn = useCtx
|
|
170
|
+
? new Function("ctx", `with(ctx) { return (${expr}); }`)
|
|
171
|
+
: new Function(`"use strict"; return (${expr})`);
|
|
172
|
+
compiledCache.set(key, fn);
|
|
173
|
+
}
|
|
174
|
+
return fn;
|
|
175
|
+
}
|
package/dev/index.js
CHANGED
|
@@ -29,7 +29,6 @@ export async function serve(__rootdir) {
|
|
|
29
29
|
else { console.warn(chalk.bold.yellow("WARNING!"), `port not defined in chocola.config.json file: using default ${__config.port} port.`) }
|
|
30
30
|
|
|
31
31
|
const srcDir = paths.src;
|
|
32
|
-
const componentsDir = paths.components;
|
|
33
32
|
|
|
34
33
|
let compileTimeout;
|
|
35
34
|
const scheduleRecompile = () => {
|
|
@@ -51,11 +50,6 @@ export async function serve(__rootdir) {
|
|
|
51
50
|
scheduleRecompile();
|
|
52
51
|
}
|
|
53
52
|
});
|
|
54
|
-
fs.watch(componentsDir, { recursive: true }, (eventType, filename) => {
|
|
55
|
-
if (filename && !filename.includes("node_modules")) {
|
|
56
|
-
scheduleRecompile();
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
53
|
|
|
60
54
|
const server = http.createServer((req, res) => {
|
|
61
55
|
if (req.url === "/api/hot-reload") {
|
|
@@ -152,4 +146,27 @@ export async function serve(__rootdir) {
|
|
|
152
146
|
server.listen(__config.port, __config.hostname, () => {
|
|
153
147
|
console.log(`Chocola App running at http://${__config.hostname}:${__config.port}/`);
|
|
154
148
|
});
|
|
155
|
-
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* An intrinsic object that contains the Chocola Dev methods.
|
|
153
|
+
*/
|
|
154
|
+
export const dev = {
|
|
155
|
+
/**
|
|
156
|
+
* Initializes your Chocola dev server using a root directory.
|
|
157
|
+
*
|
|
158
|
+
* ```js
|
|
159
|
+
* import { app } from "chocola/compiler"
|
|
160
|
+
import path from "path";
|
|
161
|
+
import { fileURLToPath } from "url";
|
|
162
|
+
|
|
163
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
164
|
+
const __dirname = path.dirname(__filename);
|
|
165
|
+
|
|
166
|
+
app.build(__dirname);
|
|
167
|
+
```
|
|
168
|
+
* @example
|
|
169
|
+
* @param {PathLike} __rootdir the directory where your Chocola App is
|
|
170
|
+
*/
|
|
171
|
+
async server(__rootdir) { return serve(__rootdir) }
|
|
172
|
+
};
|
package/index.js
CHANGED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import compile from "./compiler/index.js";
|
|
2
|
-
import * as development from "./dev/index.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* An intrinsic object that contains the Chocola App methods.
|
|
6
|
-
*/
|
|
7
|
-
export const app = {
|
|
8
|
-
/**
|
|
9
|
-
* Initializes your Chocola App using a root directory and a source directory.
|
|
10
|
-
*
|
|
11
|
-
* ```js
|
|
12
|
-
* import { app } from "chocola"
|
|
13
|
-
import path from "path";
|
|
14
|
-
import { fileURLToPath } from "url";
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = path.dirname(__filename);
|
|
18
|
-
|
|
19
|
-
app.build(__dirname, "src");
|
|
20
|
-
```
|
|
21
|
-
* @example
|
|
22
|
-
* @param {PathLike} __rootdir the directory where your Chocola App is
|
|
23
|
-
* @param {PathLike} __srcdir the directory where your app sources files are
|
|
24
|
-
* @returns {Object}
|
|
25
|
-
*/
|
|
26
|
-
build(__rootdir, __srcdir) { compile(__rootdir, __srcdir) }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const dev = {
|
|
30
|
-
server(__rootdir, __srcdir, port) { development.serve(__rootdir, __srcdir, port) }
|
|
31
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chocola",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-next.2",
|
|
4
4
|
"description": "The sweetest way to build the web",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
32
32
|
},
|
|
33
33
|
"exports": {
|
|
34
|
-
"
|
|
35
|
-
"./
|
|
34
|
+
"./compiler": "./compiler/index.js",
|
|
35
|
+
"./dev": "./dev/index.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"chalk": "^5.6.2",
|