Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ domstack (v12.0.0)
`domstack` is also aliased to a `dom` bin.

- Running `domstack` will result in a `build` by default.
- Running `domstack --watch` or `domstack -w` will build the site and start an auto-reloading development web-server that watches for changes (provided by [Browsersync](https://browsersync.io)).
- Running `domstack --watch` or `domstack -w` will build the site and start an auto-reloading development web-server that watches for changes (provided by [`@domstack/sync`][domstack-sync]).
- Running `domstack --eject` or `domstack -e` will extract the default layout, global styles, and client-side JavaScript into your source directory and add the necessary dependencies to your package.json.

`domstack` is primarily a unix `bin` written for the [Node.js](https://nodejs.org) runtime that is intended to be installed from `npm` as a `devDependency` inside a `package.json` committed to a `git` repository.
Expand Down Expand Up @@ -1754,7 +1754,7 @@ Variable Resolution Layers:

When you run `domstack --watch` (or `domstack -w`), domstack performs an initial build and then watches for changes, rebuilding only what's necessary. Watch mode uses two independent watch loops:

**esbuild watch** — JS and CSS bundles are handled by esbuild's native `context.watch()`. In watch mode, output filenames are stable (no content hashes), so bundle changes never require a page HTML rebuild. Browser-sync detects the updated files on disk and reloads the browser directly.
**esbuild watch** — JS and CSS bundles are handled by esbuild's native `context.watch()`. In watch mode, output filenames are stable (no content hashes), so bundle changes never require a page HTML rebuild. `@domstack/sync` detects the updated files on disk and reloads the browser directly.

**chokidar watch** — Page files, layouts, templates, and config files are watched by chokidar. When a file changes, domstack determines the minimal set of pages to rebuild using dependency tracking maps built at startup.

Expand Down Expand Up @@ -1823,7 +1823,7 @@ Some notable features are included below, see the [roadmap](https://github.com/u
- [x] Docs website built with `domstack`: https://domstack.net
- [x] `--eject` cli flag
- [x] Global assets can live anywhere
- [x] Built in browsersync dev server
- [x] Built in `@domstack/sync` dev server
- [x] Real default layout style builds
- [x] Esbuild settings escape hatch
- [x] Copy folders
Expand Down Expand Up @@ -1875,6 +1875,7 @@ It is also an homage to [substack](https://substack.net) as well as a play on th
[fragtml]: https://www.npmjs.com/package/fragtml
[fragtml-docs]: https://github.com/bcomnes/fragtml#readme
[preact]: https://preactjs.com/
[domstack-sync]: https://www.npmjs.com/package/@domstack/sync
[hb]: https://handlebarsjs.com
[esbuild]: http://esbuild.github.io
[neocities-img]: https://img.shields.io/website/https/domstack.neocities.org?label=neocities&logo=data:image/png;base64,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
71 changes: 35 additions & 36 deletions bin.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/usr/bin/env node

/**
* @import {DomStackOpts as DomStackOpts} from './lib/builder.js'
* @import { BuildStepWarnings, DomStackOpts as DomStackOpts } from './lib/builder.js'
* @import { ArgscloptsParseArgsOptionsConfig } from 'argsclopts'
* @import { Logger as PinoLogger } from 'pino'
*/

import { readFile } from 'node:fs/promises'
Expand All @@ -23,6 +24,7 @@ import { DomStack } from './index.js'
import { DomStackAggregateError } from './lib/helpers/domstack-aggregate-error.js'
import { generateTreeData } from './lib/helpers/generate-tree-data.js'
import { askYesNo } from './lib/helpers/cli-prompt.js'
import { createDomStackLogger } from './lib/logger.js'

const __dirname = import.meta.dirname

Expand Down Expand Up @@ -212,67 +214,64 @@ domstack eject actions:
opts.copy = copyPaths.map(p => resolve(cwd, p))
}

const logger = createDomStackLogger()
opts.logger = logger
const domStack = new DomStack(src, dest, opts)

process.once('SIGINT', quit)
process.once('SIGTERM', quit)

async function quit () {
if (domStack.watching) {
const results = await domStack.stopWatching()
console.log(results)
console.log('watching stopped')
await domStack.stopWatching()
logger.info('Watching stopped')
}
console.log('\nquitting cleanly')
logger.info('Quitting cleanly')
process.exit(0)
}

if (!argv['watch'] && !argv['watch-only']) {
try {
const results = await domStack.build()
console.log(tree(generateTreeData(cwd, src, dest, results)))
if (results?.warnings?.length > 0) {
console.log(
'\nThere were build warnings:\n'
)
}
for (const warning of results?.warnings) {
if ('message' in warning) {
console.log(` ${warning.message}`)
} else {
console.warn(warning)
}
}
console.log('\nBuild Success!\n\n')
logger.info(tree(generateTreeData(cwd, src, dest, results)))
logWarnings(logger, results?.warnings)
logger.info('\nBuild Success!\n\n')
} catch (err) {
if (!(err instanceof Error || err instanceof AggregateError)) throw new Error('Non-error thrown', { cause: err })
if (err instanceof DomStackAggregateError) {
if (err?.results?.siteData?.pages) {
console.log(tree(generateTreeData(cwd, src, dest, err.results)))
logger.error(tree(generateTreeData(cwd, src, dest, err.results)))
}
}
if ('results' in err) delete err.results
console.error(inspect(err, { depth: 999, colors: true }))

console.log('\nBuild Failed!\n\n')
logger.error(inspect(err, { depth: 999, colors: true }))
logger.error('\nBuild Failed!\n\n')
process.exit(1)
}
} else {
const initialResults = await domStack.watch({
await domStack.watch({
serve: !argv['watch-only'],
onInitialBuild: (initialResults) => {
logger.info(tree(generateTreeData(cwd, src, dest, initialResults)))
logWarnings(logger, initialResults?.warnings)
},
})
console.log(tree(generateTreeData(cwd, src, dest, initialResults)))
if (initialResults?.warnings?.length > 0) {
console.log(
'\nThere were build warnings:\n'
)
}
for (const warning of initialResults?.warnings) {
if ('message' in warning) {
console.log(` ${warning.message}`)
} else {
console.warn(warning)
}
}
}

/**
* @param {PinoLogger} logger
* @param {BuildStepWarnings | undefined} warnings
*/
function logWarnings (logger, warnings) {
if ((warnings?.length ?? 0) === 0) return

logger.warn('\nThere were build warnings:\n')
for (const warning of warnings ?? []) {
if ('message' in warning) {
logger.warn(` ${warning.message}`)
} else {
logger.warn(inspect(warning, { depth: 999, colors: true }))
}
}
}
Expand Down
27 changes: 19 additions & 8 deletions docs/v12-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ Then apply the v12 changes below.
## Table of Contents

1. [Type exports moved to `@domstack/static/types.js`](#1-type-exports-moved-to-domstackstatictypesjs)
2. [Default Layout Uses fragtml](#2-default-layout-uses-fragtml)
3. [Keep Layout Dependencies Explicit](#3-keep-layout-dependencies-explicit)
4. [JSX Runtime Is Opt-In](#4-jsx-runtime-is-opt-in)
5. [Migration Checklist](#5-migration-checklist)
2. [Development Server Uses @domstack/sync](#2-development-server-uses-domstacksync)
3. [Default Layout Uses fragtml](#3-default-layout-uses-fragtml)
4. [Keep Layout Dependencies Explicit](#4-keep-layout-dependencies-explicit)
5. [JSX Runtime Is Opt-In](#5-jsx-runtime-is-opt-in)
6. [Migration Checklist](#6-migration-checklist)

---

Expand All @@ -30,7 +31,16 @@ import type { LayoutFunction, PageFunction, DomStackOpts } from '@domstack/stati

---

## 2. Default Layout Uses fragtml
## 2. Development Server Uses @domstack/sync

Watch/serve mode now uses [`@domstack/sync`](https://www.npmjs.com/package/@domstack/sync) for the local development server.

This provides live reload, CSS injection, ghost mode, and the UI panel.
If you were relying on BrowserSync-specific behavior or output, update your expectations around logs, access URLs, and reload handling.

---

## 3. Default Layout Uses fragtml

The bundled default `root.layout.js` now uses [`fragtml`](https://github.com/bcomnes/fragtml#readme) for server-side HTML rendering.

Expand Down Expand Up @@ -62,7 +72,7 @@ Markdown output passed to a layout as `children` is one example.

---

## 3. Keep Layout Dependencies Explicit
## 4. Keep Layout Dependencies Explicit

DOMStack only installs dependencies for its bundled defaults.
Your project is responsible for any packages imported by pages, layouts, globals, or browser clients.
Expand All @@ -72,7 +82,7 @@ If you migrate those server-side templates to `fragtml`, replace those dependenc

---

## 4. JSX Runtime Is Opt-In
## 5. JSX Runtime Is Opt-In

Client `.jsx` and `.tsx` bundles are still supported through esbuild.
Domstack no longer configures Preact as the default JSX runtime.
Expand Down Expand Up @@ -113,9 +123,10 @@ export default async function esbuildSettingsOverride (esbuildSettings) {

---

## 5. Migration Checklist
## 6. Migration Checklist

- [ ] If you import public types from `@domstack/static`, update those imports to `@domstack/static/types.js`.
- [ ] If you rely on BrowserSync-specific dev-server behavior, test watch mode with `@domstack/sync`.
- [ ] If you rely on the bundled default layout, make sure pages and child layouts return HTML strings or `fragtml` template results, not Preact or HTM VNodes.
- [ ] If you want to keep the v11 Preact default layout, eject on v11 before upgrading to v12.
- [ ] If your ejected layout or server-side pages still import `htm/preact`, `preact`, or `preact-render-to-string`, keep those dependencies in your own `package.json`.
Expand Down
2 changes: 0 additions & 2 deletions examples/blog/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@ at build time, with no manual wiring needed in this file.
{{{ vars.recentPostsHtml }}}

See [all posts →](/blog/)

Hello
8 changes: 1 addition & 7 deletions examples/default-layout/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,11 @@
"start": "npm run watch",
"build": "npm run clean && domstack",
"clean": "rm -rf public && mkdir -p public",
"watch": "npm run clean && run-p watch:*",
"watch:serve": "browser-sync start --server 'public' --files 'public'",
"watch:domstack": "npm run build -- --watch"
"watch": "npm run clean && domstack --watch"
},
"dependencies": {
"@domstack/static": "file:../../."
},
"devDependencies": {
"browser-sync": "^2.26.7",
"npm-run-all2": "^6.0.0"
},
"keywords": [],
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io/)",
"license": "MIT"
Expand Down
8 changes: 1 addition & 7 deletions examples/string-layouts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,11 @@
"start": "npm run watch",
"build": "npm run clean && domstack",
"clean": "rm -rf public && mkdir -p public",
"watch": "npm run clean && run-p watch:*",
"watch:serve": "browser-sync start --server 'public' --files 'public'",
"watch:domstack": "npm run build -- --watch"
"watch": "npm run clean && domstack --watch"
},
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io/)",
"license": "MIT",
"dependencies": {
"@domstack/static": "file:../../."
},
"devDependencies": {
"browser-sync": "^2.26.7",
"npm-run-all2": "^6.0.0"
}
}
Loading