emily-css 1.2.7 → 1.2.9
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 +45 -1
- package/README.md +18 -0
- package/package.json +1 -1
- package/src/generators/background.js +11 -0
- package/src/generators/display.js +2 -0
- package/src/generators/effects.js +2 -0
- package/src/generators/overflow.js +9 -0
- package/src/generators/positioning.js +14 -0
- package/src/generators/sizing.js +1 -0
- package/src/generators/transforms.js +7 -1
- package/src/index.js +1890 -1849
- package/src/init.js +1011 -812
- package/src/migrate.js +6 -0
- package/src/purge.js +8 -0
- package/src/purgeConfig.js +9 -1
package/src/init.js
CHANGED
|
@@ -1,812 +1,1011 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const crossSpawn = require("cross-spawn");
|
|
4
|
-
const { Select, Input, Confirm } = require("enquirer");
|
|
5
|
-
const chalk = require("chalk");
|
|
6
|
-
const ora = require("ora");
|
|
7
|
-
const boxen = require("boxen");
|
|
8
|
-
const { DEFAULT_PURGE_IGNORE, PURGE_EXTENSIONS } = require("./constants.js");
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// CONSTANTS
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
|
-
const COLOUR_PRESETS = {
|
|
15
|
-
primary: [
|
|
16
|
-
{ value: "custom", label: "Enter your own hex" },
|
|
17
|
-
{ value: "#DB2777", label: "Emily Pink" },
|
|
18
|
-
{ value: "#2563EB", label: "Blue" },
|
|
19
|
-
{ value: "#028090", label: "Teal" },
|
|
20
|
-
{ value: "#114B5F", label: "Deep Teal" },
|
|
21
|
-
{ value: "#15803D", label: "Green" },
|
|
22
|
-
{ value: "#7C3AED", label: "Purple" },
|
|
23
|
-
{ value: "#E05C00", label: "Burnt Orange" },
|
|
24
|
-
],
|
|
25
|
-
secondary: [
|
|
26
|
-
{ value: "custom", label: "Enter your own hex" },
|
|
27
|
-
{ value: "#2563EB", label: "Blue" },
|
|
28
|
-
{ value: "#028090", label: "Teal" },
|
|
29
|
-
{ value: "#7C3AED", label: "Purple" },
|
|
30
|
-
{ value: "#DB2777", label: "Emily Pink" },
|
|
31
|
-
{ value: "#F59E0B", label: "Amber" },
|
|
32
|
-
{ value: "#57534E", label: "Warm Grey" },
|
|
33
|
-
],
|
|
34
|
-
success: [
|
|
35
|
-
{ value: "#017F65", label: "Accessible Green (recommended)" },
|
|
36
|
-
{ value: "#15803D", label: "Forest Green" },
|
|
37
|
-
{ value: "custom", label: "Enter your own hex" },
|
|
38
|
-
],
|
|
39
|
-
warning: [
|
|
40
|
-
{ value: "#FFC107", label: "Amber (recommended)" },
|
|
41
|
-
{ value: "#F59E0B", label: "Orange Amber" },
|
|
42
|
-
{ value: "custom", label: "Enter your own hex" },
|
|
43
|
-
],
|
|
44
|
-
error: [
|
|
45
|
-
{ value: "#B20000", label: "Accessible Red (recommended)" },
|
|
46
|
-
{ value: "#DC2626", label: "Red" },
|
|
47
|
-
{ value: "custom", label: "Enter your own hex" },
|
|
48
|
-
],
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const FONT_OPTIONS = [
|
|
52
|
-
{ name: "lexend", message: "Lexend (clear, accessible - recommended)" },
|
|
53
|
-
{ name: "inter", message: "Inter (clean, widely used)" },
|
|
54
|
-
{ name: "dm-sans", message: "DM Sans (modern, geometric)" },
|
|
55
|
-
{ name: "nunito", message: "Nunito (friendly, rounded)" },
|
|
56
|
-
{ name: "atkinson", message: "Atkinson Hyperlegible (maximum legibility)" },
|
|
57
|
-
{ name: "system", message: "System sans-serif (no download required)" },
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
"
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
},
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
name: "
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
//
|
|
675
|
-
//
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const crossSpawn = require("cross-spawn");
|
|
4
|
+
const { Select, Input, Confirm } = require("enquirer");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const ora = require("ora");
|
|
7
|
+
const boxen = require("boxen");
|
|
8
|
+
const { DEFAULT_PURGE_IGNORE, PURGE_EXTENSIONS } = require("./constants.js");
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
const COLOUR_PRESETS = {
|
|
15
|
+
primary: [
|
|
16
|
+
{ value: "custom", label: "Enter your own hex" },
|
|
17
|
+
{ value: "#DB2777", label: "Emily Pink" },
|
|
18
|
+
{ value: "#2563EB", label: "Blue" },
|
|
19
|
+
{ value: "#028090", label: "Teal" },
|
|
20
|
+
{ value: "#114B5F", label: "Deep Teal" },
|
|
21
|
+
{ value: "#15803D", label: "Green" },
|
|
22
|
+
{ value: "#7C3AED", label: "Purple" },
|
|
23
|
+
{ value: "#E05C00", label: "Burnt Orange" },
|
|
24
|
+
],
|
|
25
|
+
secondary: [
|
|
26
|
+
{ value: "custom", label: "Enter your own hex" },
|
|
27
|
+
{ value: "#2563EB", label: "Blue" },
|
|
28
|
+
{ value: "#028090", label: "Teal" },
|
|
29
|
+
{ value: "#7C3AED", label: "Purple" },
|
|
30
|
+
{ value: "#DB2777", label: "Emily Pink" },
|
|
31
|
+
{ value: "#F59E0B", label: "Amber" },
|
|
32
|
+
{ value: "#57534E", label: "Warm Grey" },
|
|
33
|
+
],
|
|
34
|
+
success: [
|
|
35
|
+
{ value: "#017F65", label: "Accessible Green (recommended)" },
|
|
36
|
+
{ value: "#15803D", label: "Forest Green" },
|
|
37
|
+
{ value: "custom", label: "Enter your own hex" },
|
|
38
|
+
],
|
|
39
|
+
warning: [
|
|
40
|
+
{ value: "#FFC107", label: "Amber (recommended)" },
|
|
41
|
+
{ value: "#F59E0B", label: "Orange Amber" },
|
|
42
|
+
{ value: "custom", label: "Enter your own hex" },
|
|
43
|
+
],
|
|
44
|
+
error: [
|
|
45
|
+
{ value: "#B20000", label: "Accessible Red (recommended)" },
|
|
46
|
+
{ value: "#DC2626", label: "Red" },
|
|
47
|
+
{ value: "custom", label: "Enter your own hex" },
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const FONT_OPTIONS = [
|
|
52
|
+
{ name: "lexend", message: "Lexend (clear, accessible - recommended)" },
|
|
53
|
+
{ name: "inter", message: "Inter (clean, widely used)" },
|
|
54
|
+
{ name: "dm-sans", message: "DM Sans (modern, geometric)" },
|
|
55
|
+
{ name: "nunito", message: "Nunito (friendly, rounded)" },
|
|
56
|
+
{ name: "atkinson", message: "Atkinson Hyperlegible (maximum legibility)" },
|
|
57
|
+
{ name: "system", message: "System sans-serif (no download required)" },
|
|
58
|
+
];
|
|
59
|
+
const CORE_COLOUR_KEYS = new Set([
|
|
60
|
+
"brand",
|
|
61
|
+
"accent",
|
|
62
|
+
"btn-primary",
|
|
63
|
+
"btn-secondary",
|
|
64
|
+
"success",
|
|
65
|
+
"warning",
|
|
66
|
+
"error",
|
|
67
|
+
"neutral",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// HELPERS
|
|
72
|
+
// ============================================================================
|
|
73
|
+
|
|
74
|
+
function isValidHex(hex) {
|
|
75
|
+
return /^#[0-9A-F]{6}$/i.test(hex);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isPlainObject(value) {
|
|
79
|
+
return (
|
|
80
|
+
value !== null &&
|
|
81
|
+
typeof value === "object" &&
|
|
82
|
+
!Array.isArray(value)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function mergeWithDefaults(defaults, existing) {
|
|
87
|
+
if (!isPlainObject(defaults)) {
|
|
88
|
+
return existing === undefined ? defaults : existing;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const output = { ...defaults };
|
|
92
|
+
|
|
93
|
+
if (!isPlainObject(existing)) {
|
|
94
|
+
return output;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Object.keys(existing).forEach((key) => {
|
|
98
|
+
if (isPlainObject(defaults[key]) && isPlainObject(existing[key])) {
|
|
99
|
+
output[key] = mergeWithDefaults(defaults[key], existing[key]);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
output[key] = existing[key];
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return output;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function colourSwatch(hex) {
|
|
110
|
+
return chalk.hex(hex)("■");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normaliseHex(value) {
|
|
114
|
+
return typeof value === "string" && isValidHex(value)
|
|
115
|
+
? value.toUpperCase()
|
|
116
|
+
: null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function askHex(promptName, message, initial) {
|
|
120
|
+
const value = await new Input({
|
|
121
|
+
name: promptName,
|
|
122
|
+
message,
|
|
123
|
+
initial: initial || "#000000",
|
|
124
|
+
validate(value) {
|
|
125
|
+
return isValidHex(value)
|
|
126
|
+
? true
|
|
127
|
+
: "Enter a valid hex colour, e.g. #0077B6";
|
|
128
|
+
},
|
|
129
|
+
}).run();
|
|
130
|
+
|
|
131
|
+
return value.toUpperCase();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function askColourFromPresets(label, presets, defaultHex, currentHex) {
|
|
135
|
+
const defaultHexValue = normaliseHex(defaultHex);
|
|
136
|
+
const currentHexValue = normaliseHex(currentHex);
|
|
137
|
+
|
|
138
|
+
const choices = presets.map(function (opt) {
|
|
139
|
+
if (opt.value === "custom") {
|
|
140
|
+
return { name: "custom", message: "Enter your own hex" };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const upperHex = String(opt.value).toUpperCase();
|
|
144
|
+
return {
|
|
145
|
+
name: upperHex,
|
|
146
|
+
message:
|
|
147
|
+
colourSwatch(upperHex) + " " + opt.label + " " + chalk.gray(upperHex),
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
let initial = Math.max(
|
|
152
|
+
0,
|
|
153
|
+
choices.findIndex((choice) => choice.name === "custom"),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (currentHexValue) {
|
|
157
|
+
const currentIndex = choices.findIndex(
|
|
158
|
+
(choice) => choice.name === currentHexValue,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (currentIndex !== -1) {
|
|
162
|
+
initial = currentIndex;
|
|
163
|
+
} else {
|
|
164
|
+
choices.unshift({
|
|
165
|
+
name: "__current__",
|
|
166
|
+
message:
|
|
167
|
+
"Keep current " +
|
|
168
|
+
label +
|
|
169
|
+
" " +
|
|
170
|
+
colourSwatch(currentHexValue) +
|
|
171
|
+
" " +
|
|
172
|
+
chalk.gray(currentHexValue),
|
|
173
|
+
});
|
|
174
|
+
initial = 0;
|
|
175
|
+
}
|
|
176
|
+
} else if (defaultHexValue) {
|
|
177
|
+
const defaultIndex = choices.findIndex(
|
|
178
|
+
(choice) => choice.name === defaultHexValue,
|
|
179
|
+
);
|
|
180
|
+
if (defaultIndex !== -1) {
|
|
181
|
+
initial = defaultIndex;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const selected = await new Select({
|
|
186
|
+
name: label,
|
|
187
|
+
message: label + " colour",
|
|
188
|
+
choices,
|
|
189
|
+
initial,
|
|
190
|
+
}).run();
|
|
191
|
+
|
|
192
|
+
if (selected === "__current__" && currentHexValue) return currentHexValue;
|
|
193
|
+
if (selected !== "custom") return selected.toUpperCase();
|
|
194
|
+
|
|
195
|
+
const fallbackHex = currentHexValue || defaultHexValue || "#000000";
|
|
196
|
+
return askHex(label + "Custom", "Enter " + label + " hex", fallbackHex);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function hasFile(fileName) {
|
|
200
|
+
return fs.existsSync(path.join(process.cwd(), fileName));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function readPackageJson() {
|
|
204
|
+
const packagePath = path.join(process.cwd(), "package.json");
|
|
205
|
+
|
|
206
|
+
if (!fs.existsSync(packagePath)) return null;
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
return JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function readExistingConfig() {
|
|
216
|
+
const configPath = path.join(process.cwd(), "emily.config.json");
|
|
217
|
+
|
|
218
|
+
if (!fs.existsSync(configPath)) return null;
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
return JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getFontInitialIndex(fontKey, fallbackIndex) {
|
|
228
|
+
if (!fontKey || typeof fontKey !== "string") return fallbackIndex;
|
|
229
|
+
const normalised = fontKey.toLowerCase();
|
|
230
|
+
const index = FONT_OPTIONS.findIndex((option) => option.name === normalised);
|
|
231
|
+
return index === -1 ? fallbackIndex : index;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getExistingAdditionalColours(existingColours) {
|
|
235
|
+
if (!isPlainObject(existingColours)) return {};
|
|
236
|
+
|
|
237
|
+
const additional = {};
|
|
238
|
+
Object.entries(existingColours).forEach(([name, value]) => {
|
|
239
|
+
if (CORE_COLOUR_KEYS.has(name)) return;
|
|
240
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) return;
|
|
241
|
+
|
|
242
|
+
const upperHex = normaliseHex(value);
|
|
243
|
+
if (!upperHex) return;
|
|
244
|
+
additional[name] = upperHex;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
return additional;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function getBaseUnitInitial(config) {
|
|
251
|
+
const rawBaseUnit = config && typeof config.baseUnit === "string"
|
|
252
|
+
? config.baseUnit
|
|
253
|
+
: "";
|
|
254
|
+
const parsed = Number.parseInt(rawBaseUnit, 10);
|
|
255
|
+
if (Number.isNaN(parsed) || parsed <= 0) return "18";
|
|
256
|
+
return String(parsed);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function hasDependency(packageJson, dependencyName) {
|
|
260
|
+
if (!packageJson) return false;
|
|
261
|
+
|
|
262
|
+
return Boolean(
|
|
263
|
+
packageJson.dependencies?.[dependencyName] ||
|
|
264
|
+
packageJson.devDependencies?.[dependencyName],
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function titleCasePackageName(name) {
|
|
269
|
+
return name.replace(/-/g, " ").replace(/\b\w/g, function (c) {
|
|
270
|
+
return c.toUpperCase();
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function addEmilyScriptsToPackageJson() {
|
|
275
|
+
const packagePath = path.join(process.cwd(), "package.json");
|
|
276
|
+
|
|
277
|
+
if (!fs.existsSync(packagePath)) return false;
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
281
|
+
|
|
282
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
283
|
+
|
|
284
|
+
let changed = false;
|
|
285
|
+
|
|
286
|
+
const scripts = {
|
|
287
|
+
"emily:build": "emily-css build",
|
|
288
|
+
"emily:watch": "emily-css watch",
|
|
289
|
+
"emily:doctor": "emily-css doctor",
|
|
290
|
+
"emily:migrate": "emily-css migrate",
|
|
291
|
+
"emily:info": "emily-css info",
|
|
292
|
+
"emily:manifest": "emily-css manifest",
|
|
293
|
+
"emily:version": "emily-css version",
|
|
294
|
+
"emily:help": "emily-css help",
|
|
295
|
+
"emily:showcase": "emily-css showcase",
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
for (const [key, value] of Object.entries(scripts)) {
|
|
299
|
+
if (!packageJson.scripts[key]) {
|
|
300
|
+
packageJson.scripts[key] = value;
|
|
301
|
+
changed = true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (changed) {
|
|
306
|
+
fs.writeFileSync(
|
|
307
|
+
packagePath,
|
|
308
|
+
JSON.stringify(packageJson, null, 2) + "\n",
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return true;
|
|
313
|
+
} catch {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// PROJECT DETECTION
|
|
320
|
+
// ============================================================================
|
|
321
|
+
|
|
322
|
+
function detectProject() {
|
|
323
|
+
const packageJson = readPackageJson();
|
|
324
|
+
|
|
325
|
+
if (
|
|
326
|
+
hasFile("nuxt.config.ts") ||
|
|
327
|
+
hasFile("nuxt.config.js") ||
|
|
328
|
+
hasDependency(packageJson, "nuxt")
|
|
329
|
+
) {
|
|
330
|
+
return {
|
|
331
|
+
name: "Nuxt",
|
|
332
|
+
sourceDir: ".",
|
|
333
|
+
outputPath: "public/emily.min.css",
|
|
334
|
+
sourceGlobs: [
|
|
335
|
+
"./components/**/*.{vue,js,ts}",
|
|
336
|
+
"./pages/**/*.vue",
|
|
337
|
+
"./layouts/**/*.vue",
|
|
338
|
+
"./app.vue",
|
|
339
|
+
],
|
|
340
|
+
linkHint: '<link rel="stylesheet" href="/emily.min.css">',
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (hasDependency(packageJson, "next")) {
|
|
345
|
+
return {
|
|
346
|
+
name: "Next.js",
|
|
347
|
+
sourceDir: ".",
|
|
348
|
+
outputPath: "public/emily.min.css",
|
|
349
|
+
sourceGlobs: [
|
|
350
|
+
"./app/**/*.{js,jsx,ts,tsx}",
|
|
351
|
+
"./pages/**/*.{js,jsx,ts,tsx}",
|
|
352
|
+
"./components/**/*.{js,jsx,ts,tsx}",
|
|
353
|
+
"./src/**/*.{js,jsx,ts,tsx}",
|
|
354
|
+
],
|
|
355
|
+
linkHint: '<link rel="stylesheet" href="/emily.min.css">',
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (hasDependency(packageJson, "react")) {
|
|
360
|
+
return {
|
|
361
|
+
name: "React",
|
|
362
|
+
sourceDir: "./src",
|
|
363
|
+
outputPath: hasFile("public")
|
|
364
|
+
? "public/emily.min.css"
|
|
365
|
+
: "dist/emily.min.css",
|
|
366
|
+
sourceGlobs: [
|
|
367
|
+
"./src/**/*.{js,jsx,ts,tsx}",
|
|
368
|
+
"./components/**/*.{js,jsx,ts,tsx}",
|
|
369
|
+
],
|
|
370
|
+
linkHint: hasFile("public")
|
|
371
|
+
? '<link rel="stylesheet" href="/emily.min.css">'
|
|
372
|
+
: '<link rel="stylesheet" href="./dist/emily.min.css">',
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (
|
|
377
|
+
hasDependency(packageJson, "vue") ||
|
|
378
|
+
hasFile("vite.config.ts") ||
|
|
379
|
+
hasFile("vite.config.js")
|
|
380
|
+
) {
|
|
381
|
+
return {
|
|
382
|
+
name: "Vue/Vite",
|
|
383
|
+
sourceDir: "./src",
|
|
384
|
+
outputPath: "public/emily.min.css",
|
|
385
|
+
sourceGlobs: ["./src/**/*.{vue,js,ts}"],
|
|
386
|
+
linkHint: '<link rel="stylesheet" href="/emily.min.css">',
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (hasDependency(packageJson, "astro") || hasFile("astro.config.mjs")) {
|
|
391
|
+
return {
|
|
392
|
+
name: "Astro",
|
|
393
|
+
sourceDir: "./src",
|
|
394
|
+
outputPath: "public/emily.min.css",
|
|
395
|
+
sourceGlobs: ["./src/**/*.{astro,html,js,ts,vue,jsx,tsx,svelte}"],
|
|
396
|
+
linkHint: '<link rel="stylesheet" href="/emily.min.css">',
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const rootFiles = fs.readdirSync(process.cwd());
|
|
401
|
+
const hasDrupalInfoFile = rootFiles.some(function (file) {
|
|
402
|
+
return file.endsWith(".info.yml");
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
hasDrupalInfoFile ||
|
|
407
|
+
fs.existsSync(path.join(process.cwd(), "web/core"))
|
|
408
|
+
) {
|
|
409
|
+
return {
|
|
410
|
+
name: "Drupal",
|
|
411
|
+
sourceDir: ".",
|
|
412
|
+
outputPath: "dist/emily.min.css",
|
|
413
|
+
sourceGlobs: [
|
|
414
|
+
"./web/themes/custom/**/*.{twig,js,ts}",
|
|
415
|
+
"./templates/**/*.html.twig",
|
|
416
|
+
"./components/**/*.twig",
|
|
417
|
+
"./**/*.theme",
|
|
418
|
+
],
|
|
419
|
+
linkHint: "Attach dist/emily.min.css through your theme library YAML.",
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
name: "Static/Generic",
|
|
425
|
+
sourceDir: ".",
|
|
426
|
+
outputPath: "dist/emily.min.css",
|
|
427
|
+
sourceGlobs: [
|
|
428
|
+
"./**/*.{html,htm,twig,njk,liquid,hbs,php,astro,svelte,vue,blade.php,jinja,jinja2,j2}",
|
|
429
|
+
],
|
|
430
|
+
linkHint: '<link rel="stylesheet" href="./dist/emily.min.css">',
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ============================================================================
|
|
435
|
+
// CONFIG BUILDER
|
|
436
|
+
// ============================================================================
|
|
437
|
+
|
|
438
|
+
function createDefaultConfig({
|
|
439
|
+
name,
|
|
440
|
+
colours,
|
|
441
|
+
headingFont,
|
|
442
|
+
bodyFont,
|
|
443
|
+
baseUnit,
|
|
444
|
+
baseFontSize,
|
|
445
|
+
detectedProject,
|
|
446
|
+
}) {
|
|
447
|
+
return {
|
|
448
|
+
name,
|
|
449
|
+
description: name + " design system",
|
|
450
|
+
|
|
451
|
+
baseUnit: baseUnit + "px",
|
|
452
|
+
baseFontSize: baseFontSize || "16px",
|
|
453
|
+
|
|
454
|
+
fontFamily: {
|
|
455
|
+
heading: headingFont,
|
|
456
|
+
body: bodyFont,
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
customFonts: [],
|
|
460
|
+
|
|
461
|
+
output: {
|
|
462
|
+
css: detectedProject.outputPath,
|
|
463
|
+
fullCss: "dist/emily.css",
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
manifest: true,
|
|
467
|
+
|
|
468
|
+
colours,
|
|
469
|
+
|
|
470
|
+
semanticColours: {
|
|
471
|
+
dark: "#1A1A1A",
|
|
472
|
+
light: "#FAFAFA",
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
purge: {
|
|
476
|
+
projectType: detectedProject.name,
|
|
477
|
+
sourceDir: detectedProject.sourceDir,
|
|
478
|
+
sourceGlobs: detectedProject.sourceGlobs,
|
|
479
|
+
ignore: DEFAULT_PURGE_IGNORE,
|
|
480
|
+
safelist: [
|
|
481
|
+
"bg-dark",
|
|
482
|
+
"text-dark",
|
|
483
|
+
"border-dark",
|
|
484
|
+
"fill-dark",
|
|
485
|
+
"bg-light",
|
|
486
|
+
"text-light",
|
|
487
|
+
"border-light",
|
|
488
|
+
"fill-light",
|
|
489
|
+
],
|
|
490
|
+
extensions: PURGE_EXTENSIONS,
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
breakpoints: {
|
|
494
|
+
sm: "640px",
|
|
495
|
+
md: "768px",
|
|
496
|
+
lg: "1024px",
|
|
497
|
+
xl: "1280px",
|
|
498
|
+
"2xl": "1536px",
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
spacing: {
|
|
502
|
+
scale: {
|
|
503
|
+
0: "0px",
|
|
504
|
+
px: "1px",
|
|
505
|
+
0.5: "0.125rem",
|
|
506
|
+
1: "0.25rem",
|
|
507
|
+
1.5: "0.375rem",
|
|
508
|
+
2: "0.5rem",
|
|
509
|
+
2.5: "0.625rem",
|
|
510
|
+
3: "0.75rem",
|
|
511
|
+
3.5: "0.875rem",
|
|
512
|
+
4: "1rem",
|
|
513
|
+
5: "1.25rem",
|
|
514
|
+
6: "1.5rem",
|
|
515
|
+
7: "1.75rem",
|
|
516
|
+
8: "2rem",
|
|
517
|
+
9: "2.25rem",
|
|
518
|
+
10: "2.5rem",
|
|
519
|
+
11: "2.75rem",
|
|
520
|
+
12: "3rem",
|
|
521
|
+
14: "3.5rem",
|
|
522
|
+
16: "4rem",
|
|
523
|
+
20: "5rem",
|
|
524
|
+
24: "6rem",
|
|
525
|
+
28: "7rem",
|
|
526
|
+
32: "8rem",
|
|
527
|
+
36: "9rem",
|
|
528
|
+
40: "10rem",
|
|
529
|
+
44: "11rem",
|
|
530
|
+
48: "12rem",
|
|
531
|
+
52: "13rem",
|
|
532
|
+
56: "14rem",
|
|
533
|
+
60: "15rem",
|
|
534
|
+
64: "16rem",
|
|
535
|
+
72: "18rem",
|
|
536
|
+
80: "20rem",
|
|
537
|
+
96: "24rem",
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
borderWidths: [0, 2, 4, 8],
|
|
541
|
+
|
|
542
|
+
borderRadius: {
|
|
543
|
+
none: "0",
|
|
544
|
+
sm: "4px",
|
|
545
|
+
base: "8px",
|
|
546
|
+
md: "12px",
|
|
547
|
+
lg: "16px",
|
|
548
|
+
xl: "20px",
|
|
549
|
+
"2xl": "24px",
|
|
550
|
+
"3xl": "32px",
|
|
551
|
+
full: "9999px",
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
typography: {
|
|
556
|
+
lineHeightRatio: 1.5,
|
|
557
|
+
|
|
558
|
+
fontWeights: {
|
|
559
|
+
light: 300,
|
|
560
|
+
normal: 400,
|
|
561
|
+
medium: 500,
|
|
562
|
+
semibold: 600,
|
|
563
|
+
bold: 700,
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
fontSizes: [
|
|
567
|
+
{ name: "xs", value: "12px", lineHeight: 1.5 },
|
|
568
|
+
{ name: "sm", value: "14px", lineHeight: 1.5 },
|
|
569
|
+
{ name: "base", value: "16px", lineHeight: 1.6 },
|
|
570
|
+
{ name: "lg", value: "18px", lineHeight: 1.6 },
|
|
571
|
+
{ name: "xl", value: "20px", lineHeight: 1.6 },
|
|
572
|
+
{ name: "2xl", value: "24px", lineHeight: 1.4 },
|
|
573
|
+
{ name: "3xl", value: "30px", lineHeight: 1.4 },
|
|
574
|
+
{ name: "4xl", value: "36px", lineHeight: 1.3 },
|
|
575
|
+
{ name: "5xl", value: "48px", lineHeight: 1.15 },
|
|
576
|
+
{ name: "6xl", value: "60px", lineHeight: 1.1 },
|
|
577
|
+
{ name: "7xl", value: "72px", lineHeight: 1.05 },
|
|
578
|
+
{ name: "8xl", value: "96px", lineHeight: 1 },
|
|
579
|
+
{ name: "9xl", value: "128px", lineHeight: 1 },
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
|
|
583
|
+
shadows: {
|
|
584
|
+
sm: "0 1px 2px rgba(0, 0, 0, 0.05)",
|
|
585
|
+
base: "0 4px 6px rgba(0, 0, 0, 0.1)",
|
|
586
|
+
md: "0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)",
|
|
587
|
+
lg: "0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05)",
|
|
588
|
+
xl: "0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04)",
|
|
589
|
+
"2xl": "0 25px 50px rgba(0, 0, 0, 0.25)",
|
|
590
|
+
inner: "inset 0 2px 4px rgba(0, 0, 0, 0.06)",
|
|
591
|
+
none: "none",
|
|
592
|
+
},
|
|
593
|
+
|
|
594
|
+
transitions: {
|
|
595
|
+
fast: "100ms",
|
|
596
|
+
base: "200ms",
|
|
597
|
+
slow: "300ms",
|
|
598
|
+
timing: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
zIndex: {
|
|
602
|
+
auto: "auto",
|
|
603
|
+
0: "0",
|
|
604
|
+
10: "10",
|
|
605
|
+
20: "20",
|
|
606
|
+
30: "30",
|
|
607
|
+
40: "40",
|
|
608
|
+
50: "50",
|
|
609
|
+
dropdown: "1000",
|
|
610
|
+
sticky: "1020",
|
|
611
|
+
fixed: "1030",
|
|
612
|
+
modal: "1040",
|
|
613
|
+
popover: "1060",
|
|
614
|
+
tooltip: "1070",
|
|
615
|
+
},
|
|
616
|
+
|
|
617
|
+
opacity: [0, 5, 10, 25, 50, 75, 90, 95, 100],
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ============================================================================
|
|
622
|
+
// INIT
|
|
623
|
+
// ============================================================================
|
|
624
|
+
|
|
625
|
+
async function init() {
|
|
626
|
+
console.log(
|
|
627
|
+
chalk.bold.magenta("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),
|
|
628
|
+
);
|
|
629
|
+
console.log(chalk.bold.magenta(" EmilyUI Setup"));
|
|
630
|
+
console.log(
|
|
631
|
+
chalk.bold.magenta("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"),
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
try {
|
|
635
|
+
const spinner = ora("Analysing project structure...").start();
|
|
636
|
+
const detectedProject = detectProject();
|
|
637
|
+
spinner.succeed("Detected project: " + chalk.cyan(detectedProject.name));
|
|
638
|
+
const existingConfig = readExistingConfig();
|
|
639
|
+
const existingColours = isPlainObject(existingConfig && existingConfig.colours)
|
|
640
|
+
? existingConfig.colours
|
|
641
|
+
: {};
|
|
642
|
+
|
|
643
|
+
if (existingConfig) {
|
|
644
|
+
console.log(
|
|
645
|
+
chalk.gray(
|
|
646
|
+
" Found existing emily.config.json. Prompts are pre-filled from current settings.",
|
|
647
|
+
),
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const packageJsonData = readPackageJson();
|
|
652
|
+
const pkgName =
|
|
653
|
+
existingConfig && typeof existingConfig.name === "string" && existingConfig.name.trim()
|
|
654
|
+
? existingConfig.name.trim()
|
|
655
|
+
: packageJsonData && packageJsonData.name
|
|
656
|
+
? titleCasePackageName(packageJsonData.name)
|
|
657
|
+
: "My Design System";
|
|
658
|
+
|
|
659
|
+
const projectName = await new Input({
|
|
660
|
+
name: "projectName",
|
|
661
|
+
message: "Project name",
|
|
662
|
+
initial: pkgName,
|
|
663
|
+
validate: function (value) {
|
|
664
|
+
return value.trim() ? true : "Project name is required";
|
|
665
|
+
},
|
|
666
|
+
}).run();
|
|
667
|
+
|
|
668
|
+
if (!projectName || !projectName.trim()) {
|
|
669
|
+
console.log(chalk.red("\nProject name is required.\n"));
|
|
670
|
+
process.exit(1);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// =========================================================================
|
|
674
|
+
// COLOURS
|
|
675
|
+
// =========================================================================
|
|
676
|
+
|
|
677
|
+
console.log(chalk.bold("\n" + chalk.magenta("→") + " Brand colours"));
|
|
678
|
+
|
|
679
|
+
const brand = await askColourFromPresets(
|
|
680
|
+
"brand",
|
|
681
|
+
COLOUR_PRESETS.primary,
|
|
682
|
+
"#DB2777",
|
|
683
|
+
existingColours.brand,
|
|
684
|
+
);
|
|
685
|
+
const accent = await askColourFromPresets(
|
|
686
|
+
"accent",
|
|
687
|
+
COLOUR_PRESETS.secondary,
|
|
688
|
+
"#2563EB",
|
|
689
|
+
existingColours.accent,
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
console.log(
|
|
693
|
+
chalk.gray(
|
|
694
|
+
"\n Button colour tokens will use your brand colours by default:",
|
|
695
|
+
),
|
|
696
|
+
);
|
|
697
|
+
console.log(chalk.gray(" - btn-primary = brand"));
|
|
698
|
+
console.log(chalk.gray(" - btn-secondary = accent"));
|
|
699
|
+
|
|
700
|
+
console.log(chalk.bold("\n" + chalk.magenta("→") + " Utility colours"));
|
|
701
|
+
console.log(
|
|
702
|
+
chalk.gray(
|
|
703
|
+
" Defaults shown. Press enter to accept or pick an alternative.\n",
|
|
704
|
+
),
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
const success = await askColourFromPresets(
|
|
708
|
+
"success",
|
|
709
|
+
COLOUR_PRESETS.success,
|
|
710
|
+
"#017F65",
|
|
711
|
+
existingColours.success,
|
|
712
|
+
);
|
|
713
|
+
const warning = await askColourFromPresets(
|
|
714
|
+
"warning",
|
|
715
|
+
COLOUR_PRESETS.warning,
|
|
716
|
+
"#FFC107",
|
|
717
|
+
existingColours.warning,
|
|
718
|
+
);
|
|
719
|
+
const error = await askColourFromPresets(
|
|
720
|
+
"error",
|
|
721
|
+
COLOUR_PRESETS.error,
|
|
722
|
+
"#B20000",
|
|
723
|
+
existingColours.error,
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
const colours = {
|
|
727
|
+
brand,
|
|
728
|
+
accent,
|
|
729
|
+
"btn-primary": brand,
|
|
730
|
+
"btn-secondary": accent,
|
|
731
|
+
success,
|
|
732
|
+
warning,
|
|
733
|
+
error,
|
|
734
|
+
neutral: normaliseHex(existingColours.neutral) || "#57534E",
|
|
735
|
+
...getExistingAdditionalColours(existingColours),
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
let addingMore = true;
|
|
739
|
+
|
|
740
|
+
while (addingMore) {
|
|
741
|
+
const wantsMore = await new Confirm({
|
|
742
|
+
name: "addMore",
|
|
743
|
+
message: "Add another utility colour?",
|
|
744
|
+
initial: false,
|
|
745
|
+
}).run();
|
|
746
|
+
|
|
747
|
+
if (!wantsMore) {
|
|
748
|
+
addingMore = false;
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const customName = await new Input({
|
|
753
|
+
name: "customName",
|
|
754
|
+
message: "Colour name (e.g. accent, highlight, brand-dark)",
|
|
755
|
+
validate: function (value) {
|
|
756
|
+
const trimmed = value.trim();
|
|
757
|
+
|
|
758
|
+
if (!trimmed) return "Name is required";
|
|
759
|
+
if (!/^[a-z][a-z0-9-]*$/.test(trimmed)) {
|
|
760
|
+
return "Use lowercase letters, numbers, and hyphens only";
|
|
761
|
+
}
|
|
762
|
+
if (colours[trimmed]) return '"' + trimmed + '" is already defined';
|
|
763
|
+
|
|
764
|
+
return true;
|
|
765
|
+
},
|
|
766
|
+
}).run();
|
|
767
|
+
|
|
768
|
+
colours[customName.trim()] = await askHex(
|
|
769
|
+
"hex-" + customName,
|
|
770
|
+
"Hex for " + customName,
|
|
771
|
+
"#000000",
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// =========================================================================
|
|
776
|
+
// TYPOGRAPHY
|
|
777
|
+
// =========================================================================
|
|
778
|
+
|
|
779
|
+
console.log(chalk.bold("\n" + chalk.magenta("→") + " Typography"));
|
|
780
|
+
|
|
781
|
+
const headingFont = await new Select({
|
|
782
|
+
name: "headingFont",
|
|
783
|
+
message: "Heading font",
|
|
784
|
+
choices: FONT_OPTIONS,
|
|
785
|
+
initial: getFontInitialIndex(
|
|
786
|
+
isPlainObject(existingConfig && existingConfig.fontFamily)
|
|
787
|
+
? existingConfig.fontFamily.heading
|
|
788
|
+
: existingConfig && existingConfig.fontFamily,
|
|
789
|
+
0,
|
|
790
|
+
),
|
|
791
|
+
}).run();
|
|
792
|
+
|
|
793
|
+
const bodyFont = await new Select({
|
|
794
|
+
name: "bodyFont",
|
|
795
|
+
message: "Body font",
|
|
796
|
+
choices: FONT_OPTIONS,
|
|
797
|
+
initial: getFontInitialIndex(
|
|
798
|
+
isPlainObject(existingConfig && existingConfig.fontFamily)
|
|
799
|
+
? existingConfig.fontFamily.body
|
|
800
|
+
: existingConfig && existingConfig.fontFamily,
|
|
801
|
+
1,
|
|
802
|
+
),
|
|
803
|
+
}).run();
|
|
804
|
+
|
|
805
|
+
const baseFontSize = await new Select({
|
|
806
|
+
name: "baseFontSize",
|
|
807
|
+
message: "Base font size (sets html font-size, scales all rem values)",
|
|
808
|
+
choices: ["14px", "16px", "18px", "20px"],
|
|
809
|
+
initial: (function () {
|
|
810
|
+
const existing = existingConfig && existingConfig.baseFontSize;
|
|
811
|
+
const idx = ["14px", "16px", "18px", "20px"].indexOf(existing);
|
|
812
|
+
return idx >= 0 ? idx : 1;
|
|
813
|
+
})(),
|
|
814
|
+
}).run();
|
|
815
|
+
|
|
816
|
+
// =========================================================================
|
|
817
|
+
// SPACING
|
|
818
|
+
// =========================================================================
|
|
819
|
+
|
|
820
|
+
const baseUnitRaw = await new Input({
|
|
821
|
+
name: "baseUnit",
|
|
822
|
+
message: "Base spacing unit in px (label/documentation only)",
|
|
823
|
+
initial: getBaseUnitInitial(existingConfig),
|
|
824
|
+
validate: function (value) {
|
|
825
|
+
const parsed = Number.parseInt(value, 10);
|
|
826
|
+
|
|
827
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
828
|
+
return "Must be a positive number.";
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return true;
|
|
832
|
+
},
|
|
833
|
+
}).run();
|
|
834
|
+
|
|
835
|
+
const baseUnit = Number.parseInt(baseUnitRaw, 10);
|
|
836
|
+
|
|
837
|
+
// =========================================================================
|
|
838
|
+
// PURGE / OUTPUT
|
|
839
|
+
// =========================================================================
|
|
840
|
+
|
|
841
|
+
console.log(chalk.bold("\n" + chalk.magenta("→") + " Project files"));
|
|
842
|
+
|
|
843
|
+
console.log(
|
|
844
|
+
chalk.gray(
|
|
845
|
+
" Detected " +
|
|
846
|
+
detectedProject.name +
|
|
847
|
+
". EmilyCSS will scan the recommended files automatically.",
|
|
848
|
+
),
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
detectedProject.sourceGlobs.forEach(function (glob) {
|
|
852
|
+
console.log(chalk.gray(" - " + glob));
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
console.log(chalk.bold("\n" + chalk.magenta("→") + " CSS output"));
|
|
856
|
+
console.log(chalk.gray(" Output: " + detectedProject.outputPath));
|
|
857
|
+
|
|
858
|
+
// =========================================================================
|
|
859
|
+
// BUILD
|
|
860
|
+
// =========================================================================
|
|
861
|
+
|
|
862
|
+
const generatedDefaults = createDefaultConfig({
|
|
863
|
+
name: projectName.trim(),
|
|
864
|
+
colours,
|
|
865
|
+
headingFont,
|
|
866
|
+
bodyFont,
|
|
867
|
+
baseUnit,
|
|
868
|
+
baseFontSize,
|
|
869
|
+
detectedProject,
|
|
870
|
+
});
|
|
871
|
+
const config = mergeWithDefaults(generatedDefaults, existingConfig);
|
|
872
|
+
config.name = projectName.trim();
|
|
873
|
+
|
|
874
|
+
if (!existingConfig || !existingConfig.description) {
|
|
875
|
+
config.description = config.name + " design system";
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
config.baseUnit = baseUnit + "px";
|
|
879
|
+
config.baseFontSize = baseFontSize || "16px";
|
|
880
|
+
config.fontFamily = {
|
|
881
|
+
heading: headingFont,
|
|
882
|
+
body: bodyFont,
|
|
883
|
+
};
|
|
884
|
+
config.colours = colours;
|
|
885
|
+
|
|
886
|
+
const configPath = path.join(process.cwd(), "emily.config.json");
|
|
887
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
888
|
+
|
|
889
|
+
console.log("");
|
|
890
|
+
|
|
891
|
+
const buildSpinner = ora("Building EmilyUI CSS...").start();
|
|
892
|
+
|
|
893
|
+
const build = crossSpawn("npx", ["emily-css", "build"], {
|
|
894
|
+
cwd: process.cwd(),
|
|
895
|
+
stdio: "pipe",
|
|
896
|
+
shell: process.platform === "win32",
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
let stderr = "";
|
|
900
|
+
|
|
901
|
+
build.stderr.on("data", function (data) {
|
|
902
|
+
stderr += data.toString();
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
build.on("close", async function (code) {
|
|
906
|
+
if (code === 0) {
|
|
907
|
+
buildSpinner.succeed("EmilyUI CSS built successfully.");
|
|
908
|
+
|
|
909
|
+
const scriptsAdded = addEmilyScriptsToPackageJson();
|
|
910
|
+
|
|
911
|
+
console.log(
|
|
912
|
+
"\n" +
|
|
913
|
+
boxen(
|
|
914
|
+
chalk.green.bold("Setup complete") +
|
|
915
|
+
"\n\nConfig: " +
|
|
916
|
+
chalk.cyan("emily.config.json") +
|
|
917
|
+
"\nOutput: " +
|
|
918
|
+
chalk.cyan(config.output.css) +
|
|
919
|
+
"\nProject: " +
|
|
920
|
+
chalk.cyan(detectedProject.name) +
|
|
921
|
+
"\nScan:\n " +
|
|
922
|
+
chalk.cyan(config.purge.sourceGlobs.join("\n ")) +
|
|
923
|
+
"\n\nNext: add this stylesheet to your project:" +
|
|
924
|
+
"\n" +
|
|
925
|
+
chalk.yellow(" " + detectedProject.linkHint) +
|
|
926
|
+
(scriptsAdded
|
|
927
|
+
? "\n\nScripts added:\n" +
|
|
928
|
+
chalk.cyan(" npm run emily:build\n") +
|
|
929
|
+
chalk.cyan(" npm run emily:watch\n") +
|
|
930
|
+
chalk.cyan(" npm run emily:doctor\n") +
|
|
931
|
+
chalk.cyan(" npm run emily:migrate\n") +
|
|
932
|
+
chalk.cyan(" npm run emily:info\n") +
|
|
933
|
+
chalk.cyan(" npm run emily:manifest\n") +
|
|
934
|
+
chalk.cyan(" npm run emily:version\n") +
|
|
935
|
+
chalk.cyan(" npm run emily:showcase\n") +
|
|
936
|
+
chalk.cyan(" npm run emily:help")
|
|
937
|
+
: ""),
|
|
938
|
+
{
|
|
939
|
+
padding: 1,
|
|
940
|
+
margin: 1,
|
|
941
|
+
borderStyle: "round",
|
|
942
|
+
borderColor: "magenta",
|
|
943
|
+
},
|
|
944
|
+
),
|
|
945
|
+
);
|
|
946
|
+
|
|
947
|
+
const startWatch = await new Confirm({
|
|
948
|
+
name: "startWatch",
|
|
949
|
+
message: "Start the file watcher now?",
|
|
950
|
+
initial: true,
|
|
951
|
+
}).run();
|
|
952
|
+
|
|
953
|
+
if (startWatch) {
|
|
954
|
+
console.log(
|
|
955
|
+
chalk.cyan("\nStarting watcher. Press Ctrl+C to stop.\n"),
|
|
956
|
+
);
|
|
957
|
+
|
|
958
|
+
const watcher = crossSpawn("npx", ["emily-css", "watch"], {
|
|
959
|
+
cwd: process.cwd(),
|
|
960
|
+
stdio: "inherit",
|
|
961
|
+
shell: process.platform === "win32",
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
watcher.on("close", function (c) {
|
|
965
|
+
process.exit(c || 0);
|
|
966
|
+
});
|
|
967
|
+
} else {
|
|
968
|
+
console.log(
|
|
969
|
+
chalk.gray(
|
|
970
|
+
"\nRun the watcher any time with: npm run emily:watch\n",
|
|
971
|
+
),
|
|
972
|
+
);
|
|
973
|
+
process.exit(0);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
buildSpinner.fail("Automatic build failed.");
|
|
980
|
+
console.log("\nYour config was created, but CSS was not built.");
|
|
981
|
+
console.log("\nRun manually:\n");
|
|
982
|
+
console.log(chalk.cyan(" npx emily-css build"));
|
|
983
|
+
|
|
984
|
+
if (stderr.trim()) {
|
|
985
|
+
console.log(chalk.gray("\nBuild error:\n"));
|
|
986
|
+
console.log(stderr.trim());
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
process.exit(1);
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
build.on("error", function (error) {
|
|
993
|
+
buildSpinner.fail("Automatic build failed.");
|
|
994
|
+
console.log("\nYour config was created, but CSS was not built.");
|
|
995
|
+
console.log("Reason: " + error.message);
|
|
996
|
+
console.log("\nRun manually:\n");
|
|
997
|
+
console.log(chalk.cyan(" npx emily-css build\n"));
|
|
998
|
+
process.exit(1);
|
|
999
|
+
});
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
console.log(chalk.red("\nSetup cancelled or failed."));
|
|
1002
|
+
|
|
1003
|
+
if (error && error.message) {
|
|
1004
|
+
console.log(chalk.gray(error.message));
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
process.exit(1);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
init();
|