poops 1.2.1 → 1.2.3
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 +34 -1
- package/lib/markup/collections.js +7 -4
- package/lib/markup/engines/liquid.js +3 -2
- package/lib/markup/engines/nunjucks.js +6 -5
- package/lib/markup/helpers.js +53 -2
- package/lib/markups.js +6 -4
- package/package.json +1 -1
- package/poops.js +13 -1
package/README.md
CHANGED
|
@@ -68,6 +68,22 @@ or pass a custom config. This is useful when you have multiple environments:
|
|
|
68
68
|
|
|
69
69
|
`poops yourAwesomeConfig.json` or `💩 yourAwesomeConfig.json`
|
|
70
70
|
|
|
71
|
+
**CLI Options:**
|
|
72
|
+
|
|
73
|
+
| Flag | Short | Description |
|
|
74
|
+
|------|-------|-------------|
|
|
75
|
+
| `--build` | `-b` | Build the project and exit |
|
|
76
|
+
| `--config <path>` | `-c` | Specify the config file |
|
|
77
|
+
| `--port <number>` | `-p` | Specify the server port, overrides config |
|
|
78
|
+
| `--livereload-port <number>` | `-l` | Specify the livereload port, overrides config |
|
|
79
|
+
| `--base-url <path>` | `-u` | Set the base URL prefix for markup, overrides config |
|
|
80
|
+
|
|
81
|
+
The `--base-url` flag is particularly useful for CI/CD pipelines where the deploy path may differ per environment:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
poops --build --base-url /blog
|
|
85
|
+
```
|
|
86
|
+
|
|
71
87
|
If you have installed Poops locally you can run it with `npx poops` or `npx 💩` or add a script to your `package.json`:
|
|
72
88
|
|
|
73
89
|
```json
|
|
@@ -507,6 +523,7 @@ Then use Tailwind utility classes directly in your markup templates. Tailwind v4
|
|
|
507
523
|
- `site` (optional) - global data that will be available to all templates in the markup directory. Like site title, description, social media links, etc. You can then use this data in your templates `{{ site.title }}` for instance.
|
|
508
524
|
- `data` (optional) - is an array of JSON or YAML data files, that once loaded will be available to all templates in the markup directory. If you provide a path to a file for instance `links.json` with a `facebook` property, you can then use this data in your templates `{{ links.facebook }}`. The base name of the file will be used as the variable name, with spaces, dashes and dots replaced with underscores. So `the awesome-links.json` will be available as `{{ the_awesome_links.facebook }}` in your templates. The root directory of the data files is `in` directory. So if you have a `data` directory in your `in` directory, you can specify the data files like this `data: ["data/links.json"]`. The same goes for the YAML files.
|
|
509
525
|
- `includePaths` - an array of paths to directories that will be added to the template engine's include paths. Useful if you want to separate template partials and layouts. For instance, if you have a `_includes` directory with a `header.njk` (or `header.liquid`) partial that you want to include in your markup, you can add it to the include paths and then include the templates like this `{% include "header.njk" %}`, without specifying the full path to the partial.
|
|
526
|
+
- `baseURL` (optional) - a base URL prefix to use instead of relative path prefixes. When set, `{{ relativePathPrefix }}` will always resolve to this value (with a trailing slash ensured) instead of being computed relative to each page's depth. Useful when deploying under a subdirectory (e.g. `"/blog"` for `domain.com/blog/`). When not set, relative prefixes (`./`, `../`, etc.) are used, which work for any deployment location including subdirectories and `file://` URLs.
|
|
510
527
|
|
|
511
528
|
**💡 NOTE:** If, for instance, you are building a simple static onepager for your library, and want to pass a version variable from your `package.json`, Poops automatically reads your `package.json` if it exists in your working directory and sets the global variable `package` to the parsed JSON. So you can use it in your markup files, for example like this: `{{ package.version }}`.
|
|
512
529
|
|
|
@@ -527,7 +544,8 @@ Here is a sample markup configuration using the default Nunjucks engine:
|
|
|
527
544
|
],
|
|
528
545
|
"includePaths": [
|
|
529
546
|
"_includes"
|
|
530
|
-
]
|
|
547
|
+
],
|
|
548
|
+
"baseURL": "/blog"
|
|
531
549
|
}
|
|
532
550
|
}
|
|
533
551
|
```
|
|
@@ -726,6 +744,21 @@ All filters are available in both engines. The only syntax difference is how arg
|
|
|
726
744
|
- Nunjucks: `{{ someCodeVariable | highlight('javascript') }}`
|
|
727
745
|
- Liquid: `{{ someCodeVariable | highlight: 'javascript' }}`
|
|
728
746
|
|
|
747
|
+
- `groupby` — groups an array of objects by a field value. Returns an array of `{ key, items }` objects. Supports an optional second argument for date part extraction (`year`, `month`, `day`). Groups preserve insertion order, so if items are sorted by date descending, groups will be too.
|
|
748
|
+
- Nunjucks: `{{ changelog.items | groupby("author") }}` or `{{ changelog.items | groupby("date", "year") }}`
|
|
749
|
+
- Liquid: `{{ changelog.items | groupby: "author" }}` or `{{ changelog.items | groupby: "date", "year" }}`
|
|
750
|
+
|
|
751
|
+
Example — group posts by year:
|
|
752
|
+
```nunjucks
|
|
753
|
+
{% set byYear = changelog.items | groupby("date", "year") %}
|
|
754
|
+
{% for group in byYear %}
|
|
755
|
+
<h2>{{ group.key }}</h2>
|
|
756
|
+
{% for post in group.items %}
|
|
757
|
+
<p>{{ post.title }}</p>
|
|
758
|
+
{% endfor %}
|
|
759
|
+
{% endfor %}
|
|
760
|
+
```
|
|
761
|
+
|
|
729
762
|
- `srcset` — returns just the srcset attribute value:
|
|
730
763
|
|
|
731
764
|
```html
|
|
@@ -3,16 +3,18 @@ import { globSync } from 'glob'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import log from '../utils/log.js'
|
|
5
5
|
import { mkDir } from '../utils/helpers.js'
|
|
6
|
-
import { replaceOutExtensions, getRelativePathPrefix, getPageUrl, parseFrontMatter } from './helpers.js'
|
|
6
|
+
import { replaceOutExtensions, getRelativePathPrefix, getPageUrl, parseFrontMatter, wordcount } from './helpers.js'
|
|
7
7
|
|
|
8
8
|
export function getSingleCollectionData(markupInDir, collectionName) {
|
|
9
9
|
const collectionData = []
|
|
10
10
|
globSync(path.join(process.cwd(), markupInDir, collectionName, '**/*.+(html|njk|liquid|md)'), { ignore: ['**/index.+(html|njk|liquid|md)'] }).forEach((file) => {
|
|
11
11
|
let frontMatter = {}
|
|
12
12
|
|
|
13
|
+
let content = ''
|
|
13
14
|
try {
|
|
14
15
|
const frontMatterResult = parseFrontMatter(file)
|
|
15
16
|
frontMatter = frontMatterResult.frontMatter
|
|
17
|
+
content = frontMatterResult.content
|
|
16
18
|
} catch (err) {
|
|
17
19
|
log({ tag: 'error', text: 'Failed parsing front matter:', link: file })
|
|
18
20
|
console.error(err)
|
|
@@ -23,6 +25,7 @@ export function getSingleCollectionData(markupInDir, collectionName) {
|
|
|
23
25
|
if (!frontMatter.date) {
|
|
24
26
|
frontMatter.date = fs.statSync(file).ctime.toISOString().slice(0, 16)
|
|
25
27
|
}
|
|
28
|
+
frontMatter.wordcount = wordcount(content)
|
|
26
29
|
frontMatter.fileName = path.basename(file)
|
|
27
30
|
frontMatter.filePath = path.relative(process.cwd(), file)
|
|
28
31
|
frontMatter.collection = collectionName
|
|
@@ -182,7 +185,7 @@ export function getCollectionIndexFile(markupInDir, collectionName) {
|
|
|
182
185
|
return indexFiles[0]
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
export function generateCollectionPaginationPages(collectionData, markupInDir, markupOutDir, compileEntryFn) {
|
|
188
|
+
export function generateCollectionPaginationPages(collectionData, markupInDir, markupOutDir, compileEntryFn, baseURL) {
|
|
186
189
|
if (!collectionData) return []
|
|
187
190
|
|
|
188
191
|
const compilePromises = []
|
|
@@ -201,7 +204,7 @@ export function generateCollectionPaginationPages(collectionData, markupInDir, m
|
|
|
201
204
|
const pageUrl = pageNumber === 1 ? collection.name : `${collection.name}/${pageNumber}`
|
|
202
205
|
const nextPage = pageNumber === collection.totalPages ? null : pageNumber + 1
|
|
203
206
|
const nextPageUrl = pageNumber === collection.totalPages ? null : `${collection.name}/${pageNumber + 1}`
|
|
204
|
-
|
|
207
|
+
const prevPage = pageNumber === 1 ? null : pageNumber - 1
|
|
205
208
|
let prevPageUrl = pageNumber === 1 ? null : `${collection.name}/${pageNumber - 1}`
|
|
206
209
|
if (prevPage === 1) {
|
|
207
210
|
prevPageUrl = collection.name
|
|
@@ -228,7 +231,7 @@ export function generateCollectionPaginationPages(collectionData, markupInDir, m
|
|
|
228
231
|
const context = {
|
|
229
232
|
...collectionData,
|
|
230
233
|
[collectionName]: pageSnapshot,
|
|
231
|
-
relativePathPrefix: getRelativePathPrefix(markupOutDirFull, fromPath),
|
|
234
|
+
relativePathPrefix: getRelativePathPrefix(markupOutDirFull, fromPath, baseURL),
|
|
232
235
|
_url: getPageUrl(markupOut)
|
|
233
236
|
}
|
|
234
237
|
|
|
@@ -3,7 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
import { Liquid } from 'liquidjs'
|
|
4
4
|
import { Marked } from 'marked'
|
|
5
5
|
import { highlightRenderer, highlightCode } from '../highlight.js'
|
|
6
|
-
import { discoverImageVariants, parseFrontMatter } from '../helpers.js'
|
|
6
|
+
import { discoverImageVariants, parseFrontMatter, groupby, decodeTemplateEntities } from '../helpers.js'
|
|
7
7
|
import { slugify } from 'book-of-spells'
|
|
8
8
|
import dayjs from 'dayjs'
|
|
9
9
|
|
|
@@ -75,6 +75,7 @@ export default class LiquidEngine {
|
|
|
75
75
|
if (variants.length === 0) return ''
|
|
76
76
|
return variants.map(v => `${v.path} ${v.width}w`).join(', ')
|
|
77
77
|
})
|
|
78
|
+
engine.registerFilter('groupby', (arr, key, datePart) => groupby(arr, key, datePart))
|
|
78
79
|
engine.registerFilter('highlight', (code, lang) => {
|
|
79
80
|
const highlighted = highlightCode(code, lang)
|
|
80
81
|
const langClass = lang ? ` language-${lang}` : ''
|
|
@@ -98,7 +99,7 @@ export default class LiquidEngine {
|
|
|
98
99
|
source = frontMatterResult.content
|
|
99
100
|
|
|
100
101
|
if (path.extname(templateName) === '.md') {
|
|
101
|
-
source = marked.parse(source)
|
|
102
|
+
source = decodeTemplateEntities(marked.parse(source))
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
const frontMatter = context.page || {}
|
|
@@ -3,7 +3,7 @@ import { globSync } from 'glob'
|
|
|
3
3
|
import { Marked } from 'marked'
|
|
4
4
|
import nunjucks from 'nunjucks'
|
|
5
5
|
import path from 'node:path'
|
|
6
|
-
import { discoverImageVariants, parseFrontMatter } from '../helpers.js'
|
|
6
|
+
import { discoverImageVariants, parseFrontMatter, groupby, decodeTemplateEntities } from '../helpers.js'
|
|
7
7
|
import { highlightRenderer, highlightCode } from '../highlight.js'
|
|
8
8
|
import { slugify } from 'book-of-spells'
|
|
9
9
|
import dayjs from 'dayjs'
|
|
@@ -46,7 +46,7 @@ class RelativeLoader extends nunjucks.Loader {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
if (path.extname(fullPath) === '.md') {
|
|
49
|
-
source = marked.parse(source)
|
|
49
|
+
source = decodeTemplateEntities(marked.parse(source))
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (frontMatter.layout) {
|
|
@@ -108,6 +108,7 @@ export default class NunjucksEngine {
|
|
|
108
108
|
if (variants.length === 0) return ''
|
|
109
109
|
return variants.map(v => `${v.path} ${v.width}w`).join(', ')
|
|
110
110
|
})
|
|
111
|
+
env.addFilter('groupby', (arr, key, datePart) => groupby(arr, key, datePart))
|
|
111
112
|
env.addFilter('highlight', (code, lang) => {
|
|
112
113
|
const highlighted = highlightCode(code, lang)
|
|
113
114
|
const langClass = lang ? ` language-${lang}` : ''
|
|
@@ -152,7 +153,7 @@ export default class NunjucksEngine {
|
|
|
152
153
|
|
|
153
154
|
// --- Nunjucks Extensions ---
|
|
154
155
|
|
|
155
|
-
class GoogleFontsExtension {
|
|
156
|
+
export class GoogleFontsExtension {
|
|
156
157
|
constructor() { this.tags = ['googleFonts'] }
|
|
157
158
|
|
|
158
159
|
parse(parser, nodes) {
|
|
@@ -195,7 +196,7 @@ class GoogleFontsExtension {
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
class ImageExtension {
|
|
199
|
+
export class ImageExtension {
|
|
199
200
|
constructor(getOutputDir) { this.tags = ['image']; this.getOutputDir = getOutputDir }
|
|
200
201
|
|
|
201
202
|
parse(parser, nodes) {
|
|
@@ -238,7 +239,7 @@ class ImageExtension {
|
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
|
|
241
|
-
class HighlightExtension {
|
|
242
|
+
export class HighlightExtension {
|
|
242
243
|
constructor() { this.tags = ['highlight'] }
|
|
243
244
|
|
|
244
245
|
parse(parser, nodes) {
|
package/lib/markup/helpers.js
CHANGED
|
@@ -58,6 +58,53 @@ export function clearFrontMatterCache(filePath) {
|
|
|
58
58
|
frontMatterCache.delete(filePath)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
export function wordcount(text) {
|
|
62
|
+
if (!text) return 0
|
|
63
|
+
// eslint-disable-next-line no-useless-escape
|
|
64
|
+
const stripped = text.replace(/<[^>]*>/g, ' ').replace(/[#*_`~\[\]()>|{}\\-]/g, ' ')
|
|
65
|
+
const words = stripped.match(/\S+/g)
|
|
66
|
+
return words ? words.length : 0
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// marked HTML-encodes quotes/brackets in template tags it treats as prose,
|
|
70
|
+
// breaking string args like groupby("date"). Decode entities inside {{ }} / {% %}
|
|
71
|
+
// after markdown, before the template engine parses. Decode & last so
|
|
72
|
+
// &lt; doesn't double-decode into <.
|
|
73
|
+
export function decodeTemplateEntities(html) {
|
|
74
|
+
return html.replace(/(\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\})/g, (tag) =>
|
|
75
|
+
tag
|
|
76
|
+
.replace(/"/g, '"')
|
|
77
|
+
.replace(/'/g, "'")
|
|
78
|
+
.replace(/</g, '<')
|
|
79
|
+
.replace(/>/g, '>')
|
|
80
|
+
.replace(/&/g, '&')
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function groupby(arr, key, datePart) {
|
|
85
|
+
if (!Array.isArray(arr)) return []
|
|
86
|
+
|
|
87
|
+
const map = new Map()
|
|
88
|
+
for (const item of arr) {
|
|
89
|
+
let value = item[key]
|
|
90
|
+
if (datePart && value) {
|
|
91
|
+
const date = new Date(value)
|
|
92
|
+
if (!isNaN(date)) {
|
|
93
|
+
switch (datePart) {
|
|
94
|
+
case 'year': value = date.getUTCFullYear(); break
|
|
95
|
+
case 'month': value = date.getUTCMonth() + 1; break
|
|
96
|
+
case 'day': value = date.getUTCDate(); break
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const groupKey = value != null ? String(value) : ''
|
|
101
|
+
if (!map.has(groupKey)) map.set(groupKey, [])
|
|
102
|
+
map.get(groupKey).push(item)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return Array.from(map, ([key, items]) => ({ key, items }))
|
|
106
|
+
}
|
|
107
|
+
|
|
61
108
|
const FORMAT_PRIORITY = ['avif', 'webp']
|
|
62
109
|
|
|
63
110
|
export function discoverImageVariants(imagePath, outputDir) {
|
|
@@ -148,7 +195,11 @@ export function getUpDirPrefix(relativeDir) {
|
|
|
148
195
|
return upDir
|
|
149
196
|
}
|
|
150
197
|
|
|
151
|
-
export function getRelativePathPrefix(outputDir, fromDir) {
|
|
198
|
+
export function getRelativePathPrefix(outputDir, fromDir, baseURL) {
|
|
199
|
+
if (baseURL != null) {
|
|
200
|
+
return baseURL.endsWith('/') ? baseURL : baseURL + '/'
|
|
201
|
+
}
|
|
202
|
+
|
|
152
203
|
let relativeDir = path.relative(process.cwd(), outputDir)
|
|
153
204
|
const fromRelativeDir = fromDir ? path.relative(process.cwd(), fromDir) : ''
|
|
154
205
|
|
|
@@ -156,7 +207,7 @@ export function getRelativePathPrefix(outputDir, fromDir) {
|
|
|
156
207
|
relativeDir = relativeDir.replace(fromRelativeDir, '')
|
|
157
208
|
}
|
|
158
209
|
|
|
159
|
-
return getUpDirPrefix(relativeDir)
|
|
210
|
+
return getUpDirPrefix(relativeDir) || './'
|
|
160
211
|
}
|
|
161
212
|
|
|
162
213
|
export function getPageUrl(outputPath) {
|
package/lib/markups.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pathExists, pathIsDirectory, readDataFile, mkDir, buildTime } from './utils/helpers.js'
|
|
2
|
-
import { replaceOutExtensions, getRelativePathPrefix, getPageUrl, getPageUrlRelativeToOutput, parseFrontMatter, clearFrontMatterCache } from './markup/helpers.js'
|
|
2
|
+
import { replaceOutExtensions, getRelativePathPrefix, getPageUrl, getPageUrlRelativeToOutput, parseFrontMatter, clearFrontMatterCache, wordcount } from './markup/helpers.js'
|
|
3
3
|
import { collectionAutoDiscovery, getCollectionDataBasedOnConfig, buildCollectionPaginationData, generateCollectionPaginationPages } from './markup/collections.js'
|
|
4
4
|
import { generateIndexFiles } from './markup/indexer.js'
|
|
5
5
|
import NunjucksEngine from './markup/engines/nunjucks.js'
|
|
@@ -35,6 +35,7 @@ export default class Markups {
|
|
|
35
35
|
this.includePaths = moduleConfig.includePaths || moduleConfig.options.includePaths || []
|
|
36
36
|
this.searchIndexConfig = moduleConfig.options.searchIndex || moduleConfig.searchIndex
|
|
37
37
|
this.sitemapConfig = moduleConfig.options.sitemap || moduleConfig.sitemap
|
|
38
|
+
this.baseURL = moduleConfig.baseURL || moduleConfig.options.baseURL || null
|
|
38
39
|
this.dataFiles = []
|
|
39
40
|
|
|
40
41
|
// Instantiate engine
|
|
@@ -151,6 +152,7 @@ export default class Markups {
|
|
|
151
152
|
try {
|
|
152
153
|
const frontMatterResult = parseFrontMatter(templateName)
|
|
153
154
|
context.page = frontMatterResult.frontMatter
|
|
155
|
+
context.page.wordcount = wordcount(frontMatterResult.content)
|
|
154
156
|
} catch (err) {
|
|
155
157
|
log({ tag: 'error', text: 'Failed parsing front matter:', link: templateName })
|
|
156
158
|
console.error(err)
|
|
@@ -196,7 +198,7 @@ export default class Markups {
|
|
|
196
198
|
|
|
197
199
|
const fileContext = {
|
|
198
200
|
...collectionData,
|
|
199
|
-
relativePathPrefix: getRelativePathPrefix(markupOutDir, fromPath),
|
|
201
|
+
relativePathPrefix: getRelativePathPrefix(markupOutDir, fromPath, this.baseURL),
|
|
200
202
|
_url: getPageUrl(markupOut)
|
|
201
203
|
}
|
|
202
204
|
|
|
@@ -254,7 +256,7 @@ export default class Markups {
|
|
|
254
256
|
|
|
255
257
|
const fileContext = {
|
|
256
258
|
...collectionData,
|
|
257
|
-
relativePathPrefix: getRelativePathPrefix(markupOutDir),
|
|
259
|
+
relativePathPrefix: getRelativePathPrefix(markupOutDir, null, this.baseURL),
|
|
258
260
|
_url: getPageUrl(markupOut)
|
|
259
261
|
}
|
|
260
262
|
|
|
@@ -321,7 +323,7 @@ export default class Markups {
|
|
|
321
323
|
const pageEntries = shouldIndex ? [] : null
|
|
322
324
|
|
|
323
325
|
buildCollectionPaginationData(collectionData)
|
|
324
|
-
const collectionPromises = generateCollectionPaginationPages(collectionData, this.markupIn, this.markupOut, this.compileEntry.bind(this))
|
|
326
|
+
const collectionPromises = generateCollectionPaginationPages(collectionData, this.markupIn, this.markupOut, this.compileEntry.bind(this), this.baseURL)
|
|
325
327
|
|
|
326
328
|
await Promise.all(collectionPromises)
|
|
327
329
|
|
package/package.json
CHANGED
package/poops.js
CHANGED
|
@@ -28,6 +28,7 @@ const cli = new Argoyle(pkg.version)
|
|
|
28
28
|
.option('config', { short: 'c', value: '<path>', description: 'Specify the config file' })
|
|
29
29
|
.option('port', { short: 'p', value: '<number>', description: 'Specify the port for the server, overrides the config file' })
|
|
30
30
|
.option('livereload-port', { short: 'l', value: '<number>', description: 'Specify the port for the livereload server, overrides the config file' })
|
|
31
|
+
.option('base-url', { short: 'u', value: '<path>', description: 'Set the base URL prefix for markup, overrides the config file' })
|
|
31
32
|
|
|
32
33
|
let flags, positionals
|
|
33
34
|
try {
|
|
@@ -41,6 +42,7 @@ const build = flags.build
|
|
|
41
42
|
const defaultConfigPath = flags.config || positionals[0] || 'poops.json'
|
|
42
43
|
const overridePort = flags.port
|
|
43
44
|
const overrideLivereloadPort = flags['livereload-port']
|
|
45
|
+
const overrideBaseURL = flags['base-url']
|
|
44
46
|
|
|
45
47
|
let configPath = path.join(cwd, defaultConfigPath)
|
|
46
48
|
if (!pathExists(configPath)) configPath = path.join(cwd, '💩.json') // TODO: Ok dude, I know it's late, but you can do better than this.
|
|
@@ -82,7 +84,13 @@ function setupWatchers(config, modules) {
|
|
|
82
84
|
|
|
83
85
|
// TODO: think about watching the updates of the config file itself, we can reload the config and recompile everything.
|
|
84
86
|
// 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.
|
|
85
|
-
|
|
87
|
+
// awaitWriteFinish: wait for saves to finish writing before recompiling, so a
|
|
88
|
+
// mid-write (truncated/partial) file is never read. Fixes intermittent broken
|
|
89
|
+
// builds on editor save. ponytail: default thresholds are fine; bump if slow disks flake.
|
|
90
|
+
chokidar.watch(config.watch, {
|
|
91
|
+
ignoreInitial: true,
|
|
92
|
+
awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 50 }
|
|
93
|
+
}).on('change', (file) => {
|
|
86
94
|
if (/(\.m?jsx?|\.tsx?)$/i.test(file)) {
|
|
87
95
|
modules.scripts.compile().catch(err => console.error(err))
|
|
88
96
|
|
|
@@ -179,6 +187,10 @@ if (!config.reactor && config.ssg) {
|
|
|
179
187
|
config.reactor = config.ssg
|
|
180
188
|
}
|
|
181
189
|
|
|
190
|
+
if (overrideBaseURL && config.markup) {
|
|
191
|
+
config.markup.baseURL = overrideBaseURL
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
async function getAvailablePort(port, max) {
|
|
183
195
|
while (port < max) {
|
|
184
196
|
const status = await portscanner.checkPortStatus(port, 'localhost')
|