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/src/watch.js CHANGED
@@ -1,9 +1,16 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const chokidar = require('chokidar');
4
- const chalk = require('chalk');
5
- const { buildFullFramework, buildProductionCss, ensureFullFramework } = require('./index.js');
6
- const { getAllFiles, extractClassNames } = require('./purge.js');
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const chokidar = require("chokidar");
4
+ const chalk = require("chalk");
5
+ const fg = require("fast-glob");
6
+
7
+ const {
8
+ buildFullFramework,
9
+ buildProductionCss,
10
+ ensureFullFramework,
11
+ } = require("./index.js");
12
+
13
+ const { extractClassNames } = require("./purge.js");
7
14
 
8
15
  let isRunning = false;
9
16
  let pendingRun = false;
@@ -11,31 +18,35 @@ let previousClasses = new Set();
11
18
  let hasRunOnce = false;
12
19
 
13
20
  function readConfig() {
14
- const configPath = path.join(process.cwd(), 'emily.config.json');
21
+ const configPath = path.join(process.cwd(), "emily.config.json");
15
22
 
16
23
  if (!fs.existsSync(configPath)) {
17
24
  console.error('\n emily-css: No config found. Run "emily-css init" first.\n');
18
25
  process.exit(1);
19
26
  }
20
27
 
21
- return JSON.parse(fs.readFileSync(configPath, 'utf8'));
28
+ return JSON.parse(fs.readFileSync(configPath, "utf8"));
29
+ }
30
+
31
+ function normalisePath(filePath) {
32
+ return filePath.replace(/\\/g, "/");
22
33
  }
23
34
 
24
35
  function shouldIgnore(filePath) {
25
- const normalised = filePath.replace(/\\/g, '/');
36
+ const normalised = normalisePath(filePath);
26
37
 
27
38
  return [
28
- 'node_modules/',
29
- '.git/',
30
- '.nuxt/',
31
- '.next/',
32
- '.output/',
33
- 'dist/',
34
- 'build/',
35
- 'coverage/',
36
- '.cache/',
37
- '.vite/'
38
- ].some(part => normalised.includes('/' + part) || normalised.startsWith(part));
39
+ "node_modules/",
40
+ ".git/",
41
+ ".nuxt/",
42
+ ".next/",
43
+ ".output/",
44
+ "dist/",
45
+ "build/",
46
+ "coverage/",
47
+ ".cache/",
48
+ ".vite/",
49
+ ].some((part) => normalised.includes("/" + part) || normalised.startsWith(part));
39
50
  }
40
51
 
41
52
  function runQuietly(fn) {
@@ -53,16 +64,58 @@ function runQuietly(fn) {
53
64
  }
54
65
  }
55
66
 
56
- function collectUsedClasses(sourceDir, config) {
57
- const files = getAllFiles(sourceDir, config.purge?.extensions);
67
+ function getScanFiles(config) {
68
+ const sourceGlobs = config.purge?.sourceGlobs;
69
+
70
+ if (sourceGlobs && sourceGlobs.length) {
71
+ return fg.sync(sourceGlobs, {
72
+ ignore: config.purge?.ignore || [],
73
+ onlyFiles: true,
74
+ unique: true,
75
+ });
76
+ }
77
+
78
+ const sourceDir = config.purge?.sourceDir || ".";
79
+ const extensions = config.purge?.extensions || [
80
+ ".html",
81
+ ".htm",
82
+ ".twig",
83
+ ".njk",
84
+ ".liquid",
85
+ ".hbs",
86
+ ".jsx",
87
+ ".tsx",
88
+ ".vue",
89
+ ".php",
90
+ ".astro",
91
+ ".svelte",
92
+ ".blade.php",
93
+ ".jinja",
94
+ ".jinja2",
95
+ ".j2",
96
+ ".md",
97
+ ];
98
+
99
+ return fg.sync(
100
+ extensions.map((ext) => `${sourceDir.replace(/\/$/, "")}/**/*${ext}`),
101
+ {
102
+ ignore: config.purge?.ignore || [],
103
+ onlyFiles: true,
104
+ unique: true,
105
+ },
106
+ );
107
+ }
108
+
109
+ function collectUsedClasses(config) {
110
+ const files = getScanFiles(config);
58
111
  const usedClasses = new Set();
59
112
 
60
113
  for (const file of files) {
61
114
  if (shouldIgnore(file)) continue;
62
115
 
63
116
  try {
64
- const content = fs.readFileSync(file, 'utf8');
65
- extractClassNames(content).forEach(cls => usedClasses.add(cls));
117
+ const content = fs.readFileSync(file, "utf8");
118
+ extractClassNames(content).forEach((cls) => usedClasses.add(cls));
66
119
  } catch {}
67
120
  }
68
121
 
@@ -70,8 +123,8 @@ function collectUsedClasses(sourceDir, config) {
70
123
  }
71
124
 
72
125
  function getClassDiff(currentClasses) {
73
- const added = [...currentClasses].filter(cls => !previousClasses.has(cls));
74
- const removed = [...previousClasses].filter(cls => !currentClasses.has(cls));
126
+ const added = [...currentClasses].filter((cls) => !previousClasses.has(cls));
127
+ const removed = [...previousClasses].filter((cls) => !currentClasses.has(cls));
75
128
 
76
129
  previousClasses = new Set(currentClasses);
77
130
 
@@ -79,32 +132,63 @@ function getClassDiff(currentClasses) {
79
132
  }
80
133
 
81
134
  function formatClassList(classes) {
82
- if (classes.length === 0) return '';
135
+ if (classes.length === 0) return "";
83
136
 
84
- const shown = classes.slice(0, 8).join(', ');
85
- const extra = classes.length > 8 ? ' +' + (classes.length - 8) + ' more' : '';
137
+ const shown = classes.slice(0, 8).join(", ");
138
+ const extra = classes.length > 8 ? " +" + (classes.length - 8) + " more" : "";
86
139
 
87
140
  return shown + extra;
88
141
  }
89
142
 
90
143
  function printSummary({ currentClasses, result, added, removed }) {
91
- const reduction = (((result.originalSize - result.outputSize) / result.originalSize) * 100).toFixed(1);
144
+ const reduction = (
145
+ ((result.originalSize - result.outputSize) / result.originalSize) *
146
+ 100
147
+ ).toFixed(1);
148
+
92
149
  const sizeKb = (result.outputSize / 1024).toFixed(1);
150
+ const outputPath = result.outputPath
151
+ ? path.relative(process.cwd(), result.outputPath)
152
+ : "emily.min.css";
153
+
93
154
  const time = new Date().toLocaleTimeString();
94
155
 
95
156
  console.log(
96
- chalk.green('' + time + ' updated') +
97
- chalk.gray(' | ' + currentClasses.size + ' classes | ' + sizeKb + ' KB | ' + reduction + '% reduced')
157
+ chalk.green("" + time + " updated") +
158
+ chalk.gray(
159
+ " | " +
160
+ currentClasses.size +
161
+ " classes | " +
162
+ sizeKb +
163
+ " KB | " +
164
+ reduction +
165
+ "% reduced | " +
166
+ outputPath,
167
+ ),
98
168
  );
99
169
 
100
170
  if (!hasRunOnce) return;
101
171
 
102
172
  if (removed.length > 0) {
103
- console.log(chalk.red('− removed ' + removed.length + ' class' + (removed.length === 1 ? '' : 'es')) + chalk.gray(' (' + formatClassList(removed) + ')'));
173
+ console.log(
174
+ chalk.red(
175
+ "− removed " +
176
+ removed.length +
177
+ " class" +
178
+ (removed.length === 1 ? "" : "es"),
179
+ ) + chalk.gray(" (" + formatClassList(removed) + ")"),
180
+ );
104
181
  }
105
182
 
106
183
  if (added.length > 0) {
107
- console.log(chalk.green('+ added ' + added.length + ' class' + (added.length === 1 ? '' : 'es')) + chalk.gray(' (' + formatClassList(added) + ')'));
184
+ console.log(
185
+ chalk.green(
186
+ "+ added " +
187
+ added.length +
188
+ " class" +
189
+ (added.length === 1 ? "" : "es"),
190
+ ) + chalk.gray(" (" + formatClassList(added) + ")"),
191
+ );
108
192
  }
109
193
  }
110
194
 
@@ -118,25 +202,24 @@ function runProductionUpdate(filePath) {
118
202
 
119
203
  try {
120
204
  const config = readConfig();
121
- const sourceDir = config.purge?.sourceDir || '.';
122
- const isConfigChange = filePath && filePath.replace(/\\/g, '/').endsWith('emily.config.json');
123
- const cssPath = path.join(process.cwd(), 'dist/emily.css');
205
+ const normalisedFilePath = filePath ? normalisePath(filePath) : "";
206
+ const isConfigChange = normalisedFilePath.endsWith("emily.config.json");
124
207
 
125
- if (isConfigChange) {
126
- runQuietly(() => buildFullFramework());
127
- } else {
128
- runQuietly(() => ensureFullFramework());
129
- }
208
+ if (isConfigChange) {
209
+ runQuietly(() => buildFullFramework());
210
+ } else {
211
+ runQuietly(() => ensureFullFramework());
212
+ }
130
213
 
131
214
  const result = runQuietly(() => buildProductionCss());
132
- const currentClasses = collectUsedClasses(sourceDir, config);
215
+ const currentClasses = collectUsedClasses(config);
133
216
  const { added, removed } = getClassDiff(currentClasses);
134
217
 
135
218
  printSummary({ currentClasses, result, added, removed });
136
219
 
137
220
  hasRunOnce = true;
138
221
  } catch (error) {
139
- console.error('\n❌ EmilyUI watch failed');
222
+ console.error("\n❌ EmilyUI watch failed");
140
223
  console.error(error.message);
141
224
  } finally {
142
225
  isRunning = false;
@@ -150,8 +233,8 @@ function runProductionUpdate(filePath) {
150
233
 
151
234
  function getWatchPaths(config) {
152
235
  return [
153
- config.purge?.sourceDir || '.',
154
- 'emily.config.json'
236
+ config.purge?.sourceDir || ".",
237
+ "emily.config.json",
155
238
  ];
156
239
  }
157
240
 
@@ -164,9 +247,14 @@ function runWatch() {
164
247
  const config = readConfig();
165
248
  const watchPaths = getWatchPaths(config);
166
249
 
167
- console.log('\n👀 EmilyUI is watching...');
168
- console.log(chalk.gray(' Watching:'));
169
- watchPaths.forEach(item => console.log(chalk.gray(' - ' + item)));
250
+ console.log("\n👀 EmilyUI is watching...");
251
+ console.log(chalk.gray(" Project: " + (config.purge?.projectType || "Unknown")));
252
+ console.log(chalk.gray(" Output: " + (config.output?.css || "dist/emily.min.css")));
253
+ console.log(chalk.gray(" Watching:"));
254
+
255
+ watchPaths.forEach((item) => {
256
+ console.log(chalk.gray(" - " + item));
257
+ });
170
258
 
171
259
  runQuietly(() => ensureFullFramework());
172
260
  runProductionUpdate();
@@ -176,18 +264,18 @@ function runWatch() {
176
264
  ignoreInitial: true,
177
265
  awaitWriteFinish: {
178
266
  stabilityThreshold: 500,
179
- pollInterval: 100
180
- }
267
+ pollInterval: 100,
268
+ },
181
269
  });
182
270
 
183
- watcher.on('change', queueUpdate);
184
- watcher.on('add', queueUpdate);
185
- watcher.on('unlink', queueUpdate);
271
+ watcher.on("change", queueUpdate);
272
+ watcher.on("add", queueUpdate);
273
+ watcher.on("unlink", queueUpdate);
186
274
 
187
- watcher.on('error', error => {
188
- console.error('\n❌ EmilyUI watcher error');
275
+ watcher.on("error", (error) => {
276
+ console.error("\n❌ EmilyUI watcher error");
189
277
  console.error(error.message);
190
278
  });
191
279
  }
192
280
 
193
- runWatch();
281
+ runWatch();
package/src/purge-cmd.js DELETED
@@ -1,55 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- function runPurge() {
5
- const configPath = path.join(process.cwd(), 'emily.config.json');
6
-
7
- if (!fs.existsSync(configPath)) {
8
- console.error('\n emily-css: No config found. Run "emily-css init" first.\n');
9
- process.exit(1);
10
- }
11
-
12
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
13
- const sourceDir = config.purge && config.purge.sourceDir;
14
-
15
- if (!sourceDir) {
16
- console.error('\n emily-css: No purge sourceDir in emily.config.json.');
17
- console.error(' Add: "purge": { "sourceDir": "./src" }\n');
18
- process.exit(1);
19
- }
20
-
21
- const cssPath = path.join(process.cwd(), 'dist/emily.css');
22
-
23
- if (!fs.existsSync(cssPath)) {
24
- console.error('\n emily-css: No CSS found. Run "emily-css build" first.\n');
25
- process.exit(1);
26
- }
27
-
28
- const { purgeCSS } = require('./purge.js');
29
-
30
- console.log('\nPurging unused utilities from ' + sourceDir + '...');
31
-
32
- const css = fs.readFileSync(cssPath, 'utf8');
33
- const purged = purgeCSS(css, sourceDir, config);
34
- const minified = purged
35
- .replace(/\/\*[\s\S]*?\*\//g, '')
36
- .replace(/\s+/g, ' ')
37
- .replace(/\s?\{/g, '{')
38
- .replace(/\s?\}/g, '}')
39
- .replace(/;\s/g, ';')
40
- .trim();
41
-
42
- fs.writeFileSync(path.join(process.cwd(), 'dist/emily.purged.css'), purged);
43
- fs.writeFileSync(path.join(process.cwd(), 'dist/emily.purged.min.css'), minified);
44
-
45
- const original = Buffer.byteLength(css, 'utf8');
46
- const purgedSize = Buffer.byteLength(purged, 'utf8');
47
- const reduction = Math.round((1 - purgedSize / original) * 100);
48
-
49
- console.log('\n\u2713 Purged CSS written:');
50
- console.log(' dist/emily.purged.css');
51
- console.log(' dist/emily.purged.min.css');
52
- console.log('\n ' + Math.round(original / 1024) + 'KB -> ' + Math.round(purgedSize / 1024) + 'KB (' + reduction + '% reduction)\n');
53
- }
54
-
55
- runPurge();