chocola 2.0.0-next.7 → 2.0.0-next.9

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 CHANGED
@@ -1,35 +1,42 @@
1
- # chocola
2
-
3
- **The sweetest way to build the web.**
4
-
5
- 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.
6
-
7
- Import components. Instantiate them. Mount, update, remove. All client-side. Same file, zero overhead.
8
-
9
- ```html
10
- <script>
11
- let self = new HTMLDivElement;
12
- let input = new HTMLInputElement;
13
-
14
- export let title = "Hello";
15
-
16
- function $runtime() {
17
- input.focus();
18
- }
19
- </script>
20
-
21
- <template>
22
- <div>
23
- <h1>{title}</h1>
24
- <input bind:self="input" type="text" placeholder="Your name">
25
- </div>
26
- </template>
27
-
28
- <style>
29
- h1 { color: chocolate; }
30
- </style>
31
- ```
32
-
33
- ## Documentation
34
-
35
- https://github.com/chocolajs/chocola/wiki/Overview
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="assets/banner_dark.jpg">
3
+ <img src="assets/banner.jpg" alt="Chocola • The sweetest way to build the web" />
4
+ </picture>
5
+
6
+ ## What is Chocola
7
+
8
+ Chocola is a new and sweeter way to build your web apps.
9
+
10
+ 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.
11
+
12
+ Import components. Instantiate them. Mount, update, remove. All client-side. Same file, zero overhead.
13
+
14
+ ```html
15
+ <script>
16
+ let self;
17
+ let input;
18
+
19
+ export let title = "Hello";
20
+
21
+ function $runtime() {
22
+ input.focus();
23
+ }
24
+ </script>
25
+
26
+ <template>
27
+ <div>
28
+ <h1>{title}</h1>
29
+ <input bind:self="input" type="text" placeholder="Your name">
30
+ </div>
31
+ </template>
32
+
33
+ <style>
34
+ h1 { color: chocolate; }
35
+ </style>
36
+ ```
37
+
38
+ Chocola 2 is in current development and will add CSR SPA and much more features!
39
+
40
+ ## Documentation
41
+
42
+ https://github.com/chocolajs/chocola/wiki/Overview
@@ -1,12 +1,12 @@
1
1
  import path from "path";
2
- import { JSDOM } from "jsdom";
2
+ import { parseHTML } from "linkedom";
3
3
  import { protectCurlyBraces } from "../utils.js";
4
- import { genRandomId, incrementAlfabet, throwError, deterministicHash } from "./utils.js";
4
+ import { genRandomId, incrementAlfabet, throwError, deterministicHash, warnConstantCondition, warnUnusedDeclaration, findElementLine } from "./utils.js";
5
5
  import {
6
- extractPropsDefaults, extractRuntime, extractTopLevelFunctions,
7
- extractContextFromElement, hasDelIfAttr, getDelIfAttr, removeDelIfAttr,
6
+ extractPropsDefaults, extractRuntime, extractTopLevelFunctions, extractTopLevelVariables,
7
+ extractCtxFromEl, hasMountIf, getMountIf,
8
8
  reservedAttrs, validateChainStructure, applyConditionalToElement, interpolateNode,
9
- scopeCss, compileExpression,
9
+ scopeCss, compileExpr, evaluateConstant,
10
10
  } from "../parser/index.js";
11
11
  import chalk from "./chalk.js";
12
12
 
@@ -24,6 +24,7 @@ class ProcessContext {
24
24
  this.scopedStyles = scopedStyles;
25
25
  this.staticCtxRegistry = staticCtxRegistry;
26
26
  this.csrClasses = csrClasses;
27
+ this.unusedWarned = new Set();
27
28
  }
28
29
  }
29
30
 
@@ -31,6 +32,17 @@ function escapeForTemplateLiteral(str) {
31
32
  return str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\${/g, "\\${");
32
33
  }
33
34
 
35
+ function parseFragment(html, doc) {
36
+ const fragment = doc.createDocumentFragment();
37
+ const temp = doc.createElement("div");
38
+ temp.innerHTML = html;
39
+ const children = [...temp.childNodes];
40
+ for (const child of children) {
41
+ fragment.appendChild(child);
42
+ }
43
+ return fragment;
44
+ }
45
+
34
46
  function generateCSRClass(compName, cx, explicitClassName) {
35
47
  if (cx.csrClasses.has(compName)) return;
36
48
 
@@ -38,8 +50,8 @@ function generateCSRClass(compName, cx, explicitClassName) {
38
50
  if (!instance) return;
39
51
 
40
52
  instance = protectCurlyBraces(instance);
41
- const dom = new JSDOM(instance);
42
- const doc = dom.window.document;
53
+ const dom = parseHTML(instance);
54
+ const doc = dom.document;
43
55
  const script = doc.querySelector("script")?.innerHTML;
44
56
  const template = doc.querySelector("template")?.innerHTML;
45
57
  const styles = doc.querySelector("style")?.innerHTML;
@@ -48,6 +60,7 @@ function generateCSRClass(compName, cx, explicitClassName) {
48
60
 
49
61
  const compProps = extractPropsDefaults(script);
50
62
  const topFuncSrc = extractTopLevelFunctions(script || "", RUNTIME_KW);
63
+ const topVars = extractTopLevelVariables(script || "");
51
64
  let runtime = extractRuntime(script || "", compName);
52
65
 
53
66
  if (!cx.cssScopesMap.has(compName)) {
@@ -61,7 +74,7 @@ function generateCSRClass(compName, cx, explicitClassName) {
61
74
 
62
75
  const childMappings = [];
63
76
  const bindVarNames = new Set();
64
- const templateDoc = JSDOM.fragment(template);
77
+ const templateDoc = parseFragment(template, doc);
65
78
  const seenTags = new Set();
66
79
  for (const el of templateDoc.querySelectorAll("*")) {
67
80
  const tag = el.tagName.toLowerCase();
@@ -82,15 +95,25 @@ function generateCSRClass(compName, cx, explicitClassName) {
82
95
  }
83
96
 
84
97
  let csrRuntimeSource = null;
98
+ const injectedNames = new Set(compProps.map(p => p.name));
99
+ for (const varName of bindVarNames) injectedNames.add(varName);
100
+ const topVarsToInject = topVars.filter(v => !injectedNames.has(v.name));
85
101
  if (runtime) {
86
102
  let injectCode = "";
87
103
  for (const { name, defaultValue } of compProps) {
88
104
  if (defaultValue !== undefined) {
89
- injectCode += `let ${name} = ctx.${name}||${defaultValue};\n`;
105
+ injectCode += `let ${name} = ctx.${name}??(${defaultValue});\n`;
90
106
  } else {
91
107
  injectCode += `let ${name} = ctx.${name};\n`;
92
108
  }
93
109
  }
110
+ for (const { keyword, name, value } of topVarsToInject) {
111
+ if (value !== undefined) {
112
+ injectCode += `${keyword} ${name} = ctx.${name}??(${value});\n`;
113
+ } else {
114
+ injectCode += `${keyword} ${name};\n`;
115
+ }
116
+ }
94
117
  for (const varName of bindVarNames) {
95
118
  if (varName !== "self") {
96
119
  injectCode += `let ${varName} = ctx.${varName};\n`;
@@ -105,20 +128,110 @@ function generateCSRClass(compName, cx, explicitClassName) {
105
128
  }
106
129
 
107
130
  const className = explicitClassName || compName.replace(".html", "").replace(/^\w/, c => c.toUpperCase());
108
- const propsObj = {};
131
+ const propsParts = [];
109
132
  for (const { name, defaultValue } of compProps) {
110
- propsObj[name] = defaultValue !== undefined ? defaultValue : null;
133
+ propsParts.push(`${JSON.stringify(name)}: ${defaultValue !== undefined ? defaultValue : "null"}`);
134
+ }
135
+ for (const { name, value } of topVarsToInject) {
136
+ if (value !== undefined) {
137
+ propsParts.push(`${JSON.stringify(name)}: ${value}`);
138
+ }
111
139
  }
112
140
 
113
141
  const childrenPart = childMappings.length > 0
114
142
  ? ",\n children: [" + childMappings.map(m => `{tag:"${m.tag}",compClass:${m.compClass}}`).join(",") + "]"
115
143
  : "";
116
144
  const runtimePart = csrRuntimeSource ? `,\n runtime: ${csrRuntimeSource}` : "";
117
- 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}`;
145
+ const propsPart = propsParts.length > 0 ? `{ ${propsParts.join(", ")} }` : "{}";
146
+ const classDef = `class ${className} extends ChocolaComponent {\n constructor() {\n super({\n template: \`${escapeForTemplateLiteral(template)}\`,\n hash: "${cssId}",\n props: ${propsPart}${runtimePart}${childrenPart}\n });\n }\n}`;
118
147
  cx.csrClasses.set(compName, classDef);
119
148
  }
120
149
 
150
+ const BARE_IDENTIFIER_RE = /(?<![.\w$])[a-zA-Z_$][0-9a-zA-Z_$]*/g;
151
+ const PROPERTY_IDENTIFIER_RE = /\.([a-zA-Z_$][0-9a-zA-Z_$]*)/g;
152
+
153
+ function countIdentifiers(text, counts) {
154
+ for (const re of [BARE_IDENTIFIER_RE, PROPERTY_IDENTIFIER_RE]) {
155
+ re.lastIndex = 0;
156
+ for (const m of text.matchAll(re)) counts.set(m[1] ?? m[0], (counts.get(m[1] ?? m[0]) || 0) + 1);
157
+ }
158
+ return counts;
159
+ }
160
+
161
+ function escapeNameForRegex(name) {
162
+ return name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
163
+ }
164
+
165
+ function findLineInSource(source, regex) {
166
+ const match = regex.exec(source);
167
+ if (!match) return null;
168
+ return source.substring(0, match.index).split("\n").length;
169
+ }
170
+
171
+ function warnUnusedDeclarations(cx, compName, instance, script, fragment) {
172
+ if (!script && !fragment.querySelector("[bind\\:]")) return;
173
+ if (cx.unusedWarned.has(compName)) return;
174
+ cx.unusedWarned.add(compName);
175
+
176
+ const props = extractPropsDefaults(script).map(p => p.name);
177
+ const topVars = extractTopLevelVariables(script).map(v => v.name);
178
+ const topFuncs = extractTopLevelFunctions(script, RUNTIME_KW)
179
+ .map(src => src.match(/^(?:async\s+)?function\s+([a-zA-Z_$][0-9a-zA-Z_$]*)/)?.[1])
180
+ .filter(Boolean);
121
181
 
182
+ const bindings = [];
183
+ for (const el of fragment.querySelectorAll("*")) {
184
+ for (const attr of el.attributes) {
185
+ if (attr.name.startsWith("bind:")) bindings.push({ name: attr.value, element: el });
186
+ }
187
+ }
188
+
189
+ const counts = new Map();
190
+ if (script) countIdentifiers(script, counts);
191
+
192
+ const walk = (node) => {
193
+ if (node.nodeType === 3) {
194
+ for (const m of node.textContent.matchAll(/\{([^}]+)\}/g)) countIdentifiers(m[1], counts);
195
+ return;
196
+ }
197
+ if (node.attributes) {
198
+ for (const attr of node.attributes) {
199
+ if (attr.name.startsWith("bind:")) {
200
+ countIdentifiers(attr.value, counts);
201
+ } else {
202
+ for (const m of attr.value.matchAll(/\{([^}]+)\}/g)) countIdentifiers(m[1], counts);
203
+ }
204
+ }
205
+ }
206
+ for (const child of node.childNodes) walk(child);
207
+ };
208
+ walk(fragment);
209
+
210
+ const isUnused = (name) => (counts.get(name) || 0) <= 1;
211
+ const warn = (kind, name, declRegex) => {
212
+ const lineNum = findLineInSource(instance, declRegex);
213
+ warnUnusedDeclaration(lineNum !== null ? `${compName}:${lineNum}` : compName, kind, name);
214
+ };
215
+
216
+ for (const name of props) {
217
+ if (isUnused(name)) warn("prop", name, new RegExp("export\\s+let\\s+" + escapeNameForRegex(name) + "\\b"));
218
+ }
219
+ for (const name of topVars) {
220
+ if (isUnused(name)) warn("variable", name, new RegExp("(?:^|[^\\w])(?:let|const)\\s+" + escapeNameForRegex(name) + "\\b"));
221
+ }
222
+ for (const name of topFuncs) {
223
+ if (isUnused(name)) warn("function", name, new RegExp("(?:async\\s+)?function\\s+" + escapeNameForRegex(name) + "\\b"));
224
+ }
225
+ const seenBindings = new Set();
226
+ for (const { name, element } of bindings) {
227
+ if (seenBindings.has(name)) continue;
228
+ seenBindings.add(name);
229
+ if (isUnused(name)) {
230
+ const lineNum = findElementLine(instance, element.outerHTML);
231
+ warnUnusedDeclaration(lineNum !== null ? `${compName}:${lineNum}` : compName, "binding", name);
232
+ }
233
+ }
234
+ }
122
235
 
123
236
  export function processComponentElement(
124
237
  element,
@@ -135,8 +248,8 @@ export function processComponentElement(
135
248
  if (renderChain && renderChain.includes(compName)) return false;
136
249
 
137
250
  instance = protectCurlyBraces(instance);
138
- const dom = new JSDOM(instance);
139
- const doc = dom.window.document;
251
+ const dom = parseHTML(instance);
252
+ const doc = dom.document;
140
253
  let script = doc.querySelector("script")?.innerHTML;
141
254
  let template = doc.querySelector("template")?.innerHTML;
142
255
  let styles = doc.querySelector("style")?.innerHTML;
@@ -163,7 +276,7 @@ export function processComponentElement(
163
276
  if (cx.staticCtxRegistry && cx.staticCtxRegistry.has(element)) {
164
277
  ctx = cx.staticCtxRegistry.get(element);
165
278
  } else {
166
- ctx = extractContextFromElement(element);
279
+ ctx = extractCtxFromEl(element);
167
280
  cx.staticCtxRegistry && cx.staticCtxRegistry.set(element, ctx);
168
281
  }
169
282
 
@@ -171,7 +284,7 @@ export function processComponentElement(
171
284
  compProps.forEach(({ name, defaultValue }) => {
172
285
  if (defaultValue !== undefined && !(name in ctx)) {
173
286
  try {
174
- ctx[name] = compileExpression(defaultValue, false)();
287
+ ctx[name] = compileExpr(defaultValue, false)();
175
288
  } catch {
176
289
  ctx[name] = defaultValue;
177
290
  }
@@ -180,6 +293,15 @@ export function processComponentElement(
180
293
  }
181
294
 
182
295
  const topFuncSrc = extractTopLevelFunctions(script || "", RUNTIME_KW);
296
+ const topVars = extractTopLevelVariables(script || "");
297
+ for (const { name, value } of topVars) {
298
+ if (name in ctx) continue;
299
+ if (value !== undefined) {
300
+ try {
301
+ ctx[name] = compileExpr(value, false)();
302
+ } catch {}
303
+ }
304
+ }
183
305
  for (const src of topFuncSrc) {
184
306
  try {
185
307
  const fn = (0, eval)("(" + src + ")");
@@ -195,9 +317,11 @@ export function processComponentElement(
195
317
  get(target, key) { return target[key]; }
196
318
  });
197
319
 
198
- const fragment = JSDOM.fragment(template);
320
+ const fragment = parseFragment(template, doc);
321
+
322
+ warnUnusedDeclarations(cx, compName, instance, script, fragment);
199
323
 
200
- const slotFragment = JSDOM.fragment(elInnerHtml);
324
+ const slotFragment = parseFragment(elInnerHtml, doc);
201
325
  if (sourceFile) {
202
326
  validateChainStructure(slotFragment, sourceFile, sourceContent, elInnerHtml);
203
327
  }
@@ -215,6 +339,21 @@ export function processComponentElement(
215
339
  const elBindIds = new Map();
216
340
  let bindCounter = 0;
217
341
 
342
+ const conditionalLocations = new Map();
343
+ for (const { el } of childEntries) {
344
+ if (!el.hasAttribute("if") && !hasMountIf(el) && !el.hasAttribute("elif")) continue;
345
+ let location = sourceFile;
346
+ if (instance) {
347
+ const lineNum = findElementLine(instance, el.outerHTML);
348
+ if (lineNum !== null) location = `${compName}:${lineNum}`;
349
+ }
350
+ if (location === sourceFile && sourceContent) {
351
+ const lineNum = findElementLine(sourceContent, el.outerHTML);
352
+ if (lineNum !== null) location = `${sourceFile}:${lineNum}`;
353
+ }
354
+ conditionalLocations.set(el, location);
355
+ }
356
+
218
357
  childEntries.forEach(({ el: child, parent }) => {
219
358
  if (!condChains.has(parent)) {
220
359
  condChains.set(parent, { active: false, rendered: false });
@@ -222,13 +361,26 @@ export function processComponentElement(
222
361
  const condChain = condChains.get(parent);
223
362
 
224
363
  const hasIf = child.hasAttribute("if");
225
- const hasDelIf = hasDelIfAttr(child);
364
+ const hasDelIf = hasMountIf(child);
226
365
  const hasElif = child.hasAttribute("elif");
227
366
  const hasElse = child.hasAttribute("else");
228
367
 
368
+ if (hasIf || hasDelIf || hasElif) {
369
+ const location = conditionalLocations.get(child) || sourceFile;
370
+ const stripBraces = (raw) => raw.startsWith("{") ? raw.slice(1, -1) : raw;
371
+ const tag = child.tagName.toLowerCase();
372
+ const warnIfConstant = (expr, attr) => {
373
+ const { constant, value } = evaluateConstant(expr);
374
+ if (constant) warnConstantCondition(location, tag, attr, Boolean(value));
375
+ };
376
+ if (hasIf) warnIfConstant(stripBraces(child.getAttribute("if")), "if");
377
+ if (hasDelIf) warnIfConstant(stripBraces(getMountIf(child)), "mount:if");
378
+ if (hasElif) warnIfConstant(stripBraces(child.getAttribute("elif")), "elif");
379
+ }
380
+
229
381
  if (hasElif || hasElse) {
230
382
  if (!condChain.active) {
231
- throwError(`${instance.__sourceFile || compName}: <${child.tagName.toLowerCase()}> has ${hasElif ? "elif" : "else"} without a preceding if/del-if sibling`);
383
+ throwError(`${instance.__sourceFile || compName}: <${child.tagName.toLowerCase()}> has ${hasElif ? "elif" : "else"} without a preceding if/mount:if sibling`);
232
384
  }
233
385
  if (condChain.rendered) {
234
386
  child.remove();
@@ -243,7 +395,7 @@ export function processComponentElement(
243
395
  if (hasElif || hasElse) {
244
396
  if (hasElif) {
245
397
  const expr = child.getAttribute("elif").slice(1, -1);
246
- const fn = compileExpression(expr, true);
398
+ const fn = compileExpr(expr, true);
247
399
  if (!fn(ctxProxy)) {
248
400
  child.remove();
249
401
  return;
@@ -255,9 +407,9 @@ export function processComponentElement(
255
407
  condChain.active = false;
256
408
  }
257
409
  } else if (hasIf || hasDelIf) {
258
- const raw = hasIf ? child.getAttribute("if") : getDelIfAttr(child);
410
+ const raw = hasIf ? child.getAttribute("if") : getMountIf(child);
259
411
  const expr = raw.slice(1, -1);
260
- const fn = compileExpression(expr, true);
412
+ const fn = compileExpr(expr, true);
261
413
  condChain.active = true;
262
414
  if (fn(ctxProxy)) {
263
415
  child.replaceWith(...child.children);
@@ -292,19 +444,28 @@ export function processComponentElement(
292
444
  return;
293
445
  }
294
446
 
295
- attribute.value = attribute.value.replace(
296
- /\{([^}]+)\}/g,
297
- (_, expr) => {
298
- try {
299
- return compileExpression(expr, true)(ctxProxy);
300
- } catch {
301
- return "";
447
+ child.setAttribute(
448
+ attribute.name,
449
+ attribute.value.replace(
450
+ /\{([^}]+)\}/g,
451
+ (_, expr) => {
452
+ try {
453
+ return compileExpr(expr, true)(ctxProxy);
454
+ } catch {
455
+ return "";
456
+ }
302
457
  }
303
- }
458
+ )
304
459
  );
305
460
  });
306
461
 
307
- processComponentElement(
462
+ const condAttrs = {};
463
+ if (hasIf) condAttrs["if"] = child.getAttribute("if");
464
+ if (hasDelIf) condAttrs["mount:if"] = getMountIf(child);
465
+ if (hasElif) condAttrs["elif"] = child.getAttribute("elif");
466
+ if (hasElse) condAttrs["else"] = "";
467
+
468
+ const processed = processComponentElement(
308
469
  child,
309
470
  cx,
310
471
  renderChain.concat(compName),
@@ -312,9 +473,17 @@ export function processComponentElement(
312
473
  template
313
474
  );
314
475
 
315
- applyConditionalToElement(child, ctxProxy, condChain, hasIf, hasDelIf, hasElif, hasElse);
476
+ let condTarget = child;
477
+ if (processed && processed.nodeType === 1) {
478
+ condTarget = processed;
479
+ for (const [name, value] of Object.entries(condAttrs)) {
480
+ condTarget.setAttribute(name, value);
481
+ }
482
+ }
483
+
484
+ applyConditionalToElement(condTarget, ctxProxy, condChain, hasIf, hasDelIf, hasElif, hasElse);
316
485
 
317
- interpolateNode(child, ctxProxy)
486
+ interpolateNode(condTarget, ctxProxy)
318
487
  });
319
488
 
320
489
  let csrRuntimeSource = null;
@@ -338,14 +507,14 @@ export function processComponentElement(
338
507
  const ctxDefParts = [];
339
508
  const declared = new Set();
340
509
  for (const [key, value] of Object.entries(runtimeCtx)) {
341
- ctxDefParts.push(`let ${key} = ctx.${key}||${JSON.stringify(value)};\n`);
510
+ ctxDefParts.push(`let ${key} = ctx.${key}??${JSON.stringify(value)};\n`);
342
511
  declared.add(key);
343
512
  }
344
513
 
345
514
  for (const { name, defaultValue } of compProps) {
346
515
  if (declared.has(name)) continue;
347
516
  if (defaultValue !== undefined) {
348
- ctxDefParts.push(`let ${name} = ctx.${name}||${defaultValue};\n`);
517
+ ctxDefParts.push(`let ${name} = ctx.${name}??(${defaultValue});\n`);
349
518
  } else {
350
519
  ctxDefParts.push(`let ${name} = ctx.${name};\n`);
351
520
  }
@@ -365,6 +534,14 @@ export function processComponentElement(
365
534
  const topFuncs = topFuncSrc;
366
535
 
367
536
  let injectCode = ctxDef;
537
+ for (const b of bindings) declared.add(b.varName);
538
+ const undeclaredTopVars = topVars.filter(v => !declared.has(v.name));
539
+ if (undeclaredTopVars.length > 0) {
540
+ injectCode += "\n" + undeclaredTopVars.map(v => v.value !== undefined
541
+ ? `${v.keyword} ${v.name} = ctx.${v.name}??(${v.value});`
542
+ : `${v.keyword} ${v.name};`
543
+ ).join("\n") + "\n";
544
+ }
368
545
  if (bindings.length > 0) {
369
546
  injectCode += "\n" + bindings.map(b => {
370
547
  const accessor = b.prop === "self" ? "" : "." + b.prop;
@@ -406,8 +583,12 @@ export function processComponentElement(
406
583
  generateCSRClass(compName, cx);
407
584
  }
408
585
 
586
+ if (element.style.display === "none" && firstChild && firstChild.nodeType === 1) {
587
+ firstChild.style.display = "none";
588
+ }
589
+
409
590
  element.replaceWith(fragment);
410
- return true;
591
+ return firstChild && firstChild.nodeType === 1 ? firstChild : true;
411
592
  }
412
593
 
413
594
  export function processAllComponents(appElements, loadedComponents, pageSourceFile, pageSourceContent) {
@@ -1,8 +1,8 @@
1
1
  import path from "path";
2
- import { getChocolaConfig } from "../utils.js";
2
+ import { getConfig } from "../utils.js";
3
3
 
4
4
  export async function loadConfig(rootDir) {
5
- const config = await getChocolaConfig(rootDir);
5
+ const config = await getConfig(rootDir);
6
6
  const bundleConfig = config.bundle || {};
7
7
 
8
8
  const srcDir = bundleConfig.srcDir || "src";
@@ -1,13 +1,15 @@
1
- import { JSDOM } from "jsdom";
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";
5
- import { extractContextFromElement } from "../parser/context.js";
6
5
  import { throwError } from "./utils.js";
7
6
  import { readMyFile } from "./fs.js";
8
7
 
9
8
  export function createDOM(srcIndexContent) {
10
- return new JSDOM(protectCurlyBraces(srcIndexContent));
9
+ const protectedContent = protectCurlyBraces(srcIndexContent);
10
+ const dom = parseHTML(protectedContent);
11
+ dom.protectedContent = protectedContent;
12
+ return dom;
11
13
  }
12
14
 
13
15
  export function validateAppContainer(doc) {
@@ -23,7 +25,7 @@ export function getAppElements(appContainer) {
23
25
  }
24
26
 
25
27
  export async function serializeDOM(dom) {
26
- return restoreCurlyBraces(dom.serialize());
28
+ return restoreCurlyBraces(dom.document.toString());
27
29
  }
28
30
 
29
31
  export async function writeHTMLOutput(html, outDirPath) {