chocola 2.0.0-next.4 → 2.0.0-next.6
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 +12 -8
- package/compiler/chalk.js +32 -0
- package/compiler/component-processor.js +110 -330
- package/compiler/dom-processor.js +8 -22
- package/compiler/index.js +84 -80
- package/compiler/pipeline.js +40 -13
- package/compiler/utils.js +52 -81
- package/dev/index.js +1 -1
- package/package.json +4 -9
- package/parser/component.js +129 -0
- package/parser/context.js +29 -0
- package/parser/css.js +96 -0
- package/parser/index.js +5 -0
- package/parser/template.js +121 -0
- package/parser/utils.js +13 -0
- package/utils.js +15 -0
package/compiler/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "path";
|
|
3
|
+
import chalk from "./chalk.js";
|
|
3
4
|
import { loadConfig, resolvePaths } from "./config.js";
|
|
4
5
|
import {
|
|
5
6
|
createDOM,
|
|
6
7
|
validateAppContainer,
|
|
7
8
|
getAppElements,
|
|
8
9
|
getAssetLinks,
|
|
10
|
+
getScriptElements,
|
|
9
11
|
appendRuntimeScript,
|
|
10
12
|
appendStylesheetLink,
|
|
11
13
|
serializeDOM,
|
|
@@ -14,12 +16,14 @@ import {
|
|
|
14
16
|
} from "./dom-processor.js";
|
|
15
17
|
import { processAllComponents } from "./component-processor.js";
|
|
16
18
|
import { generateRuntimeScript } from "./runtime-generator.js";
|
|
17
|
-
import { genRandomId, throwError
|
|
19
|
+
import { genRandomId, throwError } from "./utils.js";
|
|
20
|
+
import { compileExpression, hasDelIfAttr, getDelIfAttr, removeDelIfAttr } from "../parser/index.js";
|
|
18
21
|
import {
|
|
19
22
|
copyStaticDir,
|
|
20
23
|
getComponents,
|
|
21
24
|
getSrcIndex,
|
|
22
25
|
processIcons,
|
|
26
|
+
processScript,
|
|
23
27
|
processStylesheet,
|
|
24
28
|
} from "./pipeline.js";
|
|
25
29
|
|
|
@@ -91,6 +95,7 @@ async function loadAndDisplayComponents(srcComponentsPath) {
|
|
|
91
95
|
|
|
92
96
|
async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
93
97
|
const { stylesheets, icons } = getAssetLinks(doc);
|
|
98
|
+
const scripts = getScriptElements(doc);
|
|
94
99
|
const fileIds = [];
|
|
95
100
|
let cssContents = [];
|
|
96
101
|
|
|
@@ -102,9 +107,81 @@ async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
|
102
107
|
for (const link of icons) {
|
|
103
108
|
await processIcons(link, rootDir, srcDir, outDirPath);
|
|
104
109
|
}
|
|
110
|
+
|
|
111
|
+
for (const script of scripts) {
|
|
112
|
+
await processScript(doc, script, rootDir, srcDir, outDirPath, fileIds);
|
|
113
|
+
}
|
|
105
114
|
return cssContents
|
|
106
115
|
}
|
|
107
116
|
|
|
117
|
+
function processPageConditionals(parent) {
|
|
118
|
+
const children = [...parent.children];
|
|
119
|
+
let chainActive = false;
|
|
120
|
+
let chainRendered = false;
|
|
121
|
+
|
|
122
|
+
for (const child of children) {
|
|
123
|
+
const hasIf = child.hasAttribute("if");
|
|
124
|
+
const hasDelIf = hasDelIfAttr(child);
|
|
125
|
+
const hasElif = child.hasAttribute("elif");
|
|
126
|
+
const hasElse = child.hasAttribute("else");
|
|
127
|
+
|
|
128
|
+
if (hasElif || hasElse) {
|
|
129
|
+
if (!chainActive) continue;
|
|
130
|
+
if (chainRendered) { child.remove(); continue; }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (hasIf) {
|
|
134
|
+
const raw = child.getAttribute("if");
|
|
135
|
+
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
136
|
+
const fn = compileExpression(expr, false);
|
|
137
|
+
const result = fn();
|
|
138
|
+
chainActive = true;
|
|
139
|
+
if (result) {
|
|
140
|
+
chainRendered = true;
|
|
141
|
+
} else {
|
|
142
|
+
child.remove();
|
|
143
|
+
chainRendered = false;
|
|
144
|
+
}
|
|
145
|
+
child.removeAttribute("if");
|
|
146
|
+
} else if (hasDelIf) {
|
|
147
|
+
const raw = getDelIfAttr(child);
|
|
148
|
+
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
149
|
+
const fn = compileExpression(expr, false);
|
|
150
|
+
const result = fn();
|
|
151
|
+
chainActive = true;
|
|
152
|
+
if (result) {
|
|
153
|
+
chainRendered = true;
|
|
154
|
+
} else {
|
|
155
|
+
child.remove();
|
|
156
|
+
chainRendered = false;
|
|
157
|
+
}
|
|
158
|
+
removeDelIfAttr(child);
|
|
159
|
+
} else if (hasElif) {
|
|
160
|
+
const raw = child.getAttribute("elif");
|
|
161
|
+
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
162
|
+
const fn = compileExpression(expr, false);
|
|
163
|
+
const result = fn();
|
|
164
|
+
if (result) {
|
|
165
|
+
chainRendered = true;
|
|
166
|
+
} else {
|
|
167
|
+
child.remove();
|
|
168
|
+
}
|
|
169
|
+
child.removeAttribute("elif");
|
|
170
|
+
} else if (hasElse) {
|
|
171
|
+
chainRendered = true;
|
|
172
|
+
chainActive = false;
|
|
173
|
+
child.removeAttribute("else");
|
|
174
|
+
} else {
|
|
175
|
+
chainActive = false;
|
|
176
|
+
chainRendered = false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (child.parentNode) {
|
|
180
|
+
processPageConditionals(child);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
108
185
|
export default async function compile(rootDir, buildConfig) {
|
|
109
186
|
const isHotReload = buildConfig?.isHotReload || null;
|
|
110
187
|
!isHotReload && logBanner();
|
|
@@ -129,87 +206,10 @@ export default async function compile(rootDir, buildConfig) {
|
|
|
129
206
|
const doc = dom.window.document;
|
|
130
207
|
const appContainer = validateAppContainer(doc);
|
|
131
208
|
|
|
132
|
-
function hasDelIfAttr(el) {
|
|
133
|
-
return el.hasAttribute("del:if");
|
|
134
|
-
}
|
|
135
|
-
function getDelIfAttr(el) {
|
|
136
|
-
return el.getAttribute("del:if");
|
|
137
|
-
}
|
|
138
|
-
function removeDelIfAttr(el) {
|
|
139
|
-
el.removeAttribute("del:if");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function processPageConditionals(parent) {
|
|
143
|
-
const children = [...parent.children];
|
|
144
|
-
let chainActive = false;
|
|
145
|
-
let chainRendered = false;
|
|
146
|
-
|
|
147
|
-
for (const child of children) {
|
|
148
|
-
const hasIf = child.hasAttribute("if");
|
|
149
|
-
const hasDelIf = hasDelIfAttr(child);
|
|
150
|
-
const hasElif = child.hasAttribute("elif");
|
|
151
|
-
const hasElse = child.hasAttribute("else");
|
|
152
|
-
|
|
153
|
-
if (hasElif || hasElse) {
|
|
154
|
-
if (!chainActive) continue;
|
|
155
|
-
if (chainRendered) { child.remove(); continue; }
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (hasIf) {
|
|
159
|
-
const raw = child.getAttribute("if");
|
|
160
|
-
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
161
|
-
const fn = compileExpression(expr, false);
|
|
162
|
-
const result = fn();
|
|
163
|
-
chainActive = true;
|
|
164
|
-
if (result) {
|
|
165
|
-
chainRendered = true;
|
|
166
|
-
} else {
|
|
167
|
-
child.remove();
|
|
168
|
-
chainRendered = false;
|
|
169
|
-
}
|
|
170
|
-
child.removeAttribute("if");
|
|
171
|
-
} else if (hasDelIf) {
|
|
172
|
-
const raw = getDelIfAttr(child);
|
|
173
|
-
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
174
|
-
const fn = compileExpression(expr, false);
|
|
175
|
-
const result = fn();
|
|
176
|
-
chainActive = true;
|
|
177
|
-
if (result) {
|
|
178
|
-
chainRendered = true;
|
|
179
|
-
} else {
|
|
180
|
-
child.remove();
|
|
181
|
-
chainRendered = false;
|
|
182
|
-
}
|
|
183
|
-
removeDelIfAttr(child);
|
|
184
|
-
} else if (hasElif) {
|
|
185
|
-
const raw = child.getAttribute("elif");
|
|
186
|
-
const expr = raw.startsWith("{") ? raw.slice(1, -1) : raw;
|
|
187
|
-
const fn = compileExpression(expr, false);
|
|
188
|
-
const result = fn();
|
|
189
|
-
if (result) {
|
|
190
|
-
chainRendered = true;
|
|
191
|
-
} else {
|
|
192
|
-
child.remove();
|
|
193
|
-
}
|
|
194
|
-
child.removeAttribute("elif");
|
|
195
|
-
} else if (hasElse) {
|
|
196
|
-
chainRendered = true;
|
|
197
|
-
chainActive = false;
|
|
198
|
-
child.removeAttribute("else");
|
|
199
|
-
} else {
|
|
200
|
-
chainActive = false;
|
|
201
|
-
chainRendered = false;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (child.parentNode) {
|
|
205
|
-
processPageConditionals(child);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
209
|
processPageConditionals(appContainer);
|
|
210
210
|
|
|
211
211
|
const appElements = getAppElements(appContainer);
|
|
212
|
-
const { runtimeScript, scopesCss } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
|
|
212
|
+
const { runtimeScript, scopesCss, hashMap } = processAllComponents(appElements, loadedComponents, pageSourcePath, srcIndexContent);
|
|
213
213
|
const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
|
|
214
214
|
await processAssets(doc, rootDir, config.srcDir, paths.outDir);
|
|
215
215
|
|
|
@@ -229,6 +229,10 @@ export default async function compile(rootDir, buildConfig) {
|
|
|
229
229
|
throwError(err.message || err);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
const chocolaDir = path.join(rootDir, ".chocola");
|
|
233
|
+
await fs.mkdir(chocolaDir, { recursive: true });
|
|
234
|
+
await fs.writeFile(path.join(chocolaDir, "hashes.json"), JSON.stringify(hashMap, null, 2) + "\n");
|
|
235
|
+
|
|
232
236
|
!isHotReload && logSuccess(paths.outDir);
|
|
233
237
|
isHotReload && console.log("Dev server updated");
|
|
234
238
|
}
|
package/compiler/pipeline.js
CHANGED
|
@@ -15,22 +15,23 @@ export async function getComponents(libDir) {
|
|
|
15
15
|
throw Error(`The specified components folder ${libDir} could not be found.`);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const reads = components
|
|
19
|
+
.filter(comp => comp.endsWith(".html"))
|
|
20
|
+
.map(async (comp) => {
|
|
21
|
+
try {
|
|
22
|
+
const compPath = path.join(libDir, comp);
|
|
23
|
+
const instance = await fs.readFile(compPath, "utf-8");
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
if (instance === "" || instance.trim().length === 0) emptyComps.push(comp);
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
loadedComponents.set(comp.toLowerCase(), instance);
|
|
28
|
+
componentsLib.push(comp);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
throwError(`Failed to load component "${comp}": ${err.message || err}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
loadedComponents.set(comp.toLowerCase(), instance);
|
|
30
|
-
} catch (err) {
|
|
31
|
-
throwError(`Failed to load component "${comp}": ${err.message || err}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
+
await Promise.all(reads);
|
|
34
35
|
|
|
35
36
|
return { componentsLib, loadedComponents, emptyComps };
|
|
36
37
|
} catch (err) {
|
|
@@ -83,6 +84,32 @@ export async function processIcons(link, rootDir, srcDir, outDirPath) {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
export async function processScript(doc, script, rootDir, srcDir, outDirPath, fileIds) {
|
|
88
|
+
try {
|
|
89
|
+
const src = script.getAttribute("src");
|
|
90
|
+
if (src.startsWith("http://") || src.startsWith("https://")) return;
|
|
91
|
+
const scriptPath = path.join(rootDir, srcDir, src);
|
|
92
|
+
const content = await fs.readFile(scriptPath, { encoding: "utf8" });
|
|
93
|
+
const jsFileName = "js-" + genRandomId(fileIds, 6) + ".js";
|
|
94
|
+
await fs.writeFile(path.join(outDirPath, jsFileName), content);
|
|
95
|
+
|
|
96
|
+
const newScript = doc.createElement("script");
|
|
97
|
+
for (const attr of script.attributes) {
|
|
98
|
+
if (attr.name === "src") {
|
|
99
|
+
newScript.setAttribute("src", "./" + jsFileName);
|
|
100
|
+
} else {
|
|
101
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (script.textContent.trim()) {
|
|
105
|
+
newScript.textContent = script.textContent;
|
|
106
|
+
}
|
|
107
|
+
script.parentNode.replaceChild(newScript, script);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throwError(`Failed to process script: ${err}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
export async function copyStaticDir(srcPath, outDirPath) {
|
|
87
114
|
const staticSrc = path.join(srcPath, "static");
|
|
88
115
|
const staticDest = path.join(outDirPath, "static");
|
package/compiler/utils.js
CHANGED
|
@@ -1,81 +1,52 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
id =
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
|
|
55
|
-
const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
|
|
56
|
-
|
|
57
|
-
export function protectCurlyBraces(html) {
|
|
58
|
-
return html
|
|
59
|
-
.replace(/&(?:lbrace|#123|#x7B);/gi, LBRACE_PH)
|
|
60
|
-
.replace(/&(?:rcub|#125|#x7D);/gi, RBRACE_PH);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function restoreCurlyBraces(html) {
|
|
64
|
-
return html
|
|
65
|
-
.replace(/_%%CHOCOLA-LBRACE%%_/g, "{")
|
|
66
|
-
.replace(/_%%CHOCOLA-RBRACE%%_/g, "}");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const ID_LETTERS = "abcdefghijklmnopqrstuvwxyz";
|
|
70
|
-
const compiledCache = new Map();
|
|
71
|
-
export function compileExpression(expr, useCtx) {
|
|
72
|
-
const key = useCtx ? `ctx:${expr}` : `raw:${expr}`;
|
|
73
|
-
let fn = compiledCache.get(key);
|
|
74
|
-
if (!fn) {
|
|
75
|
-
fn = useCtx
|
|
76
|
-
? new Function("ctx", `with(ctx) { return (${expr}); }`)
|
|
77
|
-
: new Function(`"use strict"; return (${expr})`);
|
|
78
|
-
compiledCache.set(key, fn);
|
|
79
|
-
}
|
|
80
|
-
return fn;
|
|
81
|
-
}
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import chalk from "./chalk.js";
|
|
3
|
+
|
|
4
|
+
export function throwError(err) {
|
|
5
|
+
console.log(chalk.red.bold("Error!"), "A fatal error has occurred:\n");
|
|
6
|
+
throw new Error(err);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function genRandomId(collection = null, length = 10, lettersOnly = false) {
|
|
10
|
+
let id;
|
|
11
|
+
if (lettersOnly) {
|
|
12
|
+
id = Array.from({ length }, () => ID_LETTERS[Math.floor(Math.random() * ID_LETTERS.length)]).join("");
|
|
13
|
+
} else {
|
|
14
|
+
id = Math.random().toString(36).substring(2, length + 2);
|
|
15
|
+
}
|
|
16
|
+
if (!collection) return id;
|
|
17
|
+
if (collection.includes(id)) {
|
|
18
|
+
return genRandomId(collection, length, lettersOnly);
|
|
19
|
+
} else {
|
|
20
|
+
collection.push(id);
|
|
21
|
+
return id;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function incrementAlfabet(letters) {
|
|
26
|
+
let arr = letters.split("");
|
|
27
|
+
let i = arr.length - 1;
|
|
28
|
+
|
|
29
|
+
while (i >= 0) {
|
|
30
|
+
let pos = arr[i].charCodeAt(0) - 97;
|
|
31
|
+
if (pos < 25) {
|
|
32
|
+
arr[i] = String.fromCharCode(pos + 97 + 1);
|
|
33
|
+
return arr.join("");
|
|
34
|
+
} else {
|
|
35
|
+
arr[i] = "a";
|
|
36
|
+
i--;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return "a" + arr.join("");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function deterministicHash(content, length = 8) {
|
|
44
|
+
const hash = createHash("sha256").update(content).digest();
|
|
45
|
+
let result = "";
|
|
46
|
+
for (let i = 0; i < length; i++) {
|
|
47
|
+
result += String.fromCharCode(97 + (hash[i % hash.length] % 26));
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ID_LETTERS = "abcdefghijklmnopqrstuvwxyz";
|
package/dev/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import http from "http";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import chalk from "chalk";
|
|
4
|
+
import chalk from "../compiler/chalk.js";
|
|
5
5
|
import compile from "../compiler/index.js";
|
|
6
6
|
import { loadConfig, resolvePaths } from "../compiler/config.js";
|
|
7
7
|
import { getChocolaConfig } from "../utils.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chocola",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.6",
|
|
4
4
|
"description": "The sweetest way to build the web",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web",
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://chocolaframework.web.app/",
|
|
15
15
|
"bugs": {
|
|
16
|
-
"url": "https://github.com/
|
|
16
|
+
"url": "https://github.com/chocolajs/chocola/issues"
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/
|
|
20
|
+
"url": "git+https://github.com/chocolajs/chocola.git"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"author": "SadGabi",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"main": "index.js",
|
|
26
26
|
"funding": {
|
|
27
27
|
"type": "github",
|
|
28
|
-
"url": "https://github.com/sponsors/
|
|
28
|
+
"url": "https://github.com/sponsors/chocolajs"
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
31
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -35,11 +35,6 @@
|
|
|
35
35
|
"./dev": "./dev/index.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"chalk": "^5.6.2",
|
|
39
|
-
"js-beautify": "^1.15.4",
|
|
40
38
|
"jsdom": "^27.4.0"
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"js-beautify": "^1.15.4"
|
|
44
39
|
}
|
|
45
40
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export function extractPropsDefaults(script) {
|
|
2
|
+
if (!script) return [];
|
|
3
|
+
const propsRegex = /export\s+let\s+([a-zA-Z_$][0-9a-zA-Z_$]*)\s*(?:=\s*([^;]+))?;/g;
|
|
4
|
+
let props = [];
|
|
5
|
+
let match;
|
|
6
|
+
while ((match = propsRegex.exec(script)) !== null) {
|
|
7
|
+
props.push({ name: match[1].trim(), defaultValue: match[2]?.trim() });
|
|
8
|
+
}
|
|
9
|
+
return props;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function extractRuntime(script, compName) {
|
|
13
|
+
const startRegex = /(?:async\s+)?function\s+\$runtime\(([^)]*)\)\s*\{/;
|
|
14
|
+
const match = script.match(startRegex);
|
|
15
|
+
if (!match) return null;
|
|
16
|
+
|
|
17
|
+
const startIndexBrace = match.index + match[0].length - 1;
|
|
18
|
+
let bracesCount = 0;
|
|
19
|
+
let inString = false;
|
|
20
|
+
let stringChar = null;
|
|
21
|
+
|
|
22
|
+
for (let i = startIndexBrace; i < script.length; i++) {
|
|
23
|
+
const ch = script[i];
|
|
24
|
+
|
|
25
|
+
if (inString) {
|
|
26
|
+
if (ch === '\\') { i++; continue; }
|
|
27
|
+
if (ch === stringChar) inString = false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
32
|
+
inString = true;
|
|
33
|
+
stringChar = ch;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (ch === '{') {
|
|
38
|
+
bracesCount++;
|
|
39
|
+
} else if (ch === '}') {
|
|
40
|
+
bracesCount--;
|
|
41
|
+
if (bracesCount === 0) {
|
|
42
|
+
return script.substring(match.index, i + 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw new Error(`${compName} $runtime function has unclosed curly braces.`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function extractTopLevelFunctions(script, excludeName) {
|
|
51
|
+
const funcs = [];
|
|
52
|
+
let i = 0;
|
|
53
|
+
let depth = 0;
|
|
54
|
+
let inStr = false;
|
|
55
|
+
let strChar = null;
|
|
56
|
+
|
|
57
|
+
while (i < script.length) {
|
|
58
|
+
const ch = script[i];
|
|
59
|
+
|
|
60
|
+
if (inStr) {
|
|
61
|
+
if (ch === '\\') i++;
|
|
62
|
+
else if (ch === strChar) inStr = false;
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
68
|
+
inStr = true;
|
|
69
|
+
strChar = ch;
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (ch === '{') { depth++; i++; continue; }
|
|
75
|
+
if (ch === '}') { depth--; i++; continue; }
|
|
76
|
+
|
|
77
|
+
if (depth === 0) {
|
|
78
|
+
const rest = script.slice(i);
|
|
79
|
+
const m = rest.match(/^(?:async\s+)?function\s+([a-zA-Z_$][0-9a-zA-Z_$]*)\s*\(/);
|
|
80
|
+
if (m) {
|
|
81
|
+
const name = m[1];
|
|
82
|
+
const pastParen = i + m[0].length;
|
|
83
|
+
|
|
84
|
+
let parenDepth = 1;
|
|
85
|
+
let p = pastParen;
|
|
86
|
+
while (p < script.length && parenDepth > 0) {
|
|
87
|
+
if (script[p] === '(') parenDepth++;
|
|
88
|
+
if (script[p] === ')') parenDepth--;
|
|
89
|
+
p++;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
while (p < script.length && script[p] !== '{') p++;
|
|
93
|
+
if (p >= script.length) { i++; continue; }
|
|
94
|
+
|
|
95
|
+
let bDepth = 1;
|
|
96
|
+
let bodyStr = false;
|
|
97
|
+
let bodyStrChar = null;
|
|
98
|
+
let end = p + 1;
|
|
99
|
+
while (end < script.length && bDepth > 0) {
|
|
100
|
+
const c = script[end];
|
|
101
|
+
if (bodyStr) {
|
|
102
|
+
if (c === '\\') end++;
|
|
103
|
+
else if (c === bodyStrChar) bodyStr = false;
|
|
104
|
+
end++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
108
|
+
bodyStr = true;
|
|
109
|
+
bodyStrChar = c;
|
|
110
|
+
end++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (c === '{') bDepth++;
|
|
114
|
+
if (c === '}') bDepth--;
|
|
115
|
+
end++;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (name !== excludeName) {
|
|
119
|
+
funcs.push(script.slice(i, end));
|
|
120
|
+
}
|
|
121
|
+
i = end;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return funcs;
|
|
129
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { compileExpression } from "./utils.js";
|
|
2
|
+
|
|
3
|
+
export function extractContextFromElement(element) {
|
|
4
|
+
const ctx = {};
|
|
5
|
+
for (const attr of element.attributes) {
|
|
6
|
+
const key = attr.name;
|
|
7
|
+
const val = attr.value;
|
|
8
|
+
if (!val.includes("{")) { ctx[key] = val; continue; }
|
|
9
|
+
const matches = [...val.matchAll(/\{([^}]+)\}/g)];
|
|
10
|
+
if (matches.length === 1 && matches[0][0] === val) {
|
|
11
|
+
try {
|
|
12
|
+
ctx[key] = compileExpression(matches[0][1], false)();
|
|
13
|
+
continue;
|
|
14
|
+
} catch {}
|
|
15
|
+
}
|
|
16
|
+
ctx[key] = val;
|
|
17
|
+
}
|
|
18
|
+
return ctx;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function hasDelIfAttr(el) {
|
|
22
|
+
return el.hasAttribute("del:if");
|
|
23
|
+
}
|
|
24
|
+
export function getDelIfAttr(el) {
|
|
25
|
+
return el.getAttribute("del:if");
|
|
26
|
+
}
|
|
27
|
+
export function removeDelIfAttr(el) {
|
|
28
|
+
el.removeAttribute("del:if");
|
|
29
|
+
}
|