emily-css 1.0.18 → 1.0.20
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/bin/emilyui.js +4 -8
- package/package.json +7 -2
- package/src/index.js +64 -67
- 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.20 — May 2026
|
|
8
|
+
|
|
9
|
+
**replace folder structure template to tempalates**
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- replace folder structure template to tempalates
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
## v1.0.19 — May 2026
|
|
16
|
+
|
|
17
|
+
**Add framework-aware output paths and bundled showcase template**
|
|
18
|
+
|
|
19
|
+
---
|
|
7
20
|
## v1.0.18 — May 2026
|
|
8
21
|
|
|
9
22
|
****
|
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.20",
|
|
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,12 +994,10 @@ 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
|
-
/* Code — terminal style by default */
|
|
987
1001
|
code {
|
|
988
1002
|
font-family: "Menlo", "Monaco", "Courier New", monospace;
|
|
989
1003
|
font-size: 0.875em;
|
|
@@ -994,7 +1008,6 @@ function buildFullFramework() {
|
|
|
994
1008
|
display: inline;
|
|
995
1009
|
}
|
|
996
1010
|
|
|
997
|
-
/* Block code — terminal command style, no extra classes needed */
|
|
998
1011
|
code.block {
|
|
999
1012
|
display: block;
|
|
1000
1013
|
padding: 0.625rem 1rem;
|
|
@@ -1003,7 +1016,6 @@ function buildFullFramework() {
|
|
|
1003
1016
|
line-height: 1.6;
|
|
1004
1017
|
}
|
|
1005
1018
|
|
|
1006
|
-
/* Pre — wraps multi-line code, consistent terminal look */
|
|
1007
1019
|
pre {
|
|
1008
1020
|
background-color: #0d0c0b;
|
|
1009
1021
|
color: #e4e0db;
|
|
@@ -1016,7 +1028,6 @@ function buildFullFramework() {
|
|
|
1016
1028
|
border: 1px solid #2a2520;
|
|
1017
1029
|
}
|
|
1018
1030
|
|
|
1019
|
-
/* Reset code inside pre — inherits pre's colours */
|
|
1020
1031
|
pre code {
|
|
1021
1032
|
background: none;
|
|
1022
1033
|
padding: 0;
|
|
@@ -1028,25 +1039,21 @@ function buildFullFramework() {
|
|
|
1028
1039
|
}
|
|
1029
1040
|
${bodyFont}`;
|
|
1030
1041
|
|
|
1031
|
-
// @font-face must sit outside @layer for broadest browser compatibility
|
|
1032
1042
|
let css = fontFace ? `${fontFace}\n` : '';
|
|
1033
1043
|
css += `@layer theme, base, components, utilities;\n\n`;
|
|
1034
1044
|
css += `@layer theme {\n${variablesCss}}\n\n`;
|
|
1045
|
+
|
|
1035
1046
|
const baseStylesCss = generateBaseStyles(config);
|
|
1036
1047
|
css += `@layer base {${baseCss}${baseStylesCss}}\n\n`;
|
|
1037
1048
|
css += `@layer components {\n${generatePatternComponents()}}\n\n`;
|
|
1038
1049
|
css += `@layer utilities {\n${utilityCss}}\n`;
|
|
1039
1050
|
|
|
1040
|
-
|
|
1041
|
-
const outputPath = path.join(process.cwd(), 'dist/emily.css');
|
|
1042
|
-
const outputDir = path.dirname(outputPath);
|
|
1051
|
+
const fullCssPath = getFullCssPath(config);
|
|
1043
1052
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1053
|
+
ensureDirectoryForFile(fullCssPath);
|
|
1054
|
+
fs.writeFileSync(fullCssPath, css);
|
|
1047
1055
|
|
|
1048
|
-
|
|
1049
|
-
console.log(`✓ Generated CSS: ${outputPath}`);
|
|
1056
|
+
console.log(`✓ Generated CSS: ${fullCssPath}`);
|
|
1050
1057
|
console.log(`✓ File size: ${(css.length / 1024).toFixed(2)} KB (unminified)`);
|
|
1051
1058
|
console.log('Full framework build complete');
|
|
1052
1059
|
}
|
|
@@ -1061,55 +1068,44 @@ function minify(css) {
|
|
|
1061
1068
|
.trim();
|
|
1062
1069
|
}
|
|
1063
1070
|
|
|
1064
|
-
function getConfig() {
|
|
1065
|
-
const configPath = path.join(process.cwd(), 'emily.config.json');
|
|
1066
|
-
|
|
1067
|
-
if (!fs.existsSync(configPath)) {
|
|
1068
|
-
console.error('\n emily-css: No config found. Run "emily-css init" first.\n');
|
|
1069
|
-
process.exit(1);
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
function getSourceDir(config) {
|
|
1076
|
-
return config.purge && config.purge.sourceDir ? config.purge.sourceDir : '.';
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
1071
|
function buildProductionCss() {
|
|
1080
1072
|
const config = getConfig();
|
|
1081
1073
|
const sourceDir = getSourceDir(config);
|
|
1082
|
-
const
|
|
1083
|
-
const
|
|
1074
|
+
const fullCssPath = getFullCssPath(config);
|
|
1075
|
+
const productionCssPath = getProductionCssPath(config);
|
|
1084
1076
|
|
|
1085
|
-
if (!fs.existsSync(
|
|
1077
|
+
if (!fs.existsSync(fullCssPath)) {
|
|
1086
1078
|
buildFullFramework();
|
|
1087
1079
|
}
|
|
1088
1080
|
|
|
1089
1081
|
const { purgeCSS } = require('./purge.js');
|
|
1090
|
-
const css = fs.readFileSync(
|
|
1082
|
+
const css = fs.readFileSync(fullCssPath, 'utf8');
|
|
1091
1083
|
const purged = purgeCSS(css, sourceDir, config);
|
|
1092
1084
|
const minified = minify(purged);
|
|
1093
1085
|
|
|
1094
|
-
|
|
1086
|
+
ensureDirectoryForFile(productionCssPath);
|
|
1087
|
+
fs.writeFileSync(productionCssPath, minified);
|
|
1095
1088
|
|
|
1096
1089
|
return {
|
|
1097
1090
|
css,
|
|
1098
1091
|
purged,
|
|
1099
1092
|
minified,
|
|
1100
1093
|
originalSize: Buffer.byteLength(css, 'utf8'),
|
|
1101
|
-
outputSize: Buffer.byteLength(minified, 'utf8')
|
|
1094
|
+
outputSize: Buffer.byteLength(minified, 'utf8'),
|
|
1095
|
+
outputPath: productionCssPath,
|
|
1096
|
+
fullCssPath,
|
|
1102
1097
|
};
|
|
1103
1098
|
}
|
|
1104
1099
|
|
|
1105
1100
|
function isFrameworkStale() {
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1101
|
+
const config = getConfig();
|
|
1102
|
+
const configPath = getConfigPath();
|
|
1103
|
+
const fullCssPath = getFullCssPath(config);
|
|
1108
1104
|
|
|
1109
|
-
if (!fs.existsSync(
|
|
1105
|
+
if (!fs.existsSync(fullCssPath)) return true;
|
|
1110
1106
|
if (!fs.existsSync(configPath)) return true;
|
|
1111
1107
|
|
|
1112
|
-
return fs.statSync(configPath).mtimeMs > fs.statSync(
|
|
1108
|
+
return fs.statSync(configPath).mtimeMs > fs.statSync(fullCssPath).mtimeMs;
|
|
1113
1109
|
}
|
|
1114
1110
|
|
|
1115
1111
|
function ensureFullFramework() {
|
|
@@ -1121,18 +1117,19 @@ function ensureFullFramework() {
|
|
|
1121
1117
|
function build(options = {}) {
|
|
1122
1118
|
ensureFullFramework();
|
|
1123
1119
|
|
|
1120
|
+
const config = getConfig();
|
|
1121
|
+
const fullCssPath = getFullCssPath(config);
|
|
1124
1122
|
const result = buildProductionCss();
|
|
1125
|
-
const cssPath = path.join(process.cwd(), 'dist/emily.css');
|
|
1126
1123
|
|
|
1127
|
-
console.log('✓ Generated production CSS:
|
|
1124
|
+
console.log('✓ Generated production CSS: ' + path.relative(process.cwd(), result.outputPath));
|
|
1128
1125
|
console.log('✓ File size: ' + (result.outputSize / 1024).toFixed(2) + ' KB');
|
|
1129
1126
|
|
|
1130
|
-
if (!options.keepFull && fs.existsSync(
|
|
1127
|
+
if (!options.keepFull && fs.existsSync(fullCssPath)) {
|
|
1131
1128
|
try {
|
|
1132
|
-
fs.unlinkSync(
|
|
1133
|
-
console.log('Removed
|
|
1134
|
-
} catch (
|
|
1135
|
-
// 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.
|
|
1136
1133
|
}
|
|
1137
1134
|
}
|
|
1138
1135
|
|
|
@@ -1162,4 +1159,4 @@ module.exports = {
|
|
|
1162
1159
|
addResponsiveVariants,
|
|
1163
1160
|
generateFontCSS,
|
|
1164
1161
|
codeUtilities,
|
|
1165
|
-
};
|
|
1162
|
+
};
|