emily-css 1.0.28 → 1.0.29
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/index.js +38 -0
- package/src/manifest.js +307 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { generateManifest } = require('./manifest');
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
// ============================================================================
|
|
@@ -1479,6 +1480,32 @@ function getProductionCssPath(config) {
|
|
|
1479
1480
|
return path.join(process.cwd(), config.output?.css || 'dist/emily.min.css');
|
|
1480
1481
|
}
|
|
1481
1482
|
|
|
1483
|
+
function getManifestSettings(config) {
|
|
1484
|
+
const manifestConfig = config.manifest;
|
|
1485
|
+
|
|
1486
|
+
if (manifestConfig === true) {
|
|
1487
|
+
return { enabled: true, output: 'dist/emily.manifest.json' };
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
if (manifestConfig && typeof manifestConfig === 'object') {
|
|
1491
|
+
return {
|
|
1492
|
+
enabled: manifestConfig.enabled === true,
|
|
1493
|
+
output: manifestConfig.output || 'dist/emily.manifest.json',
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
return { enabled: false, output: 'dist/emily.manifest.json' };
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
function getManifestOutputPath(config) {
|
|
1501
|
+
const manifestSettings = getManifestSettings(config);
|
|
1502
|
+
const outputPath = manifestSettings.output || 'dist/emily.manifest.json';
|
|
1503
|
+
|
|
1504
|
+
return path.isAbsolute(outputPath)
|
|
1505
|
+
? outputPath
|
|
1506
|
+
: path.join(process.cwd(), outputPath);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1482
1509
|
function ensureDirectoryForFile(filePath) {
|
|
1483
1510
|
const dir = path.dirname(filePath);
|
|
1484
1511
|
|
|
@@ -1700,6 +1727,16 @@ ${bodyFont}`;
|
|
|
1700
1727
|
ensureDirectoryForFile(fullCssPath);
|
|
1701
1728
|
fs.writeFileSync(fullCssPath, css);
|
|
1702
1729
|
|
|
1730
|
+
const manifestSettings = getManifestSettings(config);
|
|
1731
|
+
if (manifestSettings.enabled) {
|
|
1732
|
+
const manifestPath = getManifestOutputPath(config);
|
|
1733
|
+
const manifest = generateManifest(css, config);
|
|
1734
|
+
|
|
1735
|
+
ensureDirectoryForFile(manifestPath);
|
|
1736
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
1737
|
+
console.log(`✓ Generated manifest: ${manifestPath}`);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1703
1740
|
console.log(`✓ Generated CSS: ${fullCssPath}`);
|
|
1704
1741
|
console.log(`✓ File size: ${(css.length / 1024).toFixed(2)} KB (unminified)`);
|
|
1705
1742
|
console.log('Full framework build complete');
|
|
@@ -1808,6 +1845,7 @@ module.exports = {
|
|
|
1808
1845
|
addStateVariants,
|
|
1809
1846
|
addAriaDataVariants,
|
|
1810
1847
|
addResponsiveVariants,
|
|
1848
|
+
generateManifest,
|
|
1811
1849
|
generateFontCSS,
|
|
1812
1850
|
codeUtilities,
|
|
1813
1851
|
};
|
package/src/manifest.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
const MANIFEST_VERSION = '1.1.0';
|
|
2
|
+
const DEFAULT_RESPONSIVE_VARIANTS = ['sm', 'md', 'lg', 'xl', '2xl'];
|
|
3
|
+
const BASE_VARIANTS = [
|
|
4
|
+
'hover',
|
|
5
|
+
'focus',
|
|
6
|
+
'focus-within',
|
|
7
|
+
'focus-visible',
|
|
8
|
+
'active',
|
|
9
|
+
'disabled',
|
|
10
|
+
'motion-reduce',
|
|
11
|
+
'motion-safe',
|
|
12
|
+
'aria-expanded',
|
|
13
|
+
'aria-selected',
|
|
14
|
+
'aria-current',
|
|
15
|
+
'aria-disabled',
|
|
16
|
+
'data-open',
|
|
17
|
+
'data-closed',
|
|
18
|
+
'dark',
|
|
19
|
+
'forced-colors',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
function parseDeclarations(block) {
|
|
23
|
+
const declarations = {};
|
|
24
|
+
const declarationRegex = /([a-zA-Z-]+)\s*:\s*([^;]+)\s*;?/g;
|
|
25
|
+
let firstProperty = null;
|
|
26
|
+
let firstValue = null;
|
|
27
|
+
let match;
|
|
28
|
+
|
|
29
|
+
while ((match = declarationRegex.exec(block)) !== null) {
|
|
30
|
+
const property = match[1].trim();
|
|
31
|
+
const value = match[2].trim();
|
|
32
|
+
declarations[property] = value;
|
|
33
|
+
|
|
34
|
+
if (!firstProperty) {
|
|
35
|
+
firstProperty = property;
|
|
36
|
+
firstValue = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { declarations, firstProperty, firstValue };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getTokenFromDeclarations(declarations) {
|
|
44
|
+
const values = Object.values(declarations);
|
|
45
|
+
|
|
46
|
+
for (const value of values) {
|
|
47
|
+
const tokenMatch = value.match(/var\(\s*(--[a-zA-Z0-9-_]+)\s*(?:,[^)]+)?\)/);
|
|
48
|
+
if (tokenMatch) {
|
|
49
|
+
return tokenMatch[1];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isSimpleBaseClassSelector(selector) {
|
|
57
|
+
if (!selector || !selector.startsWith('.')) return false;
|
|
58
|
+
if (selector.includes(' ')) return false;
|
|
59
|
+
if (selector.includes(',')) return false;
|
|
60
|
+
if (selector.includes('[')) return false;
|
|
61
|
+
if (selector.includes(':')) return false;
|
|
62
|
+
if (selector.includes('::')) return false;
|
|
63
|
+
if (selector.includes('>')) return false;
|
|
64
|
+
if (selector.includes('+')) return false;
|
|
65
|
+
if (selector.includes('~')) return false;
|
|
66
|
+
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function inferCategory(className, property) {
|
|
71
|
+
if (
|
|
72
|
+
className === 'prose' ||
|
|
73
|
+
className === 'prose-emily' ||
|
|
74
|
+
className === 'center-screen' ||
|
|
75
|
+
className === 'center-absolute' ||
|
|
76
|
+
className === 'field-container' ||
|
|
77
|
+
className === 'form-hint' ||
|
|
78
|
+
className === 'form-error-message' ||
|
|
79
|
+
className === 'error-summary' ||
|
|
80
|
+
className === 'btn' ||
|
|
81
|
+
className === 'btn-primary' ||
|
|
82
|
+
className === 'btn-secondary' ||
|
|
83
|
+
className === 'btn-ghost' ||
|
|
84
|
+
className === 'btn-danger' ||
|
|
85
|
+
className === 'btn-sm' ||
|
|
86
|
+
className === 'btn-lg' ||
|
|
87
|
+
className === 'code-window' ||
|
|
88
|
+
className === 'code-title-bar' ||
|
|
89
|
+
className === 'code-dot' ||
|
|
90
|
+
className === 'code-dot-red' ||
|
|
91
|
+
className === 'code-dot-yellow' ||
|
|
92
|
+
className === 'code-dot-green' ||
|
|
93
|
+
className === 'code-filename' ||
|
|
94
|
+
className.startsWith('token-')
|
|
95
|
+
) {
|
|
96
|
+
return 'component';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (className.startsWith('text-')) {
|
|
100
|
+
return property === 'color' ? 'colour' : 'typography';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
className.startsWith('font-') ||
|
|
105
|
+
className.startsWith('leading-') ||
|
|
106
|
+
className.startsWith('tracking-') ||
|
|
107
|
+
className.startsWith('list-')
|
|
108
|
+
) {
|
|
109
|
+
return 'typography';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (className.startsWith('bg-')) return 'background';
|
|
113
|
+
|
|
114
|
+
if (
|
|
115
|
+
className === 'border' ||
|
|
116
|
+
className.startsWith('border-') ||
|
|
117
|
+
className.startsWith('divide-') ||
|
|
118
|
+
className === 'outline' ||
|
|
119
|
+
className.startsWith('outline-') ||
|
|
120
|
+
className === 'ring' ||
|
|
121
|
+
className.startsWith('ring-')
|
|
122
|
+
) {
|
|
123
|
+
return 'border';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
className.startsWith('p-') ||
|
|
128
|
+
className.startsWith('px-') ||
|
|
129
|
+
className.startsWith('py-') ||
|
|
130
|
+
className.startsWith('pt-') ||
|
|
131
|
+
className.startsWith('pr-') ||
|
|
132
|
+
className.startsWith('pb-') ||
|
|
133
|
+
className.startsWith('pl-') ||
|
|
134
|
+
className.startsWith('m-') ||
|
|
135
|
+
className.startsWith('mx-') ||
|
|
136
|
+
className.startsWith('my-') ||
|
|
137
|
+
className.startsWith('mt-') ||
|
|
138
|
+
className.startsWith('mr-') ||
|
|
139
|
+
className.startsWith('mb-') ||
|
|
140
|
+
className.startsWith('ml-') ||
|
|
141
|
+
className.startsWith('gap-') ||
|
|
142
|
+
className.startsWith('space-') ||
|
|
143
|
+
className.startsWith('inset-') ||
|
|
144
|
+
className.startsWith('top-') ||
|
|
145
|
+
className.startsWith('right-') ||
|
|
146
|
+
className.startsWith('bottom-') ||
|
|
147
|
+
className.startsWith('left-')
|
|
148
|
+
) {
|
|
149
|
+
return 'spacing';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
className.startsWith('w-') ||
|
|
154
|
+
className.startsWith('h-') ||
|
|
155
|
+
className.startsWith('min-w-') ||
|
|
156
|
+
className.startsWith('max-w-') ||
|
|
157
|
+
className.startsWith('min-h-') ||
|
|
158
|
+
className.startsWith('max-h-') ||
|
|
159
|
+
className.startsWith('size-')
|
|
160
|
+
) {
|
|
161
|
+
return 'sizing';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (
|
|
165
|
+
className === 'flex' ||
|
|
166
|
+
className === 'grid' ||
|
|
167
|
+
className === 'block' ||
|
|
168
|
+
className === 'inline' ||
|
|
169
|
+
className === 'inline-block' ||
|
|
170
|
+
className === 'inline-flex' ||
|
|
171
|
+
className === 'hidden' ||
|
|
172
|
+
className === 'container' ||
|
|
173
|
+
className === 'relative' ||
|
|
174
|
+
className === 'absolute' ||
|
|
175
|
+
className === 'fixed' ||
|
|
176
|
+
className === 'sticky' ||
|
|
177
|
+
className === 'static'
|
|
178
|
+
) {
|
|
179
|
+
return 'layout';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
className.startsWith('items-') ||
|
|
184
|
+
className.startsWith('justify-') ||
|
|
185
|
+
className.startsWith('content-') ||
|
|
186
|
+
className.startsWith('self-') ||
|
|
187
|
+
className.startsWith('place-') ||
|
|
188
|
+
className.startsWith('order-') ||
|
|
189
|
+
className.startsWith('col-') ||
|
|
190
|
+
className.startsWith('row-')
|
|
191
|
+
) {
|
|
192
|
+
return 'layout';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (className === 'rounded' || className.startsWith('rounded-')) return 'radius';
|
|
196
|
+
if (className === 'shadow' || className.startsWith('shadow-')) return 'shadow';
|
|
197
|
+
|
|
198
|
+
if (
|
|
199
|
+
className.startsWith('opacity-') ||
|
|
200
|
+
className.startsWith('blur-') ||
|
|
201
|
+
className.startsWith('backdrop-') ||
|
|
202
|
+
className === 'filter' ||
|
|
203
|
+
className === 'grayscale' ||
|
|
204
|
+
className.startsWith('saturate-') ||
|
|
205
|
+
className.startsWith('brightness-') ||
|
|
206
|
+
className.startsWith('contrast-')
|
|
207
|
+
) {
|
|
208
|
+
return 'effects';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
className === 'transition' ||
|
|
213
|
+
className.startsWith('transition-') ||
|
|
214
|
+
className.startsWith('duration-') ||
|
|
215
|
+
className.startsWith('ease-') ||
|
|
216
|
+
className.startsWith('delay-') ||
|
|
217
|
+
className.startsWith('animate-')
|
|
218
|
+
) {
|
|
219
|
+
return 'motion';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
className === 'sr-only' ||
|
|
224
|
+
className === 'not-sr-only' ||
|
|
225
|
+
className === 'focus-ring' ||
|
|
226
|
+
className === 'skip-to-content' ||
|
|
227
|
+
className === 'js-hidden' ||
|
|
228
|
+
className === 'no-js'
|
|
229
|
+
) {
|
|
230
|
+
return 'accessibility';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
className === 'input' ||
|
|
235
|
+
className === 'select' ||
|
|
236
|
+
className === 'textarea' ||
|
|
237
|
+
className === 'checkbox' ||
|
|
238
|
+
className === 'radio' ||
|
|
239
|
+
className === 'stack' ||
|
|
240
|
+
className === 'cluster' ||
|
|
241
|
+
className === 'width-container'
|
|
242
|
+
) {
|
|
243
|
+
return 'component';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return 'utility';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function normalizeClassName(selector) {
|
|
250
|
+
return selector.slice(1).replace(/\\(.)/g, '$1');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function getVariants(config) {
|
|
254
|
+
const breakpoints =
|
|
255
|
+
config &&
|
|
256
|
+
config.breakpoints &&
|
|
257
|
+
typeof config.breakpoints === 'object' &&
|
|
258
|
+
Object.keys(config.breakpoints).length > 0
|
|
259
|
+
? Object.keys(config.breakpoints)
|
|
260
|
+
: DEFAULT_RESPONSIVE_VARIANTS;
|
|
261
|
+
|
|
262
|
+
return [...BASE_VARIANTS, ...breakpoints];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function generateManifest(css, config = {}) {
|
|
266
|
+
const manifest = {
|
|
267
|
+
version: MANIFEST_VERSION,
|
|
268
|
+
generatedAt: new Date().toISOString(),
|
|
269
|
+
utilities: [],
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
if (typeof css !== 'string' || css.length === 0) {
|
|
273
|
+
return manifest;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const variants = getVariants(config);
|
|
277
|
+
const cleanedCss = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
278
|
+
const ruleRegex = /([^{}]+)\{([^{}]*)\}/g;
|
|
279
|
+
let ruleMatch;
|
|
280
|
+
|
|
281
|
+
while ((ruleMatch = ruleRegex.exec(cleanedCss)) !== null) {
|
|
282
|
+
const selector = ruleMatch[1].trim();
|
|
283
|
+
const declarationBlock = ruleMatch[2].trim();
|
|
284
|
+
|
|
285
|
+
if (!isSimpleBaseClassSelector(selector)) continue;
|
|
286
|
+
|
|
287
|
+
const { declarations, firstProperty, firstValue } = parseDeclarations(declarationBlock);
|
|
288
|
+
if (!firstProperty) continue;
|
|
289
|
+
|
|
290
|
+
manifest.utilities.push({
|
|
291
|
+
class: normalizeClassName(selector),
|
|
292
|
+
category: inferCategory(normalizeClassName(selector), firstProperty),
|
|
293
|
+
property: firstProperty,
|
|
294
|
+
value: firstValue,
|
|
295
|
+
token: getTokenFromDeclarations(declarations),
|
|
296
|
+
declarations,
|
|
297
|
+
variants,
|
|
298
|
+
source: 'generated-css',
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return manifest;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
module.exports = {
|
|
306
|
+
generateManifest,
|
|
307
|
+
};
|