esm-styles 0.2.8 → 0.3.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/README.md +26 -6
- package/dist/build.js +0 -0
- package/dist/lib/build.js +19 -4
- package/doc/ai-guide.md +29 -2
- package/doc/api-reference.md +30 -14
- package/doc/best-practices.md +793 -0
- package/doc/usage-guide.md +66 -15
- package/package.json +1 -1
- package/dist/lib/getCss.d.ts +0 -14
- package/dist/lib/getCss.js +0 -260
- package/dist/lib/utils/common.d.ts +0 -28
- package/dist/lib/utils/common.js +0 -47
- package/dist/lib/utils/endValue.d.ts +0 -2
- package/dist/lib/utils/endValue.js +0 -10
- package/dist/lib/utils/media.d.ts +0 -24
- package/dist/lib/utils/media.js +0 -93
- package/dist/lib/utils/obj2css.d.ts +0 -15
- package/dist/lib/utils/obj2css.js +0 -79
- package/dist/lib/utils/selectors.d.ts +0 -15
- package/dist/lib/utils/selectors.js +0 -87
- package/dist/lib/utils/tags.d.ts +0 -10
- package/dist/lib/utils/tags.js +0 -128
- package/dist/lib/utils/traversal.d.ts +0 -25
- package/dist/lib/utils/traversal.js +0 -78
- package/dist/watch.js +0 -37
package/doc/usage-guide.md
CHANGED
|
@@ -73,13 +73,14 @@ export default {
|
|
|
73
73
|
outputPath: 'css',
|
|
74
74
|
sourceFilesSuffix: '.styles.mjs',
|
|
75
75
|
|
|
76
|
-
// Input
|
|
76
|
+
// Input floors (replaces layers parameter)
|
|
77
77
|
floors: [
|
|
78
78
|
{ source: 'defaults', layer: 'defaults' }, // wrap in layer 'defaults'
|
|
79
79
|
{ source: 'components', layer: 'components' }, // wrap in layer 'components'
|
|
80
80
|
{ source: 'layout', layer: 'layout' }, // wrap in layer 'layout'
|
|
81
81
|
{ source: 'basic' }, // stay out of any layer
|
|
82
82
|
{ source: 'more-layout', layer: 'layout' }, // wrap in layer 'layout'
|
|
83
|
+
{ source: 'special', outputPath: 'alt' }, // output to custom path
|
|
83
84
|
],
|
|
84
85
|
|
|
85
86
|
// Output
|
|
@@ -149,7 +150,8 @@ export default {
|
|
|
149
150
|
| `sourcePath` | Directory inside `basePath` containing source style files |
|
|
150
151
|
| `outputPath` | Directory inside `basePath` where output CSS files will be written |
|
|
151
152
|
| `sourceFilesSuffix` | Suffix for source style files (default: `.styles.mjs`) |
|
|
152
|
-
| `
|
|
153
|
+
| `floors` | Array of floor configurations, defining sources, layers, and output paths |
|
|
154
|
+
| `importFloors` | Array of floor names to include in the main CSS file |
|
|
153
155
|
| `mainCssFile` | Name of the output CSS file that imports all layer and variable files |
|
|
154
156
|
| `globalVariables` | Name of the file containing global CSS variables |
|
|
155
157
|
| `globalRootSelector` | Root selector for CSS variables (default: `:root`) |
|
|
@@ -316,9 +318,13 @@ Use commas to target multiple selectors:
|
|
|
316
318
|
}
|
|
317
319
|
```
|
|
318
320
|
|
|
319
|
-
## Layers
|
|
321
|
+
## Floors and Layers
|
|
320
322
|
|
|
321
|
-
ESM Styles supports @layer directives
|
|
323
|
+
ESM Styles supports @layer directives through its floors configuration system.
|
|
324
|
+
|
|
325
|
+
### Inline Layers
|
|
326
|
+
|
|
327
|
+
You can still use inline @layer directives in your styles:
|
|
322
328
|
|
|
323
329
|
```js
|
|
324
330
|
{
|
|
@@ -333,43 +339,88 @@ ESM Styles supports @layer directives:
|
|
|
333
339
|
}
|
|
334
340
|
```
|
|
335
341
|
|
|
336
|
-
|
|
342
|
+
### Floors Configuration
|
|
343
|
+
|
|
344
|
+
The floors system replaces the old layers configuration and provides more flexibility:
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
floors: [
|
|
348
|
+
{ source: 'defaults', layer: 'defaults' },
|
|
349
|
+
{ source: 'components', layer: 'components' },
|
|
350
|
+
{ source: 'layout', layer: 'layout' },
|
|
351
|
+
{ source: 'utilities' }, // No layer wrapper
|
|
352
|
+
{ source: 'overrides', outputPath: 'special' }, // Custom output path
|
|
353
|
+
]
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Each floor can:
|
|
357
|
+
|
|
358
|
+
- **source**: Name of the source file (required)
|
|
359
|
+
- **layer**: CSS layer name to wrap the styles in (optional)
|
|
360
|
+
- **outputPath**: Custom output directory for this floor's CSS (optional)
|
|
361
|
+
|
|
362
|
+
### Floor Examples
|
|
337
363
|
|
|
338
364
|
```js
|
|
339
365
|
// defaults.styles.mjs
|
|
340
366
|
export default {
|
|
341
|
-
// Base styles
|
|
367
|
+
// Base styles - will be wrapped in @layer defaults
|
|
342
368
|
}
|
|
343
369
|
|
|
344
370
|
// components.styles.mjs
|
|
345
371
|
export default {
|
|
346
|
-
// Component styles
|
|
372
|
+
// Component styles - will be wrapped in @layer components
|
|
347
373
|
}
|
|
348
374
|
|
|
349
|
-
//
|
|
375
|
+
// utilities.styles.mjs
|
|
350
376
|
export default {
|
|
351
|
-
//
|
|
377
|
+
// Utility styles - no layer wrapper
|
|
352
378
|
}
|
|
353
379
|
```
|
|
354
380
|
|
|
355
|
-
|
|
381
|
+
### Build Output
|
|
382
|
+
|
|
383
|
+
The build process generates CSS files based on your floors configuration:
|
|
356
384
|
|
|
357
385
|
```css
|
|
386
|
+
/* styles.css (main file) */
|
|
358
387
|
@layer defaults, components, layout;
|
|
359
388
|
|
|
389
|
+
@import url('./defaults.css');
|
|
390
|
+
@import url('./components.css');
|
|
391
|
+
@import url('./layout.css');
|
|
392
|
+
@import url('./utilities.css'); /* no layer */
|
|
393
|
+
|
|
394
|
+
/* Files with custom outputPath go to their specified directories */
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Each layered CSS file has its content wrapped in the appropriate layer:
|
|
398
|
+
|
|
399
|
+
```css
|
|
400
|
+
/* defaults.css */
|
|
360
401
|
@layer defaults {
|
|
361
|
-
/* defaults
|
|
402
|
+
/* defaults styles content */
|
|
362
403
|
}
|
|
363
404
|
|
|
405
|
+
/* components.css */
|
|
364
406
|
@layer components {
|
|
365
|
-
/* components
|
|
407
|
+
/* components styles content */
|
|
366
408
|
}
|
|
367
409
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
410
|
+
/* utilities.css (no layer) */
|
|
411
|
+
/* utilities styles content directly, no layer wrapper */
|
|
371
412
|
```
|
|
372
413
|
|
|
414
|
+
### Import Control
|
|
415
|
+
|
|
416
|
+
Use `importFloors` to control which floors are included in the main CSS file:
|
|
417
|
+
|
|
418
|
+
```js
|
|
419
|
+
importFloors: ['defaults', 'components', 'layout'], // utilities excluded from main CSS
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
This allows you to generate standalone CSS files that can be imported separately or conditionally.
|
|
423
|
+
|
|
373
424
|
## Media Queries
|
|
374
425
|
|
|
375
426
|
### Standard Media Queries
|
package/package.json
CHANGED
package/dist/lib/getCss.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Main function for converting JavaScript objects to CSS
|
|
3
|
-
*/
|
|
4
|
-
import { CssStyles, MediaQueries, MediaPrefixes, AutoConfig } from './types/index.js';
|
|
5
|
-
/**
|
|
6
|
-
* Converts a JavaScript style object to CSS string
|
|
7
|
-
* @param object - The JavaScript object to convert to CSS
|
|
8
|
-
* @param mediaQueries - Media query definitions (e.g., { 'phone': '(max-width: 499px)' })
|
|
9
|
-
* @param mediaPrefixes - Media prefix definitions (e.g., { 'dark': ':root.dark' })
|
|
10
|
-
* @param auto - Auto mode configuration (e.g., { 'dark': [':root.auto', 'screen and (prefers-color-scheme: dark)'] })
|
|
11
|
-
* @returns CSS string
|
|
12
|
-
*/
|
|
13
|
-
export declare function getCss(object: CssStyles, mediaQueries?: MediaQueries, mediaPrefixes?: MediaPrefixes, auto?: AutoConfig): string;
|
|
14
|
-
export default getCss;
|
package/dist/lib/getCss.js
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Main function for converting JavaScript objects to CSS
|
|
3
|
-
*/
|
|
4
|
-
import { traverseObject, determineNodeType } from './utils/traversal.js';
|
|
5
|
-
import { indent } from './utils/common.js';
|
|
6
|
-
import { obj2css, prettifyCss } from './utils/obj2css.js';
|
|
7
|
-
import { joinSelectors, cartesianSelectors } from './utils/selectors.js';
|
|
8
|
-
import { formatContentValue } from './utils/content.js';
|
|
9
|
-
import { jsKeyToCssKey } from './utils/common.js';
|
|
10
|
-
import { processMediaQueries } from './utils/media.js';
|
|
11
|
-
/**
|
|
12
|
-
* Converts a JavaScript style object to CSS string
|
|
13
|
-
* @param object - The JavaScript object to convert to CSS
|
|
14
|
-
* @param mediaQueries - Media query definitions (e.g., { 'phone': '(max-width: 499px)' })
|
|
15
|
-
* @param mediaPrefixes - Media prefix definitions (e.g., { 'dark': ':root.dark' })
|
|
16
|
-
* @param auto - Auto mode configuration (e.g., { 'dark': [':root.auto', 'screen and (prefers-color-scheme: dark)'] })
|
|
17
|
-
* @returns CSS string
|
|
18
|
-
*/
|
|
19
|
-
export function getCss(object, mediaQueries = {}, mediaPrefixes = {}, auto) {
|
|
20
|
-
// Initialize various objects to collect different types of CSS rules
|
|
21
|
-
let cssStyle = {};
|
|
22
|
-
const layerStatements = [];
|
|
23
|
-
let layerObject = {};
|
|
24
|
-
let containerObject = {};
|
|
25
|
-
const mediaObject = {};
|
|
26
|
-
let prefixObject = {};
|
|
27
|
-
// Process the object by traversing it and handling different node types
|
|
28
|
-
traverseObject(object, (node, path, _, __) => {
|
|
29
|
-
if (!path)
|
|
30
|
-
return node;
|
|
31
|
-
const nodeType = determineNodeType(node, path);
|
|
32
|
-
const pathParts = path.split('\\');
|
|
33
|
-
const key = pathParts.pop() || '';
|
|
34
|
-
// Skip processing media query nodes that are nested within selectors
|
|
35
|
-
// as they will be handled separately in the media query section
|
|
36
|
-
const isNestedMediaQuery = pathParts.some((part) => part.startsWith('@media ') ||
|
|
37
|
-
(part.startsWith('@') &&
|
|
38
|
-
mediaQueries[part.replace(/^@\s*/, '')] !== undefined));
|
|
39
|
-
switch (nodeType) {
|
|
40
|
-
case 'selector': {
|
|
41
|
-
// Skip if this is inside a media query - it will be handled separately
|
|
42
|
-
if (isNestedMediaQuery) {
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
// Handle CSS property-value pairs
|
|
46
|
-
const selector = joinSelectors(pathParts);
|
|
47
|
-
// Create cartesian product of selectors for comma-separated parts
|
|
48
|
-
if (pathParts.some((part) => part.includes(','))) {
|
|
49
|
-
const classPaths = pathParts.map((part) => part.split(',').map((p) => p.trim()));
|
|
50
|
-
const allPaths = cartesianSelectors(classPaths);
|
|
51
|
-
// Apply the property-value to each selector
|
|
52
|
-
allPaths.forEach((selectorPath) => {
|
|
53
|
-
const cssKey = jsKeyToCssKey(key);
|
|
54
|
-
let value = node;
|
|
55
|
-
// Special handling for content property
|
|
56
|
-
if (cssKey === 'content') {
|
|
57
|
-
value = formatContentValue(value);
|
|
58
|
-
}
|
|
59
|
-
// Create the CSS object and merge it
|
|
60
|
-
const cssObject = {
|
|
61
|
-
[selectorPath]: { [cssKey]: value },
|
|
62
|
-
};
|
|
63
|
-
cssStyle = mergeDeep(cssStyle, cssObject);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
// Simple case - no commas in selectors
|
|
68
|
-
const cssKey = jsKeyToCssKey(key);
|
|
69
|
-
let value = node;
|
|
70
|
-
// Special handling for content property
|
|
71
|
-
if (cssKey === 'content') {
|
|
72
|
-
value = formatContentValue(value);
|
|
73
|
-
}
|
|
74
|
-
// Create the CSS object and merge it
|
|
75
|
-
const cssObject = {
|
|
76
|
-
[selector]: { [cssKey]: value },
|
|
77
|
-
};
|
|
78
|
-
cssStyle = mergeDeep(cssStyle, cssObject);
|
|
79
|
-
}
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
case 'layer statement': {
|
|
83
|
-
// Handle @layer statements
|
|
84
|
-
const statement = `${key}${node ? ' ' + node : ''};`;
|
|
85
|
-
if (!layerStatements.includes(statement)) {
|
|
86
|
-
layerStatements.push(statement);
|
|
87
|
-
}
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
case 'layer block': {
|
|
91
|
-
// Handle @layer blocks
|
|
92
|
-
const selector = joinSelectors(pathParts);
|
|
93
|
-
const object = { [key]: { [selector]: node } };
|
|
94
|
-
layerObject = mergeDeep(layerObject, object);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
case 'container query block': {
|
|
98
|
-
// Handle @container queries
|
|
99
|
-
const selector = joinSelectors(pathParts);
|
|
100
|
-
const object = { [key]: { [selector]: node } };
|
|
101
|
-
containerObject = mergeDeep(containerObject, object);
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case 'media query or prefix': {
|
|
105
|
-
// Handle media queries and prefixes
|
|
106
|
-
const selector = joinSelectors(pathParts);
|
|
107
|
-
const name = key.replace(/^@\s*/, '');
|
|
108
|
-
if (mediaPrefixes[name]) {
|
|
109
|
-
// Handle media prefix
|
|
110
|
-
const prefix = mediaPrefixes[name];
|
|
111
|
-
const rules = selector ? { ['& ' + selector]: node } : node;
|
|
112
|
-
const mediaPrefixObject = { [prefix]: rules };
|
|
113
|
-
prefixObject = mergeDeep(prefixObject, mediaPrefixObject);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
// Handle media query
|
|
117
|
-
let mediaQuery = key;
|
|
118
|
-
if (key.startsWith('@media ')) {
|
|
119
|
-
// Use the media query as is, just remove @media prefix
|
|
120
|
-
mediaQuery = key.replace(/^@media\s+/, '');
|
|
121
|
-
}
|
|
122
|
-
else if (mediaQueries[name]) {
|
|
123
|
-
// Use the named media query from configuration
|
|
124
|
-
mediaQuery = mediaQueries[name];
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
// Unknown media query type
|
|
128
|
-
console.warn(`Warning: Media query type ${key} is unknown`);
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
// Store the media query with the selector and node
|
|
132
|
-
// This way we collect all the rules for each media query
|
|
133
|
-
const mediaQueryKey = '@media ' + mediaQuery;
|
|
134
|
-
if (!mediaObject[mediaQueryKey]) {
|
|
135
|
-
mediaObject[mediaQueryKey] = {};
|
|
136
|
-
}
|
|
137
|
-
// Add the selector and its rules to this media query
|
|
138
|
-
mediaObject[mediaQueryKey] = mergeDeep(mediaObject[mediaQueryKey], { [selector]: node });
|
|
139
|
-
}
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return node;
|
|
144
|
-
});
|
|
145
|
-
// Convert the main CSS style object to string
|
|
146
|
-
let cssString = obj2css(cssStyle);
|
|
147
|
-
// Add layer statements if any
|
|
148
|
-
if (layerStatements.length > 0) {
|
|
149
|
-
cssString = layerStatements.join('\n') + '\n\n' + cssString;
|
|
150
|
-
}
|
|
151
|
-
// Process and add layer blocks if any
|
|
152
|
-
if (Object.keys(layerObject).length > 0) {
|
|
153
|
-
const layers = Object.keys(layerObject);
|
|
154
|
-
const layerCssString = layers
|
|
155
|
-
.map((layer) => {
|
|
156
|
-
// Type guard for object values
|
|
157
|
-
const layerStyles = layerObject[layer];
|
|
158
|
-
if (!isObject(layerStyles))
|
|
159
|
-
return '';
|
|
160
|
-
const layerContent = getCss(layerStyles, mediaQueries, mediaPrefixes, auto);
|
|
161
|
-
return layerContent ? `${layer} {\n${indent(layerContent)}\n}` : '';
|
|
162
|
-
})
|
|
163
|
-
.filter(Boolean)
|
|
164
|
-
.join('\n\n');
|
|
165
|
-
if (layerCssString) {
|
|
166
|
-
cssString += '\n\n' + layerCssString;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
// Process and add container queries if any
|
|
170
|
-
if (Object.keys(containerObject).length > 0) {
|
|
171
|
-
const containerQueries = Object.keys(containerObject);
|
|
172
|
-
const containerCssString = containerQueries
|
|
173
|
-
.map((containerQuery) => {
|
|
174
|
-
// Type guard for object values
|
|
175
|
-
const containerStyles = containerObject[containerQuery];
|
|
176
|
-
if (!isObject(containerStyles))
|
|
177
|
-
return '';
|
|
178
|
-
const containerContent = getCss(containerStyles, mediaQueries, mediaPrefixes, auto);
|
|
179
|
-
return containerContent
|
|
180
|
-
? `${containerQuery} {\n${indent(containerContent)}\n}`
|
|
181
|
-
: '';
|
|
182
|
-
})
|
|
183
|
-
.filter(Boolean)
|
|
184
|
-
.join('\n\n');
|
|
185
|
-
if (containerCssString) {
|
|
186
|
-
cssString += '\n\n' + containerCssString;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// Process and add media queries if any
|
|
190
|
-
if (Object.keys(mediaObject).length > 0) {
|
|
191
|
-
const mediaCssString = processMediaQueries(mediaObject, mediaQueries);
|
|
192
|
-
if (mediaCssString) {
|
|
193
|
-
cssString += '\n\n' + mediaCssString;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// Process and add media prefixes if any
|
|
197
|
-
if (Object.keys(prefixObject).length > 0) {
|
|
198
|
-
// First, process the prefix object as normal CSS
|
|
199
|
-
const mediaPrefixedCssString = getCss(prefixObject, mediaQueries, mediaPrefixes);
|
|
200
|
-
cssString += '\n\n' + mediaPrefixedCssString;
|
|
201
|
-
// Handle auto mode if configured
|
|
202
|
-
if (auto) {
|
|
203
|
-
// Create a modified prefix object for auto mode
|
|
204
|
-
const autoPrefixObject = {};
|
|
205
|
-
for (const key of Object.keys(auto)) {
|
|
206
|
-
const selector = mediaPrefixes[key];
|
|
207
|
-
if (!selector || !prefixObject[selector])
|
|
208
|
-
continue;
|
|
209
|
-
const [autoSelector, mediaQuery] = auto[key];
|
|
210
|
-
// Create media query for auto mode
|
|
211
|
-
autoPrefixObject[`@media ${mediaQuery}`] = {
|
|
212
|
-
[autoSelector]: prefixObject[selector],
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
// Process the auto prefix object if not empty
|
|
216
|
-
if (Object.keys(autoPrefixObject).length > 0) {
|
|
217
|
-
const autoCssString = processMediaQueries(autoPrefixObject, mediaQueries);
|
|
218
|
-
if (autoCssString) {
|
|
219
|
-
cssString += '\n\n' + autoCssString;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
// Prettify the final CSS string
|
|
225
|
-
return prettifyCss(cssString.replace(/__bs__/g, '\\'));
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Deep merge two objects
|
|
229
|
-
* @param target - Target object to merge into
|
|
230
|
-
* @param source - Source object to merge from
|
|
231
|
-
* @returns Merged object
|
|
232
|
-
*/
|
|
233
|
-
function mergeDeep(target, source) {
|
|
234
|
-
const output = { ...target };
|
|
235
|
-
if (isObject(target) && isObject(source)) {
|
|
236
|
-
Object.keys(source).forEach((key) => {
|
|
237
|
-
if (isObject(source[key])) {
|
|
238
|
-
if (!(key in target)) {
|
|
239
|
-
output[key] = source[key];
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
output[key] = mergeDeep(target[key], source[key]);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
output[key] = source[key];
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
return output;
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Checks if value is a non-null object
|
|
254
|
-
* @param item - Value to check
|
|
255
|
-
* @returns True if the value is a non-null object
|
|
256
|
-
*/
|
|
257
|
-
function isObject(item) {
|
|
258
|
-
return item && typeof item === 'object' && !Array.isArray(item);
|
|
259
|
-
}
|
|
260
|
-
export default getCss;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for CSS processing
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Determines the object type in a more precise way than typeof
|
|
6
|
-
* @param obj - Any value to check type
|
|
7
|
-
* @returns The lowercase string representing the object type
|
|
8
|
-
*/
|
|
9
|
-
export declare function getObjectType(obj: any): string;
|
|
10
|
-
/**
|
|
11
|
-
* Checks if a value is a primitive end value (string, number, boolean, null)
|
|
12
|
-
* @param value - Value to check
|
|
13
|
-
* @returns True if the value is a primitive end value
|
|
14
|
-
*/
|
|
15
|
-
export declare function isEndValue(value: any): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Indents a string with spaces
|
|
18
|
-
* @param str - String to indent
|
|
19
|
-
* @param spaces - Number of spaces to indent with
|
|
20
|
-
* @returns Indented string
|
|
21
|
-
*/
|
|
22
|
-
export declare function indent(str: string, spaces?: number): string;
|
|
23
|
-
/**
|
|
24
|
-
* Converts a JavaScript property name in camelCase to CSS kebab-case
|
|
25
|
-
* @param key - Property name in camelCase
|
|
26
|
-
* @returns Property name in kebab-case
|
|
27
|
-
*/
|
|
28
|
-
export declare function jsKeyToCssKey(key: string): string;
|
package/dist/lib/utils/common.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for CSS processing
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Determines the object type in a more precise way than typeof
|
|
6
|
-
* @param obj - Any value to check type
|
|
7
|
-
* @returns The lowercase string representing the object type
|
|
8
|
-
*/
|
|
9
|
-
export function getObjectType(obj) {
|
|
10
|
-
return (Object.prototype.toString
|
|
11
|
-
.call(obj)
|
|
12
|
-
.match(/^\[object (\w+)\]$/)?.[1]
|
|
13
|
-
.toLowerCase() || 'unknown');
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Checks if a value is a primitive end value (string, number, boolean, null)
|
|
17
|
-
* @param value - Value to check
|
|
18
|
-
* @returns True if the value is a primitive end value
|
|
19
|
-
*/
|
|
20
|
-
export function isEndValue(value) {
|
|
21
|
-
return ['string', 'number', 'boolean', 'null'].includes(getObjectType(value));
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Indents a string with spaces
|
|
25
|
-
* @param str - String to indent
|
|
26
|
-
* @param spaces - Number of spaces to indent with
|
|
27
|
-
* @returns Indented string
|
|
28
|
-
*/
|
|
29
|
-
export function indent(str, spaces = 2) {
|
|
30
|
-
return str
|
|
31
|
-
.split('\n')
|
|
32
|
-
.map((s) => ' '.repeat(spaces) + s)
|
|
33
|
-
.join('\n');
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Converts a JavaScript property name in camelCase to CSS kebab-case
|
|
37
|
-
* @param key - Property name in camelCase
|
|
38
|
-
* @returns Property name in kebab-case
|
|
39
|
-
*/
|
|
40
|
-
export function jsKeyToCssKey(key) {
|
|
41
|
-
// Keep vendor prefixes intact
|
|
42
|
-
const prefix = key.match(/^[-]+/)?.[0] || '';
|
|
43
|
-
const baseKey = key.replace(/^[-]+/, '');
|
|
44
|
-
// Convert camelCase to kebab-case
|
|
45
|
-
const kebabKey = baseKey.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
46
|
-
return prefix + kebabKey;
|
|
47
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export const isEndValue = (value) => {
|
|
2
|
-
if (value == null)
|
|
3
|
-
return false;
|
|
4
|
-
if (typeof value === 'string' || typeof value === 'number')
|
|
5
|
-
return true;
|
|
6
|
-
if (Array.isArray(value)) {
|
|
7
|
-
return value.every((v) => typeof v === 'string' || typeof v === 'number');
|
|
8
|
-
}
|
|
9
|
-
return false;
|
|
10
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for handling media queries
|
|
3
|
-
*/
|
|
4
|
-
import { CssStyles, MediaQueries } from '../types/index.js';
|
|
5
|
-
/**
|
|
6
|
-
* Processes media query objects and converts them to CSS
|
|
7
|
-
* @param mediaObject - Object containing media queries and their styles
|
|
8
|
-
* @param mediaQueries - Named media query definitions
|
|
9
|
-
* @returns CSS string with processed media queries
|
|
10
|
-
*/
|
|
11
|
-
export declare function processMediaQueries(mediaObject: CssStyles, mediaQueries?: MediaQueries): string;
|
|
12
|
-
/**
|
|
13
|
-
* Checks if a key represents a media query
|
|
14
|
-
* @param key - Key to check
|
|
15
|
-
* @returns True if the key is a media query
|
|
16
|
-
*/
|
|
17
|
-
export declare function isMediaQuery(key: string): boolean;
|
|
18
|
-
/**
|
|
19
|
-
* Checks if a key is a named media query that needs to be resolved
|
|
20
|
-
* @param key - Key to check
|
|
21
|
-
* @param mediaQueries - Named media query definitions
|
|
22
|
-
* @returns True if the key is a named media query
|
|
23
|
-
*/
|
|
24
|
-
export declare function isNamedMediaQuery(key: string, mediaQueries: MediaQueries): boolean;
|
package/dist/lib/utils/media.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for handling media queries
|
|
3
|
-
*/
|
|
4
|
-
import { indent, jsKeyToCssKey } from './common.js';
|
|
5
|
-
import { obj2css } from './obj2css.js';
|
|
6
|
-
import { isEndValue } from './common.js';
|
|
7
|
-
/**
|
|
8
|
-
* Processes media query objects and converts them to CSS
|
|
9
|
-
* @param mediaObject - Object containing media queries and their styles
|
|
10
|
-
* @param mediaQueries - Named media query definitions
|
|
11
|
-
* @returns CSS string with processed media queries
|
|
12
|
-
*/
|
|
13
|
-
export function processMediaQueries(mediaObject, mediaQueries = {}) {
|
|
14
|
-
if (!mediaObject || Object.keys(mediaObject).length === 0) {
|
|
15
|
-
return '';
|
|
16
|
-
}
|
|
17
|
-
const mediaQueriesToProcess = Object.keys(mediaObject);
|
|
18
|
-
// Process each media query and convert to CSS
|
|
19
|
-
const mediaCssStrings = mediaQueriesToProcess.map((queryKey) => {
|
|
20
|
-
// Handle named media queries (using @name syntax)
|
|
21
|
-
const mediaQueryMatch = queryKey.match(/^@(\w+)$/);
|
|
22
|
-
let actualQuery = queryKey;
|
|
23
|
-
if (mediaQueryMatch &&
|
|
24
|
-
mediaQueryMatch[1] &&
|
|
25
|
-
mediaQueries[mediaQueryMatch[1]]) {
|
|
26
|
-
// Replace named query with actual query definition
|
|
27
|
-
actualQuery = mediaQueries[mediaQueryMatch[1]];
|
|
28
|
-
}
|
|
29
|
-
else if (queryKey.startsWith('@media ')) {
|
|
30
|
-
// Strip @media prefix if present
|
|
31
|
-
actualQuery = queryKey.replace(/^@media\s+/, '');
|
|
32
|
-
}
|
|
33
|
-
// Convert the styles for this media query to CSS
|
|
34
|
-
const styles = mediaObject[queryKey];
|
|
35
|
-
// Skip if the styles aren't an object
|
|
36
|
-
if (isEndValue(styles)) {
|
|
37
|
-
return '';
|
|
38
|
-
}
|
|
39
|
-
// Process the styles to convert camelCase properties to kebab-case
|
|
40
|
-
const processedStyles = processStylesForMedia(styles);
|
|
41
|
-
const stylesCss = obj2css(processedStyles);
|
|
42
|
-
if (!stylesCss.trim()) {
|
|
43
|
-
return '';
|
|
44
|
-
}
|
|
45
|
-
// Wrap the styles in a media query block
|
|
46
|
-
return `@media ${actualQuery} {\n${indent(stylesCss)}\n}`;
|
|
47
|
-
});
|
|
48
|
-
// Join all media query blocks with newlines
|
|
49
|
-
return mediaCssStrings.filter((css) => css).join('\n\n');
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Processes styles for media queries to ensure camelCase is converted to kebab-case
|
|
53
|
-
* @param styles - Style object to process
|
|
54
|
-
* @returns Processed style object with converted property names
|
|
55
|
-
*/
|
|
56
|
-
function processStylesForMedia(styles) {
|
|
57
|
-
const result = {};
|
|
58
|
-
// Process each selector in the styles
|
|
59
|
-
Object.keys(styles).forEach((selector) => {
|
|
60
|
-
const selectorStyles = styles[selector];
|
|
61
|
-
// Skip if not an object
|
|
62
|
-
if (isEndValue(selectorStyles)) {
|
|
63
|
-
result[selector] = selectorStyles;
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const processedProps = {};
|
|
67
|
-
// Convert each property name to kebab-case
|
|
68
|
-
Object.keys(selectorStyles).forEach((prop) => {
|
|
69
|
-
const cssKey = jsKeyToCssKey(prop);
|
|
70
|
-
processedProps[cssKey] = selectorStyles[prop];
|
|
71
|
-
});
|
|
72
|
-
result[selector] = processedProps;
|
|
73
|
-
});
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Checks if a key represents a media query
|
|
78
|
-
* @param key - Key to check
|
|
79
|
-
* @returns True if the key is a media query
|
|
80
|
-
*/
|
|
81
|
-
export function isMediaQuery(key) {
|
|
82
|
-
return key.startsWith('@media') || key.startsWith('@');
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Checks if a key is a named media query that needs to be resolved
|
|
86
|
-
* @param key - Key to check
|
|
87
|
-
* @param mediaQueries - Named media query definitions
|
|
88
|
-
* @returns True if the key is a named media query
|
|
89
|
-
*/
|
|
90
|
-
export function isNamedMediaQuery(key, mediaQueries) {
|
|
91
|
-
const match = key.match(/^@(\w+)$/);
|
|
92
|
-
return Boolean(match && match[1] && mediaQueries[match[1]]);
|
|
93
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for converting JavaScript objects to CSS strings
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Converts a CSS object to a string representation
|
|
6
|
-
* @param cssObject - Object with CSS selectors and properties
|
|
7
|
-
* @returns CSS string
|
|
8
|
-
*/
|
|
9
|
-
export declare function obj2css(cssObject: Record<string, any>): string;
|
|
10
|
-
/**
|
|
11
|
-
* Prettifies a CSS string by ensuring consistent spacing and formatting
|
|
12
|
-
* @param cssString - CSS string to prettify
|
|
13
|
-
* @returns Prettified CSS string
|
|
14
|
-
*/
|
|
15
|
-
export declare function prettifyCss(cssString: string): string;
|