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/parser/utils.js CHANGED
@@ -1,6 +1,9 @@
1
+ import { restoreTemplateChars } from "../utils.js";
2
+
1
3
  const compiledCache = new Map();
2
4
 
3
- export function compileExpression(expr, useCtx) {
5
+ export function compileExpr(expr, useCtx) {
6
+ expr = restoreTemplateChars(expr);
4
7
  const key = useCtx ? `ctx:${expr}` : `raw:${expr}`;
5
8
  let fn = compiledCache.get(key);
6
9
  if (!fn) {
@@ -11,3 +14,19 @@ export function compileExpression(expr, useCtx) {
11
14
  }
12
15
  return fn;
13
16
  }
17
+
18
+ const constantEvalError = Symbol("constant-eval-error");
19
+
20
+ export function evaluateConstant(expr) {
21
+ const trap = new Proxy({}, {
22
+ has() { return true; },
23
+ get() { throw constantEvalError; },
24
+ });
25
+ try {
26
+ const fn = compileExpr(expr, true);
27
+ const value = fn(trap);
28
+ return { constant: true, value };
29
+ } catch {
30
+ return { constant: false };
31
+ }
32
+ }
package/runtime/index.js CHANGED
@@ -1,6 +1,12 @@
1
+ const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
2
+ const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
3
+
1
4
  const exprCache = new Map();
2
5
 
3
- function compileExpression(expr, useCtx) {
6
+ function compileExp(expr, useCtx) {
7
+ expr = expr.replace(/_%%CHOCOLA-(?:AMP|LT|GT)\d+%%_/g, char =>
8
+ char.includes("AMP") ? "&" : char.includes("LT") ? "<" : ">"
9
+ );
4
10
  const key = useCtx ? "ctx:" + expr : "raw:" + expr;
5
11
  let fn = exprCache.get(key);
6
12
  if (!fn) {
@@ -22,7 +28,7 @@ function interpolateNode(root, ctx) {
22
28
  .replace(new RegExp(RBRACE_PH, "g"), "}");
23
29
  node.textContent = text.replace(/\{([^}]+)\}/g, (_, expr) => {
24
30
  try {
25
- return String(compileExpression(expr, true)(ctx));
31
+ return String(compileExp(expr, true)(ctx));
26
32
  } catch {
27
33
  return "";
28
34
  }
@@ -35,30 +41,30 @@ function interpolateNode(root, ctx) {
35
41
  }
36
42
  }
37
43
 
38
- function applyConditionalToElement(child, ctx, chain, hasIf, hasDelIf, hasElif, hasElse) {
44
+ function applyDirectiveToEl(child, ctx, chain, hasIf, hasMountIf, hasElif, hasElse) {
39
45
  if (hasIf) {
40
46
  const expr = child.getAttribute("if").slice(1, -1);
41
47
  chain.active = true;
42
- if (compileExpression(expr, true)(ctx)) {
48
+ if (compileExp(expr, true)(ctx)) {
43
49
  chain.rendered = true;
44
50
  } else {
45
51
  child.style.display = "none";
46
52
  chain.rendered = false;
47
53
  }
48
54
  child.removeAttribute("if");
49
- } else if (hasDelIf) {
50
- const expr = child.getAttribute("del:if").slice(1, -1);
55
+ } else if (hasMountIf) {
56
+ const expr = child.getAttribute("mount:if").slice(1, -1);
51
57
  chain.active = true;
52
- if (compileExpression(expr, true)(ctx)) {
58
+ if (compileExp(expr, true)(ctx)) {
53
59
  chain.rendered = true;
54
60
  } else {
55
61
  child.remove();
56
62
  chain.rendered = false;
57
63
  }
58
- child.removeAttribute("del:if");
64
+ child.removeAttribute("mount:if");
59
65
  } else if (hasElif) {
60
66
  const expr = child.getAttribute("elif").slice(1, -1);
61
- if (compileExpression(expr, true)(ctx)) {
67
+ if (compileExp(expr, true)(ctx)) {
62
68
  chain.rendered = true;
63
69
  } else {
64
70
  child.remove();
@@ -74,9 +80,6 @@ function applyConditionalToElement(child, ctx, chain, hasIf, hasDelIf, hasElif,
74
80
  }
75
81
  }
76
82
 
77
- const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
78
- const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
79
-
80
83
  function interpolateAttributes(element, ctx) {
81
84
  for (const el of [element, ...element.querySelectorAll("*")]) {
82
85
  for (const attr of [...el.attributes]) {
@@ -87,7 +90,7 @@ function interpolateAttributes(element, ctx) {
87
90
  .replace(new RegExp(RBRACE_PH, "g"), "}");
88
91
  attr.value = raw.replace(/\{([^}]+)\}/g, (_, expr) => {
89
92
  try {
90
- return String(compileExpression(expr, true)(ctx));
93
+ return String(compileExp(expr, true)(ctx));
91
94
  } catch {
92
95
  return "";
93
96
  }
@@ -96,10 +99,10 @@ function interpolateAttributes(element, ctx) {
96
99
  }
97
100
  }
98
101
 
99
- function processConditionals(element, ctx, chain) {
102
+ function processDirectives(element, ctx, chain) {
100
103
  for (const child of [...element.children]) {
101
104
  const hasIf = child.hasAttribute("if");
102
- const hasDelIf = child.hasAttribute("del:if");
105
+ const hasMountIf = child.hasAttribute("mount:if");
103
106
  const hasElif = child.hasAttribute("elif");
104
107
  const hasElse = child.hasAttribute("else");
105
108
 
@@ -109,46 +112,46 @@ function processConditionals(element, ctx, chain) {
109
112
  continue;
110
113
  }
111
114
 
112
- applyConditionalToElement(child, ctx, chain, hasIf, hasDelIf, hasElif, hasElse);
115
+ applyDirectiveToEl(child, ctx, chain, hasIf, hasMountIf, hasElif, hasElse);
113
116
 
114
117
  if (child.parentNode) {
115
- processConditionals(child, ctx, chain);
118
+ processDirectives(child, ctx, chain);
116
119
  }
117
120
  }
118
121
  }
119
122
 
120
- function processSlots(element, projectedChildren) {
121
- const slots = element.querySelectorAll("slot");
123
+ function processSlots(el, projected) {
124
+ const slots = el.querySelectorAll("slot");
122
125
  for (const slot of slots) {
123
- const clone = projectedChildren.map(c => c.cloneNode(true));
126
+ const clone = projected.map(c => c.cloneNode(true));
124
127
  slot.replaceWith(...clone);
125
128
  }
126
129
  }
127
130
 
128
131
  let bindCounter = 0;
129
132
 
130
- function processBindAttributes(element) {
133
+ function processBindings(el) {
131
134
  const bindings = [];
132
- for (const attr of [...element.attributes]) {
135
+ for (const attr of [...el.attributes]) {
133
136
  if (attr.name.startsWith("bind:")) {
134
137
  const prop = attr.name.slice(5);
135
138
  const varName = attr.value;
136
139
  const bindId = "b" + (bindCounter++);
137
- element.setAttribute("data-chbind-" + bindId, "");
140
+ el.setAttribute("data-chbind-" + bindId, "");
138
141
  bindings.push({ prop, varName, bindId });
139
- element.removeAttribute(attr.name);
142
+ el.removeAttribute(attr.name);
140
143
  }
141
144
  }
142
145
  return bindings;
143
146
  }
144
147
 
145
- function resolveDataBindings(element) {
148
+ function resolveBindings(el) {
146
149
  const ids = [];
147
- for (const attr of [...element.attributes]) {
150
+ for (const attr of [...el.attributes]) {
148
151
  const match = attr.name.match(/^data-chbind-(b\d+)$/);
149
152
  if (match) {
150
153
  ids.push(match[1]);
151
- element.removeAttribute(attr.name);
154
+ el.removeAttribute(attr.name);
152
155
  }
153
156
  }
154
157
  return ids;
@@ -159,8 +162,8 @@ function collectBindings(root) {
159
162
  const idToEl = {};
160
163
 
161
164
  for (const el of [root, ...root.querySelectorAll("*")]) {
162
- bindingDefs.push(...processBindAttributes(el));
163
- for (const id of resolveDataBindings(el)) {
165
+ bindingDefs.push(...processBindings(el));
166
+ for (const id of resolveBindings(el)) {
164
167
  idToEl[id] = el;
165
168
  }
166
169
  }
@@ -222,7 +225,7 @@ class ChocolaComponent {
222
225
  }
223
226
 
224
227
  processSlots(root, this.#children || []);
225
- processConditionals(root, mergedCtx, { active: false, rendered: false });
228
+ processDirectives(root, mergedCtx, { active: false, rendered: false });
226
229
  interpolateAttributes(root, mergedCtx);
227
230
  interpolateNode(root, mergedCtx);
228
231
  root.classList.add(this.#hash);
@@ -255,7 +258,7 @@ class ChocolaComponent {
255
258
  for (const el of els) {
256
259
  const props = {};
257
260
  for (const attr of [...el.attributes]) {
258
- if (attr.name.startsWith("data-ch") || attr.name === "if" || attr.name === "del:if" || attr.name === "elif" || attr.name === "else") continue;
261
+ if (attr.name.startsWith("data-ch") || attr.name === "if" || attr.name === "mount:if" || attr.name === "elif" || attr.name === "else") continue;
259
262
  props[attr.name] = attr.value;
260
263
  }
261
264
  const instance = new compClass();
package/utils.js CHANGED
@@ -2,7 +2,7 @@ import { promises as fs } from "fs";
2
2
  import path from "path";
3
3
  import { throwError } from "./compiler/utils.js";
4
4
 
5
- export async function getChocolaConfig(__rootdir) {
5
+ export async function getConfig(__rootdir) {
6
6
  try {
7
7
  const config = await fs.readFile(path.join(__rootdir, "chocola.config.json"), "utf-8");
8
8
  return JSON.parse(config);
@@ -13,15 +13,40 @@ export async function getChocolaConfig(__rootdir) {
13
13
 
14
14
  const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
15
15
  const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
16
+ const PROTECTED_REGEX = /_%%CHOCOLA-(?:AMP|LT|GT)\d+%%_/g;
17
+
18
+ let protectSeq = 0;
16
19
 
17
20
  export function protectCurlyBraces(html) {
18
21
  return html
19
22
  .replace(/&(?:lbrace|#123|#x7B);/gi, LBRACE_PH)
20
- .replace(/&(?:rcub|#125|#x7D);/gi, RBRACE_PH);
23
+ .replace(/&(?:rcub|#125|#x7D);/gi, RBRACE_PH)
24
+ .split(/(<script\b[^>]*>[\s\S]*?<\/script>|<style\b[^>]*>[\s\S]*?<\/style>)/gi)
25
+ .map((part, i) => i % 2 === 0
26
+ ? part.replace(/\{([^{}]*)\}/g, (m, inner) =>
27
+ `{${inner
28
+ .replace(/&/g, () => `_%%CHOCOLA-AMP${protectSeq++}%%_`)
29
+ .replace(/</g, () => `_%%CHOCOLA-LT${protectSeq++}%%_`)
30
+ .replace(/>/g, () => `_%%CHOCOLA-GT${protectSeq++}%%_`)}}`)
31
+ : part)
32
+ .join("");
21
33
  }
22
34
 
23
35
  export function restoreCurlyBraces(html) {
24
36
  return html
25
37
  .replace(/_%%CHOCOLA-LBRACE%%_/g, "{")
26
- .replace(/_%%CHOCOLA-RBRACE%%_/g, "}");
38
+ .replace(/_%%CHOCOLA-RBRACE%%_/g, "}")
39
+ .replace(PROTECTED_REGEX, char => {
40
+ if (char.includes("AMP")) return "&";
41
+ if (char.includes("LT")) return "<";
42
+ return ">";
43
+ });
44
+ }
45
+
46
+ export function restoreTemplateChars(str) {
47
+ return str.replace(PROTECTED_REGEX, char => {
48
+ if (char.includes("AMP")) return "&";
49
+ if (char.includes("LT")) return "<";
50
+ return ">";
51
+ });
27
52
  }