@stacksjs/stx 0.2.121 → 0.2.123
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/appearance-bootstrap.d.ts +20 -0
- package/dist/appearance-bootstrap.js +92 -0
- package/dist/cli.js +524 -524
- package/dist/component-renderer.js +18 -5
- package/dist/craft.js +275 -275
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/process.js +5 -1
- package/dist/pwa.js +292 -292
- package/dist/ssg.js +365 -365
- package/package.json +5 -5
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform every `@appearanceBootstrap({...})` directive in a template.
|
|
3
|
+
*/
|
|
4
|
+
export declare function processAppearanceBootstrapDirective(template: string, context: Record<string, unknown>): string;
|
|
5
|
+
export declare interface AppearanceBootstrapValue {
|
|
6
|
+
key: string
|
|
7
|
+
attribute: string
|
|
8
|
+
allowed: string[]
|
|
9
|
+
default: string
|
|
10
|
+
}
|
|
11
|
+
export declare interface ColorModeBootstrapValue {
|
|
12
|
+
key: string
|
|
13
|
+
attribute: string
|
|
14
|
+
default: 'light' | 'dark' | 'system'
|
|
15
|
+
}
|
|
16
|
+
export declare interface AppearanceBootstrapOptions {
|
|
17
|
+
storageKey: string
|
|
18
|
+
appearance: AppearanceBootstrapValue
|
|
19
|
+
colorMode: ColorModeBootstrapValue
|
|
20
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { safeEvaluate } from "./safe-evaluator.js";
|
|
2
|
+
const DATA_ATTRIBUTE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, PROPERTY_PATTERN = /^[A-Za-z_$][\w$]*$/, COLOR_MODES = new Set(["light", "dark", "system"]);
|
|
3
|
+
function assertString(value, label) {
|
|
4
|
+
if (typeof value !== "string" || value.length === 0)
|
|
5
|
+
throw Error(`@appearanceBootstrap ${label} must be a non-empty string`);
|
|
6
|
+
}
|
|
7
|
+
function normalizeOptions(value) {
|
|
8
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9
|
+
throw Error("@appearanceBootstrap expects an options object");
|
|
10
|
+
const input = value, appearance = input.appearance, colorMode = input.colorMode;
|
|
11
|
+
assertString(input.storageKey, "storageKey");
|
|
12
|
+
if (!appearance || typeof appearance !== "object" || Array.isArray(appearance))
|
|
13
|
+
throw Error("@appearanceBootstrap appearance must be an object");
|
|
14
|
+
if (!colorMode || typeof colorMode !== "object" || Array.isArray(colorMode))
|
|
15
|
+
throw Error("@appearanceBootstrap colorMode must be an object");
|
|
16
|
+
assertString(appearance.key, "appearance.key");
|
|
17
|
+
assertString(appearance.attribute, "appearance.attribute");
|
|
18
|
+
assertString(appearance.default, "appearance.default");
|
|
19
|
+
assertString(colorMode.key, "colorMode.key");
|
|
20
|
+
assertString(colorMode.attribute, "colorMode.attribute");
|
|
21
|
+
assertString(colorMode.default, "colorMode.default");
|
|
22
|
+
if (!PROPERTY_PATTERN.test(appearance.key) || !PROPERTY_PATTERN.test(colorMode.key))
|
|
23
|
+
throw Error("@appearanceBootstrap preference keys must be JavaScript property names");
|
|
24
|
+
if (!DATA_ATTRIBUTE_PATTERN.test(appearance.attribute) || !DATA_ATTRIBUTE_PATTERN.test(colorMode.attribute))
|
|
25
|
+
throw Error("@appearanceBootstrap attributes must be lowercase data attribute names");
|
|
26
|
+
if (!Array.isArray(appearance.allowed) || appearance.allowed.length === 0 || appearance.allowed.some((item) => typeof item !== "string" || item.length === 0))
|
|
27
|
+
throw Error("@appearanceBootstrap appearance.allowed must contain one or more strings");
|
|
28
|
+
if (!appearance.allowed.includes(appearance.default))
|
|
29
|
+
throw Error("@appearanceBootstrap appearance.default must be in appearance.allowed");
|
|
30
|
+
if (!COLOR_MODES.has(colorMode.default))
|
|
31
|
+
throw Error("@appearanceBootstrap colorMode.default must be light, dark, or system");
|
|
32
|
+
return {
|
|
33
|
+
storageKey: input.storageKey,
|
|
34
|
+
appearance: {
|
|
35
|
+
key: appearance.key,
|
|
36
|
+
attribute: appearance.attribute,
|
|
37
|
+
allowed: [...new Set(appearance.allowed)],
|
|
38
|
+
default: appearance.default
|
|
39
|
+
},
|
|
40
|
+
colorMode: {
|
|
41
|
+
key: colorMode.key,
|
|
42
|
+
attribute: colorMode.attribute,
|
|
43
|
+
default: colorMode.default
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function serializeForScript(value) {
|
|
48
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
49
|
+
}
|
|
50
|
+
function generateBootstrap(options) {
|
|
51
|
+
return `<script data-stx-scoped data-stx-appearance-bootstrap>(function(){"use strict";const config=${serializeForScript(options)};const root=document.documentElement;let stored={};try{const raw=localStorage.getItem(config.storageKey);const parsed=raw?JSON.parse(raw):{};if(parsed&&typeof parsed==="object"&&!Array.isArray(parsed))stored=parsed}catch{}const selectedAppearance=config.appearance.allowed.includes(stored[config.appearance.key])?stored[config.appearance.key]:config.appearance.default;const selectedMode=["light","dark","system"].includes(stored[config.colorMode.key])?stored[config.colorMode.key]:config.colorMode.default;root.setAttribute("data-"+config.appearance.attribute,selectedAppearance);root.setAttribute("data-"+config.colorMode.attribute,selectedMode);let dark=selectedMode==="dark";if(selectedMode==="system"){try{dark=matchMedia("(prefers-color-scheme: dark)").matches}catch{dark=false}}root.classList.toggle("dark",dark);root.dataset.theme=dark?"dark":"light"}());</script>`;
|
|
52
|
+
}
|
|
53
|
+
function findClosingParenthesis(source, openIndex) {
|
|
54
|
+
let depth = 1, quote = null, escaped = !1;
|
|
55
|
+
for (let index = openIndex + 1;index < source.length; index++) {
|
|
56
|
+
const character = source[index];
|
|
57
|
+
if (escaped) {
|
|
58
|
+
escaped = !1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (quote) {
|
|
62
|
+
if (character === "\\")
|
|
63
|
+
escaped = !0;
|
|
64
|
+
else if (character === quote)
|
|
65
|
+
quote = null;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
69
|
+
quote = character;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (character === "(")
|
|
73
|
+
depth++;
|
|
74
|
+
else if (character === ")" && --depth === 0)
|
|
75
|
+
return index;
|
|
76
|
+
}
|
|
77
|
+
return -1;
|
|
78
|
+
}
|
|
79
|
+
export function processAppearanceBootstrapDirective(template, context) {
|
|
80
|
+
let output = template;
|
|
81
|
+
const pattern = /@appearanceBootstrap\s*\(/g;
|
|
82
|
+
let match;
|
|
83
|
+
while ((match = pattern.exec(output)) !== null) {
|
|
84
|
+
const openIndex = match.index + match[0].length - 1, closeIndex = findClosingParenthesis(output, openIndex);
|
|
85
|
+
if (closeIndex === -1)
|
|
86
|
+
throw Error("@appearanceBootstrap has an unclosed options object");
|
|
87
|
+
const expression = output.slice(openIndex + 1, closeIndex).trim(), evaluated = safeEvaluate(`(${expression})`, context), replacement = generateBootstrap(normalizeOptions(evaluated));
|
|
88
|
+
output = output.slice(0, match.index) + replacement + output.slice(closeIndex + 1);
|
|
89
|
+
pattern.lastIndex = match.index + replacement.length;
|
|
90
|
+
}
|
|
91
|
+
return output;
|
|
92
|
+
}
|