@symbo.ls/brender 3.5.0 → 3.5.1
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/dist/cjs/env.js +23 -0
- package/dist/cjs/load.js +62 -16
- package/dist/cjs/render.js +182 -18
- package/dist/esm/env.js +23 -0
- package/dist/esm/load.js +52 -16
- package/dist/esm/render.js +182 -18
- package/env.js +17 -0
- package/load.js +58 -19
- package/package.json +3 -3
- package/render.js +175 -18
package/dist/cjs/env.js
CHANGED
|
@@ -48,6 +48,29 @@ const createEnv = (html = "<!DOCTYPE html><html><head></head><body></body></html
|
|
|
48
48
|
window.scrollTo = () => {
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
const createStorage = () => {
|
|
52
|
+
const store = {};
|
|
53
|
+
return {
|
|
54
|
+
getItem: (k) => store[k] ?? null,
|
|
55
|
+
setItem: (k, v) => {
|
|
56
|
+
store[k] = String(v);
|
|
57
|
+
},
|
|
58
|
+
removeItem: (k) => {
|
|
59
|
+
delete store[k];
|
|
60
|
+
},
|
|
61
|
+
clear: () => {
|
|
62
|
+
for (const k in store) delete store[k];
|
|
63
|
+
},
|
|
64
|
+
get length() {
|
|
65
|
+
return Object.keys(store).length;
|
|
66
|
+
},
|
|
67
|
+
key: (i) => Object.keys(store)[i] ?? null
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
if (!window.localStorage) window.localStorage = createStorage();
|
|
71
|
+
if (!window.sessionStorage) window.sessionStorage = createStorage();
|
|
72
|
+
if (!globalThis.localStorage) globalThis.localStorage = window.localStorage;
|
|
73
|
+
if (!globalThis.sessionStorage) globalThis.sessionStorage = window.sessionStorage;
|
|
51
74
|
globalThis.window = window;
|
|
52
75
|
globalThis.document = document;
|
|
53
76
|
globalThis.Node = window.Node || globalThis.Node;
|
package/dist/cjs/load.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
7
|
var __export = (target, all) => {
|
|
6
8
|
for (var name in all)
|
|
@@ -14,6 +16,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
14
16
|
}
|
|
15
17
|
return to;
|
|
16
18
|
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
17
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
28
|
var load_exports = {};
|
|
19
29
|
__export(load_exports, {
|
|
@@ -22,15 +32,51 @@ __export(load_exports, {
|
|
|
22
32
|
});
|
|
23
33
|
module.exports = __toCommonJS(load_exports);
|
|
24
34
|
var import_path = require("path");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
var import_fs = require("fs");
|
|
36
|
+
var import_os = require("os");
|
|
37
|
+
var import_crypto = require("crypto");
|
|
38
|
+
const bundleAndImport = async (entryPath) => {
|
|
39
|
+
if (!(0, import_fs.existsSync)(entryPath)) return null;
|
|
40
|
+
let esbuild;
|
|
41
|
+
try {
|
|
42
|
+
esbuild = await import("esbuild");
|
|
43
|
+
} catch {
|
|
28
44
|
try {
|
|
29
|
-
return await import(
|
|
45
|
+
return await import(entryPath);
|
|
30
46
|
} catch {
|
|
31
47
|
return null;
|
|
32
48
|
}
|
|
33
|
-
}
|
|
49
|
+
}
|
|
50
|
+
const outFile = (0, import_path.join)((0, import_os.tmpdir)(), `brender_${(0, import_crypto.randomBytes)(8).toString("hex")}.mjs`);
|
|
51
|
+
try {
|
|
52
|
+
await esbuild.build({
|
|
53
|
+
entryPoints: [entryPath],
|
|
54
|
+
bundle: true,
|
|
55
|
+
format: "esm",
|
|
56
|
+
platform: "node",
|
|
57
|
+
outfile: outFile,
|
|
58
|
+
write: true,
|
|
59
|
+
logLevel: "silent",
|
|
60
|
+
// Mark node builtins as external
|
|
61
|
+
external: ["fs", "path", "os", "crypto", "url", "http", "https", "stream", "util", "events", "buffer", "child_process", "worker_threads", "net", "tls", "dns", "dgram", "zlib", "assert", "querystring", "string_decoder", "readline", "perf_hooks", "async_hooks", "v8", "vm", "cluster", "inspector", "module", "process", "tty"]
|
|
62
|
+
});
|
|
63
|
+
const mod = await import(`file://${outFile}`);
|
|
64
|
+
return mod;
|
|
65
|
+
} catch {
|
|
66
|
+
try {
|
|
67
|
+
return await import(entryPath);
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
try {
|
|
73
|
+
(0, import_fs.unlinkSync)(outFile);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const loadProject = async (projectPath) => {
|
|
79
|
+
const symbolsDir = (0, import_path.resolve)(projectPath, "symbols");
|
|
34
80
|
const [
|
|
35
81
|
appModule,
|
|
36
82
|
stateModule,
|
|
@@ -44,17 +90,17 @@ const loadProject = async (projectPath) => {
|
|
|
44
90
|
designSystemModule,
|
|
45
91
|
filesModule
|
|
46
92
|
] = await Promise.all([
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
93
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "app.js")),
|
|
94
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "state.js")),
|
|
95
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "config.js")),
|
|
96
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "dependencies.js")),
|
|
97
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "components", "index.js")),
|
|
98
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "snippets", "index.js")),
|
|
99
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "pages", "index.js")),
|
|
100
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "functions", "index.js")),
|
|
101
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "methods", "index.js")),
|
|
102
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "designSystem", "index.js")),
|
|
103
|
+
bundleAndImport((0, import_path.join)(symbolsDir, "files", "index.js"))
|
|
58
104
|
]);
|
|
59
105
|
return {
|
|
60
106
|
app: appModule?.default || {},
|
package/dist/cjs/render.js
CHANGED
|
@@ -61,7 +61,13 @@ const UIKIT_STUBS = {
|
|
|
61
61
|
Img: {
|
|
62
62
|
tag: "img",
|
|
63
63
|
attr: {
|
|
64
|
-
src: (el) =>
|
|
64
|
+
src: (el) => {
|
|
65
|
+
let src = el.props?.src;
|
|
66
|
+
if (typeof src === "string" && src.includes("{{")) {
|
|
67
|
+
src = el.call("replaceLiteralsWithObjectFields", src, el.state);
|
|
68
|
+
}
|
|
69
|
+
return src;
|
|
70
|
+
},
|
|
65
71
|
alt: (el) => el.props?.alt,
|
|
66
72
|
loading: (el) => el.props?.loading
|
|
67
73
|
}
|
|
@@ -127,13 +133,23 @@ const renderElement = async (elementDef, options = {}) => {
|
|
|
127
133
|
const { window, document } = (0, import_env.createEnv)();
|
|
128
134
|
const body = document.body;
|
|
129
135
|
const { create } = await import("@domql/element");
|
|
136
|
+
const domqlUtils = await import("@domql/utils");
|
|
130
137
|
const components = { ...UIKIT_STUBS, ...context.components || {} };
|
|
138
|
+
const utils = {
|
|
139
|
+
...domqlUtils,
|
|
140
|
+
...context.utils || {},
|
|
141
|
+
...context.functions || {}
|
|
142
|
+
};
|
|
131
143
|
(0, import_keys.resetKeys)();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
let element;
|
|
145
|
+
try {
|
|
146
|
+
element = create(elementDef, { node: body }, "root", {
|
|
147
|
+
context: { document, window, ...context, components, utils }
|
|
148
|
+
});
|
|
149
|
+
} catch (err) {
|
|
150
|
+
}
|
|
135
151
|
(0, import_keys.assignKeys)(body);
|
|
136
|
-
const registry = (0, import_keys.mapKeysToElements)(element);
|
|
152
|
+
const registry = element ? (0, import_keys.mapKeysToElements)(element) : {};
|
|
137
153
|
const html = body.innerHTML;
|
|
138
154
|
return { html, registry, element };
|
|
139
155
|
};
|
|
@@ -198,6 +214,138 @@ ${result.html}
|
|
|
198
214
|
</html>`;
|
|
199
215
|
return { html, route, brKeyCount: result.brKeyCount };
|
|
200
216
|
};
|
|
217
|
+
const LETTER_TO_INDEX = {
|
|
218
|
+
U: -6,
|
|
219
|
+
V: -5,
|
|
220
|
+
W: -4,
|
|
221
|
+
X: -3,
|
|
222
|
+
Y: -2,
|
|
223
|
+
Z: -1,
|
|
224
|
+
A: 0,
|
|
225
|
+
B: 1,
|
|
226
|
+
C: 2,
|
|
227
|
+
D: 3,
|
|
228
|
+
E: 4,
|
|
229
|
+
F: 5,
|
|
230
|
+
G: 6,
|
|
231
|
+
H: 7,
|
|
232
|
+
I: 8,
|
|
233
|
+
J: 9,
|
|
234
|
+
K: 10,
|
|
235
|
+
L: 11,
|
|
236
|
+
M: 12,
|
|
237
|
+
N: 13,
|
|
238
|
+
O: 14,
|
|
239
|
+
P: 15
|
|
240
|
+
};
|
|
241
|
+
const SPACING_PROPS = /* @__PURE__ */ new Set([
|
|
242
|
+
"padding",
|
|
243
|
+
"paddingTop",
|
|
244
|
+
"paddingRight",
|
|
245
|
+
"paddingBottom",
|
|
246
|
+
"paddingLeft",
|
|
247
|
+
"paddingBlock",
|
|
248
|
+
"paddingInline",
|
|
249
|
+
"paddingBlockStart",
|
|
250
|
+
"paddingBlockEnd",
|
|
251
|
+
"paddingInlineStart",
|
|
252
|
+
"paddingInlineEnd",
|
|
253
|
+
"margin",
|
|
254
|
+
"marginTop",
|
|
255
|
+
"marginRight",
|
|
256
|
+
"marginBottom",
|
|
257
|
+
"marginLeft",
|
|
258
|
+
"marginBlock",
|
|
259
|
+
"marginInline",
|
|
260
|
+
"marginBlockStart",
|
|
261
|
+
"marginBlockEnd",
|
|
262
|
+
"marginInlineStart",
|
|
263
|
+
"marginInlineEnd",
|
|
264
|
+
"gap",
|
|
265
|
+
"rowGap",
|
|
266
|
+
"columnGap",
|
|
267
|
+
"top",
|
|
268
|
+
"right",
|
|
269
|
+
"bottom",
|
|
270
|
+
"left",
|
|
271
|
+
"width",
|
|
272
|
+
"height",
|
|
273
|
+
"minWidth",
|
|
274
|
+
"maxWidth",
|
|
275
|
+
"minHeight",
|
|
276
|
+
"maxHeight",
|
|
277
|
+
"flexBasis",
|
|
278
|
+
"fontSize",
|
|
279
|
+
"lineHeight",
|
|
280
|
+
"letterSpacing",
|
|
281
|
+
"borderWidth",
|
|
282
|
+
"borderRadius",
|
|
283
|
+
"outlineWidth",
|
|
284
|
+
"outlineOffset",
|
|
285
|
+
"inset",
|
|
286
|
+
"insetBlock",
|
|
287
|
+
"insetInline",
|
|
288
|
+
"boxSize",
|
|
289
|
+
"round"
|
|
290
|
+
]);
|
|
291
|
+
const resolveSpacingToken = (token, spacingConfig) => {
|
|
292
|
+
if (!token || typeof token !== "string") return null;
|
|
293
|
+
if (!spacingConfig) return null;
|
|
294
|
+
const base = spacingConfig.base || 16;
|
|
295
|
+
const ratio = spacingConfig.ratio || 1.618;
|
|
296
|
+
const unit = spacingConfig.unit || "px";
|
|
297
|
+
const hasSubSequence = spacingConfig.subSequence !== false;
|
|
298
|
+
if (token.includes(" ")) {
|
|
299
|
+
const parts = token.split(" ").map((part) => {
|
|
300
|
+
if (part === "-" || part === "") return part;
|
|
301
|
+
return resolveSpacingToken(part, spacingConfig) || part;
|
|
302
|
+
});
|
|
303
|
+
return parts.join(" ");
|
|
304
|
+
}
|
|
305
|
+
if (/^(none|auto|inherit|initial|unset|0)$/i.test(token)) return null;
|
|
306
|
+
if (/\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|fr|s|ms)$/i.test(token)) return null;
|
|
307
|
+
if (/^(#|rgb|hsl|var\()/i.test(token)) return null;
|
|
308
|
+
const isNegative = token.startsWith("-");
|
|
309
|
+
const abs = isNegative ? token.slice(1) : token;
|
|
310
|
+
const m = abs.match(/^([A-Z])(\d)?$/i);
|
|
311
|
+
if (!m) return null;
|
|
312
|
+
const letter = m[1].toUpperCase();
|
|
313
|
+
const subStep = m[2] ? parseInt(m[2]) : 0;
|
|
314
|
+
const idx = LETTER_TO_INDEX[letter];
|
|
315
|
+
if (idx === void 0) return null;
|
|
316
|
+
let value = base * Math.pow(ratio, idx);
|
|
317
|
+
if (subStep > 0 && hasSubSequence) {
|
|
318
|
+
const next = base * Math.pow(ratio, idx + 1);
|
|
319
|
+
const diff = next - value;
|
|
320
|
+
const subRatio = diff / ratio;
|
|
321
|
+
const first = next - subRatio;
|
|
322
|
+
const second = value + subRatio;
|
|
323
|
+
const middle = (first + second) / 2;
|
|
324
|
+
const subs = ~~next - ~~value > 16 ? [first, middle, second] : [first, second];
|
|
325
|
+
if (subStep <= subs.length) {
|
|
326
|
+
value = subs[subStep - 1];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const rounded = Math.round(value * 100) / 100;
|
|
330
|
+
const sign = isNegative ? "-" : "";
|
|
331
|
+
return `${sign}${rounded}${unit}`;
|
|
332
|
+
};
|
|
333
|
+
const SPACING_PROPS_KEBAB = new Set(
|
|
334
|
+
[...SPACING_PROPS].map((k) => k.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()))
|
|
335
|
+
);
|
|
336
|
+
const resolveDSValue = (key, val, ds) => {
|
|
337
|
+
if (typeof val !== "string") return val;
|
|
338
|
+
if (CSS_COLOR_PROPS.has(key)) {
|
|
339
|
+
const colorMap = ds?.color || {};
|
|
340
|
+
if (colorMap[val]) return colorMap[val];
|
|
341
|
+
}
|
|
342
|
+
if (SPACING_PROPS.has(key) || SPACING_PROPS_KEBAB.has(key)) {
|
|
343
|
+
const spacing = ds?.spacing || {};
|
|
344
|
+
const resolved = resolveSpacingToken(val, spacing);
|
|
345
|
+
if (resolved) return resolved;
|
|
346
|
+
}
|
|
347
|
+
return val;
|
|
348
|
+
};
|
|
201
349
|
const CSS_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
202
350
|
"color",
|
|
203
351
|
"background",
|
|
@@ -237,7 +385,11 @@ const NON_CSS_PROPS = /* @__PURE__ */ new Set([
|
|
|
237
385
|
"autofocus",
|
|
238
386
|
"theme",
|
|
239
387
|
"__element",
|
|
240
|
-
"update"
|
|
388
|
+
"update",
|
|
389
|
+
"childrenAs",
|
|
390
|
+
"childExtends",
|
|
391
|
+
"childProps",
|
|
392
|
+
"children"
|
|
241
393
|
]);
|
|
242
394
|
const camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
243
395
|
const resolveShorthand = (key, val) => {
|
|
@@ -253,11 +405,15 @@ const resolveShorthand = (key, val) => {
|
|
|
253
405
|
}
|
|
254
406
|
if ((key === "align" || key === "flexAlign") && typeof val === "string") {
|
|
255
407
|
const [alignItems, justifyContent] = val.split(" ");
|
|
256
|
-
|
|
408
|
+
const result = { display: "flex", "align-items": alignItems };
|
|
409
|
+
if (justifyContent) result["justify-content"] = justifyContent;
|
|
410
|
+
return result;
|
|
257
411
|
}
|
|
258
412
|
if (key === "gridAlign" && typeof val === "string") {
|
|
259
413
|
const [alignItems, justifyContent] = val.split(" ");
|
|
260
|
-
|
|
414
|
+
const result = { display: "grid", "align-items": alignItems };
|
|
415
|
+
if (justifyContent) result["justify-content"] = justifyContent;
|
|
416
|
+
return result;
|
|
261
417
|
}
|
|
262
418
|
if (key === "flexFlow" && typeof val === "string") {
|
|
263
419
|
let [direction, wrap] = (val || "row").split(" ");
|
|
@@ -268,6 +424,9 @@ const resolveShorthand = (key, val) => {
|
|
|
268
424
|
if (key === "flexWrap") {
|
|
269
425
|
return { display: "flex", "flex-wrap": val };
|
|
270
426
|
}
|
|
427
|
+
if (key === "backgroundImage" && typeof val === "string" && !val.startsWith("url(") && !val.startsWith("linear-gradient") && !val.startsWith("radial-gradient") && !val.startsWith("none")) {
|
|
428
|
+
return { "background-image": `url(${val})` };
|
|
429
|
+
}
|
|
271
430
|
if (key === "round" || key === "borderRadius" && val) {
|
|
272
431
|
return { "border-radius": typeof val === "number" ? val + "px" : val };
|
|
273
432
|
}
|
|
@@ -299,21 +458,23 @@ const resolveShorthand = (key, val) => {
|
|
|
299
458
|
if (key === "rowStart") return { "grid-row-start": val };
|
|
300
459
|
return null;
|
|
301
460
|
};
|
|
302
|
-
const resolveInnerProps = (obj,
|
|
461
|
+
const resolveInnerProps = (obj, ds) => {
|
|
303
462
|
const result = {};
|
|
304
463
|
for (const k in obj) {
|
|
305
464
|
const v = obj[k];
|
|
306
465
|
const expanded = resolveShorthand(k, v);
|
|
307
466
|
if (expanded) {
|
|
308
|
-
|
|
467
|
+
for (const ek in expanded) {
|
|
468
|
+
result[ek] = resolveDSValue(ek, expanded[ek], ds);
|
|
469
|
+
}
|
|
309
470
|
continue;
|
|
310
471
|
}
|
|
311
472
|
if (typeof v !== "string" && typeof v !== "number") continue;
|
|
312
|
-
result[camelToKebab(k)] =
|
|
473
|
+
result[camelToKebab(k)] = resolveDSValue(k, v, ds);
|
|
313
474
|
}
|
|
314
475
|
return result;
|
|
315
476
|
};
|
|
316
|
-
const buildCSSFromProps = (props,
|
|
477
|
+
const buildCSSFromProps = (props, ds, mediaMap) => {
|
|
317
478
|
const base = {};
|
|
318
479
|
const mediaRules = {};
|
|
319
480
|
const pseudoRules = {};
|
|
@@ -322,13 +483,13 @@ const buildCSSFromProps = (props, colorMap, mediaMap) => {
|
|
|
322
483
|
if (key.charCodeAt(0) === 64 && typeof val === "object") {
|
|
323
484
|
const bp = mediaMap?.[key.slice(1)];
|
|
324
485
|
if (bp) {
|
|
325
|
-
const inner = resolveInnerProps(val,
|
|
486
|
+
const inner = resolveInnerProps(val, ds);
|
|
326
487
|
if (Object.keys(inner).length) mediaRules[bp] = inner;
|
|
327
488
|
}
|
|
328
489
|
continue;
|
|
329
490
|
}
|
|
330
491
|
if (key.charCodeAt(0) === 58 && typeof val === "object") {
|
|
331
|
-
const inner = resolveInnerProps(val,
|
|
492
|
+
const inner = resolveInnerProps(val, ds);
|
|
332
493
|
if (Object.keys(inner).length) pseudoRules[key] = inner;
|
|
333
494
|
continue;
|
|
334
495
|
}
|
|
@@ -337,10 +498,12 @@ const buildCSSFromProps = (props, colorMap, mediaMap) => {
|
|
|
337
498
|
if (NON_CSS_PROPS.has(key)) continue;
|
|
338
499
|
const expanded = resolveShorthand(key, val);
|
|
339
500
|
if (expanded) {
|
|
340
|
-
|
|
501
|
+
for (const ek in expanded) {
|
|
502
|
+
base[ek] = resolveDSValue(ek, expanded[ek], ds);
|
|
503
|
+
}
|
|
341
504
|
continue;
|
|
342
505
|
}
|
|
343
|
-
base[camelToKebab(key)] =
|
|
506
|
+
base[camelToKebab(key)] = resolveDSValue(key, val, ds);
|
|
344
507
|
}
|
|
345
508
|
return { base, mediaRules, pseudoRules };
|
|
346
509
|
};
|
|
@@ -376,7 +539,6 @@ const getExtendsCSS = (el) => {
|
|
|
376
539
|
return null;
|
|
377
540
|
};
|
|
378
541
|
const extractCSS = (element, ds) => {
|
|
379
|
-
const colorMap = ds?.color || {};
|
|
380
542
|
const mediaMap = ds?.media || {};
|
|
381
543
|
const animations = ds?.animation || {};
|
|
382
544
|
const rules = [];
|
|
@@ -389,7 +551,7 @@ const extractCSS = (element, ds) => {
|
|
|
389
551
|
const cls = el.node.getAttribute?.("class");
|
|
390
552
|
if (cls && !seen.has(cls)) {
|
|
391
553
|
seen.add(cls);
|
|
392
|
-
const cssResult = buildCSSFromProps(props,
|
|
554
|
+
const cssResult = buildCSSFromProps(props, ds, mediaMap);
|
|
393
555
|
const extsCss = getExtendsCSS(el);
|
|
394
556
|
if (extsCss) {
|
|
395
557
|
for (const [k, v] of Object.entries(extsCss)) {
|
|
@@ -430,6 +592,7 @@ const generateResetCSS = (reset) => {
|
|
|
430
592
|
if (!reset) return "";
|
|
431
593
|
const rules = [];
|
|
432
594
|
for (const [selector, props] of Object.entries(reset)) {
|
|
595
|
+
if (!props || typeof props !== "object") continue;
|
|
433
596
|
const decls = Object.entries(props).map(([k, v]) => `${camelToKebab(k)}: ${v}`).join("; ");
|
|
434
597
|
if (decls) rules.push(`${selector} { ${decls}; }`);
|
|
435
598
|
}
|
|
@@ -440,6 +603,7 @@ const generateFontLinks = (ds) => {
|
|
|
440
603
|
const families = ds.font_family || ds.fontFamily || {};
|
|
441
604
|
const fontNames = /* @__PURE__ */ new Set();
|
|
442
605
|
for (const val of Object.values(families)) {
|
|
606
|
+
if (typeof val !== "string") continue;
|
|
443
607
|
const match = val.match(/'([^']+)'/);
|
|
444
608
|
if (match) fontNames.add(match[1]);
|
|
445
609
|
}
|
package/dist/esm/env.js
CHANGED
|
@@ -26,6 +26,29 @@ const createEnv = (html = "<!DOCTYPE html><html><head></head><body></body></html
|
|
|
26
26
|
window.scrollTo = () => {
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
+
const createStorage = () => {
|
|
30
|
+
const store = {};
|
|
31
|
+
return {
|
|
32
|
+
getItem: (k) => store[k] ?? null,
|
|
33
|
+
setItem: (k, v) => {
|
|
34
|
+
store[k] = String(v);
|
|
35
|
+
},
|
|
36
|
+
removeItem: (k) => {
|
|
37
|
+
delete store[k];
|
|
38
|
+
},
|
|
39
|
+
clear: () => {
|
|
40
|
+
for (const k in store) delete store[k];
|
|
41
|
+
},
|
|
42
|
+
get length() {
|
|
43
|
+
return Object.keys(store).length;
|
|
44
|
+
},
|
|
45
|
+
key: (i) => Object.keys(store)[i] ?? null
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
if (!window.localStorage) window.localStorage = createStorage();
|
|
49
|
+
if (!window.sessionStorage) window.sessionStorage = createStorage();
|
|
50
|
+
if (!globalThis.localStorage) globalThis.localStorage = window.localStorage;
|
|
51
|
+
if (!globalThis.sessionStorage) globalThis.sessionStorage = window.sessionStorage;
|
|
29
52
|
globalThis.window = window;
|
|
30
53
|
globalThis.document = document;
|
|
31
54
|
globalThis.Node = window.Node || globalThis.Node;
|
package/dist/esm/load.js
CHANGED
|
@@ -1,13 +1,49 @@
|
|
|
1
1
|
import { resolve, join } from "path";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { randomBytes } from "crypto";
|
|
5
|
+
const bundleAndImport = async (entryPath) => {
|
|
6
|
+
if (!existsSync(entryPath)) return null;
|
|
7
|
+
let esbuild;
|
|
8
|
+
try {
|
|
9
|
+
esbuild = await import("esbuild");
|
|
10
|
+
} catch {
|
|
5
11
|
try {
|
|
6
|
-
return await import(
|
|
12
|
+
return await import(entryPath);
|
|
7
13
|
} catch {
|
|
8
14
|
return null;
|
|
9
15
|
}
|
|
10
|
-
}
|
|
16
|
+
}
|
|
17
|
+
const outFile = join(tmpdir(), `brender_${randomBytes(8).toString("hex")}.mjs`);
|
|
18
|
+
try {
|
|
19
|
+
await esbuild.build({
|
|
20
|
+
entryPoints: [entryPath],
|
|
21
|
+
bundle: true,
|
|
22
|
+
format: "esm",
|
|
23
|
+
platform: "node",
|
|
24
|
+
outfile: outFile,
|
|
25
|
+
write: true,
|
|
26
|
+
logLevel: "silent",
|
|
27
|
+
// Mark node builtins as external
|
|
28
|
+
external: ["fs", "path", "os", "crypto", "url", "http", "https", "stream", "util", "events", "buffer", "child_process", "worker_threads", "net", "tls", "dns", "dgram", "zlib", "assert", "querystring", "string_decoder", "readline", "perf_hooks", "async_hooks", "v8", "vm", "cluster", "inspector", "module", "process", "tty"]
|
|
29
|
+
});
|
|
30
|
+
const mod = await import(`file://${outFile}`);
|
|
31
|
+
return mod;
|
|
32
|
+
} catch {
|
|
33
|
+
try {
|
|
34
|
+
return await import(entryPath);
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
} finally {
|
|
39
|
+
try {
|
|
40
|
+
unlinkSync(outFile);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const loadProject = async (projectPath) => {
|
|
46
|
+
const symbolsDir = resolve(projectPath, "symbols");
|
|
11
47
|
const [
|
|
12
48
|
appModule,
|
|
13
49
|
stateModule,
|
|
@@ -21,17 +57,17 @@ const loadProject = async (projectPath) => {
|
|
|
21
57
|
designSystemModule,
|
|
22
58
|
filesModule
|
|
23
59
|
] = await Promise.all([
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
60
|
+
bundleAndImport(join(symbolsDir, "app.js")),
|
|
61
|
+
bundleAndImport(join(symbolsDir, "state.js")),
|
|
62
|
+
bundleAndImport(join(symbolsDir, "config.js")),
|
|
63
|
+
bundleAndImport(join(symbolsDir, "dependencies.js")),
|
|
64
|
+
bundleAndImport(join(symbolsDir, "components", "index.js")),
|
|
65
|
+
bundleAndImport(join(symbolsDir, "snippets", "index.js")),
|
|
66
|
+
bundleAndImport(join(symbolsDir, "pages", "index.js")),
|
|
67
|
+
bundleAndImport(join(symbolsDir, "functions", "index.js")),
|
|
68
|
+
bundleAndImport(join(symbolsDir, "methods", "index.js")),
|
|
69
|
+
bundleAndImport(join(symbolsDir, "designSystem", "index.js")),
|
|
70
|
+
bundleAndImport(join(symbolsDir, "files", "index.js"))
|
|
35
71
|
]);
|
|
36
72
|
return {
|
|
37
73
|
app: appModule?.default || {},
|
package/dist/esm/render.js
CHANGED
|
@@ -25,7 +25,13 @@ const UIKIT_STUBS = {
|
|
|
25
25
|
Img: {
|
|
26
26
|
tag: "img",
|
|
27
27
|
attr: {
|
|
28
|
-
src: (el) =>
|
|
28
|
+
src: (el) => {
|
|
29
|
+
let src = el.props?.src;
|
|
30
|
+
if (typeof src === "string" && src.includes("{{")) {
|
|
31
|
+
src = el.call("replaceLiteralsWithObjectFields", src, el.state);
|
|
32
|
+
}
|
|
33
|
+
return src;
|
|
34
|
+
},
|
|
29
35
|
alt: (el) => el.props?.alt,
|
|
30
36
|
loading: (el) => el.props?.loading
|
|
31
37
|
}
|
|
@@ -91,13 +97,23 @@ const renderElement = async (elementDef, options = {}) => {
|
|
|
91
97
|
const { window, document } = createEnv();
|
|
92
98
|
const body = document.body;
|
|
93
99
|
const { create } = await import("@domql/element");
|
|
100
|
+
const domqlUtils = await import("@domql/utils");
|
|
94
101
|
const components = { ...UIKIT_STUBS, ...context.components || {} };
|
|
102
|
+
const utils = {
|
|
103
|
+
...domqlUtils,
|
|
104
|
+
...context.utils || {},
|
|
105
|
+
...context.functions || {}
|
|
106
|
+
};
|
|
95
107
|
resetKeys();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
let element;
|
|
109
|
+
try {
|
|
110
|
+
element = create(elementDef, { node: body }, "root", {
|
|
111
|
+
context: { document, window, ...context, components, utils }
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
}
|
|
99
115
|
assignKeys(body);
|
|
100
|
-
const registry = mapKeysToElements(element);
|
|
116
|
+
const registry = element ? mapKeysToElements(element) : {};
|
|
101
117
|
const html = body.innerHTML;
|
|
102
118
|
return { html, registry, element };
|
|
103
119
|
};
|
|
@@ -162,6 +178,138 @@ ${result.html}
|
|
|
162
178
|
</html>`;
|
|
163
179
|
return { html, route, brKeyCount: result.brKeyCount };
|
|
164
180
|
};
|
|
181
|
+
const LETTER_TO_INDEX = {
|
|
182
|
+
U: -6,
|
|
183
|
+
V: -5,
|
|
184
|
+
W: -4,
|
|
185
|
+
X: -3,
|
|
186
|
+
Y: -2,
|
|
187
|
+
Z: -1,
|
|
188
|
+
A: 0,
|
|
189
|
+
B: 1,
|
|
190
|
+
C: 2,
|
|
191
|
+
D: 3,
|
|
192
|
+
E: 4,
|
|
193
|
+
F: 5,
|
|
194
|
+
G: 6,
|
|
195
|
+
H: 7,
|
|
196
|
+
I: 8,
|
|
197
|
+
J: 9,
|
|
198
|
+
K: 10,
|
|
199
|
+
L: 11,
|
|
200
|
+
M: 12,
|
|
201
|
+
N: 13,
|
|
202
|
+
O: 14,
|
|
203
|
+
P: 15
|
|
204
|
+
};
|
|
205
|
+
const SPACING_PROPS = /* @__PURE__ */ new Set([
|
|
206
|
+
"padding",
|
|
207
|
+
"paddingTop",
|
|
208
|
+
"paddingRight",
|
|
209
|
+
"paddingBottom",
|
|
210
|
+
"paddingLeft",
|
|
211
|
+
"paddingBlock",
|
|
212
|
+
"paddingInline",
|
|
213
|
+
"paddingBlockStart",
|
|
214
|
+
"paddingBlockEnd",
|
|
215
|
+
"paddingInlineStart",
|
|
216
|
+
"paddingInlineEnd",
|
|
217
|
+
"margin",
|
|
218
|
+
"marginTop",
|
|
219
|
+
"marginRight",
|
|
220
|
+
"marginBottom",
|
|
221
|
+
"marginLeft",
|
|
222
|
+
"marginBlock",
|
|
223
|
+
"marginInline",
|
|
224
|
+
"marginBlockStart",
|
|
225
|
+
"marginBlockEnd",
|
|
226
|
+
"marginInlineStart",
|
|
227
|
+
"marginInlineEnd",
|
|
228
|
+
"gap",
|
|
229
|
+
"rowGap",
|
|
230
|
+
"columnGap",
|
|
231
|
+
"top",
|
|
232
|
+
"right",
|
|
233
|
+
"bottom",
|
|
234
|
+
"left",
|
|
235
|
+
"width",
|
|
236
|
+
"height",
|
|
237
|
+
"minWidth",
|
|
238
|
+
"maxWidth",
|
|
239
|
+
"minHeight",
|
|
240
|
+
"maxHeight",
|
|
241
|
+
"flexBasis",
|
|
242
|
+
"fontSize",
|
|
243
|
+
"lineHeight",
|
|
244
|
+
"letterSpacing",
|
|
245
|
+
"borderWidth",
|
|
246
|
+
"borderRadius",
|
|
247
|
+
"outlineWidth",
|
|
248
|
+
"outlineOffset",
|
|
249
|
+
"inset",
|
|
250
|
+
"insetBlock",
|
|
251
|
+
"insetInline",
|
|
252
|
+
"boxSize",
|
|
253
|
+
"round"
|
|
254
|
+
]);
|
|
255
|
+
const resolveSpacingToken = (token, spacingConfig) => {
|
|
256
|
+
if (!token || typeof token !== "string") return null;
|
|
257
|
+
if (!spacingConfig) return null;
|
|
258
|
+
const base = spacingConfig.base || 16;
|
|
259
|
+
const ratio = spacingConfig.ratio || 1.618;
|
|
260
|
+
const unit = spacingConfig.unit || "px";
|
|
261
|
+
const hasSubSequence = spacingConfig.subSequence !== false;
|
|
262
|
+
if (token.includes(" ")) {
|
|
263
|
+
const parts = token.split(" ").map((part) => {
|
|
264
|
+
if (part === "-" || part === "") return part;
|
|
265
|
+
return resolveSpacingToken(part, spacingConfig) || part;
|
|
266
|
+
});
|
|
267
|
+
return parts.join(" ");
|
|
268
|
+
}
|
|
269
|
+
if (/^(none|auto|inherit|initial|unset|0)$/i.test(token)) return null;
|
|
270
|
+
if (/\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|fr|s|ms)$/i.test(token)) return null;
|
|
271
|
+
if (/^(#|rgb|hsl|var\()/i.test(token)) return null;
|
|
272
|
+
const isNegative = token.startsWith("-");
|
|
273
|
+
const abs = isNegative ? token.slice(1) : token;
|
|
274
|
+
const m = abs.match(/^([A-Z])(\d)?$/i);
|
|
275
|
+
if (!m) return null;
|
|
276
|
+
const letter = m[1].toUpperCase();
|
|
277
|
+
const subStep = m[2] ? parseInt(m[2]) : 0;
|
|
278
|
+
const idx = LETTER_TO_INDEX[letter];
|
|
279
|
+
if (idx === void 0) return null;
|
|
280
|
+
let value = base * Math.pow(ratio, idx);
|
|
281
|
+
if (subStep > 0 && hasSubSequence) {
|
|
282
|
+
const next = base * Math.pow(ratio, idx + 1);
|
|
283
|
+
const diff = next - value;
|
|
284
|
+
const subRatio = diff / ratio;
|
|
285
|
+
const first = next - subRatio;
|
|
286
|
+
const second = value + subRatio;
|
|
287
|
+
const middle = (first + second) / 2;
|
|
288
|
+
const subs = ~~next - ~~value > 16 ? [first, middle, second] : [first, second];
|
|
289
|
+
if (subStep <= subs.length) {
|
|
290
|
+
value = subs[subStep - 1];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const rounded = Math.round(value * 100) / 100;
|
|
294
|
+
const sign = isNegative ? "-" : "";
|
|
295
|
+
return `${sign}${rounded}${unit}`;
|
|
296
|
+
};
|
|
297
|
+
const SPACING_PROPS_KEBAB = new Set(
|
|
298
|
+
[...SPACING_PROPS].map((k) => k.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()))
|
|
299
|
+
);
|
|
300
|
+
const resolveDSValue = (key, val, ds) => {
|
|
301
|
+
if (typeof val !== "string") return val;
|
|
302
|
+
if (CSS_COLOR_PROPS.has(key)) {
|
|
303
|
+
const colorMap = ds?.color || {};
|
|
304
|
+
if (colorMap[val]) return colorMap[val];
|
|
305
|
+
}
|
|
306
|
+
if (SPACING_PROPS.has(key) || SPACING_PROPS_KEBAB.has(key)) {
|
|
307
|
+
const spacing = ds?.spacing || {};
|
|
308
|
+
const resolved = resolveSpacingToken(val, spacing);
|
|
309
|
+
if (resolved) return resolved;
|
|
310
|
+
}
|
|
311
|
+
return val;
|
|
312
|
+
};
|
|
165
313
|
const CSS_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
166
314
|
"color",
|
|
167
315
|
"background",
|
|
@@ -201,7 +349,11 @@ const NON_CSS_PROPS = /* @__PURE__ */ new Set([
|
|
|
201
349
|
"autofocus",
|
|
202
350
|
"theme",
|
|
203
351
|
"__element",
|
|
204
|
-
"update"
|
|
352
|
+
"update",
|
|
353
|
+
"childrenAs",
|
|
354
|
+
"childExtends",
|
|
355
|
+
"childProps",
|
|
356
|
+
"children"
|
|
205
357
|
]);
|
|
206
358
|
const camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
|
207
359
|
const resolveShorthand = (key, val) => {
|
|
@@ -217,11 +369,15 @@ const resolveShorthand = (key, val) => {
|
|
|
217
369
|
}
|
|
218
370
|
if ((key === "align" || key === "flexAlign") && typeof val === "string") {
|
|
219
371
|
const [alignItems, justifyContent] = val.split(" ");
|
|
220
|
-
|
|
372
|
+
const result = { display: "flex", "align-items": alignItems };
|
|
373
|
+
if (justifyContent) result["justify-content"] = justifyContent;
|
|
374
|
+
return result;
|
|
221
375
|
}
|
|
222
376
|
if (key === "gridAlign" && typeof val === "string") {
|
|
223
377
|
const [alignItems, justifyContent] = val.split(" ");
|
|
224
|
-
|
|
378
|
+
const result = { display: "grid", "align-items": alignItems };
|
|
379
|
+
if (justifyContent) result["justify-content"] = justifyContent;
|
|
380
|
+
return result;
|
|
225
381
|
}
|
|
226
382
|
if (key === "flexFlow" && typeof val === "string") {
|
|
227
383
|
let [direction, wrap] = (val || "row").split(" ");
|
|
@@ -232,6 +388,9 @@ const resolveShorthand = (key, val) => {
|
|
|
232
388
|
if (key === "flexWrap") {
|
|
233
389
|
return { display: "flex", "flex-wrap": val };
|
|
234
390
|
}
|
|
391
|
+
if (key === "backgroundImage" && typeof val === "string" && !val.startsWith("url(") && !val.startsWith("linear-gradient") && !val.startsWith("radial-gradient") && !val.startsWith("none")) {
|
|
392
|
+
return { "background-image": `url(${val})` };
|
|
393
|
+
}
|
|
235
394
|
if (key === "round" || key === "borderRadius" && val) {
|
|
236
395
|
return { "border-radius": typeof val === "number" ? val + "px" : val };
|
|
237
396
|
}
|
|
@@ -263,21 +422,23 @@ const resolveShorthand = (key, val) => {
|
|
|
263
422
|
if (key === "rowStart") return { "grid-row-start": val };
|
|
264
423
|
return null;
|
|
265
424
|
};
|
|
266
|
-
const resolveInnerProps = (obj,
|
|
425
|
+
const resolveInnerProps = (obj, ds) => {
|
|
267
426
|
const result = {};
|
|
268
427
|
for (const k in obj) {
|
|
269
428
|
const v = obj[k];
|
|
270
429
|
const expanded = resolveShorthand(k, v);
|
|
271
430
|
if (expanded) {
|
|
272
|
-
|
|
431
|
+
for (const ek in expanded) {
|
|
432
|
+
result[ek] = resolveDSValue(ek, expanded[ek], ds);
|
|
433
|
+
}
|
|
273
434
|
continue;
|
|
274
435
|
}
|
|
275
436
|
if (typeof v !== "string" && typeof v !== "number") continue;
|
|
276
|
-
result[camelToKebab(k)] =
|
|
437
|
+
result[camelToKebab(k)] = resolveDSValue(k, v, ds);
|
|
277
438
|
}
|
|
278
439
|
return result;
|
|
279
440
|
};
|
|
280
|
-
const buildCSSFromProps = (props,
|
|
441
|
+
const buildCSSFromProps = (props, ds, mediaMap) => {
|
|
281
442
|
const base = {};
|
|
282
443
|
const mediaRules = {};
|
|
283
444
|
const pseudoRules = {};
|
|
@@ -286,13 +447,13 @@ const buildCSSFromProps = (props, colorMap, mediaMap) => {
|
|
|
286
447
|
if (key.charCodeAt(0) === 64 && typeof val === "object") {
|
|
287
448
|
const bp = mediaMap?.[key.slice(1)];
|
|
288
449
|
if (bp) {
|
|
289
|
-
const inner = resolveInnerProps(val,
|
|
450
|
+
const inner = resolveInnerProps(val, ds);
|
|
290
451
|
if (Object.keys(inner).length) mediaRules[bp] = inner;
|
|
291
452
|
}
|
|
292
453
|
continue;
|
|
293
454
|
}
|
|
294
455
|
if (key.charCodeAt(0) === 58 && typeof val === "object") {
|
|
295
|
-
const inner = resolveInnerProps(val,
|
|
456
|
+
const inner = resolveInnerProps(val, ds);
|
|
296
457
|
if (Object.keys(inner).length) pseudoRules[key] = inner;
|
|
297
458
|
continue;
|
|
298
459
|
}
|
|
@@ -301,10 +462,12 @@ const buildCSSFromProps = (props, colorMap, mediaMap) => {
|
|
|
301
462
|
if (NON_CSS_PROPS.has(key)) continue;
|
|
302
463
|
const expanded = resolveShorthand(key, val);
|
|
303
464
|
if (expanded) {
|
|
304
|
-
|
|
465
|
+
for (const ek in expanded) {
|
|
466
|
+
base[ek] = resolveDSValue(ek, expanded[ek], ds);
|
|
467
|
+
}
|
|
305
468
|
continue;
|
|
306
469
|
}
|
|
307
|
-
base[camelToKebab(key)] =
|
|
470
|
+
base[camelToKebab(key)] = resolveDSValue(key, val, ds);
|
|
308
471
|
}
|
|
309
472
|
return { base, mediaRules, pseudoRules };
|
|
310
473
|
};
|
|
@@ -340,7 +503,6 @@ const getExtendsCSS = (el) => {
|
|
|
340
503
|
return null;
|
|
341
504
|
};
|
|
342
505
|
const extractCSS = (element, ds) => {
|
|
343
|
-
const colorMap = ds?.color || {};
|
|
344
506
|
const mediaMap = ds?.media || {};
|
|
345
507
|
const animations = ds?.animation || {};
|
|
346
508
|
const rules = [];
|
|
@@ -353,7 +515,7 @@ const extractCSS = (element, ds) => {
|
|
|
353
515
|
const cls = el.node.getAttribute?.("class");
|
|
354
516
|
if (cls && !seen.has(cls)) {
|
|
355
517
|
seen.add(cls);
|
|
356
|
-
const cssResult = buildCSSFromProps(props,
|
|
518
|
+
const cssResult = buildCSSFromProps(props, ds, mediaMap);
|
|
357
519
|
const extsCss = getExtendsCSS(el);
|
|
358
520
|
if (extsCss) {
|
|
359
521
|
for (const [k, v] of Object.entries(extsCss)) {
|
|
@@ -394,6 +556,7 @@ const generateResetCSS = (reset) => {
|
|
|
394
556
|
if (!reset) return "";
|
|
395
557
|
const rules = [];
|
|
396
558
|
for (const [selector, props] of Object.entries(reset)) {
|
|
559
|
+
if (!props || typeof props !== "object") continue;
|
|
397
560
|
const decls = Object.entries(props).map(([k, v]) => `${camelToKebab(k)}: ${v}`).join("; ");
|
|
398
561
|
if (decls) rules.push(`${selector} { ${decls}; }`);
|
|
399
562
|
}
|
|
@@ -404,6 +567,7 @@ const generateFontLinks = (ds) => {
|
|
|
404
567
|
const families = ds.font_family || ds.fontFamily || {};
|
|
405
568
|
const fontNames = /* @__PURE__ */ new Set();
|
|
406
569
|
for (const val of Object.values(families)) {
|
|
570
|
+
if (typeof val !== "string") continue;
|
|
407
571
|
const match = val.match(/'([^']+)'/);
|
|
408
572
|
if (match) fontNames.add(match[1]);
|
|
409
573
|
}
|
package/env.js
CHANGED
|
@@ -31,6 +31,23 @@ export const createEnv = (html = '<!DOCTYPE html><html><head></head><body></body
|
|
|
31
31
|
window.scrollTo = () => {}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Storage stubs
|
|
35
|
+
const createStorage = () => {
|
|
36
|
+
const store = {}
|
|
37
|
+
return {
|
|
38
|
+
getItem: (k) => store[k] ?? null,
|
|
39
|
+
setItem: (k, v) => { store[k] = String(v) },
|
|
40
|
+
removeItem: (k) => { delete store[k] },
|
|
41
|
+
clear: () => { for (const k in store) delete store[k] },
|
|
42
|
+
get length () { return Object.keys(store).length },
|
|
43
|
+
key: (i) => Object.keys(store)[i] ?? null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!window.localStorage) window.localStorage = createStorage()
|
|
47
|
+
if (!window.sessionStorage) window.sessionStorage = createStorage()
|
|
48
|
+
if (!globalThis.localStorage) globalThis.localStorage = window.localStorage
|
|
49
|
+
if (!globalThis.sessionStorage) globalThis.sessionStorage = window.sessionStorage
|
|
50
|
+
|
|
34
51
|
// Expose linkedom constructors on globalThis so @domql/utils isDOMNode
|
|
35
52
|
// can use instanceof checks (it reads from globalThis.Node, etc.)
|
|
36
53
|
globalThis.window = window
|
package/load.js
CHANGED
|
@@ -1,9 +1,56 @@
|
|
|
1
1
|
import { resolve, join } from 'path'
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from 'fs'
|
|
3
|
+
import { tmpdir } from 'os'
|
|
4
|
+
import { randomBytes } from 'crypto'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Bundles a module entry point with esbuild so that extensionless imports,
|
|
8
|
+
* bare specifiers, and other bundler conventions resolve correctly.
|
|
9
|
+
* Returns the default + named exports of the bundled module, or null on failure.
|
|
10
|
+
*/
|
|
11
|
+
const bundleAndImport = async (entryPath) => {
|
|
12
|
+
if (!existsSync(entryPath)) return null
|
|
13
|
+
|
|
14
|
+
let esbuild
|
|
15
|
+
try {
|
|
16
|
+
esbuild = await import('esbuild')
|
|
17
|
+
} catch {
|
|
18
|
+
// Fallback: try raw import if esbuild is not available
|
|
19
|
+
try { return await import(entryPath) } catch { return null }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const outFile = join(tmpdir(), `brender_${randomBytes(8).toString('hex')}.mjs`)
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await esbuild.build({
|
|
26
|
+
entryPoints: [entryPath],
|
|
27
|
+
bundle: true,
|
|
28
|
+
format: 'esm',
|
|
29
|
+
platform: 'node',
|
|
30
|
+
outfile: outFile,
|
|
31
|
+
write: true,
|
|
32
|
+
logLevel: 'silent',
|
|
33
|
+
// Mark node builtins as external
|
|
34
|
+
external: ['fs', 'path', 'os', 'crypto', 'url', 'http', 'https', 'stream', 'util', 'events', 'buffer', 'child_process', 'worker_threads', 'net', 'tls', 'dns', 'dgram', 'zlib', 'assert', 'querystring', 'string_decoder', 'readline', 'perf_hooks', 'async_hooks', 'v8', 'vm', 'cluster', 'inspector', 'module', 'process', 'tty'],
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const mod = await import(`file://${outFile}`)
|
|
38
|
+
return mod
|
|
39
|
+
} catch {
|
|
40
|
+
// Fallback: try raw import
|
|
41
|
+
try { return await import(entryPath) } catch { return null }
|
|
42
|
+
} finally {
|
|
43
|
+
try { unlinkSync(outFile) } catch {}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
2
46
|
|
|
3
47
|
/**
|
|
4
48
|
* Loads a Symbols project from a filesystem path.
|
|
5
49
|
* Expects the standard symbols/ directory structure.
|
|
6
50
|
*
|
|
51
|
+
* Uses esbuild to bundle each module so that extensionless imports
|
|
52
|
+
* and other bundler conventions work in Node.js.
|
|
53
|
+
*
|
|
7
54
|
* Used for prebuild scenarios where brender runs locally
|
|
8
55
|
* against a project directory (e.g. `smbls build --prerender`).
|
|
9
56
|
*
|
|
@@ -13,14 +60,6 @@ import { resolve, join } from 'path'
|
|
|
13
60
|
export const loadProject = async (projectPath) => {
|
|
14
61
|
const symbolsDir = resolve(projectPath, 'symbols')
|
|
15
62
|
|
|
16
|
-
const tryImport = async (modulePath) => {
|
|
17
|
-
try {
|
|
18
|
-
return await import(modulePath)
|
|
19
|
-
} catch {
|
|
20
|
-
return null
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
63
|
const [
|
|
25
64
|
appModule,
|
|
26
65
|
stateModule,
|
|
@@ -34,17 +73,17 @@ export const loadProject = async (projectPath) => {
|
|
|
34
73
|
designSystemModule,
|
|
35
74
|
filesModule
|
|
36
75
|
] = await Promise.all([
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
bundleAndImport(join(symbolsDir, 'app.js')),
|
|
77
|
+
bundleAndImport(join(symbolsDir, 'state.js')),
|
|
78
|
+
bundleAndImport(join(symbolsDir, 'config.js')),
|
|
79
|
+
bundleAndImport(join(symbolsDir, 'dependencies.js')),
|
|
80
|
+
bundleAndImport(join(symbolsDir, 'components', 'index.js')),
|
|
81
|
+
bundleAndImport(join(symbolsDir, 'snippets', 'index.js')),
|
|
82
|
+
bundleAndImport(join(symbolsDir, 'pages', 'index.js')),
|
|
83
|
+
bundleAndImport(join(symbolsDir, 'functions', 'index.js')),
|
|
84
|
+
bundleAndImport(join(symbolsDir, 'methods', 'index.js')),
|
|
85
|
+
bundleAndImport(join(symbolsDir, 'designSystem', 'index.js')),
|
|
86
|
+
bundleAndImport(join(symbolsDir, 'files', 'index.js'))
|
|
48
87
|
])
|
|
49
88
|
|
|
50
89
|
return {
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/brender",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
7
7
|
"main": "./dist/cjs/index.js",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"import": "./
|
|
10
|
+
"import": "./index.js",
|
|
11
11
|
"require": "./dist/cjs/index.js",
|
|
12
|
-
"default": "./
|
|
12
|
+
"default": "./index.js"
|
|
13
13
|
},
|
|
14
14
|
"./hydrate": {
|
|
15
15
|
"import": "./hydrate.js",
|
package/render.js
CHANGED
|
@@ -29,7 +29,13 @@ const UIKIT_STUBS = {
|
|
|
29
29
|
Img: {
|
|
30
30
|
tag: 'img',
|
|
31
31
|
attr: {
|
|
32
|
-
src: (el) =>
|
|
32
|
+
src: (el) => {
|
|
33
|
+
let src = el.props?.src
|
|
34
|
+
if (typeof src === 'string' && src.includes('{{')) {
|
|
35
|
+
src = el.call('replaceLiteralsWithObjectFields', src, el.state)
|
|
36
|
+
}
|
|
37
|
+
return src
|
|
38
|
+
},
|
|
33
39
|
alt: (el) => el.props?.alt,
|
|
34
40
|
loading: (el) => el.props?.loading
|
|
35
41
|
}
|
|
@@ -141,19 +147,35 @@ export const renderElement = async (elementDef, options = {}) => {
|
|
|
141
147
|
const body = document.body
|
|
142
148
|
|
|
143
149
|
const { create } = await import('@domql/element')
|
|
150
|
+
const domqlUtils = await import('@domql/utils')
|
|
144
151
|
|
|
145
152
|
// Merge minimal uikit stubs so DOMQL resolves extends chains
|
|
146
153
|
// (e.g. extends: 'Link' → tag: 'a', extends: 'Flex' → display: flex)
|
|
147
154
|
const components = { ...UIKIT_STUBS, ...(context.components || {}) }
|
|
148
155
|
|
|
156
|
+
// Register utility functions so element.call() can resolve them
|
|
157
|
+
// (e.g. replaceLiteralsWithObjectFields for {{ }} templates)
|
|
158
|
+
const utils = {
|
|
159
|
+
...domqlUtils,
|
|
160
|
+
...(context.utils || {}),
|
|
161
|
+
...(context.functions || {})
|
|
162
|
+
}
|
|
163
|
+
|
|
149
164
|
resetKeys()
|
|
150
165
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
166
|
+
let element
|
|
167
|
+
try {
|
|
168
|
+
element = create(elementDef, { node: body }, 'root', {
|
|
169
|
+
context: { document, window, ...context, components, utils }
|
|
170
|
+
})
|
|
171
|
+
} catch (err) {
|
|
172
|
+
// Lifecycle events (onRender, onDone, etc.) may throw in SSR
|
|
173
|
+
// because they access browser-only APIs. The DOM tree is built
|
|
174
|
+
// before these fire, so we can still extract HTML.
|
|
175
|
+
}
|
|
154
176
|
|
|
155
177
|
assignKeys(body)
|
|
156
|
-
const registry = mapKeysToElements(element)
|
|
178
|
+
const registry = element ? mapKeysToElements(element) : {}
|
|
157
179
|
const html = body.innerHTML
|
|
158
180
|
|
|
159
181
|
return { html, registry, element }
|
|
@@ -255,6 +277,120 @@ ${result.html}
|
|
|
255
277
|
return { html, route, brKeyCount: result.brKeyCount }
|
|
256
278
|
}
|
|
257
279
|
|
|
280
|
+
// ── Design system token resolution ──────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
const LETTER_TO_INDEX = {
|
|
283
|
+
U: -6, V: -5, W: -4, X: -3, Y: -2, Z: -1,
|
|
284
|
+
A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9,
|
|
285
|
+
K: 10, L: 11, M: 12, N: 13, O: 14, P: 15
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const SPACING_PROPS = new Set([
|
|
289
|
+
'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
|
|
290
|
+
'paddingBlock', 'paddingInline', 'paddingBlockStart', 'paddingBlockEnd',
|
|
291
|
+
'paddingInlineStart', 'paddingInlineEnd',
|
|
292
|
+
'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
|
|
293
|
+
'marginBlock', 'marginInline', 'marginBlockStart', 'marginBlockEnd',
|
|
294
|
+
'marginInlineStart', 'marginInlineEnd',
|
|
295
|
+
'gap', 'rowGap', 'columnGap',
|
|
296
|
+
'top', 'right', 'bottom', 'left',
|
|
297
|
+
'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight',
|
|
298
|
+
'flexBasis', 'fontSize', 'lineHeight', 'letterSpacing',
|
|
299
|
+
'borderWidth', 'borderRadius', 'outlineWidth', 'outlineOffset',
|
|
300
|
+
'inset', 'insetBlock', 'insetInline',
|
|
301
|
+
'boxSize', 'round'
|
|
302
|
+
])
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Resolves a spacing token like 'B2', 'A', 'E3' to a px/em value.
|
|
306
|
+
* Uses base * ratio^index for main steps (A=0, B=1, etc.)
|
|
307
|
+
* and sub-ratio interpolation for sub-steps (B1, B2, B3).
|
|
308
|
+
*/
|
|
309
|
+
const resolveSpacingToken = (token, spacingConfig) => {
|
|
310
|
+
if (!token || typeof token !== 'string') return null
|
|
311
|
+
if (!spacingConfig) return null
|
|
312
|
+
|
|
313
|
+
const base = spacingConfig.base || 16
|
|
314
|
+
const ratio = spacingConfig.ratio || 1.618
|
|
315
|
+
const unit = spacingConfig.unit || 'px'
|
|
316
|
+
const hasSubSequence = spacingConfig.subSequence !== false
|
|
317
|
+
|
|
318
|
+
// Handle compound values like 'B2 - -' or 'A1 B C1'
|
|
319
|
+
if (token.includes(' ')) {
|
|
320
|
+
const parts = token.split(' ').map(part => {
|
|
321
|
+
if (part === '-' || part === '') return part
|
|
322
|
+
return resolveSpacingToken(part, spacingConfig) || part
|
|
323
|
+
})
|
|
324
|
+
return parts.join(' ')
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Skip CSS keywords and values with units
|
|
328
|
+
if (/^(none|auto|inherit|initial|unset|0)$/i.test(token)) return null
|
|
329
|
+
if (/\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|fr|s|ms)$/i.test(token)) return null
|
|
330
|
+
// Skip hex colors, rgb(), etc.
|
|
331
|
+
if (/^(#|rgb|hsl|var\()/i.test(token)) return null
|
|
332
|
+
|
|
333
|
+
const isNegative = token.startsWith('-')
|
|
334
|
+
const abs = isNegative ? token.slice(1) : token
|
|
335
|
+
|
|
336
|
+
// Match letter + optional digit: A, B, B2, E3, etc.
|
|
337
|
+
const m = abs.match(/^([A-Z])(\d)?$/i)
|
|
338
|
+
if (!m) return null
|
|
339
|
+
|
|
340
|
+
const letter = m[1].toUpperCase()
|
|
341
|
+
const subStep = m[2] ? parseInt(m[2]) : 0
|
|
342
|
+
const idx = LETTER_TO_INDEX[letter]
|
|
343
|
+
if (idx === undefined) return null
|
|
344
|
+
|
|
345
|
+
let value = base * Math.pow(ratio, idx)
|
|
346
|
+
|
|
347
|
+
if (subStep > 0 && hasSubSequence) {
|
|
348
|
+
const next = base * Math.pow(ratio, idx + 1)
|
|
349
|
+
const diff = next - value
|
|
350
|
+
const subRatio = diff / ratio
|
|
351
|
+
// Sub-steps: 1 = value + (diff - subRatio), 2 = midpoint, 3 = value + subRatio
|
|
352
|
+
const first = next - subRatio
|
|
353
|
+
const second = value + subRatio
|
|
354
|
+
const middle = (first + second) / 2
|
|
355
|
+
const subs = (~~next - ~~value > 16) ? [first, middle, second] : [first, second]
|
|
356
|
+
if (subStep <= subs.length) {
|
|
357
|
+
value = subs[subStep - 1]
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const rounded = Math.round(value * 100) / 100
|
|
362
|
+
const sign = isNegative ? '-' : ''
|
|
363
|
+
return `${sign}${rounded}${unit}`
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Kebab-case versions of spacing props for post-shorthand resolution
|
|
367
|
+
const SPACING_PROPS_KEBAB = new Set(
|
|
368
|
+
[...SPACING_PROPS].map(k => k.replace(/[A-Z]/g, m => '-' + m.toLowerCase()))
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Try to resolve a CSS value through the design system.
|
|
373
|
+
* Returns the resolved value or the original if not a token.
|
|
374
|
+
*/
|
|
375
|
+
const resolveDSValue = (key, val, ds) => {
|
|
376
|
+
if (typeof val !== 'string') return val
|
|
377
|
+
|
|
378
|
+
// Color resolution
|
|
379
|
+
if (CSS_COLOR_PROPS.has(key)) {
|
|
380
|
+
const colorMap = ds?.color || {}
|
|
381
|
+
if (colorMap[val]) return colorMap[val]
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Spacing resolution (check both camelCase and kebab-case keys)
|
|
385
|
+
if (SPACING_PROPS.has(key) || SPACING_PROPS_KEBAB.has(key)) {
|
|
386
|
+
const spacing = ds?.spacing || {}
|
|
387
|
+
const resolved = resolveSpacingToken(val, spacing)
|
|
388
|
+
if (resolved) return resolved
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return val
|
|
392
|
+
}
|
|
393
|
+
|
|
258
394
|
// ── CSS helpers ─────────────────────────────────────────────────────────────
|
|
259
395
|
|
|
260
396
|
const CSS_COLOR_PROPS = new Set([
|
|
@@ -267,7 +403,8 @@ const NON_CSS_PROPS = new Set([
|
|
|
267
403
|
'href', 'src', 'alt', 'title', 'id', 'name', 'type', 'value', 'placeholder',
|
|
268
404
|
'target', 'rel', 'loading', 'srcset', 'sizes', 'media', 'role', 'tabindex',
|
|
269
405
|
'for', 'action', 'method', 'enctype', 'autocomplete', 'autofocus',
|
|
270
|
-
'theme', '__element', 'update'
|
|
406
|
+
'theme', '__element', 'update',
|
|
407
|
+
'childrenAs', 'childExtends', 'childProps', 'children'
|
|
271
408
|
])
|
|
272
409
|
|
|
273
410
|
const camelToKebab = (str) => str.replace(/[A-Z]/g, m => '-' + m.toLowerCase())
|
|
@@ -287,11 +424,15 @@ const resolveShorthand = (key, val) => {
|
|
|
287
424
|
}
|
|
288
425
|
if ((key === 'align' || key === 'flexAlign') && typeof val === 'string') {
|
|
289
426
|
const [alignItems, justifyContent] = val.split(' ')
|
|
290
|
-
|
|
427
|
+
const result = { display: 'flex', 'align-items': alignItems }
|
|
428
|
+
if (justifyContent) result['justify-content'] = justifyContent
|
|
429
|
+
return result
|
|
291
430
|
}
|
|
292
431
|
if (key === 'gridAlign' && typeof val === 'string') {
|
|
293
432
|
const [alignItems, justifyContent] = val.split(' ')
|
|
294
|
-
|
|
433
|
+
const result = { display: 'grid', 'align-items': alignItems }
|
|
434
|
+
if (justifyContent) result['justify-content'] = justifyContent
|
|
435
|
+
return result
|
|
295
436
|
}
|
|
296
437
|
if (key === 'flexFlow' && typeof val === 'string') {
|
|
297
438
|
let [direction, wrap] = (val || 'row').split(' ')
|
|
@@ -303,6 +444,11 @@ const resolveShorthand = (key, val) => {
|
|
|
303
444
|
return { display: 'flex', 'flex-wrap': val }
|
|
304
445
|
}
|
|
305
446
|
|
|
447
|
+
// Background image shorthand
|
|
448
|
+
if (key === 'backgroundImage' && typeof val === 'string' && !val.startsWith('url(') && !val.startsWith('linear-gradient') && !val.startsWith('radial-gradient') && !val.startsWith('none')) {
|
|
449
|
+
return { 'background-image': `url(${val})` }
|
|
450
|
+
}
|
|
451
|
+
|
|
306
452
|
// Box/size shorthands
|
|
307
453
|
if (key === 'round' || (key === 'borderRadius' && val)) {
|
|
308
454
|
return { 'border-radius': typeof val === 'number' ? val + 'px' : val }
|
|
@@ -339,19 +485,24 @@ const resolveShorthand = (key, val) => {
|
|
|
339
485
|
return null
|
|
340
486
|
}
|
|
341
487
|
|
|
342
|
-
const resolveInnerProps = (obj,
|
|
488
|
+
const resolveInnerProps = (obj, ds) => {
|
|
343
489
|
const result = {}
|
|
344
490
|
for (const k in obj) {
|
|
345
491
|
const v = obj[k]
|
|
346
492
|
const expanded = resolveShorthand(k, v)
|
|
347
|
-
if (expanded) {
|
|
493
|
+
if (expanded) {
|
|
494
|
+
for (const ek in expanded) {
|
|
495
|
+
result[ek] = resolveDSValue(ek, expanded[ek], ds)
|
|
496
|
+
}
|
|
497
|
+
continue
|
|
498
|
+
}
|
|
348
499
|
if (typeof v !== 'string' && typeof v !== 'number') continue
|
|
349
|
-
result[camelToKebab(k)] =
|
|
500
|
+
result[camelToKebab(k)] = resolveDSValue(k, v, ds)
|
|
350
501
|
}
|
|
351
502
|
return result
|
|
352
503
|
}
|
|
353
504
|
|
|
354
|
-
const buildCSSFromProps = (props,
|
|
505
|
+
const buildCSSFromProps = (props, ds, mediaMap) => {
|
|
355
506
|
const base = {}
|
|
356
507
|
const mediaRules = {}
|
|
357
508
|
const pseudoRules = {}
|
|
@@ -362,14 +513,14 @@ const buildCSSFromProps = (props, colorMap, mediaMap) => {
|
|
|
362
513
|
if (key.charCodeAt(0) === 64 && typeof val === 'object') {
|
|
363
514
|
const bp = mediaMap?.[key.slice(1)]
|
|
364
515
|
if (bp) {
|
|
365
|
-
const inner = resolveInnerProps(val,
|
|
516
|
+
const inner = resolveInnerProps(val, ds)
|
|
366
517
|
if (Object.keys(inner).length) mediaRules[bp] = inner
|
|
367
518
|
}
|
|
368
519
|
continue
|
|
369
520
|
}
|
|
370
521
|
|
|
371
522
|
if (key.charCodeAt(0) === 58 && typeof val === 'object') {
|
|
372
|
-
const inner = resolveInnerProps(val,
|
|
523
|
+
const inner = resolveInnerProps(val, ds)
|
|
373
524
|
if (Object.keys(inner).length) pseudoRules[key] = inner
|
|
374
525
|
continue
|
|
375
526
|
}
|
|
@@ -379,9 +530,14 @@ const buildCSSFromProps = (props, colorMap, mediaMap) => {
|
|
|
379
530
|
if (NON_CSS_PROPS.has(key)) continue
|
|
380
531
|
|
|
381
532
|
const expanded = resolveShorthand(key, val)
|
|
382
|
-
if (expanded) {
|
|
533
|
+
if (expanded) {
|
|
534
|
+
for (const ek in expanded) {
|
|
535
|
+
base[ek] = resolveDSValue(ek, expanded[ek], ds)
|
|
536
|
+
}
|
|
537
|
+
continue
|
|
538
|
+
}
|
|
383
539
|
|
|
384
|
-
base[camelToKebab(key)] =
|
|
540
|
+
base[camelToKebab(key)] = resolveDSValue(key, val, ds)
|
|
385
541
|
}
|
|
386
542
|
|
|
387
543
|
return { base, mediaRules, pseudoRules }
|
|
@@ -426,7 +582,6 @@ const getExtendsCSS = (el) => {
|
|
|
426
582
|
}
|
|
427
583
|
|
|
428
584
|
const extractCSS = (element, ds) => {
|
|
429
|
-
const colorMap = ds?.color || {}
|
|
430
585
|
const mediaMap = ds?.media || {}
|
|
431
586
|
const animations = ds?.animation || {}
|
|
432
587
|
const rules = []
|
|
@@ -440,7 +595,7 @@ const extractCSS = (element, ds) => {
|
|
|
440
595
|
const cls = el.node.getAttribute?.('class')
|
|
441
596
|
if (cls && !seen.has(cls)) {
|
|
442
597
|
seen.add(cls)
|
|
443
|
-
const cssResult = buildCSSFromProps(props,
|
|
598
|
+
const cssResult = buildCSSFromProps(props, ds, mediaMap)
|
|
444
599
|
|
|
445
600
|
// Inject CSS from extends chain (e.g. extends: 'Flex' → display: flex)
|
|
446
601
|
const extsCss = getExtendsCSS(el)
|
|
@@ -486,6 +641,7 @@ const generateResetCSS = (reset) => {
|
|
|
486
641
|
if (!reset) return ''
|
|
487
642
|
const rules = []
|
|
488
643
|
for (const [selector, props] of Object.entries(reset)) {
|
|
644
|
+
if (!props || typeof props !== 'object') continue
|
|
489
645
|
const decls = Object.entries(props)
|
|
490
646
|
.map(([k, v]) => `${camelToKebab(k)}: ${v}`)
|
|
491
647
|
.join('; ')
|
|
@@ -501,6 +657,7 @@ const generateFontLinks = (ds) => {
|
|
|
501
657
|
|
|
502
658
|
// Collect font family names from the design system
|
|
503
659
|
for (const val of Object.values(families)) {
|
|
660
|
+
if (typeof val !== 'string') continue
|
|
504
661
|
const match = val.match(/'([^']+)'/)
|
|
505
662
|
if (match) fontNames.add(match[1])
|
|
506
663
|
}
|