esm-styles 0.2.4 → 0.2.6
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 +47 -13
- package/dist/lib/build.js +44 -20
- package/doc/usage-guide.md +11 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,7 @@ A CSS-in-JS solution for JavaScript/TypeScript projects.
|
|
|
8
8
|
- Build CSS from organized source files with a simple CLI
|
|
9
9
|
- CSS layering support for proper style encapsulation
|
|
10
10
|
- Media query and device/theme selectors with shorthands
|
|
11
|
-
- CSS
|
|
12
|
-
- Supporting modules for easy CSS variable usage
|
|
11
|
+
- CSS variable sets for different themes and devices
|
|
13
12
|
|
|
14
13
|
## Installation
|
|
15
14
|
|
|
@@ -21,28 +20,63 @@ npm install esm-styles
|
|
|
21
20
|
|
|
22
21
|
### Basic Concept
|
|
23
22
|
|
|
24
|
-
ESM Styles lets you write
|
|
23
|
+
ESM Styles lets you write and store styles in JavaScript this way:
|
|
25
24
|
|
|
26
25
|
```js
|
|
27
|
-
//
|
|
26
|
+
// article.styles.mjs
|
|
27
|
+
import $device from './$device.mjs'
|
|
28
|
+
import $theme from './$theme.mjs'
|
|
29
|
+
import { card } from './card.styles.mjs'
|
|
30
|
+
|
|
28
31
|
export default {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
borderRadius: '4px',
|
|
32
|
+
article: {
|
|
33
|
+
display: 'flex',
|
|
34
|
+
|
|
35
|
+
card,
|
|
34
36
|
|
|
35
|
-
'
|
|
36
|
-
|
|
37
|
+
'@max-tablet': {
|
|
38
|
+
flexDirection: 'column',
|
|
37
39
|
},
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
button: {
|
|
42
|
+
borderRadius: $device.radii.md,
|
|
43
|
+
backgroundColor: $theme.paper.tinted,
|
|
44
|
+
|
|
45
|
+
'@dark': {
|
|
46
|
+
fontWeight: 300,
|
|
47
|
+
},
|
|
41
48
|
},
|
|
42
49
|
},
|
|
43
50
|
}
|
|
44
51
|
```
|
|
45
52
|
|
|
53
|
+
### Sample Directory Structure
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
monorepo/
|
|
57
|
+
├── package.json
|
|
58
|
+
└── packages/
|
|
59
|
+
├── app/
|
|
60
|
+
│ ├── package.json
|
|
61
|
+
│ └── src/
|
|
62
|
+
│ ├── css/
|
|
63
|
+
│ ├── styles.css
|
|
64
|
+
│ └── components/
|
|
65
|
+
│ ├── article.tsx
|
|
66
|
+
│ ├── button.tsx
|
|
67
|
+
│ └── card.tsx
|
|
68
|
+
└── styles/
|
|
69
|
+
├── $device.mjs
|
|
70
|
+
├── $theme.mjs
|
|
71
|
+
├── components/
|
|
72
|
+
│ ├── article.styles.mjs
|
|
73
|
+
│ ├── button.styles.mjs
|
|
74
|
+
│ └── card.styles.mjs
|
|
75
|
+
├── components.styles.mjs
|
|
76
|
+
├── esm-styles.config.js
|
|
77
|
+
└── package.json
|
|
78
|
+
```
|
|
79
|
+
|
|
46
80
|
### CLI Usage
|
|
47
81
|
|
|
48
82
|
Build your styles by creating a configuration file and running the CLI:
|
package/dist/lib/build.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getMediaShorthands } from './utils/media-shorthand.js';
|
|
|
4
4
|
import { isEndValue } from './utils/index.js';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import fs from 'node:fs/promises';
|
|
7
|
+
// import { inspect } from 'node:util'
|
|
7
8
|
import _ from 'lodash';
|
|
8
9
|
export async function build(configPath = 'esm-styles.config.js') {
|
|
9
10
|
// --- Supporting module generation ---
|
|
@@ -19,7 +20,7 @@ export async function build(configPath = 'esm-styles.config.js') {
|
|
|
19
20
|
const sourcePath = path.join(basePath, config.sourcePath || '');
|
|
20
21
|
const outputPath = path.join(basePath, config.outputPath || '');
|
|
21
22
|
const suffix = config.sourceFilesSuffix || '.styles.mjs';
|
|
22
|
-
const
|
|
23
|
+
const floors = config.floors || [];
|
|
23
24
|
const mainCssFile = config.mainCssFile || 'styles.css';
|
|
24
25
|
// Merge media shorthands
|
|
25
26
|
const mediaShorthands = getMediaShorthands(config);
|
|
@@ -161,41 +162,64 @@ export async function build(configPath = 'esm-styles.config.js') {
|
|
|
161
162
|
await fs.writeFile(supportingModulePath, moduleContent, 'utf8');
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
|
-
// 4. Process each
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
// 4. Process each floor (replaces legacy layers)
|
|
166
|
+
const importFloors = config.importFloors || floors.map((f) => f.source);
|
|
167
|
+
// Track unique layer names in order of first appearance (for imported floors only)
|
|
168
|
+
const uniqueLayers = [];
|
|
169
|
+
// Track output files for each floor
|
|
170
|
+
const floorFiles = [];
|
|
171
|
+
for (const floor of floors) {
|
|
172
|
+
const { source, layer } = floor;
|
|
173
|
+
const inputFile = path.join(sourcePath, `${source}${suffix}`);
|
|
174
|
+
const outputFile = path.join(outputPath, `${source}.css`);
|
|
168
175
|
const fileUrl = pathToFileUrl(inputFile).href + `?update=${Date.now()}`;
|
|
169
176
|
const stylesObj = (await import(fileUrl)).default;
|
|
170
|
-
// Always call getCss for the layer object, so media queries are rendered
|
|
171
177
|
const css = getCss(stylesObj, {
|
|
172
178
|
...mediaShorthands,
|
|
173
179
|
globalRootSelector: config.globalRootSelector,
|
|
174
180
|
});
|
|
175
|
-
|
|
181
|
+
let wrappedCss = css;
|
|
182
|
+
if (layer) {
|
|
183
|
+
// add layer to order in any case, even if the floor is not imported
|
|
184
|
+
if (!uniqueLayers.includes(layer)) {
|
|
185
|
+
uniqueLayers.push(layer);
|
|
186
|
+
}
|
|
187
|
+
wrappedCss = `@layer ${layer} {\n${css}\n}`;
|
|
188
|
+
}
|
|
176
189
|
await fs.writeFile(outputFile, wrappedCss, 'utf8');
|
|
177
|
-
|
|
190
|
+
floorFiles.push({ file: `${source}.css`, layer, source });
|
|
191
|
+
cssFiles.push({ floor: source, file: `${source}.css`, layer });
|
|
178
192
|
}
|
|
179
193
|
// 5. Create main CSS file
|
|
180
194
|
const mainCssPath = path.join(outputPath, mainCssFile);
|
|
195
|
+
// Type guard for cssFiles entries with type 'global' or 'media'
|
|
196
|
+
function isGlobalOrMedia(f) {
|
|
197
|
+
return ((f.type === 'global' || f.type === 'media') && typeof f.file === 'string');
|
|
198
|
+
}
|
|
181
199
|
// Compose imports for variable sets
|
|
182
|
-
|
|
183
|
-
.filter((f) => f.type === 'global' || f.type === 'media')
|
|
184
|
-
.map((f) => {
|
|
200
|
+
function toImportStatement(f) {
|
|
185
201
|
if (f.mediaQuery) {
|
|
186
202
|
return `@import '${f.file}' ${f.mediaQuery};`;
|
|
187
203
|
}
|
|
188
204
|
return `@import '${f.file}';`;
|
|
189
|
-
}
|
|
205
|
+
}
|
|
206
|
+
const varImports = cssFiles.filter(isGlobalOrMedia)
|
|
207
|
+
.map(toImportStatement)
|
|
190
208
|
.join('\n');
|
|
191
|
-
// Compose imports for
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
.
|
|
197
|
-
|
|
198
|
-
const
|
|
209
|
+
// Compose imports for floors (in order, only those in importFloors)
|
|
210
|
+
function isImportedFloor(f) {
|
|
211
|
+
return importFloors.includes(f.source);
|
|
212
|
+
}
|
|
213
|
+
function importStatement(f) {
|
|
214
|
+
return `@import '${f.file}';`;
|
|
215
|
+
}
|
|
216
|
+
const importedFloorFiles = floorFiles.filter(isImportedFloor);
|
|
217
|
+
const floorImports = importedFloorFiles.map(importStatement).join('\n');
|
|
218
|
+
const mainCss = [
|
|
219
|
+
uniqueLayers.length ? `@layer ${uniqueLayers.join(', ')};` : '',
|
|
220
|
+
floorImports,
|
|
221
|
+
varImports,
|
|
222
|
+
]
|
|
199
223
|
.filter(Boolean)
|
|
200
224
|
.join('\n') + '\n';
|
|
201
225
|
await fs.writeFile(mainCssPath, mainCss, 'utf8');
|
package/doc/usage-guide.md
CHANGED
|
@@ -73,12 +73,21 @@ export default {
|
|
|
73
73
|
outputPath: 'css',
|
|
74
74
|
sourceFilesSuffix: '.styles.mjs',
|
|
75
75
|
|
|
76
|
-
// Input layers
|
|
77
|
-
|
|
76
|
+
// Input files and their layers (order of layers matters)
|
|
77
|
+
floors: [
|
|
78
|
+
{ source: 'defaults', layer: 'defaults' }, // wrap in layer 'defaults'
|
|
79
|
+
{ source: 'components', layer: 'components' }, // wrap in layer 'components'
|
|
80
|
+
{ source: 'layout', layer: 'layout' }, // wrap in layer 'layout'
|
|
81
|
+
{ source: 'basic' }, // stay out of any layer
|
|
82
|
+
{ source: 'more-layout', layer: 'layout' }, // wrap in layer 'layout'
|
|
83
|
+
],
|
|
78
84
|
|
|
79
85
|
// Output
|
|
80
86
|
mainCssFile: 'styles.css',
|
|
81
87
|
|
|
88
|
+
// floors to include in the main CSS file
|
|
89
|
+
importFloors: ['defaults', 'components', 'layout'],
|
|
90
|
+
|
|
82
91
|
// CSS Variables configuration
|
|
83
92
|
globalVariables: 'global',
|
|
84
93
|
globalRootSelector: ':root',
|