poops 1.9.3 → 1.9.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/poops.js +40 -8
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "poops",
3
3
  "description": "Straightforward, no-bullshit bundler for the web.",
4
- "version": "1.9.3",
4
+ "version": "1.9.4",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "poops.js",
package/poops.js CHANGED
@@ -139,6 +139,44 @@ function setupWatchers(config, modules) {
139
139
  .filter((zone) => zone && zone !== '.')
140
140
  const isBuildOutput = (file) => outputZones.some((zone) => pathContainsPathSegment(file, zone))
141
141
 
142
+ // One chokidar event fires per written file, so a single build that writes
143
+ // several outputs into a watched dir (css + map + min.css) triggers the same
144
+ // rebuild branch once per file. The trailing debounce folds such a burst
145
+ // into one run; the window collects the paths so the handler can still
146
+ // reason per-file (the copy branch's css hot-swap vs full reload). 300ms:
147
+ // outlives the 150ms awaitWriteFinish settle between files of one burst.
148
+ const coalesce = (fn, ms = 300) => {
149
+ let timer
150
+ const files = new Set()
151
+ return (file) => {
152
+ files.add(file)
153
+ clearTimeout(timer)
154
+ timer = setTimeout(() => {
155
+ const batch = [...files]
156
+ files.clear()
157
+ fn(batch)
158
+ }, ms)
159
+ }
160
+ }
161
+
162
+ const recompileStyles = coalesce(() => {
163
+ modules.styles.compile().then(() => modules.postcss.compile())
164
+ .then(() => { hook('styles'); styleOutputs(config).forEach((out) => reload(out)) })
165
+ .catch(err => console.error(err))
166
+ })
167
+
168
+ // A copied .css (e.g. the styles compiler's own output landing in a copy
169
+ // source) stays a hot-swap; any other copied file escalates to a full
170
+ // reload — reload() itself folds the batch into one refresh.
171
+ const recopy = coalesce((batch) => {
172
+ modules.copy.execute()
173
+ .then(() => {
174
+ hook('copy')
175
+ batch.forEach((file) => reload(/\.css$/i.test(file) ? file : undefined))
176
+ })
177
+ .catch(err => console.error(err))
178
+ })
179
+
142
180
  const rebuild = (file) => {
143
181
  // Engines that keep compiled templates across compiles (nunjucks) drop
144
182
  // exactly this file's entries; shared by change/add/unlink via rebuild.
@@ -156,9 +194,7 @@ function setupWatchers(config, modules) {
156
194
  }
157
195
  }
158
196
  if (/(\.sass|\.scss|\.css)$/i.test(file) && !isBuildOutput(file)) {
159
- modules.styles.compile().then(() => modules.postcss.compile())
160
- .then(() => { hook('styles'); styleOutputs(config).forEach((out) => reload(out)) })
161
- .catch(err => console.error(err))
197
+ recompileStyles(file)
162
198
  }
163
199
  if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.liquid|\.md)$/i.test(file)) {
164
200
  // Incremental: re-render only the pages whose last render touched this
@@ -189,11 +225,7 @@ function setupWatchers(config, modules) {
189
225
  .then(() => { hook('images'); hook('markup'); reload() })
190
226
  .catch(err => console.error(err))
191
227
  }
192
- // A copied .css (e.g. the styles compiler's own output landing in a copy
193
- // source) stays a hot-swap; any other copied file needs a full reload.
194
- doesFileBelongToPath(file, config.copy) && modules.copy.execute()
195
- .then(() => { hook('copy'); reload(/\.css$/i.test(file) ? file : undefined) })
196
- .catch(err => console.error(err))
228
+ doesFileBelongToPath(file, config.copy) && recopy(file)
197
229
  }
198
230
 
199
231
  // Atomic-save editors (rename-write) fire unlink+add for every save, so an