emily-css 1.0.17 → 1.0.19
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 +13 -0
- package/README.md +21 -29
- package/bin/emilyui.js +4 -8
- package/package.json +7 -2
- package/src/index.js +86 -77
- package/src/init.js +224 -116
- package/src/purge.js +123 -56
- package/src/showcase.js +84 -39
- package/src/watch.js +145 -57
- package/src/purge-cmd.js +0 -55
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to `emily-css` are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## v1.0.19 — May 2026
|
|
8
|
+
|
|
9
|
+
**Add framework-aware output paths and bundled showcase template**
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
## v1.0.18 — May 2026
|
|
13
|
+
|
|
14
|
+
****
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- added more utitlies as a code block
|
|
18
|
+
|
|
19
|
+
---
|
|
7
20
|
## v1.0.17 — May 2026
|
|
8
21
|
|
|
9
22
|
**added new utilties, and added component patterns**
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ emilyCSS is built for real-world systems like **Drupal, Power Pages, WordPress,
|
|
|
20
20
|
npx emily-css init
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
This creates your `emily.config.json`, walks you through your brand settings, and runs your first build.
|
|
23
|
+
This creates your `emily.config.json`, walks you through your brand settings (colours, fonts, spacing, etc.), and runs your first build.
|
|
24
24
|
|
|
25
25
|
### 2. Link the CSS
|
|
26
26
|
|
|
@@ -28,17 +28,13 @@ This creates your `emily.config.json`, walks you through your brand settings, an
|
|
|
28
28
|
<link rel="stylesheet" href="./dist/emily.min.css">
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
### 3.
|
|
31
|
+
### 3. Start Building
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
npx emily-css watch # Rebuilds automatically on config/template changes
|
|
35
|
-
npx emily-css build # Manual rebuild
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Open the showcase:
|
|
33
|
+
Use the generated utilities and browse the showcase for ready-to-copy components.
|
|
39
34
|
|
|
40
35
|
```bash
|
|
41
|
-
|
|
36
|
+
npx emily-css build # Rebuild after config changes
|
|
37
|
+
npx emily-css watch # Watch mode for development
|
|
42
38
|
```
|
|
43
39
|
|
|
44
40
|
## Core Features
|
|
@@ -48,30 +44,24 @@ npm run emily:showcase # Serves at http://localhost:3456
|
|
|
48
44
|
- **Accessibility First** — Focus-visible rings, motion utilities, WCAG 2.2 AA colours
|
|
49
45
|
- **No Build Pipeline Required** — Just a static CSS file
|
|
50
46
|
- **Smart Purge** — Remove unused utilities for tiny production files
|
|
51
|
-
- **UI Starter Kit** — Copy-paste accessible components from
|
|
47
|
+
- **UI Starter Kit** — Copy-paste accessible components from showcase.html
|
|
52
48
|
|
|
53
49
|
## Commands
|
|
54
50
|
|
|
55
51
|
```bash
|
|
56
|
-
npx emily-css init
|
|
57
|
-
npx emily-css build
|
|
58
|
-
npx emily-css watch
|
|
59
|
-
npx emily-css purge
|
|
52
|
+
npx emily-css init # Setup config + first build
|
|
53
|
+
npx emily-css build # Regenerate CSS
|
|
54
|
+
npx emily-css watch # Development watch mode
|
|
55
|
+
npx emily-css purge # Remove unused styles for production
|
|
60
56
|
```
|
|
61
57
|
|
|
62
58
|
## How Purge Works
|
|
63
59
|
|
|
64
60
|
emilyCSS scans your templates for used class names and removes everything else.
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```json
|
|
69
|
-
"purge": {
|
|
70
|
-
"extensions": [".html", ".php", ".twig", ".liquid", ".jsx", ".vue", ".astro"]
|
|
71
|
-
}
|
|
72
|
-
```
|
|
62
|
+
Supported files: `.html`, `.php`, `.twig`, `.liquid`, `.jsx`, `.vue`, `.astro`, etc. (configurable).
|
|
73
63
|
|
|
74
|
-
**Important:** Dynamically constructed classes
|
|
64
|
+
**Important:** Dynamically constructed classes like `bg-${colour}` are not detected. Use static strings or add them to the safelist.
|
|
75
65
|
|
|
76
66
|
## File Size (Typical)
|
|
77
67
|
|
|
@@ -80,7 +70,9 @@ Configure it in `emily.config.json`:
|
|
|
80
70
|
| Full build | ~1.1 MB |
|
|
81
71
|
| After purge | 10–50 KB |
|
|
82
72
|
|
|
83
|
-
## Configuration
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
Edit `emily.config.json`:
|
|
84
76
|
|
|
85
77
|
```json
|
|
86
78
|
{
|
|
@@ -95,7 +87,7 @@ Configure it in `emily.config.json`:
|
|
|
95
87
|
"neutral": "#57534E"
|
|
96
88
|
},
|
|
97
89
|
"purge": {
|
|
98
|
-
"
|
|
90
|
+
"content": ["./**/*.{html,php,jsx,tsx,vue}"]
|
|
99
91
|
}
|
|
100
92
|
}
|
|
101
93
|
```
|
|
@@ -104,12 +96,12 @@ After changes: `npx emily-css build`
|
|
|
104
96
|
|
|
105
97
|
## Component Showcase
|
|
106
98
|
|
|
107
|
-
After
|
|
99
|
+
After your first build, open `showcase.html` in your browser. It contains production-ready, accessible components (buttons, forms, alerts, cards, etc.) built with your brand.
|
|
108
100
|
|
|
109
101
|
## EmilyUI vs emilyCSS
|
|
110
102
|
|
|
111
103
|
- **EmilyUI** — The broader brand / ecosystem
|
|
112
|
-
- **emilyCSS** — The current product (
|
|
104
|
+
- **emilyCSS** — The current product (the emily-css npm package + CLI)
|
|
113
105
|
|
|
114
106
|
## Example Components
|
|
115
107
|
|
|
@@ -124,7 +116,7 @@ After building, run `npm run emily:showcase` and visit `http://localhost:3456`.
|
|
|
124
116
|
### Responsive Card
|
|
125
117
|
|
|
126
118
|
```html
|
|
127
|
-
<div class="w-full md:w-96 p-6 rounded-
|
|
119
|
+
<div class="w-full md:w-96 p-6 rounded-xl bg-white border border-neutral-20 shadow-sm">
|
|
128
120
|
<h2 class="text-2xl font-semibold text-neutral-90">Card Title</h2>
|
|
129
121
|
<p class="mt-3 text-neutral-70">Content goes here.</p>
|
|
130
122
|
</div>
|
|
@@ -132,7 +124,7 @@ After building, run `npm run emily:showcase` and visit `http://localhost:3456`.
|
|
|
132
124
|
|
|
133
125
|
## Fonts
|
|
134
126
|
|
|
135
|
-
emilyCSS applies font stacks but does not include font files.
|
|
127
|
+
emilyCSS applies font stacks but does not include font files. Recommended approach:
|
|
136
128
|
|
|
137
129
|
```bash
|
|
138
130
|
npm install @fontsource/inter @fontsource/lexend
|
|
@@ -147,4 +139,4 @@ Then import the weights you need.
|
|
|
147
139
|
|
|
148
140
|
## License
|
|
149
141
|
|
|
150
|
-
MIT
|
|
142
|
+
MIT
|
package/bin/emilyui.js
CHANGED
|
@@ -7,8 +7,6 @@ if (command === "init") {
|
|
|
7
7
|
} else if (command === "build") {
|
|
8
8
|
const { build } = require("../src/index.js");
|
|
9
9
|
build({ keepFull: process.argv.includes("--keep-full") });
|
|
10
|
-
} else if (command === "purge") {
|
|
11
|
-
require("../src/purge-cmd.js");
|
|
12
10
|
} else if (command === "watch") {
|
|
13
11
|
require("../src/watch.js");
|
|
14
12
|
} else if (command === "showcase") {
|
|
@@ -19,9 +17,8 @@ if (command === "init") {
|
|
|
19
17
|
|
|
20
18
|
Commands:
|
|
21
19
|
emily-css init Set up a new project (interactive wizard)
|
|
22
|
-
emily-css build Generate production CSS to
|
|
23
|
-
emily-css watch Dev mode: watch for
|
|
24
|
-
emily-css purge Scan project files and remove unused utilities
|
|
20
|
+
emily-css build Generate production CSS to the configured output path
|
|
21
|
+
emily-css watch Dev mode: watch for changes and rebuild
|
|
25
22
|
emily-css showcase Launch the component showcase in your browser
|
|
26
23
|
emily-css help Show this help text
|
|
27
24
|
|
|
@@ -39,12 +36,11 @@ if (command === "init") {
|
|
|
39
36
|
|
|
40
37
|
Usage:
|
|
41
38
|
emily-css init Set up a new project
|
|
42
|
-
emily-css build Generate production CSS
|
|
39
|
+
emily-css build Generate production CSS to the configured output path
|
|
43
40
|
emily-css watch Dev mode: rebuild on changes
|
|
44
|
-
emily-css purge Remove unused utilities
|
|
45
41
|
emily-css showcase Browse components in your browser
|
|
46
42
|
emily-css help Full command reference
|
|
47
43
|
|
|
48
44
|
Run emily-css help for more detail.
|
|
49
45
|
`);
|
|
50
|
-
}
|
|
46
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emily-css",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "A config-driven utility CSS framework. Define your brand once, generate the CSS.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
11
|
"src/",
|
|
12
|
+
"templates/",
|
|
12
13
|
"README.md",
|
|
13
14
|
"LICENSE",
|
|
14
15
|
"CHANGELOG.md"
|
|
@@ -22,7 +23,10 @@
|
|
|
22
23
|
"emily:showcase": "node src/showcase.js",
|
|
23
24
|
"commit": "node scripts/commit.js",
|
|
24
25
|
"release": "node scripts/release.js",
|
|
25
|
-
"ship": "node scripts/ship.js"
|
|
26
|
+
"ship": "node scripts/ship.js",
|
|
27
|
+
"emily:build": "emily-css build",
|
|
28
|
+
"emily:watch": "emily-css watch",
|
|
29
|
+
"emily:help": "emily-css help"
|
|
26
30
|
},
|
|
27
31
|
"keywords": [
|
|
28
32
|
"css",
|
|
@@ -50,6 +54,7 @@
|
|
|
50
54
|
"cross-spawn": "^7.0.6",
|
|
51
55
|
"emily-css": "^1.0.8",
|
|
52
56
|
"enquirer": "^2.4.1",
|
|
57
|
+
"fast-glob": "^3.3.3",
|
|
53
58
|
"ora": "^5.4.1"
|
|
54
59
|
}
|
|
55
60
|
}
|
package/src/index.js
CHANGED
|
@@ -867,28 +867,54 @@ function generatePatternComponents() {
|
|
|
867
867
|
// BUILD FUNCTION
|
|
868
868
|
// ============================================================================
|
|
869
869
|
|
|
870
|
-
function
|
|
871
|
-
|
|
870
|
+
function getConfigPath() {
|
|
871
|
+
return path.join(process.cwd(), 'emily.config.json');
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function getConfig() {
|
|
875
|
+
const configPath = getConfigPath();
|
|
876
|
+
|
|
872
877
|
if (!fs.existsSync(configPath)) {
|
|
873
|
-
console.error(
|
|
878
|
+
console.error('\n emily-css: No config found. Run "emily-css init" first.\n');
|
|
874
879
|
process.exit(1);
|
|
875
880
|
}
|
|
876
|
-
|
|
881
|
+
|
|
882
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function getFullCssPath(config) {
|
|
886
|
+
return path.join(process.cwd(), config.output?.fullCss || 'dist/emily.css');
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function getProductionCssPath(config) {
|
|
890
|
+
return path.join(process.cwd(), config.output?.css || 'dist/emily.min.css');
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function ensureDirectoryForFile(filePath) {
|
|
894
|
+
const dir = path.dirname(filePath);
|
|
895
|
+
|
|
896
|
+
if (!fs.existsSync(dir)) {
|
|
897
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function getSourceDir(config) {
|
|
902
|
+
return config.purge?.sourceDir || '.';
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function buildFullFramework() {
|
|
906
|
+
const config = getConfig();
|
|
877
907
|
|
|
878
908
|
console.log('Building EmilyCSS full framework...');
|
|
879
909
|
|
|
880
|
-
// Generate colours
|
|
881
910
|
const colours = generateAllColours(config.colours);
|
|
882
911
|
console.log(`✓ Generated ${Object.keys(colours).length} colour scales`);
|
|
883
912
|
|
|
884
|
-
// Generate spacing
|
|
885
913
|
const spacing = generateSpacing(config.baseUnit, config.spacing.scale);
|
|
886
914
|
console.log(`✓ Generated ${Object.keys(spacing).length} spacing values`);
|
|
887
915
|
|
|
888
|
-
// 1. Generate Variables (Theme Layer)
|
|
889
916
|
const variablesCss = generateCSSVariables(colours, spacing, config);
|
|
890
917
|
|
|
891
|
-
// 2. Generate Utilities (Utilities Layer)
|
|
892
918
|
let utilityCss = '';
|
|
893
919
|
utilityCss += displayUtilities();
|
|
894
920
|
utilityCss += generateSpacingUtilities(spacing);
|
|
@@ -923,21 +949,16 @@ function buildFullFramework() {
|
|
|
923
949
|
utilityCss += backgroundUtilities();
|
|
924
950
|
utilityCss += filterUtilities();
|
|
925
951
|
|
|
926
|
-
// Add state, dark mode, and responsive variants to utilities
|
|
927
952
|
utilityCss = addStateVariants(utilityCss);
|
|
928
953
|
utilityCss = addDarkModeVariants(utilityCss);
|
|
929
954
|
utilityCss = addResponsiveVariants(utilityCss, config);
|
|
930
955
|
|
|
931
|
-
// 3. Assemble Final CSS with Layers
|
|
932
|
-
// Layer order matters: later layers win over earlier ones.
|
|
933
|
-
// theme → CSS custom properties / design tokens
|
|
934
|
-
// base → box-sizing reset, font-face, body defaults
|
|
935
|
-
// components → reserved for future component styles
|
|
936
|
-
// utilities → generated utility classes (highest priority)
|
|
937
956
|
const { fontFace, bodyFont } = generateFontCSS(config);
|
|
957
|
+
|
|
938
958
|
const fontLabel = typeof config.fontFamily === 'object'
|
|
939
959
|
? 'heading: ' + (config.fontFamily.heading || 'system') + ', body: ' + (config.fontFamily.body || 'system')
|
|
940
960
|
: (config.fontFamily || 'system');
|
|
961
|
+
|
|
941
962
|
console.log('✓ Font: ' + fontLabel);
|
|
942
963
|
|
|
943
964
|
const baseCss = `
|
|
@@ -946,7 +967,6 @@ function buildFullFramework() {
|
|
|
946
967
|
box-sizing: border-box;
|
|
947
968
|
}
|
|
948
969
|
|
|
949
|
-
/* Remove default margin/padding on common elements */
|
|
950
970
|
body, h1, h2, h3, h4, h5, h6, p,
|
|
951
971
|
ul, ol, dl, dd, figure, blockquote,
|
|
952
972
|
fieldset, textarea, pre {
|
|
@@ -954,23 +974,19 @@ function buildFullFramework() {
|
|
|
954
974
|
padding: 0;
|
|
955
975
|
}
|
|
956
976
|
|
|
957
|
-
/* Lists: remove bullets/numbers when unstyled */
|
|
958
977
|
ul, ol {
|
|
959
978
|
list-style: none;
|
|
960
979
|
}
|
|
961
980
|
|
|
962
|
-
/* Inherit fonts for form elements */
|
|
963
981
|
input, button, textarea, select {
|
|
964
982
|
font: inherit;
|
|
965
983
|
}
|
|
966
984
|
|
|
967
|
-
/* Sensible media defaults */
|
|
968
985
|
img, picture, video, canvas, svg {
|
|
969
986
|
display: block;
|
|
970
987
|
max-width: 100%;
|
|
971
988
|
}
|
|
972
989
|
|
|
973
|
-
/* Remove default button styles */
|
|
974
990
|
button {
|
|
975
991
|
background: none;
|
|
976
992
|
border: none;
|
|
@@ -978,22 +994,38 @@ function buildFullFramework() {
|
|
|
978
994
|
padding: 0;
|
|
979
995
|
}
|
|
980
996
|
|
|
981
|
-
/* Avoid overflow on long words */
|
|
982
997
|
p, h1, h2, h3, h4, h5, h6 {
|
|
983
998
|
overflow-wrap: break-word;
|
|
984
999
|
}
|
|
985
1000
|
|
|
986
|
-
|
|
1001
|
+
code {
|
|
1002
|
+
font-family: "Menlo", "Monaco", "Courier New", monospace;
|
|
1003
|
+
font-size: 0.875em;
|
|
1004
|
+
background-color: #0d0c0b;
|
|
1005
|
+
color: #a3c986;
|
|
1006
|
+
padding: 0.125rem 0.4rem;
|
|
1007
|
+
border-radius: 4px;
|
|
1008
|
+
display: inline;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
code.block {
|
|
1012
|
+
display: block;
|
|
1013
|
+
padding: 0.625rem 1rem;
|
|
1014
|
+
border-radius: 6px;
|
|
1015
|
+
font-size: 0.8125rem;
|
|
1016
|
+
line-height: 1.6;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
987
1019
|
pre {
|
|
988
|
-
background-color: #
|
|
989
|
-
color: #
|
|
1020
|
+
background-color: #0d0c0b;
|
|
1021
|
+
color: #e4e0db;
|
|
990
1022
|
padding: 1.25rem;
|
|
991
|
-
border-radius:
|
|
1023
|
+
border-radius: 6px;
|
|
992
1024
|
overflow-x: auto;
|
|
993
1025
|
font-family: "Menlo", "Monaco", "Courier New", monospace;
|
|
994
1026
|
font-size: 0.875rem;
|
|
995
1027
|
line-height: 1.7;
|
|
996
|
-
border: 1px solid #
|
|
1028
|
+
border: 1px solid #2a2520;
|
|
997
1029
|
}
|
|
998
1030
|
|
|
999
1031
|
pre code {
|
|
@@ -1003,38 +1035,25 @@ function buildFullFramework() {
|
|
|
1003
1035
|
color: inherit;
|
|
1004
1036
|
font-size: inherit;
|
|
1005
1037
|
font-family: inherit;
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
/* Inline code */
|
|
1009
|
-
code {
|
|
1010
|
-
font-family: "Menlo", "Monaco", "Courier New", monospace;
|
|
1011
|
-
font-size: 0.875em;
|
|
1012
|
-
background-color: #2d2d2d;
|
|
1013
|
-
color: #d4d4d4;
|
|
1014
|
-
padding: 0.125rem 0.375rem;
|
|
1015
|
-
border-radius: 4px;
|
|
1038
|
+
display: inline;
|
|
1016
1039
|
}
|
|
1017
1040
|
${bodyFont}`;
|
|
1018
1041
|
|
|
1019
|
-
// @font-face must sit outside @layer for broadest browser compatibility
|
|
1020
1042
|
let css = fontFace ? `${fontFace}\n` : '';
|
|
1021
1043
|
css += `@layer theme, base, components, utilities;\n\n`;
|
|
1022
1044
|
css += `@layer theme {\n${variablesCss}}\n\n`;
|
|
1045
|
+
|
|
1023
1046
|
const baseStylesCss = generateBaseStyles(config);
|
|
1024
1047
|
css += `@layer base {${baseCss}${baseStylesCss}}\n\n`;
|
|
1025
1048
|
css += `@layer components {\n${generatePatternComponents()}}\n\n`;
|
|
1026
1049
|
css += `@layer utilities {\n${utilityCss}}\n`;
|
|
1027
1050
|
|
|
1028
|
-
|
|
1029
|
-
const outputPath = path.join(process.cwd(), 'dist/emily.css');
|
|
1030
|
-
const outputDir = path.dirname(outputPath);
|
|
1051
|
+
const fullCssPath = getFullCssPath(config);
|
|
1031
1052
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1053
|
+
ensureDirectoryForFile(fullCssPath);
|
|
1054
|
+
fs.writeFileSync(fullCssPath, css);
|
|
1035
1055
|
|
|
1036
|
-
|
|
1037
|
-
console.log(`✓ Generated CSS: ${outputPath}`);
|
|
1056
|
+
console.log(`✓ Generated CSS: ${fullCssPath}`);
|
|
1038
1057
|
console.log(`✓ File size: ${(css.length / 1024).toFixed(2)} KB (unminified)`);
|
|
1039
1058
|
console.log('Full framework build complete');
|
|
1040
1059
|
}
|
|
@@ -1049,55 +1068,44 @@ function minify(css) {
|
|
|
1049
1068
|
.trim();
|
|
1050
1069
|
}
|
|
1051
1070
|
|
|
1052
|
-
function getConfig() {
|
|
1053
|
-
const configPath = path.join(process.cwd(), 'emily.config.json');
|
|
1054
|
-
|
|
1055
|
-
if (!fs.existsSync(configPath)) {
|
|
1056
|
-
console.error('\n emily-css: No config found. Run "emily-css init" first.\n');
|
|
1057
|
-
process.exit(1);
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
function getSourceDir(config) {
|
|
1064
|
-
return config.purge && config.purge.sourceDir ? config.purge.sourceDir : '.';
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
1071
|
function buildProductionCss() {
|
|
1068
1072
|
const config = getConfig();
|
|
1069
1073
|
const sourceDir = getSourceDir(config);
|
|
1070
|
-
const
|
|
1071
|
-
const
|
|
1074
|
+
const fullCssPath = getFullCssPath(config);
|
|
1075
|
+
const productionCssPath = getProductionCssPath(config);
|
|
1072
1076
|
|
|
1073
|
-
if (!fs.existsSync(
|
|
1077
|
+
if (!fs.existsSync(fullCssPath)) {
|
|
1074
1078
|
buildFullFramework();
|
|
1075
1079
|
}
|
|
1076
1080
|
|
|
1077
1081
|
const { purgeCSS } = require('./purge.js');
|
|
1078
|
-
const css = fs.readFileSync(
|
|
1082
|
+
const css = fs.readFileSync(fullCssPath, 'utf8');
|
|
1079
1083
|
const purged = purgeCSS(css, sourceDir, config);
|
|
1080
1084
|
const minified = minify(purged);
|
|
1081
1085
|
|
|
1082
|
-
|
|
1086
|
+
ensureDirectoryForFile(productionCssPath);
|
|
1087
|
+
fs.writeFileSync(productionCssPath, minified);
|
|
1083
1088
|
|
|
1084
1089
|
return {
|
|
1085
1090
|
css,
|
|
1086
1091
|
purged,
|
|
1087
1092
|
minified,
|
|
1088
1093
|
originalSize: Buffer.byteLength(css, 'utf8'),
|
|
1089
|
-
outputSize: Buffer.byteLength(minified, 'utf8')
|
|
1094
|
+
outputSize: Buffer.byteLength(minified, 'utf8'),
|
|
1095
|
+
outputPath: productionCssPath,
|
|
1096
|
+
fullCssPath,
|
|
1090
1097
|
};
|
|
1091
1098
|
}
|
|
1092
1099
|
|
|
1093
1100
|
function isFrameworkStale() {
|
|
1094
|
-
const
|
|
1095
|
-
const
|
|
1101
|
+
const config = getConfig();
|
|
1102
|
+
const configPath = getConfigPath();
|
|
1103
|
+
const fullCssPath = getFullCssPath(config);
|
|
1096
1104
|
|
|
1097
|
-
if (!fs.existsSync(
|
|
1105
|
+
if (!fs.existsSync(fullCssPath)) return true;
|
|
1098
1106
|
if (!fs.existsSync(configPath)) return true;
|
|
1099
1107
|
|
|
1100
|
-
return fs.statSync(configPath).mtimeMs > fs.statSync(
|
|
1108
|
+
return fs.statSync(configPath).mtimeMs > fs.statSync(fullCssPath).mtimeMs;
|
|
1101
1109
|
}
|
|
1102
1110
|
|
|
1103
1111
|
function ensureFullFramework() {
|
|
@@ -1109,18 +1117,19 @@ function ensureFullFramework() {
|
|
|
1109
1117
|
function build(options = {}) {
|
|
1110
1118
|
ensureFullFramework();
|
|
1111
1119
|
|
|
1120
|
+
const config = getConfig();
|
|
1121
|
+
const fullCssPath = getFullCssPath(config);
|
|
1112
1122
|
const result = buildProductionCss();
|
|
1113
|
-
const cssPath = path.join(process.cwd(), 'dist/emily.css');
|
|
1114
1123
|
|
|
1115
|
-
console.log('✓ Generated production CSS:
|
|
1124
|
+
console.log('✓ Generated production CSS: ' + path.relative(process.cwd(), result.outputPath));
|
|
1116
1125
|
console.log('✓ File size: ' + (result.outputSize / 1024).toFixed(2) + ' KB');
|
|
1117
1126
|
|
|
1118
|
-
if (!options.keepFull && fs.existsSync(
|
|
1127
|
+
if (!options.keepFull && fs.existsSync(fullCssPath)) {
|
|
1119
1128
|
try {
|
|
1120
|
-
fs.unlinkSync(
|
|
1121
|
-
console.log('Removed
|
|
1122
|
-
} catch (
|
|
1123
|
-
// Windows FUSE:
|
|
1129
|
+
fs.unlinkSync(fullCssPath);
|
|
1130
|
+
console.log('Removed ' + path.relative(process.cwd(), fullCssPath) + ' for production build.');
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
// Windows FUSE: cannot always delete files cleanly, non-fatal.
|
|
1124
1133
|
}
|
|
1125
1134
|
}
|
|
1126
1135
|
|