poops 1.2.4 → 1.4.0

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.
@@ -181,3 +181,33 @@ export function doesFileBelongToPath(filePath, configPaths) {
181
181
  }
182
182
  return false
183
183
  }
184
+
185
+ // Derive the watch list from every task's `in` path when watch is `true`.
186
+ // File entries (scripts/styles/reactor) resolve to their parent dir so sibling
187
+ // imports still retrigger a rebuild; dir entries (markup/copy/images) are
188
+ // watched as-is. Imports that reach outside a task's own dir aren't covered —
189
+ // use an explicit `watch` array for that. includePaths is skipped on purpose:
190
+ // it defaults to node_modules, which must never be watched.
191
+ export function deriveWatchDirs(config) {
192
+ const paths = []
193
+ const collect = (task) => {
194
+ if (!task) return
195
+ for (const entry of [].concat(task)) {
196
+ if (!entry || typeof entry !== 'object') continue
197
+ if (entry.in) paths.push(...(Array.isArray(entry.in) ? entry.in : [entry.in]))
198
+ if (entry.component) paths.push(entry.component) // reactor source component
199
+ const tokenPaths = entry.options && entry.options.tokenPaths
200
+ if (tokenPaths) paths.push(...[].concat(tokenPaths))
201
+ }
202
+ }
203
+ collect(config.scripts)
204
+ collect(config.styles)
205
+ collect(config.reactor)
206
+ collect(config.markup)
207
+ collect(config.copy)
208
+ collect(config.images)
209
+ // ponytail: extname()-based dir/file split misreads dot-named dirs (rare);
210
+ // the escape hatch is an explicit watch array.
211
+ return [...new Set(paths.map((p) => (path.extname(p) ? path.dirname(p) : p)))]
212
+ .filter((p) => p && p !== '.')
213
+ }
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.2.4",
4
+ "version": "1.4.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "poops.js",
@@ -62,6 +62,8 @@
62
62
  "liquidjs": "^10.24.0",
63
63
  "livereload": "^0.9.3",
64
64
  "marked": "^9.0.3",
65
+ "marked-github-alerts": "^1.0.1",
66
+ "marked-github-emoji": "^1.0.2",
65
67
  "nunjucks": "^3.2.4",
66
68
  "portscanner": "^2.2.0",
67
69
  "printstyle": "^1.0.0",
package/poops.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import chokidar from 'chokidar'
4
4
  import connect from 'connect'
5
5
  import Copy from './lib/copy.js'
6
- import { pathExists, doesFileBelongToPath } from './lib/utils/helpers.js'
6
+ import { pathExists, doesFileBelongToPath, pathContainsPathSegment, deriveWatchDirs } from './lib/utils/helpers.js'
7
7
  import http from 'node:http'
8
8
  import os from 'node:os'
9
9
  import fs from 'node:fs'
@@ -85,8 +85,6 @@ function setupLiveReloadServer(config) {
85
85
  function setupWatchers(config, modules) {
86
86
  if (!config.watch) return
87
87
 
88
- // TODO: think about watching the updates of the config file itself, we can reload the config and recompile everything.
89
- // TODO: ability to automatically create a watch list of directories if watch is set to true. The list will be generated from the `in` property of each task.
90
88
  // awaitWriteFinish: wait for saves to finish writing before recompiling, so a
91
89
  // mid-write (truncated/partial) file is never read. Fixes intermittent broken
92
90
  // builds on editor save. Bump thresholds if slow disks flake.
@@ -95,8 +93,19 @@ function setupWatchers(config, modules) {
95
93
  // rebuilds. 'add' also covers genuinely new files (e.g. a new markup page).
96
94
  // Rebuild branches shared by change/add/deletion: a deletion needs the same
97
95
  // rebuilds (a deleted-but-still-imported file must surface the error).
96
+ // Scripts/styles outputs can land inside a watched dir (e.g. a Shopify
97
+ // theme's assets/): the compiler's own write must not retrigger that
98
+ // compiler, or watch loops forever. Zones are the dirs the compilers write
99
+ // into; only their own extensions are skipped there, so e.g. a hand-edited
100
+ // .liquid asset in the same dir still rebuilds markup.
101
+ const outputZones = [config.scripts, config.styles].flat()
102
+ .filter((entry) => entry && entry.out)
103
+ .map((entry) => (path.extname(entry.out) ? path.dirname(entry.out) : entry.out))
104
+ .filter((zone) => zone && zone !== '.')
105
+ const isBuildOutput = (file) => outputZones.some((zone) => pathContainsPathSegment(file, zone))
106
+
98
107
  const rebuild = (file) => {
99
- if (/(\.m?jsx?|\.tsx?)$/i.test(file)) {
108
+ if (/(\.m?jsx?|\.tsx?)$/i.test(file) && !isBuildOutput(file)) {
100
109
  modules.scripts.compile().catch(err => console.error(err))
101
110
 
102
111
  if (modules.reactor.belongsToReactor(file)) {
@@ -108,7 +117,7 @@ function setupWatchers(config, modules) {
108
117
  }).catch(err => console.error(err))
109
118
  }
110
119
  }
111
- if (/(\.sass|\.scss|\.css)$/i.test(file)) {
120
+ if (/(\.sass|\.scss|\.css)$/i.test(file) && !isBuildOutput(file)) {
112
121
  modules.styles.compile().then(() => modules.postcss.compile()).catch(err => console.error(err))
113
122
  }
114
123
  if (/(\.html|\.xml|\.rss|\.atom|\.njk|\.liquid|\.md)$/i.test(file)) {
@@ -240,7 +249,9 @@ if (!pathExists(configPath)) {
240
249
  // Load poops.json
241
250
  const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
242
251
 
243
- if (config.watch) {
252
+ if (config.watch === true) {
253
+ config.watch = deriveWatchDirs(config)
254
+ } else if (config.watch) {
244
255
  config.watch = Array.isArray(config.watch) ? config.watch : [config.watch]
245
256
  }
246
257