From d61bea42877aa363a73ba85ed71789f084e568f6 Mon Sep 17 00:00:00 2001 From: Bret Comnes Date: Mon, 1 Jun 2026 21:41:40 -0700 Subject: [PATCH] Convert to fragtml for base layout # Conflicts: # test-cases/watch/index.test.js --- README.md | 165 ++++++++++-------- bin.js | 8 +- docs/v11-migration.md | 8 +- docs/v12-migration.md | 121 +++++++++++++ examples/basic/README.md | 2 +- examples/basic/package.json | 4 +- .../basic/src/js-page/loose-assets/page.ts | 7 +- examples/basic/src/js-page/page.js | 9 +- examples/basic/src/layouts/child.layout.ts | 14 +- examples/basic/src/layouts/root.layout.ts | 45 ++--- examples/basic/tsconfig.json | 3 +- examples/blog/package.json | 6 +- examples/blog/src/about/README.md | 2 +- .../blog/src/blog/2024/hello-world/README.md | 2 +- examples/blog/src/blog/page.ts | 3 +- examples/blog/src/global.data.ts | 3 +- examples/blog/src/layouts/post.layout.ts | 11 +- examples/blog/src/layouts/root.layout.ts | 19 +- .../blog/src/layouts/year-index.layout.ts | 17 +- examples/blog/tsconfig.json | 4 +- examples/css-modules/package.json | 6 +- .../css-modules/src/layouts/root.layout.js | 45 ++--- examples/css-modules/src/modules/page.js | 2 +- .../preact-isomorphic/src/esbuild.settings.js | 10 ++ examples/react/src/README.md | 12 +- .../react/src/globals/esbuild.settings.ts | 9 +- examples/string-layouts/README.md | 22 +-- examples/tailwind/package.json | 4 +- examples/tailwind/src/layouts/root.layout.js | 42 ++--- examples/type-stripping/package.json | 6 +- .../type-stripping/src/esbuild.settings.js | 10 ++ .../type-stripping/src/layouts/root.layout.ts | 45 ++--- lib/build-esbuild/index.js | 4 +- lib/defaults/default.root.layout.js | 46 ++--- package.json | 5 +- test-cases/default-layout/index.test.js | 11 +- .../2025/a-blog-post-from-2025/client.tsx | 1 + test-cases/general-features/src/blog/page.js | 2 +- .../general-features/src/esbuild.settings.js | 10 ++ .../general-features/src/global.data.js | 11 +- .../src/layouts/blog.layout.js | 26 +-- .../src/layouts/root.layout.js | 17 +- .../general-features/src/layouts/ts.layout.ts | 26 +-- tsconfig.json | 3 +- 44 files changed, 469 insertions(+), 359 deletions(-) create mode 100644 docs/v12-migration.md create mode 100644 examples/preact-isomorphic/src/esbuild.settings.js create mode 100644 examples/type-stripping/src/esbuild.settings.js create mode 100644 test-cases/general-features/src/esbuild.settings.js diff --git a/README.md b/README.md index 9495137..5023ee8 100644 --- a/README.md +++ b/README.md @@ -20,25 +20,10 @@ npm install @domstack/static - 🌎 [domstack docs website](https://domstack.net) - 💬 [Discord Chat](https://discord.gg/AVTsPRGeR9) -- 📢 [v11 - top-bun is now domstack](./docs/v11-migration.md) +- 📢 [v12 Migration Guide](docs/v12-migration.md) - 📢 [v7 Announcement](https://bret.io/blog/2023/reintroducing-top-bun/) - 📘 [Full TypeScript Support](#typescript-support) -## Migrating from top-bun - -domstack v11 is a major release that renames the project from `top-bun` to `@domstack/static`. The full migration guide is at [docs/v11-migration.md](docs/v11-migration.md). Key changes at a glance: - -- **Package**: `top-bun` → `@domstack/static` -- **CLI**: `top-bun`/`tb` → `domstack`/`dom` -- **Programmatic API**: `TopBun` class → `DomStack`, all `TopBun*` types/errors/warnings renamed to `DomStack*` -- **`postVars` removed**: migrate `postVars` exports from `page.vars.js` files to a single `global.data.js` with a default export -- **New reserved filenames**: `global.data.js`, `markdown-it.settings.js`, `page.md`, `*.worker.{js,ts}` are now special — rename any colliding files -- **Default layout**: switched from `uhtml-isomorphic` to `preact`; add `uhtml-isomorphic` to your own deps if you import it directly -- **Output paths**: `top-bun-esbuild-meta.json` → `domstack-esbuild-meta.json`, `top-bun-defaults/` → `domstack-defaults/` -- **Conflict now throws**: using both `browser` in `global.vars.js` and `define` in `esbuild.settings.js` is now a hard error - -See [docs/v11-migration.md](docs/v11-migration.md) for the complete migration guide with code examples. - ## Table of Contents [[toc]] @@ -61,14 +46,14 @@ Usage: domstack [options] --copy path to directories to copy into dist; can be used multiple times --help, -h show help --version, -v show version information -domstack (v11.0.0) +domstack (v12.0.0) ``` `domstack` builds a `src` directory into a `dest` directory (default: `public`). `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. @@ -277,11 +262,12 @@ const page: PageFunction = async ({ export default page ``` -It is recommended to use some level of template processing over raw string templates so that HTML is well-formed and variable values are properly escaped. Here is a more realistic TypeScript example that uses [`preact`](https://preactjs.com/) with [`htm`](https://github.com/developit/htm) and `domstack` page introspection. +It is recommended to use some level of template processing over raw string templates so that HTML is well-formed and variable values are properly escaped. DOMStack's default layout uses [`fragtml`][fragtml], a safe-by-default HTML tagged template library. Here is a more realistic TypeScript example that uses `fragtml` and `domstack` page introspection. ```typescript -import { html } from 'htm/preact' +import { html } from 'fragtml' +import type { HtmlResult } from 'fragtml/types.js' import { dirname, basename } from 'node:path' import type { PageFunction } from '@domstack/static' @@ -293,7 +279,7 @@ export const vars = { favoriteCake: 'Chocolate Cloud Cake' } -const blogIndex: PageFunction = async ({ +const blogIndex: PageFunction = async ({ vars: { favoriteCake }, pages }) => { @@ -355,8 +341,24 @@ await funnyLibrary() #### .tsx/.jsx -Client bundles support .jsx and .tsx. They default to preact, so if you want mainlain react, customize your esbuild settings to load that instead. -See the [react](./examples/react/) example for more details. +Client bundles support .jsx and .tsx through esbuild. DOMStack does not include a JSX runtime by default; install the runtime you want and configure it with `esbuild.settings`. +See the [react](./examples/react/) and [preact-isomorphic](./examples/preact-isomorphic/) examples for more details. + +For example, to use [Preact][preact] in browser JSX/TSX bundles, add it to your project and opt into Preact's automatic JSX runtime: + +```console +npm install preact +``` + +```js +// src/esbuild.settings.js +export default async function esbuildSettingsOverride (esbuildSettings) { + esbuildSettings.jsx = 'automatic' + esbuildSettings.jsxImportSource = 'preact' + + return esbuildSettings +} +``` ### Page variable files @@ -488,13 +490,13 @@ It is always passed a single object argument with the following entries: - `pages`: An array of page data that you can use to generate index pages with, or any other page-introspection based content that you desire. - `page`: An object with metadata and other facts about the current page being rendered into the template. This will also be found somewhere in the `pages` array. -The default `root.layout.ts` is featured below, and is implemented with [`preact`](https://preactjs.com/) and [`htm`](https://github.com/developit/htm), though it could just be done with a template literal or any other template system that runs in Node.js. +The default `root.layout.ts` is featured below, and is implemented with [`fragtml`][fragtml], though it could just be done with a template literal or any other template system that runs in Node.js. See the [`fragtml` docs][fragtml-docs] for escaping, raw HTML, rendering, and fragment usage. `root.layout.ts` can live anywhere in the `src` directory. ```typescript -import { html } from 'htm/preact' -import { render } from 'preact-render-to-string' +import { html, raw, render } from 'fragtml' +import type { HtmlResult } from 'fragtml/types.js' import type { LayoutFunction } from '@domstack/static' type RootLayoutVars = { @@ -504,7 +506,7 @@ type RootLayoutVars = { basePath?: string } -const defaultRootLayout: LayoutFunction = ({ +const defaultRootLayout: LayoutFunction = ({ vars: { title, siteName = 'Domstack', @@ -517,32 +519,25 @@ const defaultRootLayout: LayoutFunction = ({ pages, page, }) => { - return /* html */` + return render(html` - ${render(html` - - - ${title ? `${title}` : ''}${title && siteName ? ' | ' : ''}${siteName} - - ${scripts - ? scripts.map(script => html``) + : null} + ${styles + ? styles.map(style => html``) + : null} + + +
${typeof children === 'string' ? raw(children) : children}
+ - ` + `) } export default defaultRootLayout @@ -558,8 +553,8 @@ For example, you could define a `blog.layout.ts` that re-uses the `root.layout.t ```typescript import defaultRootLayout from './root.layout.js' -import { html } from 'htm/preact' -import { render } from 'preact-render-to-string' +import { html, raw, render } from 'fragtml' +import type { HtmlResult } from 'fragtml/types.js' import type { LayoutFunction } from '@domstack/static' // Import the type from root layout @@ -575,23 +570,23 @@ interface BlogLayoutVars extends RootLayoutVars { updatedDate?: string; } -const blogLayout: LayoutFunction = (layoutVars) => { +const blogLayout: LayoutFunction = (layoutVars) => { const { children: innerChildren, ...rest } = layoutVars const vars = layoutVars.vars const children = render(html` -
-
-

${vars.title}

-
-
- ${typeof children === 'string' - ? html`
` - : children - } + ${typeof children === 'string' ? raw(children) : children}