chocola 1.3.7 → 1.3.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/compiler/component-processor.js +38 -8
- package/compiler/index.js +7 -3
- package/compiler/pipeline.js +86 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JSDOM } from "jsdom";
|
|
2
2
|
import { extractContextFromElement } from "./dom-processor.js";
|
|
3
|
-
import { genRandomId, incrementAlfabet } from "./utils.js";
|
|
3
|
+
import { genRandomId, incrementAlfabet, throwError } from "./utils.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import beautify from "js-beautify";
|
|
6
6
|
|
|
@@ -17,6 +17,20 @@ function scopeCss(cssString, cssId) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function interpolateNode(node, ctxProxy) {
|
|
21
|
+
if (node.nodeType === 3) {
|
|
22
|
+
node.textContent = node.textContent.replace(/\{([^}]+)\}/g, (_, expr) => {
|
|
23
|
+
try {
|
|
24
|
+
return Function("ctx", `with(ctx) { return (${expr}); }`)(ctxProxy);
|
|
25
|
+
} catch {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
node.childNodes.forEach(child => interpolateNode(child, ctxProxy));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
20
34
|
|
|
21
35
|
/**
|
|
22
36
|
* Processes a single component element and inserts it into the DOM
|
|
@@ -43,31 +57,47 @@ export function processComponentElement(
|
|
|
43
57
|
const ctx = extractContextFromElement(element);
|
|
44
58
|
const srcInnerHtml = element.innerHTML;
|
|
45
59
|
|
|
60
|
+
const ctxProxy = new Proxy(ctx, {
|
|
61
|
+
has() { return true; },
|
|
62
|
+
get(target, key) { return target[key]; }
|
|
63
|
+
});
|
|
64
|
+
|
|
46
65
|
const instance = loadedComponents.get(compName);
|
|
47
66
|
if (!instance || instance === undefined) return false;
|
|
48
67
|
|
|
49
68
|
if (instance.body) {
|
|
50
69
|
let body = instance.body;
|
|
51
|
-
|
|
52
|
-
/(?<!\b(?:if|del-if)=)\{(\w+)\}/g,
|
|
53
|
-
(_, key) => ctx[key] || ""
|
|
54
|
-
);
|
|
70
|
+
|
|
55
71
|
const fragment = JSDOM.fragment(body);
|
|
56
72
|
const children = Array.from(fragment.querySelectorAll("*"));
|
|
73
|
+
|
|
57
74
|
children.forEach(child => {
|
|
75
|
+
const reservedAttrs = ["if", "del-if"];
|
|
76
|
+
Array.from(child.attributes).forEach(attribute => {
|
|
77
|
+
if (!attribute || attribute === undefined) return;
|
|
78
|
+
if (reservedAttrs.includes(attribute.name)) return;
|
|
79
|
+
attribute.value = attribute.value.replace(
|
|
80
|
+
/\{([^}]+)\}/g,
|
|
81
|
+
(_, key) => ctx[key] ?? ""
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
58
85
|
["if", "del-if"].forEach(statement => {
|
|
59
86
|
const statAtt = child.getAttribute(statement);
|
|
60
87
|
if (!statAtt) return;
|
|
61
88
|
const expr = statAtt.slice(1, -1);
|
|
62
|
-
const
|
|
63
|
-
|
|
89
|
+
const eva = ctxProxy[expr] ? ctxProxy[expr].slice(1, -1) : null;
|
|
90
|
+
const fn = new Function("ctx", `{ return (${eva}); }`);
|
|
91
|
+
if (!fn()) {
|
|
64
92
|
if (statement === "if") child.style.display = "none";
|
|
65
93
|
if (statement === "del-if") child.remove();
|
|
66
94
|
}
|
|
67
|
-
|
|
68
95
|
child.removeAttribute(statement);
|
|
69
96
|
});
|
|
97
|
+
|
|
98
|
+
interpolateNode(child, ctxProxy)
|
|
70
99
|
});
|
|
100
|
+
|
|
71
101
|
const firstChild = fragment.firstChild;
|
|
72
102
|
|
|
73
103
|
if (firstChild && firstChild.nodeType === 1) {
|
package/compiler/index.js
CHANGED
|
@@ -93,14 +93,17 @@ async function loadAndDisplayComponents(srcComponentsPath) {
|
|
|
93
93
|
async function processAssets(doc, rootDir, srcDir, outDirPath) {
|
|
94
94
|
const { stylesheets, icons } = getAssetLinks(doc);
|
|
95
95
|
const fileIds = [];
|
|
96
|
+
let cssContents = [];
|
|
96
97
|
|
|
97
98
|
for (const link of stylesheets) {
|
|
98
|
-
await processStylesheet(link, rootDir, srcDir, outDirPath, fileIds);
|
|
99
|
+
const css = await processStylesheet(link, rootDir, srcDir, outDirPath, fileIds);
|
|
100
|
+
cssContents.push(css);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
for (const link of icons) {
|
|
102
104
|
await processIcons(link, rootDir, srcDir, outDirPath, fileIds);
|
|
103
105
|
}
|
|
106
|
+
return cssContents
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
/**
|
|
@@ -137,7 +140,8 @@ export default async function runtime(rootDir, buildConfig) {
|
|
|
137
140
|
|
|
138
141
|
const { runtimeScript, scopesCss } = processAllComponents(appElements, loadedComponents);
|
|
139
142
|
const runtimeFilename = await generateRuntimeScript(runtimeScript, paths.outDir);
|
|
140
|
-
await processAssets(doc, rootDir, config.srcDir, paths.outDir);
|
|
143
|
+
const globalCss = (await processAssets(doc, rootDir, config.srcDir, paths.outDir)).join("\n");
|
|
144
|
+
console.log(globalCss)
|
|
141
145
|
|
|
142
146
|
if (scopesCss) {
|
|
143
147
|
const fileName = "sc-" + genRandomId(null, 6) + ".css";
|
|
@@ -149,7 +153,7 @@ export default async function runtime(rootDir, buildConfig) {
|
|
|
149
153
|
const html = await serializeDOM(dom);
|
|
150
154
|
await writeHTMLOutput(html, paths.outDir);
|
|
151
155
|
|
|
152
|
-
await copyResources(rootDir, config.srcDir, paths.outDir);
|
|
156
|
+
await copyResources(rootDir, scopesCss, globalCss, config.srcDir, paths.outDir);
|
|
153
157
|
|
|
154
158
|
!isHotReload && logSuccess(paths.outDir);
|
|
155
159
|
isHotReload && console.log("Dev server updated");
|
package/compiler/pipeline.js
CHANGED
|
@@ -109,6 +109,8 @@ export async function processStylesheet(link, rootDir, srcDir, outDirPath, fileI
|
|
|
109
109
|
|
|
110
110
|
await fs.writeFile(path.join(outDirPath, cssFileName), css);
|
|
111
111
|
link.setAttribute("href", "./" + cssFileName);
|
|
112
|
+
|
|
113
|
+
return css;
|
|
112
114
|
} catch (err) {
|
|
113
115
|
throwError(err);
|
|
114
116
|
}
|
|
@@ -132,6 +134,51 @@ export async function processIcons(link, rootDir, srcDir, outDirPath) {
|
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
export function getCssAssets(css) {
|
|
138
|
+
const results = new Set();
|
|
139
|
+
|
|
140
|
+
const urlRegex = /url\(([^)]+)\)/gi;
|
|
141
|
+
|
|
142
|
+
const importStringRegex = /@import\s+["']([^"']+)["']/gi;
|
|
143
|
+
|
|
144
|
+
const importUrlRegex = /@import\s+url\(([^)]+)\)/gi;
|
|
145
|
+
|
|
146
|
+
const clean = (raw) => {
|
|
147
|
+
let p = raw.trim();
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
(p.startsWith('"') && p.endsWith('"')) ||
|
|
151
|
+
(p.startsWith("'") && p.endsWith("'"))
|
|
152
|
+
) {
|
|
153
|
+
p = p.slice(1, -1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (p.startsWith("data:")) return null;
|
|
157
|
+
|
|
158
|
+
return p;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
let match;
|
|
162
|
+
|
|
163
|
+
while ((match = urlRegex.exec(css))) {
|
|
164
|
+
const p = clean(match[1]);
|
|
165
|
+
if (p) results.add(p);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
while ((match = importStringRegex.exec(css))) {
|
|
169
|
+
const p = clean(match[1]);
|
|
170
|
+
if (p) results.add(p);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
while ((match = importUrlRegex.exec(css))) {
|
|
174
|
+
const p = clean(match[1]);
|
|
175
|
+
if (p) results.add(p);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return [...results];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
135
182
|
/**
|
|
136
183
|
* Copies all local resources (images, fonts, etc.) to output directory
|
|
137
184
|
* Excludes web links and script/link tags
|
|
@@ -140,11 +187,12 @@ export async function processIcons(link, rootDir, srcDir, outDirPath) {
|
|
|
140
187
|
* @param {import("fs").PathLike} outDirPath - Output directory path
|
|
141
188
|
* @throws {Error} if resources cannot be copied
|
|
142
189
|
*/
|
|
143
|
-
export async function copyResources(rootDir, srcDir, outDirPath) {
|
|
190
|
+
export async function copyResources(rootDir, scopesCss, globalCss, srcDir, outDirPath) {
|
|
144
191
|
try {
|
|
145
192
|
const newIndex = await fs.readFile(path.join(outDirPath, "index.html"), "utf8");
|
|
146
193
|
const newDoc = new JSDOM(newIndex);
|
|
147
194
|
const newElements = Array.from(newDoc.window.document.querySelectorAll("*"));
|
|
195
|
+
const inlinePathsRegex = /url\((.*?)\)/gi;
|
|
148
196
|
|
|
149
197
|
for (const el of newElements) {
|
|
150
198
|
if (el.tagName === "LINK" || el.tagName === "SCRIPT") continue;
|
|
@@ -158,6 +206,43 @@ export async function copyResources(rootDir, srcDir, outDirPath) {
|
|
|
158
206
|
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
159
207
|
await fs.copyFile(srcPath, destPath);
|
|
160
208
|
}
|
|
209
|
+
|
|
210
|
+
const styles = el.getAttribute("style");
|
|
211
|
+
|
|
212
|
+
if (styles) {
|
|
213
|
+
let stylesMatch;
|
|
214
|
+
while ((stylesMatch = inlinePathsRegex.exec(styles)) !== null) {
|
|
215
|
+
let filePath = stylesMatch[1].trim();
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
(filePath.startsWith('"') && filePath.endsWith('"')) ||
|
|
219
|
+
(filePath.startsWith("'") && filePath.endsWith("'"))
|
|
220
|
+
) {
|
|
221
|
+
filePath = filePath.slice(1, -1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const srcPath = path.join(rootDir, srcDir, filePath);
|
|
225
|
+
const destPath = path.join(outDirPath, filePath);
|
|
226
|
+
|
|
227
|
+
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
228
|
+
await fs.copyFile(srcPath, destPath);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(globalCss)
|
|
234
|
+
const cssAssets = [...getCssAssets(scopesCss), ...getCssAssets(globalCss)];
|
|
235
|
+
console.log(cssAssets)
|
|
236
|
+
for (const assetPath of cssAssets) {
|
|
237
|
+
try {
|
|
238
|
+
const srcPath = path.join(rootDir, srcDir, assetPath);
|
|
239
|
+
const destPath = path.join(outDirPath, assetPath);
|
|
240
|
+
|
|
241
|
+
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
242
|
+
await fs.copyFile(srcPath, destPath);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
throwError(err)
|
|
245
|
+
}
|
|
161
246
|
}
|
|
162
247
|
} catch (err) {
|
|
163
248
|
throwError(err);
|