Skip to content

Commit abf5992

Browse files
committed
Migrate to domstack-sync
1 parent 1d97307 commit abf5992

7 files changed

Lines changed: 125 additions & 98 deletions

File tree

bin.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { readPackage } from 'read-pkg'
1919
import { addPackageDependencies } from 'write-package'
2020

2121
import { copyFile } from './lib/helpers/copy-file.js'
22-
import { DomStack } from './index.js'
22+
import { DomStack, createLogger } from './index.js'
2323
import { DomStackAggregateError } from './lib/helpers/domstack-aggregate-error.js'
2424
import { generateTreeData } from './lib/helpers/generate-tree-data.js'
2525
import { askYesNo } from './lib/helpers/cli-prompt.js'
@@ -201,6 +201,8 @@ domstack eject actions:
201201

202202
/** @type {DomStackOpts} */
203203
const opts = {}
204+
const logger = createLogger('info')
205+
opts.logger = logger
204206

205207
if (argv['ignore']) opts.ignore = String(argv['ignore']).split(',')
206208
if (argv['target']) opts.target = String(argv['target']).split(',')
@@ -219,11 +221,10 @@ domstack eject actions:
219221

220222
async function quit () {
221223
if (domStack.watching) {
222-
const results = await domStack.stopWatching()
223-
console.log(results)
224-
console.log('watching stopped')
224+
await domStack.stopWatching()
225+
logger.info('Watching stopped')
225226
}
226-
console.log('\nquitting cleanly')
227+
logger.info('Quitting cleanly')
227228
process.exit(0)
228229
}
229230

@@ -258,22 +259,24 @@ domstack eject actions:
258259
process.exit(1)
259260
}
260261
} else {
261-
const initialResults = await domStack.watch({
262+
await domStack.watch({
262263
serve: !argv['watch-only'],
264+
onInitialBuild: (initialResults) => {
265+
console.log(tree(generateTreeData(cwd, src, dest, initialResults)))
266+
if (initialResults?.warnings?.length > 0) {
267+
console.log(
268+
'\nThere were build warnings:\n'
269+
)
270+
}
271+
for (const warning of initialResults?.warnings) {
272+
if ('message' in warning) {
273+
console.log(` ${warning.message}`)
274+
} else {
275+
console.warn(warning)
276+
}
277+
}
278+
},
263279
})
264-
console.log(tree(generateTreeData(cwd, src, dest, initialResults)))
265-
if (initialResults?.warnings?.length > 0) {
266-
console.log(
267-
'\nThere were build warnings:\n'
268-
)
269-
}
270-
for (const warning of initialResults?.warnings) {
271-
if ('message' in warning) {
272-
console.log(` ${warning.message}`)
273-
} else {
274-
console.warn(warning)
275-
}
276-
}
277280
}
278281
}
279282

examples/default-layout/package.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,11 @@
77
"start": "npm run watch",
88
"build": "npm run clean && domstack",
99
"clean": "rm -rf public && mkdir -p public",
10-
"watch": "npm run clean && run-p watch:*",
11-
"watch:serve": "browser-sync start --server 'public' --files 'public'",
12-
"watch:domstack": "npm run build -- --watch"
10+
"watch": "npm run clean && domstack --watch"
1311
},
1412
"dependencies": {
1513
"@domstack/static": "file:../../."
1614
},
17-
"devDependencies": {
18-
"browser-sync": "^2.26.7",
19-
"npm-run-all2": "^6.0.0"
20-
},
2115
"keywords": [],
2216
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io/)",
2317
"license": "MIT"

examples/string-layouts/package.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,11 @@
66
"start": "npm run watch",
77
"build": "npm run clean && domstack",
88
"clean": "rm -rf public && mkdir -p public",
9-
"watch": "npm run clean && run-p watch:*",
10-
"watch:serve": "browser-sync start --server 'public' --files 'public'",
11-
"watch:domstack": "npm run build -- --watch"
9+
"watch": "npm run clean && domstack --watch"
1210
},
1311
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io/)",
1412
"license": "MIT",
1513
"dependencies": {
1614
"@domstack/static": "file:../../."
17-
},
18-
"devDependencies": {
19-
"browser-sync": "^2.26.7",
20-
"npm-run-all2": "^6.0.0"
2115
}
2216
}

index.js

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* @import { GlobalDataFunction, AsyncGlobalDataFunction, WorkerBuildStepResult, GlobalDataFunctionParams } from './lib/build-pages/index.js'
1212
* @import { BuildOptions, BuildContext } from 'esbuild'
1313
* @import { PageInfo, TemplateInfo } from './lib/identify-pages.js'
14+
* @import { BsInstance } from '@domstack/sync'
1415
*/
1516
import { once } from 'events'
1617
import assert from 'node:assert'
@@ -23,7 +24,7 @@ import makeArray from 'make-array'
2324
import ignore from 'ignore'
2425
import { watch as cpxWatch } from 'cpx2'
2526
import { inspect } from 'util'
26-
import browserSync from 'browser-sync'
27+
import { createLogger as createSyncLogger, createServer } from '@domstack/sync'
2728
import { find } from '@11ty/dependency-tree-typescript'
2829

2930
import { getCopyGlob } from './lib/build-static/index.js'
@@ -52,6 +53,13 @@ import { ensureDest } from './lib/helpers/ensure-dest.js'
5253
import { DomStackAggregateError } from './lib/helpers/domstack-aggregate-error.js'
5354

5455
export { PageData } from './lib/build-pages/page-data.js'
56+
export { wrapPinoLogger } from '@domstack/sync'
57+
58+
const LOG_PREFIX = '[domstack]'
59+
60+
export function createLogger (level = 'info', streams = {}, options = {}) {
61+
return createSyncLogger(level, streams, { prefix: LOG_PREFIX, ...options })
62+
}
5563

5664
/**
5765
* @typedef {BuildOptions} BuildOptions
@@ -164,9 +172,10 @@ export class DomStack {
164172
/** @type {Readonly<CurrentOpts & { ignore: string[] }>} */ opts
165173
/** @type {FSWatcher?} */ #watcher = null
166174
/** @type {any[]?} */ #cpxWatchers = null
167-
/** @type {browserSync.BrowserSyncInstance?} */ #browserSyncServer = null
175+
/** @type {BsInstance?} */ #syncServer = null
168176
/** @type {BuildContext?} */ #esbuildContext = null
169177
/** @type {SiteData?} */ #siteData = null
178+
/** @type {ReturnType<typeof createLogger>} */ #logger
170179

171180
// Watch maps (rebuilt after every full rebuild)
172181
/** @type {Map<string, Set<string>>} depFilepath → Set<layoutName> */
@@ -206,18 +215,20 @@ export class DomStack {
206215
this.#src = src
207216
this.#dest = dest
208217

209-
const copyDirs = (opts?.copy ?? []).map(dir => resolve(dir))
218+
const { logger, ...buildOpts } = opts
219+
const copyDirs = (buildOpts?.copy ?? []).map(dir => resolve(dir))
210220

211-
this.opts = {
212-
...opts,
221+
this.opts = /** @type {Readonly<CurrentOpts & { ignore: string[] }>} */ ({
222+
...buildOpts,
213223
copy: copyDirs,
214224
ignore: [
215225
...DEFAULT_IGNORES,
216226
basename(dest),
217227
...copyDirs.map(dir => basename(dir)),
218-
...makeArray(opts.ignore),
228+
...makeArray(buildOpts.ignore),
219229
],
220-
}
230+
})
231+
this.#logger = logger ?? createLogger('info')
221232

222233
if (copyDirs.length > 0) {
223234
const absDest = resolve(this.#dest)
@@ -243,10 +254,12 @@ export class DomStack {
243254
* Build and watch a domstack build
244255
* @param {object} [params]
245256
* @param {boolean} params.serve
257+
* @param {(results: Results) => void | Promise<void>} [params.onInitialBuild]
246258
* @return {Promise<Results>}
247259
*/
248260
async watch ({
249261
serve,
262+
onInitialBuild,
250263
} = {
251264
serve: true,
252265
}) {
@@ -282,7 +295,7 @@ export class DomStack {
282295
pageBuildResults,
283296
}
284297
buildLogger(report)
285-
console.log('Initial JS, CSS and Page Build Complete')
298+
this.#logger.info('Initial JS, CSS and page build complete')
286299
} catch (err) {
287300
errorLogger(err)
288301
if (!(err instanceof DomStackAggregateError)) throw new Error('Non-aggregate error thrown', { cause: err })
@@ -292,38 +305,29 @@ export class DomStack {
292305
// Build watch maps after initial build
293306
await this.#rebuildMaps(siteData)
294307

295-
// ── Copy watchers & browser-sync ─────────────────────────────────────
308+
// ── Copy watchers & dev server ───────────────────────────────────────
296309
const copyDirs = getCopyDirs(this.opts.copy)
297310

298311
this.#cpxWatchers = [
299312
cpxWatch(getCopyGlob(this.#src), this.#dest, { ignore: this.opts.ignore }),
300313
...copyDirs.map(copyDir => cpxWatch(copyDir, this.#dest))
301314
]
302-
if (serve) {
303-
const bs = browserSync.create()
304-
this.#browserSyncServer = bs
305-
bs.watch(basename(this.#dest), { ignoreInitial: true }).on('change', bs.reload)
306-
bs.init({
307-
server: this.#dest,
308-
})
309-
}
310-
311-
this.#cpxWatchers.forEach(w => {
312-
w.on('watch-ready', () => {
313-
console.log('Copy watcher ready')
314315

315-
w.on('copy', (/** @type{{ srcPath: string, dstPath: string }} */e) => {
316-
console.log(`Copy ${e.srcPath} to ${e.dstPath}`)
317-
})
316+
const copyWatchersReady = this.#cpxWatchers.map(async w => {
317+
w.on('copy', (/** @type{{ srcPath: string, dstPath: string }} */e) => {
318+
this.#logger.info({ srcPath: e.srcPath, dstPath: e.dstPath }, 'Copy file')
319+
})
318320

319-
w.on('remove', (/** @type{{ path: string }} */e) => {
320-
console.log(`Remove ${e.path}`)
321-
})
321+
w.on('remove', (/** @type{{ path: string }} */e) => {
322+
this.#logger.info({ path: e.path }, 'Remove file')
323+
})
322324

323-
w.on('watch-error', (/** @type{Error} */err) => {
324-
console.log(`Copy error: ${err.message}`)
325-
})
325+
w.on('watch-error', (/** @type{Error} */err) => {
326+
this.#logger.error({ err }, 'Copy error')
326327
})
328+
329+
await once(w, 'watch-ready')
330+
this.#logger.info('Copy watcher ready')
327331
})
328332

329333
// ── Chokidar watcher ─────────────────────────────────────────────────
@@ -354,7 +358,20 @@ export class DomStack {
354358

355359
this.#watcher = watcher
356360

357-
await once(watcher, 'ready')
361+
await Promise.all([
362+
...copyWatchersReady,
363+
once(watcher, 'ready'),
364+
])
365+
366+
await onInitialBuild?.(report)
367+
368+
if (serve) {
369+
this.#syncServer = await createServer({
370+
server: this.#dest,
371+
files: basename(this.#dest),
372+
logger: this.#logger.child({ component: 'sync' }, { prefix: '[domstack-sync]' }),
373+
})
374+
}
358375

359376
const enqueue = (/** @type {() => Promise<void>} */ fn) => {
360377
this.#buildLock = this.#buildLock.then(() => fn().catch(errorLogger))
@@ -381,7 +398,7 @@ export class DomStack {
381398
* Used for structural changes (add/unlink), global.vars.*, esbuild.settings.*.
382399
*/
383400
async #fullRebuild () {
384-
console.log('Triggering full rebuild...')
401+
this.#logger.info('Triggering full rebuild')
385402
// Dispose the old esbuild context
386403
if (this.#esbuildContext) {
387404
await this.#esbuildContext.dispose()
@@ -391,8 +408,7 @@ export class DomStack {
391408
const siteData = await identifyPages(this.#src, this.opts)
392409

393410
if (siteData.errors.length > 0) {
394-
console.error('identifyPages errors:')
395-
for (const err of siteData.errors) console.error(' ', err.message)
411+
this.#logger.error({ errors: siteData.errors.map(err => err.message) }, 'identifyPages errors')
396412
return
397413
}
398414

@@ -429,13 +445,12 @@ export class DomStack {
429445
)
430446

431447
if (isEsbuildEntry) {
432-
console.log(`"${changedBasename}" ${event}, restarting esbuild...`)
448+
this.#logger.info({ file: changedBasename, event }, 'Restarting esbuild')
433449

434450
// Re-identify pages to discover the new/removed entry point
435451
const siteData = await identifyPages(this.#src, this.opts)
436452
if (siteData.errors.length > 0) {
437-
console.error('identifyPages errors:')
438-
for (const err of siteData.errors) console.error(' ', err.message)
453+
this.#logger.error({ errors: siteData.errors.map(err => err.message) }, 'identifyPages errors')
439454
return
440455
}
441456

@@ -490,7 +505,7 @@ export class DomStack {
490505
await this.#rebuildMaps(siteData)
491506
} else {
492507
// Non-esbuild file: structural change (page, layout, template, config, etc.)
493-
console.log(`"${changedBasename}" ${event}, triggering full rebuild...`)
508+
this.#logger.info({ file: changedBasename, event }, 'Triggering full rebuild')
494509
return this.#fullRebuild()
495510
}
496511
}
@@ -654,19 +669,19 @@ export class DomStack {
654669

655670
// 2. global.vars.* → full rebuild (esbuild restart + all pages)
656671
if (globalVarsNames.some(n => changedBasename === n)) {
657-
console.log(`"${changedBasename}" changed, triggering full rebuild...`)
672+
this.#logger.info({ file: changedBasename }, 'Triggering full rebuild')
658673
return this.#fullRebuild()
659674
}
660675

661676
// 3. global.data.* → full page rebuild (no esbuild restart)
662677
if (globalDataNames.some(n => changedBasename === n)) {
663-
console.log(`"${changedBasename}" changed, rebuilding all pages...`)
678+
this.#logger.info({ file: changedBasename }, 'Rebuilding all pages')
664679
return this.#runPageBuild(siteData)
665680
}
666681

667682
// 4. esbuild.settings.* → full rebuild
668683
if (esbuildSettingsNames.some(n => changedBasename === n)) {
669-
console.log(`"${changedBasename}" changed, triggering full rebuild...`)
684+
this.#logger.info({ file: changedBasename }, 'Triggering full rebuild')
670685
return this.#fullRebuild()
671686
}
672687

@@ -681,7 +696,7 @@ export class DomStack {
681696
// esbuild's own watcher handles these. Stable filenames mean page HTML doesn't
682697
// change, so no page rebuild is needed.
683698
if (this.#esbuildEntryPoints.has(changedPath)) {
684-
console.log(`"${changedBasename}" changed, esbuild will handle rebundling.`)
699+
this.#logger.info({ file: changedBasename }, 'Esbuild will handle rebundling')
685700
return
686701
}
687702

@@ -695,7 +710,7 @@ export class DomStack {
695710
const pageFilterPaths = Array.from(affectedPages).map(p => p.pageFile.filepath)
696711
return this.#runPageBuild(siteData, pageFilterPaths, [])
697712
}
698-
console.log(`"${changedBasename}" changed but no pages use layout "${layoutName}", skipping.`)
713+
this.#logger.info({ file: changedBasename, layout: layoutName }, 'Layout changed but no pages use it; skipping')
699714
return
700715
}
701716
// Not a registered layout — fall through to dep checks
@@ -755,7 +770,7 @@ export class DomStack {
755770
}
756771

757772
// 13. No matching rule — skip.
758-
console.log(`"${changedBasename}" changed but did not match any rebuild rule, skipping.`)
773+
this.#logger.info({ file: changedBasename }, 'Changed file did not match any rebuild rule; skipping')
759774
}
760775

761776
async stopWatching () {
@@ -770,8 +785,8 @@ export class DomStack {
770785
await this.#esbuildContext.dispose()
771786
this.#esbuildContext = null
772787
}
773-
this.#browserSyncServer?.exit() // This will kill the process
774-
this.#browserSyncServer = null
788+
await this.#syncServer?.exit()
789+
this.#syncServer = null
775790
}
776791

777792
/**

lib/builder.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { ensureDest } from './helpers/ensure-dest.js'
5151
* @property {string[]|undefined} [target=[]] - Esbuild target values used for JavaScript and CSS bundling.
5252
* @property {boolean|undefined} [buildDrafts=false] - Build files marked with the `published: false` variable.
5353
* @property {string[]|undefined} [copy=[]] - Paths to copy into the dest directory. Relative paths are resolved to absolute paths from the current working directory by the DomStack constructor, matching the CLI `--copy` behavior.
54+
* @property {import('@domstack/sync').Logger|undefined} [logger] - Logger used for build/watch output
5455
*/
5556

5657
/**

0 commit comments

Comments
 (0)