poops 1.9.5 → 1.9.7
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/README.md +14 -2
- package/lib/markup/helpers.js +10 -6
- package/lib/scripts.js +28 -5
- package/lib/styles.js +19 -4
- package/lib/utils/helpers.js +39 -0
- package/package.json +1 -1
- package/poops.js +14 -12
package/README.md
CHANGED
|
@@ -217,7 +217,7 @@ You can freely remove the properties that you don't need. For example, if you do
|
|
|
217
217
|
Scripts are bundled with [esbuild](https://esbuild.github.io/). Supports `.js`, `.ts`, `.jsx`, and `.tsx` files out of the box — including React and other JSX frameworks. You can specify multiple scripts to bundle. Each script has the following properties:
|
|
218
218
|
|
|
219
219
|
- `in` - the input path, can be a file path, an array of file paths, or a glob pattern (e.g. `"src/js/*.js"`, `"src/elements/*/index.{js,mjs,cjs,jsx,ts,tsx}"`). Globs must use `/` separators (even on Windows)
|
|
220
|
-
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — entry points from different directories nest their output under the common ancestor (esbuild's `outbase`). A **glob-matched** `index.*` is named after its directory instead, placed relative to the glob's static prefix: `"src/elements/*/index.js"` gives `<out>/accordion.js`, while `"src/*/accordion/index.js"` keeps the differing segment as `<out>/blocks/accordion.js`. A literal `in: "src/index.js"` keeps its own basename
|
|
220
|
+
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — entry points from different directories nest their output under the common ancestor (esbuild's `outbase`). A **glob-matched** `index.*` is named after its directory instead, placed relative to the glob's static prefix: `"src/elements/*/index.js"` gives `<out>/accordion.js`, while `"src/*/accordion/index.js"` keeps the differing segment as `<out>/blocks/accordion.js`. A literal `in: "src/index.js"` keeps its own basename. To name outputs yourself, `out` can be a **template**: `{{dir}}` is the input's directory relative to the glob's static prefix, `{{name}}` its basename without extension. `"src/elements/*/widget.ts"` with `out: "dist/js/{{dir}}-{{name}}.js"` gives `dist/js/accordion-widget.js` and `dist/js/tabs-widget.js` — one bundle per match, named by you instead of by the common ancestor. The template's extension is honoured too, so `out: "dist/esm/{{dir}}.mjs"` writes `.mjs` files
|
|
221
221
|
- `options` - the options for the bundler. You can apply most of the esbuild options that are not in conflict with Poops. See [esbuild's options](https://esbuild.github.io/api/#build-api) for more info.
|
|
222
222
|
|
|
223
223
|
**Options:**
|
|
@@ -351,7 +351,7 @@ Poops does not need `react` or `react-dom` as its own dependency — they are re
|
|
|
351
351
|
Styles are bundled with [Dart Sass](https://sass-lang.com/dart-sass). You can specify multiple styles to bundle. Each style has the following properties:
|
|
352
352
|
|
|
353
353
|
- `in` - the input path, can be a file path, an array of file paths, or a glob pattern (e.g. `"src/scss/*.scss"`, `"src/elements/*/index.{scss,sass,css}"`). Globs must use `/` separators (even on Windows) and skip Sass partials (`_*.scss`). Each matched file is compiled separately
|
|
354
|
-
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — each input compiles to `<out>/<basename>.css`, so inputs sharing a basename (e.g. `a/main.scss` and `b/main.scss`) will overwrite each other. A **glob-matched** `index.*` is named after its directory instead, placed relative to the glob's static prefix: `"src/elements/*/index.scss"` gives `<out>/accordion.css`, while `"src/*/accordion/index.scss"` keeps the differing segment as `<out>/blocks/accordion.css`. A literal `in: "src/scss/index.scss"` keeps its own basename
|
|
354
|
+
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — each input compiles to `<out>/<basename>.css`, so inputs sharing a basename (e.g. `a/main.scss` and `b/main.scss`) will overwrite each other. A **glob-matched** `index.*` is named after its directory instead, placed relative to the glob's static prefix: `"src/elements/*/index.scss"` gives `<out>/accordion.css`, while `"src/*/accordion/index.scss"` keeps the differing segment as `<out>/blocks/accordion.css`. A literal `in: "src/scss/index.scss"` keeps its own basename. To name outputs yourself, `out` can be a **template**: `{{dir}}` is the input's directory relative to the glob's static prefix, `{{name}}` its basename without extension. `"src/elements/*/theme.scss"` with `out: "dist/{{dir}}-theme.css"` gives `dist/accordion-theme.css` and `dist/tabs-theme.css` — one output per match, instead of every `theme.scss` overwriting the same `theme.css`
|
|
355
355
|
- `options` - the options for the bundler.
|
|
356
356
|
|
|
357
357
|
**Options:**
|
|
@@ -1093,6 +1093,18 @@ All filters are available in both engines. The only syntax difference is how arg
|
|
|
1093
1093
|
---
|
|
1094
1094
|
```
|
|
1095
1095
|
|
|
1096
|
+
The same `jsonld` object works in your `site` data, as a site-wide default — useful when every page on the site is one type. A docs site is `TechArticle`, not `WebPage`:
|
|
1097
|
+
|
|
1098
|
+
```json
|
|
1099
|
+
"markup": {
|
|
1100
|
+
"site": {
|
|
1101
|
+
"jsonld": { "@type": "TechArticle" }
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
Precedence is defaults → `site.jsonld` → `page.jsonld`, so a single page can still opt out (a `FAQPage` inside a `TechArticle` site). A site-wide `@type` also overrides the auto-detected `BlogPosting` on dated pages, so on a site mixing docs and a blog set the type per page instead. Both merge into the page's own block only — the auto-emitted `WebSite` and `BreadcrumbList` blocks are untouched.
|
|
1107
|
+
|
|
1096
1108
|
poops auto-picks `BlogPosting` (page has a `date`) or `WebPage`. Override `@type` with the `jsonld` object for any schema.org type — common ones search/generative engines act on: `Article`, `NewsArticle`, `HowTo`, `FAQPage`, `QAPage`, `Product`, `Recipe`, `Event`, `Course`, `VideoObject`, `SoftwareApplication`, `Organization`, `Person`, `BreadcrumbList`, `WebSite`. Full list at [schema.org/docs/full](https://schema.org/docs/full.html); validate with the [Rich Results Test](https://search.google.com/test/rich-results). A per-`@type` table with the notable fields is in the [Templating docs](example/src/markup/docs/quick-start/templating-html.md).
|
|
1097
1109
|
|
|
1098
1110
|
- `breadcrumb` — generates a visible breadcrumb `<nav class="breadcrumb"><ol>…</ol></nav>` trail for the page body (blog posts, nested pages), from the same URL-depth data the `jsonld` `BreadcrumbList` uses: the site root, each ancestor folder (humanized, e.g. `docs/static-site` → _Static Site_), then the current page as `aria-current` text. Pass `relativePathPrefix` so links resolve against the current output location (localhost in dev, your deployed subpath in prod) — not the absolute domain.
|
package/lib/markup/helpers.js
CHANGED
|
@@ -532,10 +532,12 @@ export function buildBreadcrumb(page, site = {}, prefix = '') {
|
|
|
532
532
|
// gains a `logo` ImageObject when `site.logo` is set — Google Article rich
|
|
533
533
|
// results require it. On the homepage (page has no `url`) a second `WebSite`
|
|
534
534
|
// block is emitted, declaring the site name for search results.
|
|
535
|
-
//
|
|
536
|
-
//
|
|
537
|
-
//
|
|
538
|
-
//
|
|
535
|
+
// `site.jsonld` and `page.jsonld` (objects) are merged over the generated
|
|
536
|
+
// defaults — site-wide first, then the page — so a site can set a default
|
|
537
|
+
// @type (a docs site is TechArticle, not WebPage) and a page can still add or
|
|
538
|
+
// replace any field as a full escape hatch. Values come from front matter, so
|
|
539
|
+
// the JSON is escaped for a <script> context to prevent a `</script>`
|
|
540
|
+
// break-out (XSS).
|
|
539
541
|
export function buildJsonLd(page, site = {}) {
|
|
540
542
|
page = page || {}
|
|
541
543
|
site = site || {}
|
|
@@ -577,8 +579,10 @@ export function buildJsonLd(page, site = {}) {
|
|
|
577
579
|
if (page.wordcount) data.wordCount = page.wordcount
|
|
578
580
|
}
|
|
579
581
|
|
|
580
|
-
// page.jsonld
|
|
581
|
-
// the escape hatch is meant for whole-key
|
|
582
|
+
// site.jsonld then page.jsonld extend/override — the page wins. Shallow
|
|
583
|
+
// merge — nested schema is rare and the escape hatch is meant for whole-key
|
|
584
|
+
// replacement.
|
|
585
|
+
if (site.jsonld && typeof site.jsonld === 'object') Object.assign(data, site.jsonld)
|
|
582
586
|
if (page.jsonld && typeof page.jsonld === 'object') Object.assign(data, page.jsonld)
|
|
583
587
|
|
|
584
588
|
const blocks = [jsonLdScript(data)]
|
package/lib/scripts.js
CHANGED
|
@@ -3,15 +3,21 @@ import { globSync, hasMagic } from 'glob'
|
|
|
3
3
|
import { deepMerge } from 'book-of-spells'
|
|
4
4
|
import {
|
|
5
5
|
pathExists,
|
|
6
|
+
mkDir,
|
|
6
7
|
mkPath,
|
|
7
8
|
pathForFile,
|
|
8
9
|
isIndexEntry,
|
|
9
10
|
globBase,
|
|
10
11
|
indexEntryOut,
|
|
12
|
+
entryBase,
|
|
13
|
+
hasOutTemplate,
|
|
14
|
+
outTemplateBase,
|
|
15
|
+
fillOutTemplateEntry,
|
|
11
16
|
fillBannerTemplate,
|
|
12
17
|
buildTime,
|
|
13
18
|
fileSize
|
|
14
19
|
} from './utils/helpers.js'
|
|
20
|
+
import path from 'node:path'
|
|
15
21
|
import minifyToFile from './utils/minify.js'
|
|
16
22
|
import log from './utils/log.js'
|
|
17
23
|
|
|
@@ -30,6 +36,11 @@ export default class Scripts {
|
|
|
30
36
|
// `in` may be an array of entry points — pathExists on an array throws
|
|
31
37
|
const configured = Array.isArray(scriptEntry.in) ? scriptEntry.in : [scriptEntry.in]
|
|
32
38
|
const entryPoints = []
|
|
39
|
+
// A templated `out` names one output per entry point. esbuild already
|
|
40
|
+
// takes a per-entry `out` relative to `outdir` — the same door the
|
|
41
|
+
// `index.*` rename goes through — so the template resolves into that.
|
|
42
|
+
const templated = hasOutTemplate(scriptEntry.out)
|
|
43
|
+
const outBase = templated ? outTemplateBase(scriptEntry.out) : null
|
|
33
44
|
let missing = false
|
|
34
45
|
for (const entry of configured) {
|
|
35
46
|
if (hasMagic(entry, { magicalBraces: true })) {
|
|
@@ -44,19 +55,31 @@ export default class Scripts {
|
|
|
44
55
|
// `index.js` or nests a level deep under esbuild's outbase. Literal
|
|
45
56
|
// entries keep their basename — see `isIndexEntry`.
|
|
46
57
|
const base = globBase(entry)
|
|
47
|
-
entryPoints.push(...matches.map(match =>
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
entryPoints.push(...matches.map(match => {
|
|
59
|
+
if (templated) return { in: match, out: fillOutTemplateEntry(scriptEntry.out, match, base, outBase) }
|
|
60
|
+
return isIndexEntry(match) ? { in: match, out: indexEntryOut(match, base) } : match
|
|
61
|
+
}))
|
|
50
62
|
} else if (!pathExists(entry)) {
|
|
51
63
|
log({ tag: 'script', error: true, text: 'Entry does not exist:', link: entry })
|
|
52
64
|
missing = true
|
|
65
|
+
} else if (templated) {
|
|
66
|
+
entryPoints.push({ in: entry, out: fillOutTemplateEntry(scriptEntry.out, entry, entryBase(entry), outBase) })
|
|
53
67
|
} else {
|
|
54
68
|
entryPoints.push(entry)
|
|
55
69
|
}
|
|
56
70
|
}
|
|
57
71
|
if (missing) continue
|
|
58
|
-
|
|
59
|
-
|
|
72
|
+
let options = scriptEntry.options
|
|
73
|
+
if (templated) {
|
|
74
|
+
mkDir(outBase)
|
|
75
|
+
// esbuild appends its own extension, so a template asking for one other
|
|
76
|
+
// than `.js` has to be honoured through `outExtension` or it's ignored
|
|
77
|
+
const ext = path.posix.extname(scriptEntry.out)
|
|
78
|
+
if (ext && ext !== '.js') options = { outExtension: { '.js': ext }, ...options }
|
|
79
|
+
} else {
|
|
80
|
+
mkPath(scriptEntry.out)
|
|
81
|
+
}
|
|
82
|
+
await this.compileEntry(entryPoints, templated ? outBase : scriptEntry.out, options)
|
|
60
83
|
}
|
|
61
84
|
}
|
|
62
85
|
|
package/lib/styles.js
CHANGED
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
isIndexEntry,
|
|
9
9
|
globBase,
|
|
10
10
|
indexEntryOut,
|
|
11
|
+
entryBase,
|
|
12
|
+
hasOutTemplate,
|
|
13
|
+
fillOutTemplate,
|
|
11
14
|
fillBannerTemplate,
|
|
12
15
|
buildTime,
|
|
13
16
|
fileSize
|
|
@@ -23,10 +26,15 @@ export default class Styles {
|
|
|
23
26
|
constructor(config) {
|
|
24
27
|
this.config = config
|
|
25
28
|
this.banner = config.banner ? fillBannerTemplate(config.banner) : null
|
|
29
|
+
// The css files the last compile wrote. Globs and templated `out` name a
|
|
30
|
+
// file per match, so the config alone can't say what landed where — the
|
|
31
|
+
// livereload chain reads this to hot-swap exactly the stylesheets rebuilt.
|
|
32
|
+
this.outputs = []
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
async compile() {
|
|
29
36
|
if (!this.config.styles) return
|
|
37
|
+
this.outputs = []
|
|
30
38
|
this.config.styles = Array.isArray(this.config.styles) ? this.config.styles : [this.config.styles]
|
|
31
39
|
for (const styleEntry of this.config.styles) {
|
|
32
40
|
if (!styleEntry.in || !styleEntry.out) continue
|
|
@@ -34,6 +42,8 @@ export default class Styles {
|
|
|
34
42
|
const configured = Array.isArray(styleEntry.in) ? styleEntry.in : [styleEntry.in]
|
|
35
43
|
const entryPoints = []
|
|
36
44
|
const indexEntries = new Map()
|
|
45
|
+
const bases = new Map()
|
|
46
|
+
const templated = hasOutTemplate(styleEntry.out)
|
|
37
47
|
let missing = false
|
|
38
48
|
for (const entry of configured) {
|
|
39
49
|
if (hasMagic(entry, { magicalBraces: true })) {
|
|
@@ -50,25 +60,29 @@ export default class Styles {
|
|
|
50
60
|
const base = globBase(entry)
|
|
51
61
|
for (const match of matches) {
|
|
52
62
|
if (isIndexEntry(match)) indexEntries.set(match, indexEntryOut(match, base))
|
|
63
|
+
bases.set(match, base)
|
|
53
64
|
}
|
|
54
65
|
entryPoints.push(...matches)
|
|
55
66
|
} else if (!pathExists(entry)) {
|
|
56
67
|
log({ tag: 'style', error: true, text: 'Entry does not exist:', link: entry })
|
|
57
68
|
missing = true
|
|
58
69
|
} else {
|
|
70
|
+
bases.set(entry, entryBase(entry))
|
|
59
71
|
entryPoints.push(entry)
|
|
60
72
|
}
|
|
61
73
|
}
|
|
62
74
|
if (missing) continue
|
|
63
|
-
|
|
75
|
+
// A templated `out` resolves to a different file per entry point, so the
|
|
76
|
+
// single-file guard doesn't apply to it
|
|
77
|
+
if (!templated && entryPoints.length > 1 && pathForFile(styleEntry.out)) {
|
|
64
78
|
log({ tag: 'error', text: 'Cannot output multiple style files to a single file. Please specify an output directory path instead.' })
|
|
65
79
|
process.exit(1)
|
|
66
80
|
}
|
|
67
81
|
for (const entryPoint of entryPoints) {
|
|
68
82
|
// Resolve the index rename here, where `out` is still known to be a dir
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
let out = styleEntry.out
|
|
84
|
+
if (templated) out = fillOutTemplate(out, entryPoint, bases.get(entryPoint))
|
|
85
|
+
else if (indexEntries.has(entryPoint) && !pathForFile(out)) out = path.join(out, `${indexEntries.get(entryPoint)}.css`)
|
|
72
86
|
await this.compileEntry(entryPoint, out, styleEntry.options)
|
|
73
87
|
}
|
|
74
88
|
}
|
|
@@ -113,6 +127,7 @@ export default class Styles {
|
|
|
113
127
|
const mapsrc = options.sourcemap ? `\n/*# sourceMappingURL=${path.basename(outfilePath)}.map */` : ''
|
|
114
128
|
if (this.banner) compiledSass.css = this.banner + '\n' + compiledSass.css
|
|
115
129
|
fs.writeFileSync(outfilePath, compiledSass.css + mapsrc)
|
|
130
|
+
this.outputs.push(outfilePath) // after the write — a failed compile has nothing to reload
|
|
116
131
|
const stylesEnd = performance.now()
|
|
117
132
|
if (!options.justMinified) log({ tag: 'style', text: 'Compiled:', link: outfilePath, size: fileSize(outfilePath), time: buildTime(stylesStart, stylesEnd) })
|
|
118
133
|
|
package/lib/utils/helpers.js
CHANGED
|
@@ -62,6 +62,45 @@ export function indexEntryOut(inputPath, base) {
|
|
|
62
62
|
return rel || path.posix.basename(dir)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// The `index.*` rename only saves entry points named `index` — every other
|
|
66
|
+
// glob match still collapses onto its own basename, so `src/elements/*/theme.scss`
|
|
67
|
+
// writes `theme.css` once per element and the last one wins. An `out` carrying
|
|
68
|
+
// `{{dir}}`/`{{name}}` names one output per match instead.
|
|
69
|
+
export function hasOutTemplate(outputPath) {
|
|
70
|
+
return /{{\s*(dir|name)\s*}}/.test(outputPath)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// `{{dir}}` is the match's directory relative to the glob's static prefix (the
|
|
74
|
+
// same name `index.*` entries get), `{{name}}` its extension-less basename.
|
|
75
|
+
export function fillOutTemplate(template, inputPath, base) {
|
|
76
|
+
const { name } = path.parse(inputPath)
|
|
77
|
+
return template
|
|
78
|
+
.replace(/{{\s*dir\s*}}/g, indexEntryOut(inputPath, base))
|
|
79
|
+
.replace(/{{\s*name\s*}}/g, name)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// What an entry's outputs are named relative to: a glob's static prefix, or a
|
|
83
|
+
// literal entry's own directory (so `{{dir}}` is that directory's name).
|
|
84
|
+
export function entryBase(entry) {
|
|
85
|
+
return hasMagic(entry, { magicalBraces: true }) ? globBase(entry) : path.posix.dirname(toPosix(entry))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// The fixed directory a templated `out` writes into — everything before its
|
|
89
|
+
// first token. Only the tail varies per entry point, so this is the one part
|
|
90
|
+
// esbuild (and the watcher's output zones) can be handed up front.
|
|
91
|
+
export function outTemplateBase(outputPath) {
|
|
92
|
+
return path.posix.dirname(toPosix(outputPath).replace(/\{\{.*$/, ''))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// esbuild names an entry point's output itself, from an `out` that is relative
|
|
96
|
+
// to `outdir` and carries no extension. Resolve the `out` template into that
|
|
97
|
+
// shape so a templated scripts `out` goes through the same machinery the
|
|
98
|
+
// `index.*` rename already uses.
|
|
99
|
+
export function fillOutTemplateEntry(template, inputPath, base, outBase) {
|
|
100
|
+
const filled = toPosix(fillOutTemplate(template, inputPath, base))
|
|
101
|
+
return path.posix.relative(outBase, filled).replace(/\.[^./]*$/, '')
|
|
102
|
+
}
|
|
103
|
+
|
|
65
104
|
export function buildStyleOutputFilePath(inputPath, outputPath) {
|
|
66
105
|
if (pathForFile(outputPath)) return outputPath
|
|
67
106
|
const { name } = path.parse(inputPath)
|
package/package.json
CHANGED
package/poops.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import chokidar from 'chokidar'
|
|
4
4
|
import Copy from './lib/copy.js'
|
|
5
5
|
import runExec, { validateExec } from './lib/exec.js'
|
|
6
|
-
import { pathExists, doesFileBelongToPath, pathContainsPathSegment, deriveWatchDirs, toPosix } from './lib/utils/helpers.js'
|
|
6
|
+
import { pathExists, doesFileBelongToPath, pathContainsPathSegment, deriveWatchDirs, toPosix, hasOutTemplate, outTemplateBase } from './lib/utils/helpers.js'
|
|
7
7
|
import http from 'node:http'
|
|
8
8
|
import net from 'node:net'
|
|
9
9
|
import os from 'node:os'
|
|
@@ -89,17 +89,15 @@ function reload(file) {
|
|
|
89
89
|
}, 500)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// The css
|
|
93
|
-
// to reload() so style edits hot-swap.
|
|
94
|
-
//
|
|
92
|
+
// The css files the last styles compile actually wrote — what the styles chain
|
|
93
|
+
// reports to reload() so style edits hot-swap. Read off the compiler rather
|
|
94
|
+
// than guessed from the config: a glob or a templated `out` names one output
|
|
95
|
+
// per match, and a guessed path the browser has no stylesheet for silently
|
|
96
|
+
// downgrades the hot-swap to a full page reload.
|
|
95
97
|
// toPosix: the livereload client matches these against URL paths, so Windows
|
|
96
98
|
// backslashes would silently break the CSS hot-swap.
|
|
97
|
-
function styleOutputs(
|
|
98
|
-
return
|
|
99
|
-
.filter((entry) => entry && entry.in && entry.out)
|
|
100
|
-
.map((entry) => toPosix(path.extname(entry.out)
|
|
101
|
-
? entry.out
|
|
102
|
-
: path.join(entry.out, path.basename(entry.in).replace(/\.(sass|scss)$/i, '.css'))))
|
|
99
|
+
function styleOutputs(styles) {
|
|
100
|
+
return styles.outputs.map(toPosix)
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
// Per-stage shell hooks (config.exec). Runs after a stage compiles in both
|
|
@@ -135,7 +133,11 @@ function setupWatchers(config, modules) {
|
|
|
135
133
|
// the watch list, or every compile retriggers itself.
|
|
136
134
|
const outputZones = [config.scripts, config.styles].flat()
|
|
137
135
|
.filter((entry) => entry && entry.out)
|
|
138
|
-
|
|
136
|
+
// A templated `out` is only static up to its first token, so zone on that
|
|
137
|
+
// prefix — `dist/{{dir}}/theme.css` writes into `dist`, not `dist/{{dir}}`
|
|
138
|
+
.map((entry) => (hasOutTemplate(entry.out)
|
|
139
|
+
? outTemplateBase(entry.out)
|
|
140
|
+
: (path.extname(entry.out) ? path.dirname(entry.out) : entry.out)))
|
|
139
141
|
.filter((zone) => zone && zone !== '.')
|
|
140
142
|
const isBuildOutput = (file) => outputZones.some((zone) => pathContainsPathSegment(file, zone))
|
|
141
143
|
|
|
@@ -161,7 +163,7 @@ function setupWatchers(config, modules) {
|
|
|
161
163
|
|
|
162
164
|
const recompileStyles = coalesce(() => {
|
|
163
165
|
modules.styles.compile().then(() => modules.postcss.compile())
|
|
164
|
-
.then(() => { hook('styles'); styleOutputs(
|
|
166
|
+
.then(() => { hook('styles'); styleOutputs(modules.styles).forEach((out) => reload(out)) })
|
|
165
167
|
.catch(err => console.error(err))
|
|
166
168
|
})
|
|
167
169
|
|