Skip to content
Open
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: 6 additions & 1 deletion docs/implementation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,16 @@ When a targeted build recomputes global data, DOMStack compares top-level values
| A module imported by `*.pages.ts` | Generated outputs owned by the importing files, then refresh dependency maps |
| `markdown-it.settings.ts` | All source-backed Markdown pages, plus subscribers of any changed global-data keys |
| `global.data.ts` | Consumers subscribed to top-level keys whose values changed |
| `global.vars.ts` or `esbuild.settings.ts` | Full rebuild |
| `global.vars.ts` | Full rebuild |
| `esbuild.settings.ts` | Full rebuild with a warning to stop and restart DOMStack to apply settings edits |
| `domstack-manifest.settings.ts` | No rebuild. The manifest pipeline is disabled in watch mode |
| Existing client, style, Web Worker, or service-worker entry | esbuild only, unless the same module also has server-side consumers |
| Static asset under `src` or a file under a `--copy` directory | cpx2 copies or removes the output directly |

Esbuild settings use ordinary Node.js module imports in the main DOMStack process.
Editing an existing `esbuild.settings.*` file triggers a warning because the full rebuild restarts esbuild contexts but does not clear the settings module cache; stop and restart DOMStack to apply changes, including `bundleRoots` changes.
Markdown settings do not require a process restart: each page build runs in a fresh worker, so edits to `markdown-it.settings.*` are loaded when the source-backed Markdown pages rebuild.

Adding or removing a file changes the set of discovered build inputs:

| Added or removed file | Rebuild scope |
Expand Down
41 changes: 41 additions & 0 deletions docs/settings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,47 @@ DOMStack preserves its reserved `define` values after the override runs.
These options also form the basis of the [service-worker](../workers/#service-workers) build.
DOMStack replaces the service-worker entry point and filename and disables code splitting, while options such as plugins, loaders, `target`, and JSX configuration carry over.

### Bundle roots

Export `bundleRoots` from `esbuild.settings.ts` when parts of a site should have independent code-splitting graphs.
This is useful for isolating an admin application from public pages so dependencies shared within the admin area are not factored into public chunks.
Each bundle root is a directory path relative to `src`.

```typescript
import type { BuildOptions } from '@domstack/static/types.js'

export const bundleRoots = ['admin', 'account/internal']

export default function esbuildSettings (options: BuildOptions): BuildOptions {
return options
}
```

DOMStack applies the default settings function once and then assigns its effective entry points to bundle-root builds.
When bundle roots are configured, entry points must be explicit file paths; glob entry points and `stdin` are rejected.
Entries outside every configured root remain together in the default build.
When roots overlap, an entry uses the deepest matching root, so `admin/reports/client.ts` belongs to `admin/reports` rather than `admin` when both are configured.
Matching uses path segments, so a root named `admin` does not include `administrator`.
Absolute paths, paths outside `src`, empty roots, and duplicate normalized roots are rejected.

Each non-empty group receives an independent esbuild build in production and an independent esbuild context in watch mode.
Entry output paths remain source-relative, while a named root's generated chunks and file-loader assets are written beneath that root to avoid collisions between contexts.
For example, the `admin` root writes its shared chunks under `admin/chunks/` instead of the default `chunks/` directory.
The root service worker remains a separate self-contained build and is never assigned to a bundle root.

DOMStack still writes one `domstack-esbuild-meta.json` containing the merged metadata from every browser group.
Programmatic build results retain the combined `report.buildResults` and `report.outputMap` fields, the unpartitioned settings in `report.buildOpts`, and exact per-group details in `report.builds`.
Multiple builds do not expose a combined `mangleCache`, because independently generated mappings can conflict; use each group's `buildResults.mangleCache` instead.
A settings plugin is configured on every resulting context, so plugins with shared mutable state must support multiple `setup()` calls.
Dependencies imported across roots are bundled independently by design, which trades some duplicate output and build work for isolation.
Bundle roots control code splitting, not access permissions or import boundaries; explicitly shared global or layout bundles can still be loaded by pages in multiple roots.
Restart the DOMStack process to load changes to `esbuild.settings.ts` (including `bundleRoots`) or its imported dependencies.
Restarting watch contexts alone does not reload the settings module or its dependencies.
Conflicting outputs from custom naming settings fail the build when reported through metafiles or in-memory `outputFiles`, but output writes are not transactional, so a failed build can leave partial output in the destination.
With both `write: true` and `metafile: false`, esbuild exposes neither output list, so DOMStack cannot detect collisions; retain metafiles when using custom output names.

### Customizing build options

You can return a shallow copy that modifies the defaults when you only need a small change.
For example, this keeps DOMStack's default asset loaders and adds a custom loader for `.wasm` files:

Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ ${siteData.errors.map(err => ` ${err.message}`).join('\n')}`)
* @returns {Promise<void>}
*/
async #executeWatchPlan (plan, event) {
if (plan.kind === 'full' && plan.warning) this.#logger.warn(plan.warning)
if (plan.message) this.#logger.info(plan.message)
if (plan.kind === 'skip') return
if (plan.kind === 'full') {
Expand Down
Loading