zss-engine 0.2.36 → 0.2.37
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/index.d.ts +111 -0
- package/dist/index.js +258 -0
- package/dist/index.mjs +247 -0
- package/package.json +9 -12
- package/dist/cjs/index.js +0 -22
- package/dist/cjs/types/common/css-properties.js +0 -2
- package/dist/cjs/types/common/css-property.js +0 -2
- package/dist/cjs/types/main/create.js +0 -2
- package/dist/cjs/types/main/global.js +0 -2
- package/dist/cjs/types/main/vars.js +0 -2
- package/dist/cjs/utils/build.js +0 -27
- package/dist/cjs/utils/hash.js +0 -34
- package/dist/cjs/utils/helper.js +0 -27
- package/dist/cjs/utils/inject-client-css.js +0 -51
- package/dist/cjs/utils/inject-client-global-css.js +0 -18
- package/dist/cjs/utils/inject-server-css.js +0 -11
- package/dist/cjs/utils/transpiler.js +0 -125
- package/dist/esm/index.js +0 -8
- package/dist/esm/types/common/css-properties.js +0 -1
- package/dist/esm/types/common/css-property.js +0 -1
- package/dist/esm/types/main/create.js +0 -1
- package/dist/esm/types/main/global.js +0 -1
- package/dist/esm/types/main/vars.js +0 -1
- package/dist/esm/utils/build.js +0 -23
- package/dist/esm/utils/hash.js +0 -31
- package/dist/esm/utils/helper.js +0 -22
- package/dist/esm/utils/inject-client-css.js +0 -47
- package/dist/esm/utils/inject-client-global-css.js +0 -15
- package/dist/esm/utils/inject-server-css.js +0 -7
- package/dist/esm/utils/transpiler.js +0 -122
- package/types/index.d.ts +0 -12
- package/types/types/common/css-properties.d.ts +0 -46
- package/types/types/common/css-property.d.ts +0 -5
- package/types/types/main/create.d.ts +0 -10
- package/types/types/main/global.d.ts +0 -37
- package/types/types/main/vars.d.ts +0 -8
- package/types/utils/build.d.ts +0 -1
- package/types/utils/hash.d.ts +0 -2
- package/types/utils/helper.d.ts +0 -5
- package/types/utils/inject-client-css.d.ts +0 -2
- package/types/utils/inject-client-global-css.d.ts +0 -1
- package/types/utils/inject-server-css.d.ts +0 -2
- package/types/utils/transpiler.d.ts +0 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Properties, Property } from "csstype";
|
|
2
|
+
|
|
3
|
+
//#region src/types/main/vars.d.ts
|
|
4
|
+
type CSSVariableKey = `--${string}`;
|
|
5
|
+
type CSSVariableValue = `var(${CSSVariableKey})`;
|
|
6
|
+
type CSSVariableProperty = {
|
|
7
|
+
[key: CSSVariableKey]: string | number;
|
|
8
|
+
};
|
|
9
|
+
type CreateVars = Record<string, string | number>;
|
|
10
|
+
type CreateTheme = Record<string, Record<string, string | number>>;
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/types/common/css-properties.d.ts
|
|
14
|
+
type ColorValue = Exclude<Property.Color, '-moz-initial'> | (string & {});
|
|
15
|
+
type CSSColorProperty = Exclude<ColorValue, SystemColorKeyword>;
|
|
16
|
+
type SystemColorKeyword = 'ActiveBorder' | 'ActiveCaption' | 'AppWorkspace' | 'Background' | 'ButtonFace' | 'ButtonHighlight' | 'ButtonShadow' | 'ButtonText' | 'CaptionText' | 'GrayText' | 'Highlight' | 'HighlightText' | 'InactiveBorder' | 'InactiveCaption' | 'InactiveCaptionText' | 'InfoBackground' | 'InfoText' | 'Menu' | 'MenuText' | 'Scrollbar' | 'ThreeDDarkShadow' | 'ThreeDFace' | 'ThreeDHighlight' | 'ThreeDLightShadow' | 'ThreeDShadow' | 'Window' | 'WindowFrame' | 'WindowText';
|
|
17
|
+
type ExcludeMozInitial<T> = Exclude<T, '-moz-initial'>;
|
|
18
|
+
type CSSTypeProperties = Properties<number | (string & {})>;
|
|
19
|
+
type CustomProperties = { [K in keyof CSSTypeProperties]: ExcludeMozInitial<CSSTypeProperties[K]> };
|
|
20
|
+
type BaseCSSProperties = { [K in keyof CustomProperties]: CustomProperties[K] | CSSVariableValue };
|
|
21
|
+
interface CommonProperties extends BaseCSSProperties {
|
|
22
|
+
accentColor?: CSSColorProperty;
|
|
23
|
+
color?: CSSColorProperty;
|
|
24
|
+
borderLeftColor?: CSSColorProperty;
|
|
25
|
+
borderRightColor?: CSSColorProperty;
|
|
26
|
+
borderTopColor?: CSSColorProperty;
|
|
27
|
+
borderBottomColor?: CSSColorProperty;
|
|
28
|
+
borderBlockColor?: CSSColorProperty;
|
|
29
|
+
borderBlockStartColor?: CSSColorProperty;
|
|
30
|
+
borderBlockEndColor?: CSSColorProperty;
|
|
31
|
+
borderInlineColor?: CSSColorProperty;
|
|
32
|
+
borderInlineStartColor?: CSSColorProperty;
|
|
33
|
+
borderInlineEndColor?: CSSColorProperty;
|
|
34
|
+
backgroundColor?: CSSColorProperty;
|
|
35
|
+
outlineColor?: CSSColorProperty;
|
|
36
|
+
textDecorationColor?: CSSColorProperty;
|
|
37
|
+
caretColor?: CSSColorProperty;
|
|
38
|
+
columnRuleColor?: CSSColorProperty;
|
|
39
|
+
}
|
|
40
|
+
type AndString = `&${string}`;
|
|
41
|
+
type AndStringType = { [key in AndString]: CommonProperties };
|
|
42
|
+
type Colon = `:${string}`;
|
|
43
|
+
type ColonType = { [key in Colon]: CommonProperties };
|
|
44
|
+
type MediaQuery = `@media ${string}`;
|
|
45
|
+
type MediaQueryType = { [K in MediaQuery]: CommonProperties | ColonType | AndStringType };
|
|
46
|
+
type CSSProperties = CommonProperties | ColonType | CSSVariableProperty | AndStringType | MediaQueryType;
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/types/main/create.d.ts
|
|
50
|
+
type CreateStyleType<T> = { readonly [K in keyof T]: T[K] extends CSSProperties ? CSSProperties : T[K] };
|
|
51
|
+
type CreateStyle = {
|
|
52
|
+
[key: string]: CSSProperties;
|
|
53
|
+
};
|
|
54
|
+
type ReturnType<T> = { [key in keyof T]: string };
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/types/main/global.d.ts
|
|
58
|
+
type JSXType = keyof HTMLElementTagNameMap | '*' | ':root';
|
|
59
|
+
type HTMLType = { [K in JSXType]: CSSProperties };
|
|
60
|
+
type ClassName = `.${string}`;
|
|
61
|
+
type ClassNameType = { [K in ClassName]: CSSProperties };
|
|
62
|
+
type Attribute = `${string}[${string}]${string}`;
|
|
63
|
+
type AttributeType = { [K in Attribute]: CSSProperties };
|
|
64
|
+
type Consecutive = `${JSXType} ${string}`;
|
|
65
|
+
type ConsecutiveType = { [K in Consecutive]: CSSProperties };
|
|
66
|
+
type PseudoClass = `${JSXType}:${string}`;
|
|
67
|
+
type PseudoClassType = { [K in PseudoClass]: CSSProperties };
|
|
68
|
+
type PseudoElement = `::${string}`;
|
|
69
|
+
type PseudoElementType = { [K in PseudoElement]: CSSProperties };
|
|
70
|
+
type KeyframeSelector = 'from' | 'to' | `${number}%`;
|
|
71
|
+
type CreateKeyframes = { [K in KeyframeSelector]?: CSSProperties };
|
|
72
|
+
type KeyframesType = { [K in `@keyframes ${string}`]: CreateKeyframes };
|
|
73
|
+
type MediaQueryHTMLType = { [K in MediaQuery]: CSSHTML };
|
|
74
|
+
type CSSHTML = HTMLType | ClassNameType | AttributeType | ConsecutiveType | PseudoClassType | PseudoElementType | KeyframesType | MediaQueryHTMLType;
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/utils/helper.d.ts
|
|
78
|
+
declare const isServer: boolean;
|
|
79
|
+
declare const isDevelopment: boolean;
|
|
80
|
+
declare const isDevAndTest: boolean;
|
|
81
|
+
declare const camelToKebabCase: (property: string) => string;
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/utils/hash.d.ts
|
|
85
|
+
declare function genBase36Hash(object: CreateStyle | CreateKeyframes, n: number): string;
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/utils/transpiler.d.ts
|
|
89
|
+
declare function transpiler(object: CSSHTML, base36Hash?: string, core?: string): {
|
|
90
|
+
styleSheet: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/utils/build.d.ts
|
|
95
|
+
declare const build: (styleSheet: string, filePath: string, global?: string) => Promise<void>;
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/utils/inject-client-css.d.ts
|
|
99
|
+
declare function injectClientCSS(hash: string, sheet: string): void;
|
|
100
|
+
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/utils/inject-client-global-css.d.ts
|
|
103
|
+
declare function injectClientGlobalCSS(sheet: string): void;
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/utils/inject-server-css.d.ts
|
|
107
|
+
declare function injectServerCSS(hash: string, sheet: string): void;
|
|
108
|
+
declare function getServerCSS(): string;
|
|
109
|
+
|
|
110
|
+
//#endregion
|
|
111
|
+
export { CSSHTML, CSSProperties, CreateKeyframes, CreateStyle, CreateStyleType, CreateTheme, CreateVars, ReturnType, build, camelToKebabCase, genBase36Hash, getServerCSS, injectClientCSS, injectClientGlobalCSS, injectServerCSS, isDevAndTest, isDevelopment, isServer, transpiler };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/helper.ts
|
|
3
|
+
const isWindowDefined = typeof window !== "undefined";
|
|
4
|
+
const isDocumentDefined = typeof document !== "undefined";
|
|
5
|
+
const isServer = !isWindowDefined || !isDocumentDefined;
|
|
6
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
7
|
+
const isDevAndTest = process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test";
|
|
8
|
+
const exception = [
|
|
9
|
+
"line-height",
|
|
10
|
+
"font-weight",
|
|
11
|
+
"opacity",
|
|
12
|
+
"scale",
|
|
13
|
+
"z-index",
|
|
14
|
+
"column-count",
|
|
15
|
+
"order",
|
|
16
|
+
"orphans",
|
|
17
|
+
"widows"
|
|
18
|
+
];
|
|
19
|
+
const applyCssValue = (value, cssProp) => {
|
|
20
|
+
if (typeof value === "number") return exception.includes(cssProp) ? value.toString() : value + "px";
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
const camelToKebabCase = (property) => {
|
|
24
|
+
if (/^(ms|Moz|Webkit)/.test(property)) property = "-" + property;
|
|
25
|
+
return property.replace(/([A-Z]+)([0-9]+)/g, "$1$2").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/utils/hash.ts
|
|
30
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
31
|
+
function simpleHash(str) {
|
|
32
|
+
let hash = 0;
|
|
33
|
+
for (let i = 0; i < str.length; i++) {
|
|
34
|
+
const char = str.charCodeAt(i);
|
|
35
|
+
hash = (hash << 5) - hash + char;
|
|
36
|
+
}
|
|
37
|
+
return Math.abs(hash);
|
|
38
|
+
}
|
|
39
|
+
function encodeBase36(num) {
|
|
40
|
+
let result = "";
|
|
41
|
+
do {
|
|
42
|
+
result = chars[num % 36] + result;
|
|
43
|
+
num = Math.floor(num / 36);
|
|
44
|
+
} while (num > 0);
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function getStartingChar(hash) {
|
|
48
|
+
const chars$1 = "abcdefghijklmnopqrstuvwxyz";
|
|
49
|
+
return chars$1[hash % chars$1.length];
|
|
50
|
+
}
|
|
51
|
+
function genBase36Hash(object, n) {
|
|
52
|
+
const serialized = JSON.stringify(object);
|
|
53
|
+
const hash = simpleHash(serialized);
|
|
54
|
+
let base36Hash = encodeBase36(hash);
|
|
55
|
+
const startingChar = getStartingChar(hash);
|
|
56
|
+
while (base36Hash.length < n - 1) base36Hash = chars[hash % chars.length] + base36Hash;
|
|
57
|
+
return startingChar + base36Hash.slice(0, n - 1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/utils/transpiler.ts
|
|
62
|
+
const createKeyframes = (property, content) => {
|
|
63
|
+
let keyframesRules = `${property} {\n`;
|
|
64
|
+
for (const key in content) if (Object.prototype.hasOwnProperty.call(content, key)) {
|
|
65
|
+
const keyframeValue = content[key];
|
|
66
|
+
keyframesRules += ` ${key} {\n`;
|
|
67
|
+
for (const prop in keyframeValue) if (Object.prototype.hasOwnProperty.call(keyframeValue, prop)) {
|
|
68
|
+
const CSSProp = camelToKebabCase(prop);
|
|
69
|
+
const value = keyframeValue[prop];
|
|
70
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
71
|
+
const applyValue = applyCssValue(value, CSSProp);
|
|
72
|
+
keyframesRules += ` ${CSSProp}: ${applyValue};\n`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
keyframesRules += ` }\n`;
|
|
76
|
+
}
|
|
77
|
+
keyframesRules += `}\n`;
|
|
78
|
+
return keyframesRules;
|
|
79
|
+
};
|
|
80
|
+
function transpiler(object, base36Hash, core) {
|
|
81
|
+
let styleSheet = "";
|
|
82
|
+
const mediaQueries = [];
|
|
83
|
+
const classNameType = (property) => {
|
|
84
|
+
return core === "--global" ? property : `.${property}_${base36Hash}`;
|
|
85
|
+
};
|
|
86
|
+
const rules = (indent, rulesValue, property) => {
|
|
87
|
+
if (typeof property !== "string") return "";
|
|
88
|
+
const value = rulesValue[property];
|
|
89
|
+
const cssProp = camelToKebabCase(property);
|
|
90
|
+
return `${indent}${cssProp}: ${value};\n`;
|
|
91
|
+
};
|
|
92
|
+
const stringConverter = (className, properties, indentLevel = 0) => {
|
|
93
|
+
const classSelector = {};
|
|
94
|
+
const indent = "".repeat(indentLevel);
|
|
95
|
+
const innerIndent = " ".repeat(indentLevel + 1);
|
|
96
|
+
let cssRule = "";
|
|
97
|
+
for (const property in properties) if (Object.prototype.hasOwnProperty.call(properties, property)) {
|
|
98
|
+
const value = properties[property];
|
|
99
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
100
|
+
let CSSProp = camelToKebabCase(property);
|
|
101
|
+
if (property.startsWith("ms")) CSSProp = `-${CSSProp}`;
|
|
102
|
+
const applyValue = applyCssValue(value, CSSProp);
|
|
103
|
+
cssRule += ` ${CSSProp}: ${applyValue};\n`;
|
|
104
|
+
} else if (!property.startsWith("@")) {
|
|
105
|
+
const kebabPseudoSelector = camelToKebabCase(property.replace("&", ""));
|
|
106
|
+
const styles = stringConverter(className + kebabPseudoSelector, value, indentLevel);
|
|
107
|
+
Object.assign(classSelector, styles);
|
|
108
|
+
} else if (property.startsWith("@media") || property.startsWith("@container")) {
|
|
109
|
+
const mediaRule = property;
|
|
110
|
+
let nestedRules = "";
|
|
111
|
+
let regularRules = "";
|
|
112
|
+
for (const mediaProp in value) if (Object.prototype.hasOwnProperty.call(value, mediaProp)) {
|
|
113
|
+
const mediaValue = value[mediaProp];
|
|
114
|
+
const isColon = mediaProp.startsWith(":");
|
|
115
|
+
const isAnd = mediaProp.startsWith("&");
|
|
116
|
+
if (isColon || isAnd) {
|
|
117
|
+
const kebabMediaProp = camelToKebabCase(mediaProp.replace("&", ""));
|
|
118
|
+
let pseudoClassRule = "";
|
|
119
|
+
if (typeof mediaValue === "object" && mediaValue !== null) {
|
|
120
|
+
for (const pseudoProp in mediaValue) if (Object.prototype.hasOwnProperty.call(mediaValue, pseudoProp)) {
|
|
121
|
+
const CSSProp = camelToKebabCase(pseudoProp);
|
|
122
|
+
const applyValue = applyCssValue(mediaValue[pseudoProp], CSSProp);
|
|
123
|
+
pseudoClassRule += rules(innerIndent + " ", { [pseudoProp]: applyValue }, pseudoProp);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
nestedRules += `${innerIndent}${className}${kebabMediaProp} {\n${pseudoClassRule}${innerIndent}}\n`;
|
|
127
|
+
} else {
|
|
128
|
+
const CSSProp = camelToKebabCase(mediaProp);
|
|
129
|
+
const applyValue = applyCssValue(mediaValue, CSSProp);
|
|
130
|
+
regularRules += rules(innerIndent + " ", { [mediaProp]: applyValue }, mediaProp);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (regularRules) mediaQueries.push({
|
|
134
|
+
media: mediaRule,
|
|
135
|
+
css: `${mediaRule} {\n${innerIndent}${className} {\n${regularRules} }\n${nestedRules}${indent}}${indent}\n`
|
|
136
|
+
});
|
|
137
|
+
else mediaQueries.push({
|
|
138
|
+
media: mediaRule,
|
|
139
|
+
css: `${mediaRule} {\n${nestedRules}${indent}}\n`
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
classSelector[className] = cssRule;
|
|
144
|
+
return classSelector;
|
|
145
|
+
};
|
|
146
|
+
for (const property in object) {
|
|
147
|
+
if (property.startsWith("@keyframes")) {
|
|
148
|
+
const keyframesContent = object[property];
|
|
149
|
+
styleSheet += createKeyframes(property, keyframesContent);
|
|
150
|
+
}
|
|
151
|
+
const classSelectors = stringConverter(classNameType(property), object[property], 1);
|
|
152
|
+
for (const selector in classSelectors) if (!selector.startsWith("@keyframes") && classSelectors[selector]) styleSheet += selector + " {\n" + classSelectors[selector] + "}\n";
|
|
153
|
+
}
|
|
154
|
+
mediaQueries.forEach(({ css }) => {
|
|
155
|
+
styleSheet += css;
|
|
156
|
+
});
|
|
157
|
+
return { styleSheet };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/utils/build.ts
|
|
162
|
+
const build = async (styleSheet, filePath, global) => {
|
|
163
|
+
if (!isServer) return;
|
|
164
|
+
const fs = await import("fs");
|
|
165
|
+
const { styleText } = await import("util");
|
|
166
|
+
const message = global === "--global" ? styleText("underline", `✅Generated global CSS\n\n`) : styleText("underline", `✅Generated create CSS\n\n`);
|
|
167
|
+
try {
|
|
168
|
+
if (fs.existsSync(filePath)) {
|
|
169
|
+
const cssData = fs.readFileSync(filePath, "utf-8");
|
|
170
|
+
if (!cssData.includes(styleSheet)) {
|
|
171
|
+
fs.appendFileSync(filePath, styleSheet, "utf-8");
|
|
172
|
+
if (process.argv.includes("--view")) console.log(message + styleSheet);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error("Error writing to file:", error);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/utils/inject-client-css.ts
|
|
183
|
+
const styleSheets$1 = {};
|
|
184
|
+
const hashCache = {};
|
|
185
|
+
function createStyleElement(hash) {
|
|
186
|
+
if (document.getElementById(hash)) return null;
|
|
187
|
+
const styleElement = document.createElement("style");
|
|
188
|
+
styleElement.setAttribute("id", hash);
|
|
189
|
+
styleElement.setAttribute("type", "text/css");
|
|
190
|
+
styleSheets$1[hash] = styleElement;
|
|
191
|
+
document.head.appendChild(styleElement);
|
|
192
|
+
return styleSheets$1[hash];
|
|
193
|
+
}
|
|
194
|
+
function injectClientCSS(hash, sheet) {
|
|
195
|
+
if (isServer) return;
|
|
196
|
+
requestAnimationFrame(() => {
|
|
197
|
+
styleCleanUp();
|
|
198
|
+
});
|
|
199
|
+
hashCache[hash] = hash;
|
|
200
|
+
const styleElement = createStyleElement(hash);
|
|
201
|
+
if (styleElement == null) return;
|
|
202
|
+
styleElement.textContent = sheet;
|
|
203
|
+
}
|
|
204
|
+
function styleCleanUp() {
|
|
205
|
+
requestAnimationFrame(() => {
|
|
206
|
+
for (const hash in hashCache) {
|
|
207
|
+
const classElements = document.querySelectorAll(`[class*="${hash}"]`);
|
|
208
|
+
if (classElements.length === 0) removeStyleElement(hashCache[hash]);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function removeStyleElement(hash) {
|
|
213
|
+
if (styleSheets$1[hash]) {
|
|
214
|
+
delete styleSheets$1[hash];
|
|
215
|
+
if (hashCache.hasOwnProperty.call(hashCache, hash)) delete hashCache[hash];
|
|
216
|
+
const styleElement = document.getElementById(hash);
|
|
217
|
+
if (styleElement) document.head.removeChild(styleElement);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/utils/inject-client-global-css.ts
|
|
223
|
+
function injectClientGlobalCSS(sheet) {
|
|
224
|
+
if (isServer) return;
|
|
225
|
+
const existingStyleElement = document.querySelector(`[data-scope="global"]`);
|
|
226
|
+
if (existingStyleElement instanceof HTMLStyleElement) {
|
|
227
|
+
existingStyleElement.textContent += sheet;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const styleElement = document.createElement("style");
|
|
231
|
+
styleElement.setAttribute("data-scope", "global");
|
|
232
|
+
styleElement.setAttribute("type", "text/css");
|
|
233
|
+
styleElement.textContent = sheet;
|
|
234
|
+
document.head.appendChild(styleElement);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/utils/inject-server-css.ts
|
|
239
|
+
const styleSheets = {};
|
|
240
|
+
function injectServerCSS(hash, sheet) {
|
|
241
|
+
styleSheets[hash] = sheet;
|
|
242
|
+
}
|
|
243
|
+
function getServerCSS() {
|
|
244
|
+
return Object.values(styleSheets).join("\n");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
exports.build = build;
|
|
249
|
+
exports.camelToKebabCase = camelToKebabCase;
|
|
250
|
+
exports.genBase36Hash = genBase36Hash;
|
|
251
|
+
exports.getServerCSS = getServerCSS;
|
|
252
|
+
exports.injectClientCSS = injectClientCSS;
|
|
253
|
+
exports.injectClientGlobalCSS = injectClientGlobalCSS;
|
|
254
|
+
exports.injectServerCSS = injectServerCSS;
|
|
255
|
+
exports.isDevAndTest = isDevAndTest;
|
|
256
|
+
exports.isDevelopment = isDevelopment;
|
|
257
|
+
exports.isServer = isServer;
|
|
258
|
+
exports.transpiler = transpiler;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
//#region src/utils/helper.ts
|
|
2
|
+
const isWindowDefined = typeof window !== "undefined";
|
|
3
|
+
const isDocumentDefined = typeof document !== "undefined";
|
|
4
|
+
const isServer = !isWindowDefined || !isDocumentDefined;
|
|
5
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
6
|
+
const isDevAndTest = process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test";
|
|
7
|
+
const exception = [
|
|
8
|
+
"line-height",
|
|
9
|
+
"font-weight",
|
|
10
|
+
"opacity",
|
|
11
|
+
"scale",
|
|
12
|
+
"z-index",
|
|
13
|
+
"column-count",
|
|
14
|
+
"order",
|
|
15
|
+
"orphans",
|
|
16
|
+
"widows"
|
|
17
|
+
];
|
|
18
|
+
const applyCssValue = (value, cssProp) => {
|
|
19
|
+
if (typeof value === "number") return exception.includes(cssProp) ? value.toString() : value + "px";
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
const camelToKebabCase = (property) => {
|
|
23
|
+
if (/^(ms|Moz|Webkit)/.test(property)) property = "-" + property;
|
|
24
|
+
return property.replace(/([A-Z]+)([0-9]+)/g, "$1$2").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/utils/hash.ts
|
|
29
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
30
|
+
function simpleHash(str) {
|
|
31
|
+
let hash = 0;
|
|
32
|
+
for (let i = 0; i < str.length; i++) {
|
|
33
|
+
const char = str.charCodeAt(i);
|
|
34
|
+
hash = (hash << 5) - hash + char;
|
|
35
|
+
}
|
|
36
|
+
return Math.abs(hash);
|
|
37
|
+
}
|
|
38
|
+
function encodeBase36(num) {
|
|
39
|
+
let result = "";
|
|
40
|
+
do {
|
|
41
|
+
result = chars[num % 36] + result;
|
|
42
|
+
num = Math.floor(num / 36);
|
|
43
|
+
} while (num > 0);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
function getStartingChar(hash) {
|
|
47
|
+
const chars$1 = "abcdefghijklmnopqrstuvwxyz";
|
|
48
|
+
return chars$1[hash % chars$1.length];
|
|
49
|
+
}
|
|
50
|
+
function genBase36Hash(object, n) {
|
|
51
|
+
const serialized = JSON.stringify(object);
|
|
52
|
+
const hash = simpleHash(serialized);
|
|
53
|
+
let base36Hash = encodeBase36(hash);
|
|
54
|
+
const startingChar = getStartingChar(hash);
|
|
55
|
+
while (base36Hash.length < n - 1) base36Hash = chars[hash % chars.length] + base36Hash;
|
|
56
|
+
return startingChar + base36Hash.slice(0, n - 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/utils/transpiler.ts
|
|
61
|
+
const createKeyframes = (property, content) => {
|
|
62
|
+
let keyframesRules = `${property} {\n`;
|
|
63
|
+
for (const key in content) if (Object.prototype.hasOwnProperty.call(content, key)) {
|
|
64
|
+
const keyframeValue = content[key];
|
|
65
|
+
keyframesRules += ` ${key} {\n`;
|
|
66
|
+
for (const prop in keyframeValue) if (Object.prototype.hasOwnProperty.call(keyframeValue, prop)) {
|
|
67
|
+
const CSSProp = camelToKebabCase(prop);
|
|
68
|
+
const value = keyframeValue[prop];
|
|
69
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
70
|
+
const applyValue = applyCssValue(value, CSSProp);
|
|
71
|
+
keyframesRules += ` ${CSSProp}: ${applyValue};\n`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
keyframesRules += ` }\n`;
|
|
75
|
+
}
|
|
76
|
+
keyframesRules += `}\n`;
|
|
77
|
+
return keyframesRules;
|
|
78
|
+
};
|
|
79
|
+
function transpiler(object, base36Hash, core) {
|
|
80
|
+
let styleSheet = "";
|
|
81
|
+
const mediaQueries = [];
|
|
82
|
+
const classNameType = (property) => {
|
|
83
|
+
return core === "--global" ? property : `.${property}_${base36Hash}`;
|
|
84
|
+
};
|
|
85
|
+
const rules = (indent, rulesValue, property) => {
|
|
86
|
+
if (typeof property !== "string") return "";
|
|
87
|
+
const value = rulesValue[property];
|
|
88
|
+
const cssProp = camelToKebabCase(property);
|
|
89
|
+
return `${indent}${cssProp}: ${value};\n`;
|
|
90
|
+
};
|
|
91
|
+
const stringConverter = (className, properties, indentLevel = 0) => {
|
|
92
|
+
const classSelector = {};
|
|
93
|
+
const indent = "".repeat(indentLevel);
|
|
94
|
+
const innerIndent = " ".repeat(indentLevel + 1);
|
|
95
|
+
let cssRule = "";
|
|
96
|
+
for (const property in properties) if (Object.prototype.hasOwnProperty.call(properties, property)) {
|
|
97
|
+
const value = properties[property];
|
|
98
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
99
|
+
let CSSProp = camelToKebabCase(property);
|
|
100
|
+
if (property.startsWith("ms")) CSSProp = `-${CSSProp}`;
|
|
101
|
+
const applyValue = applyCssValue(value, CSSProp);
|
|
102
|
+
cssRule += ` ${CSSProp}: ${applyValue};\n`;
|
|
103
|
+
} else if (!property.startsWith("@")) {
|
|
104
|
+
const kebabPseudoSelector = camelToKebabCase(property.replace("&", ""));
|
|
105
|
+
const styles = stringConverter(className + kebabPseudoSelector, value, indentLevel);
|
|
106
|
+
Object.assign(classSelector, styles);
|
|
107
|
+
} else if (property.startsWith("@media") || property.startsWith("@container")) {
|
|
108
|
+
const mediaRule = property;
|
|
109
|
+
let nestedRules = "";
|
|
110
|
+
let regularRules = "";
|
|
111
|
+
for (const mediaProp in value) if (Object.prototype.hasOwnProperty.call(value, mediaProp)) {
|
|
112
|
+
const mediaValue = value[mediaProp];
|
|
113
|
+
const isColon = mediaProp.startsWith(":");
|
|
114
|
+
const isAnd = mediaProp.startsWith("&");
|
|
115
|
+
if (isColon || isAnd) {
|
|
116
|
+
const kebabMediaProp = camelToKebabCase(mediaProp.replace("&", ""));
|
|
117
|
+
let pseudoClassRule = "";
|
|
118
|
+
if (typeof mediaValue === "object" && mediaValue !== null) {
|
|
119
|
+
for (const pseudoProp in mediaValue) if (Object.prototype.hasOwnProperty.call(mediaValue, pseudoProp)) {
|
|
120
|
+
const CSSProp = camelToKebabCase(pseudoProp);
|
|
121
|
+
const applyValue = applyCssValue(mediaValue[pseudoProp], CSSProp);
|
|
122
|
+
pseudoClassRule += rules(innerIndent + " ", { [pseudoProp]: applyValue }, pseudoProp);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
nestedRules += `${innerIndent}${className}${kebabMediaProp} {\n${pseudoClassRule}${innerIndent}}\n`;
|
|
126
|
+
} else {
|
|
127
|
+
const CSSProp = camelToKebabCase(mediaProp);
|
|
128
|
+
const applyValue = applyCssValue(mediaValue, CSSProp);
|
|
129
|
+
regularRules += rules(innerIndent + " ", { [mediaProp]: applyValue }, mediaProp);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (regularRules) mediaQueries.push({
|
|
133
|
+
media: mediaRule,
|
|
134
|
+
css: `${mediaRule} {\n${innerIndent}${className} {\n${regularRules} }\n${nestedRules}${indent}}${indent}\n`
|
|
135
|
+
});
|
|
136
|
+
else mediaQueries.push({
|
|
137
|
+
media: mediaRule,
|
|
138
|
+
css: `${mediaRule} {\n${nestedRules}${indent}}\n`
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
classSelector[className] = cssRule;
|
|
143
|
+
return classSelector;
|
|
144
|
+
};
|
|
145
|
+
for (const property in object) {
|
|
146
|
+
if (property.startsWith("@keyframes")) {
|
|
147
|
+
const keyframesContent = object[property];
|
|
148
|
+
styleSheet += createKeyframes(property, keyframesContent);
|
|
149
|
+
}
|
|
150
|
+
const classSelectors = stringConverter(classNameType(property), object[property], 1);
|
|
151
|
+
for (const selector in classSelectors) if (!selector.startsWith("@keyframes") && classSelectors[selector]) styleSheet += selector + " {\n" + classSelectors[selector] + "}\n";
|
|
152
|
+
}
|
|
153
|
+
mediaQueries.forEach(({ css }) => {
|
|
154
|
+
styleSheet += css;
|
|
155
|
+
});
|
|
156
|
+
return { styleSheet };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/utils/build.ts
|
|
161
|
+
const build = async (styleSheet, filePath, global) => {
|
|
162
|
+
if (!isServer) return;
|
|
163
|
+
const fs = await import("fs");
|
|
164
|
+
const { styleText } = await import("util");
|
|
165
|
+
const message = global === "--global" ? styleText("underline", `✅Generated global CSS\n\n`) : styleText("underline", `✅Generated create CSS\n\n`);
|
|
166
|
+
try {
|
|
167
|
+
if (fs.existsSync(filePath)) {
|
|
168
|
+
const cssData = fs.readFileSync(filePath, "utf-8");
|
|
169
|
+
if (!cssData.includes(styleSheet)) {
|
|
170
|
+
fs.appendFileSync(filePath, styleSheet, "utf-8");
|
|
171
|
+
if (process.argv.includes("--view")) console.log(message + styleSheet);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error("Error writing to file:", error);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/utils/inject-client-css.ts
|
|
182
|
+
const styleSheets$1 = {};
|
|
183
|
+
const hashCache = {};
|
|
184
|
+
function createStyleElement(hash) {
|
|
185
|
+
if (document.getElementById(hash)) return null;
|
|
186
|
+
const styleElement = document.createElement("style");
|
|
187
|
+
styleElement.setAttribute("id", hash);
|
|
188
|
+
styleElement.setAttribute("type", "text/css");
|
|
189
|
+
styleSheets$1[hash] = styleElement;
|
|
190
|
+
document.head.appendChild(styleElement);
|
|
191
|
+
return styleSheets$1[hash];
|
|
192
|
+
}
|
|
193
|
+
function injectClientCSS(hash, sheet) {
|
|
194
|
+
if (isServer) return;
|
|
195
|
+
requestAnimationFrame(() => {
|
|
196
|
+
styleCleanUp();
|
|
197
|
+
});
|
|
198
|
+
hashCache[hash] = hash;
|
|
199
|
+
const styleElement = createStyleElement(hash);
|
|
200
|
+
if (styleElement == null) return;
|
|
201
|
+
styleElement.textContent = sheet;
|
|
202
|
+
}
|
|
203
|
+
function styleCleanUp() {
|
|
204
|
+
requestAnimationFrame(() => {
|
|
205
|
+
for (const hash in hashCache) {
|
|
206
|
+
const classElements = document.querySelectorAll(`[class*="${hash}"]`);
|
|
207
|
+
if (classElements.length === 0) removeStyleElement(hashCache[hash]);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function removeStyleElement(hash) {
|
|
212
|
+
if (styleSheets$1[hash]) {
|
|
213
|
+
delete styleSheets$1[hash];
|
|
214
|
+
if (hashCache.hasOwnProperty.call(hashCache, hash)) delete hashCache[hash];
|
|
215
|
+
const styleElement = document.getElementById(hash);
|
|
216
|
+
if (styleElement) document.head.removeChild(styleElement);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/utils/inject-client-global-css.ts
|
|
222
|
+
function injectClientGlobalCSS(sheet) {
|
|
223
|
+
if (isServer) return;
|
|
224
|
+
const existingStyleElement = document.querySelector(`[data-scope="global"]`);
|
|
225
|
+
if (existingStyleElement instanceof HTMLStyleElement) {
|
|
226
|
+
existingStyleElement.textContent += sheet;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const styleElement = document.createElement("style");
|
|
230
|
+
styleElement.setAttribute("data-scope", "global");
|
|
231
|
+
styleElement.setAttribute("type", "text/css");
|
|
232
|
+
styleElement.textContent = sheet;
|
|
233
|
+
document.head.appendChild(styleElement);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/utils/inject-server-css.ts
|
|
238
|
+
const styleSheets = {};
|
|
239
|
+
function injectServerCSS(hash, sheet) {
|
|
240
|
+
styleSheets[hash] = sheet;
|
|
241
|
+
}
|
|
242
|
+
function getServerCSS() {
|
|
243
|
+
return Object.values(styleSheets).join("\n");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
export { build, camelToKebabCase, genBase36Hash, getServerCSS, injectClientCSS, injectClientGlobalCSS, injectServerCSS, isDevAndTest, isDevelopment, isServer, transpiler };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zss-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.37",
|
|
4
4
|
"description": "Zero-runtime StyleSheet Engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zero-runtime",
|
|
@@ -15,22 +15,19 @@
|
|
|
15
15
|
"exports": {
|
|
16
16
|
"./package.json": "./package.json",
|
|
17
17
|
".": {
|
|
18
|
-
"types": "./
|
|
19
|
-
"import": "./dist/
|
|
20
|
-
"default": "./dist/
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
-
"main": "dist/
|
|
24
|
-
"module": "dist/
|
|
23
|
+
"main": "dist/index.js",
|
|
24
|
+
"module": "dist/index.mjs",
|
|
25
25
|
"types": "types/index.d.ts",
|
|
26
26
|
"files": [
|
|
27
|
-
"dist/"
|
|
28
|
-
"types/"
|
|
27
|
+
"dist/"
|
|
29
28
|
],
|
|
30
29
|
"scripts": {
|
|
31
|
-
"build": "
|
|
32
|
-
"cjs": "tsc --project tsconfig.cjs.json",
|
|
33
|
-
"esm": "tsc --project tsconfig.esm.json",
|
|
30
|
+
"build": "tsdown",
|
|
34
31
|
"test": "jest"
|
|
35
32
|
},
|
|
36
33
|
"dependencies": {
|
|
@@ -41,8 +38,8 @@
|
|
|
41
38
|
"@types/node": "^22.13.10",
|
|
42
39
|
"jest": "^29.7.0",
|
|
43
40
|
"jest-environment-jsdom": "^29.7.0",
|
|
44
|
-
"rimraf": "^6.0.1",
|
|
45
41
|
"ts-jest": "^29.2.6",
|
|
42
|
+
"tsdown": "^0.11.12",
|
|
46
43
|
"typescript": "^5.8.2"
|
|
47
44
|
}
|
|
48
45
|
}
|
package/dist/cjs/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getServerCSS = exports.injectServerCSS = exports.injectClientGlobalCSS = exports.injectClientCSS = exports.camelToKebabCase = exports.build = exports.transpiler = exports.genBase36Hash = exports.isDevAndTest = exports.isDevelopment = exports.isServer = void 0;
|
|
4
|
-
var helper_js_1 = require("./utils/helper.js");
|
|
5
|
-
Object.defineProperty(exports, "isServer", { enumerable: true, get: function () { return helper_js_1.isServer; } });
|
|
6
|
-
Object.defineProperty(exports, "isDevelopment", { enumerable: true, get: function () { return helper_js_1.isDevelopment; } });
|
|
7
|
-
Object.defineProperty(exports, "isDevAndTest", { enumerable: true, get: function () { return helper_js_1.isDevAndTest; } });
|
|
8
|
-
var hash_js_1 = require("./utils/hash.js");
|
|
9
|
-
Object.defineProperty(exports, "genBase36Hash", { enumerable: true, get: function () { return hash_js_1.genBase36Hash; } });
|
|
10
|
-
var transpiler_js_1 = require("./utils/transpiler.js");
|
|
11
|
-
Object.defineProperty(exports, "transpiler", { enumerable: true, get: function () { return transpiler_js_1.transpiler; } });
|
|
12
|
-
var build_js_1 = require("./utils/build.js");
|
|
13
|
-
Object.defineProperty(exports, "build", { enumerable: true, get: function () { return build_js_1.build; } });
|
|
14
|
-
var helper_js_2 = require("./utils/helper.js");
|
|
15
|
-
Object.defineProperty(exports, "camelToKebabCase", { enumerable: true, get: function () { return helper_js_2.camelToKebabCase; } });
|
|
16
|
-
var inject_client_css_js_1 = require("./utils/inject-client-css.js");
|
|
17
|
-
Object.defineProperty(exports, "injectClientCSS", { enumerable: true, get: function () { return inject_client_css_js_1.injectClientCSS; } });
|
|
18
|
-
var inject_client_global_css_js_1 = require("./utils/inject-client-global-css.js");
|
|
19
|
-
Object.defineProperty(exports, "injectClientGlobalCSS", { enumerable: true, get: function () { return inject_client_global_css_js_1.injectClientGlobalCSS; } });
|
|
20
|
-
var inject_server_css_js_1 = require("./utils/inject-server-css.js");
|
|
21
|
-
Object.defineProperty(exports, "injectServerCSS", { enumerable: true, get: function () { return inject_server_css_js_1.injectServerCSS; } });
|
|
22
|
-
Object.defineProperty(exports, "getServerCSS", { enumerable: true, get: function () { return inject_server_css_js_1.getServerCSS; } });
|