poops 1.9.7 → 2.0.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.
- package/README.md +136 -112
- package/lib/markup/collections.js +4 -1
- package/lib/markup/engines/liquid.js +5 -12
- package/lib/markup/engines/nunjucks.js +5 -14
- package/lib/markup/highlight.js +41 -8
- package/lib/markup/image-cache.js +30 -2
- package/lib/markup/indexer.js +46 -23
- package/lib/markup/renderer.js +2 -3
- package/lib/markups.js +38 -21
- package/lib/reactor.js +3 -2
- package/lib/scripts.js +3 -2
- package/lib/server.js +96 -2
- package/lib/styles.js +10 -1
- package/lib/utils/helpers.js +6 -0
- package/package.json +11 -10
- package/poops.js +47 -48
package/poops.js
CHANGED
|
@@ -8,12 +8,11 @@ import http from 'node:http'
|
|
|
8
8
|
import net from 'node:net'
|
|
9
9
|
import os from 'node:os'
|
|
10
10
|
import fs from 'node:fs'
|
|
11
|
-
import livereload from 'livereload'
|
|
12
11
|
import Markups from './lib/markups.js'
|
|
13
12
|
import Images from './lib/images.js'
|
|
14
13
|
import path from 'node:path'
|
|
15
14
|
import Reactor from './lib/reactor.js'
|
|
16
|
-
import { createStaticHandler } from './lib/server.js'
|
|
15
|
+
import { createStaticHandler, createReloadHub } from './lib/server.js'
|
|
17
16
|
import Scripts from './lib/scripts.js'
|
|
18
17
|
import log, { styledLog, hasLoggedErrors } from './lib/utils/log.js'
|
|
19
18
|
import Styles from './lib/styles.js'
|
|
@@ -28,7 +27,6 @@ const cli = new Argoyle(pkg.version)
|
|
|
28
27
|
.option('build', { short: 'b', description: 'Build the project and exit' })
|
|
29
28
|
.option('config', { short: 'c', value: '<path>', description: 'Specify the config file' })
|
|
30
29
|
.option('port', { short: 'p', value: '<number>', description: 'Specify the port for the server, overrides the config file' })
|
|
31
|
-
.option('livereload-port', { short: 'l', value: '<number>', description: 'Specify the port for the livereload server, overrides the config file' })
|
|
32
30
|
.option('base-url', { short: 'u', value: '<path>', description: 'Set the base URL prefix for markup, overrides the config file' })
|
|
33
31
|
.option('quiet', { short: 'q', description: 'Hide the header and the server/livereload info lines' })
|
|
34
32
|
|
|
@@ -43,21 +41,13 @@ try {
|
|
|
43
41
|
const build = flags.build
|
|
44
42
|
const defaultConfigPath = flags.config || positionals[0] || 'poops.json'
|
|
45
43
|
const overridePort = flags.port
|
|
46
|
-
const overrideLivereloadPort = flags['livereload-port']
|
|
47
44
|
const overrideBaseURL = flags['base-url']
|
|
48
45
|
const quiet = flags.quiet // hides the header and the address lines only — build logs, warnings and errors still print
|
|
49
46
|
|
|
50
47
|
let configPath = path.join(cwd, defaultConfigPath)
|
|
51
48
|
if (!pathExists(configPath)) configPath = path.join(cwd, '💩.json') // the canonical alternative config name
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
if (!config.livereload) return null
|
|
55
|
-
let liveReloadPort = overrideLivereloadPort || config.livereload.port || 35729
|
|
56
|
-
if (!overrideLivereloadPort) liveReloadPort = await getAvailablePort(liveReloadPort, liveReloadPort + 10)
|
|
57
|
-
config.livereload_port = liveReloadPort
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// The livereload server never fs-watches: watching the project meant every
|
|
50
|
+
// Nothing fs-watches on behalf of the browser: watching the project meant every
|
|
61
51
|
// output file written during a build fired its own reload (dozens of browser
|
|
62
52
|
// flickers per build, some mid-write). Instead the rebuild chains in
|
|
63
53
|
// setupWatchers call reload() once their compile settles; the debounce folds
|
|
@@ -68,23 +58,23 @@ async function resolveLiveReloadPort(config) {
|
|
|
68
58
|
// the first chain's reload. One save = one refresh, after dist is current.
|
|
69
59
|
//
|
|
70
60
|
// reload(file) collects paths over the debounce window. If everything in the
|
|
71
|
-
// window is .css,
|
|
61
|
+
// window is .css, the paths ride a `css` event so the client hot-swaps
|
|
72
62
|
// stylesheets in place (no page reload, styles update without flicker);
|
|
73
|
-
// anything else escalates to one full
|
|
74
|
-
|
|
63
|
+
// anything else escalates to one full `reload`.
|
|
64
|
+
const reloadHub = createReloadHub()
|
|
75
65
|
let reloadTimer = null
|
|
76
66
|
const reloadPaths = new Set()
|
|
77
67
|
function reload(file) {
|
|
78
|
-
if (!
|
|
68
|
+
if (!config.livereload) return
|
|
79
69
|
reloadPaths.add(file || '/')
|
|
80
70
|
clearTimeout(reloadTimer)
|
|
81
71
|
reloadTimer = setTimeout(() => {
|
|
82
72
|
const paths = [...reloadPaths]
|
|
83
73
|
reloadPaths.clear()
|
|
84
74
|
if (paths.every((p) => p.endsWith('.css'))) {
|
|
85
|
-
|
|
75
|
+
reloadHub.send('css', paths)
|
|
86
76
|
} else {
|
|
87
|
-
|
|
77
|
+
reloadHub.send('reload', '/')
|
|
88
78
|
}
|
|
89
79
|
}, 500)
|
|
90
80
|
}
|
|
@@ -104,13 +94,6 @@ function styleOutputs(styles) {
|
|
|
104
94
|
// build and watch; see lib/exec.js. `hook(stage)` binds config + cwd here.
|
|
105
95
|
const hook = (stage) => runExec(config, cwd, stage)
|
|
106
96
|
|
|
107
|
-
function setupLiveReloadServer(config) {
|
|
108
|
-
if (!config.livereload) return
|
|
109
|
-
liveReloadServer = livereload.createServer({ port: config.livereload_port })
|
|
110
|
-
if (!quiet) styledLog(`🔃 {dim}LiveReload :{/} ${liveReloadServer.config.port}`)
|
|
111
|
-
console.log()
|
|
112
|
-
}
|
|
113
|
-
|
|
114
97
|
function setupWatchers(config, modules) {
|
|
115
98
|
if (!config.watch) return
|
|
116
99
|
|
|
@@ -350,14 +333,29 @@ if (!pathExists(configPath)) {
|
|
|
350
333
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
351
334
|
|
|
352
335
|
// A typo'd top-level key ("stlyes") is otherwise silently ignored — warn, same
|
|
353
|
-
// idea as validateExec for exec stages. `
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
336
|
+
// idea as validateExec for exec stages. `pkg` feeds banner templates;
|
|
337
|
+
// reactorData is set internally but tolerated here in case a config hardcodes it.
|
|
338
|
+
const KNOWN_CONFIG_KEYS = new Set(['watch', 'includePaths', 'scripts', 'styles', 'postcss', 'reactor', 'markup', 'copy', 'images', 'serve', 'livereload', 'exec', 'banner', 'pkg', 'reactorData'])
|
|
339
|
+
|
|
340
|
+
// Keys that meant something in 1.x. They fall through the check above, and
|
|
341
|
+
// "unknown config key" would be accurate and useless — name the replacement.
|
|
342
|
+
const REMOVED_CONFIG_KEYS = { ssg: 'renamed to "reactor" in 2.0' }
|
|
343
|
+
|
|
357
344
|
for (const key of Object.keys(config)) {
|
|
358
|
-
if (
|
|
359
|
-
|
|
345
|
+
if (KNOWN_CONFIG_KEYS.has(key)) continue
|
|
346
|
+
if (REMOVED_CONFIG_KEYS[key]) {
|
|
347
|
+
log({ tag: 'info', warn: true, text: `Config key "${key}" is ${REMOVED_CONFIG_KEYS[key]} — ignored.` })
|
|
348
|
+
continue
|
|
360
349
|
}
|
|
350
|
+
log({ tag: 'info', warn: true, text: `Unknown config key "${key}" — ignored. Valid: ${[...KNOWN_CONFIG_KEYS].join(', ')}` })
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// The reload channel is an endpoint on the static server, so livereload has
|
|
354
|
+
// nothing to attach to without `serve` — say so rather than idling in a mode
|
|
355
|
+
// that can never reach a browser.
|
|
356
|
+
if (!build && config.livereload && !config.serve) {
|
|
357
|
+
log({ tag: 'info', warn: true, text: 'Ignoring "livereload": it needs "serve" — the reload channel is served on the same port.' })
|
|
358
|
+
config.livereload = false
|
|
361
359
|
}
|
|
362
360
|
|
|
363
361
|
if (config.watch === true) {
|
|
@@ -372,13 +370,11 @@ if (config.includePaths) {
|
|
|
372
370
|
config.includePaths = ['node_modules']
|
|
373
371
|
}
|
|
374
372
|
|
|
375
|
-
// Backwards compatibility: support "ssg" as alias for "reactor"
|
|
376
|
-
if (!config.reactor && config.ssg) {
|
|
377
|
-
config.reactor = config.ssg
|
|
378
|
-
}
|
|
379
|
-
|
|
380
373
|
if (overrideBaseURL && config.markup) {
|
|
381
|
-
config
|
|
374
|
+
// options is the canonical home, and it also wins over a config that still
|
|
375
|
+
// carries the deprecated markup-level key — the flag must override both
|
|
376
|
+
config.markup.options = config.markup.options || {}
|
|
377
|
+
config.markup.options.baseURL = overrideBaseURL
|
|
382
378
|
}
|
|
383
379
|
|
|
384
380
|
// poops-images resolves custom handlers/composites relative to the config file;
|
|
@@ -417,24 +413,33 @@ function getLocalIP() {
|
|
|
417
413
|
}
|
|
418
414
|
|
|
419
415
|
async function startServer() {
|
|
420
|
-
await resolveLiveReloadPort(config)
|
|
421
416
|
await poops() // Initial compilation before starting the server
|
|
422
417
|
|
|
423
|
-
|
|
424
|
-
|
|
418
|
+
// Almost every config sets serve.base to the markup `out` it just built, so
|
|
419
|
+
// that is the default when it's unset. Falls back to cwd for a project with
|
|
420
|
+
// no markup — and an explicit serve.base still wins, including one pointing
|
|
421
|
+
// somewhere else entirely.
|
|
422
|
+
const serveBase = config.serve.base || (config.markup && config.markup.out)
|
|
423
|
+
const base = serveBase && pathExists(cwd, serveBase)
|
|
424
|
+
? path.join(cwd, serveBase)
|
|
425
425
|
: cwd
|
|
426
426
|
|
|
427
427
|
let port = overridePort || config.serve.port || 4040
|
|
428
428
|
if (!overridePort) port = await getAvailablePort(port, port + 10)
|
|
429
429
|
|
|
430
|
+
// The hub is only wired in when livereload is on; without it the server
|
|
431
|
+
// neither answers the reload endpoint nor injects the client into pages.
|
|
432
|
+
const handler = createStaticHandler(base, config.livereload ? reloadHub : null)
|
|
433
|
+
|
|
430
434
|
// eslint-disable-next-line @stylistic/space-before-function-paren
|
|
431
|
-
http.createServer(
|
|
435
|
+
http.createServer(handler).listen(parseInt(port), '0.0.0.0', async () => {
|
|
432
436
|
if (!quiet) {
|
|
433
437
|
console.log()
|
|
434
438
|
styledLog(`🏠 {dim}Local server:{/} {underline|http://localhost:${port}}`)
|
|
435
439
|
styledLog(`🛜 {dim} Network :{/} {underline|http://${getLocalIP()}:${port}}`)
|
|
440
|
+
if (config.livereload) styledLog('🔃 {dim}Live reload :{/} on')
|
|
441
|
+
console.log()
|
|
436
442
|
}
|
|
437
|
-
setupLiveReloadServer(config)
|
|
438
443
|
})
|
|
439
444
|
}
|
|
440
445
|
|
|
@@ -448,12 +453,6 @@ const die = (err) => {
|
|
|
448
453
|
// Start the webserver
|
|
449
454
|
if (!build && config.serve) {
|
|
450
455
|
startServer().catch(die)
|
|
451
|
-
} else if (!build && config.livereload) {
|
|
452
|
-
// livereload without serve: still needs the livereload server
|
|
453
|
-
resolveLiveReloadPort(config)
|
|
454
|
-
.then(poops)
|
|
455
|
-
.then(() => setupLiveReloadServer(config))
|
|
456
|
-
.catch(die)
|
|
457
456
|
} else {
|
|
458
457
|
poops().catch(die)
|
|
459
458
|
}
|