- TypeScript 89.9%
- JavaScript 7.4%
- HTML 2.7%
|
|
||
|---|---|---|
| scripts | ||
| src | ||
| template | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Introduction
A template for single-page applications distributed as Hono sub-app libraries. The generated package bundles a frontend shell (HTML, JS, CSS, PWA manifest, favicon) as static assets and exposes a createSpa() function that returns a Hono instance serving them. Intended to be consumed by a @temir.ra/create-hono-server backend, where it is mounted as a route group.
Table of Contents
Quick Start
# placeholder:
# <TEMPLATE_PACKAGE: @temir.ra/create-hono-spa
# <TEMPLATE_NAME: @temir.ra/hono-spa
# <NEW_PACKAGE: <NEW_PACKAGE>
# is used as:
# - the path where the package is created
# - the "name" field in the generated package.json
# <@_VERSION: <@_VERSION>
# pinned version
bun info "@temir.ra/create-hono-spa" version
bun create --no-install --no-git "@temir.ra/hono-spa<@_VERSION>" <NEW_PACKAGE>
# latest
# clear the cache to pick up the latest version
bun pm cache rm
bun create --no-install --no-git "@temir.ra/hono-spa" <NEW_PACKAGE>
# templates only copy files, run install and any setup scripts manually
cd <NEW_PACKAGE>
bun install
Documentation
The following sections explain the configurations and conventions baked into the generated package. Useful when adapting it to fit specific needs.
template/
Official documentation provided by the Hono team is a great resource.
Architecture
The generated SPA package is a library - not a runnable server. It abstracts three things from the consuming server:
Asset serving - All frontend assets (HTML shell, JS bundle, CSS, PWA manifest, favicon) are resolved from the package directory via import.meta.url and served by the sub-app itself. The consuming server does not configure static file serving or copy any files.
Client-side routing - The /app/* wildcard returns index.html for every navigation path. The <base href> tag is patched at request time from basePath and path, so relative asset URLs resolve correctly wherever the sub-app is mounted.
PWA scaffold - The assets/ directory ships with the structure expected by browsers for PWA installation: manifest, favicon, and screenshots.
The consuming server treats the package as an opaque route group passed to endpointGroups. See @temir.ra/create-hono-server for the mount pattern.
src/spa.ts
Built-in routes:
| Path | Description |
|---|---|
/health |
Returns ok with status 200 |
/buildinfo |
Returns the contents of buildinfo.txt |
/app/* |
Serves index.html with runtime substitutions applied |
/<faviconPath> |
Serves favicon.svg as image/svg+xml |
/<webmanifestPath> |
Serves manifest.webmanifest as application/manifest+json |
/<indexJsPath> |
Serves dist/index.bundle.js |
/<indexCssPath> |
Serves dist/index.css |
/<siteCssPath> |
Serves dist/site.css |
Options:
type CreateSpaOptions = {
basePath?: string; // outer base path matching the server host's basePath
path?: string; // sub-path for this SPA within the server
metaDescriptionContent?: string; // replaces content="" in the description meta tag
faviconPath?: string; // serve path for the favicon (default: 'favicon.svg')
webmanifestPath?: string; // default: 'manifest.webmanifest'
vAppContainerId?: string; // id attribute of the root app container div
indexJsPath?: string; // default: 'index.js'
indexCssPath?: string; // default: 'index.css'
siteCssPath?: string; // default: 'site.css'
}
src/constants.ts
export const packageUrl = new URL('../', import.meta.url);
export const buildinfoUrl = new URL('buildinfo.txt', packageUrl);
export const distUrl = new URL('dist/', packageUrl);
export const indexJsUrl = new URL('index.bundle.js', distUrl);
export const assetsUrl = new URL('assets/@scope/package-name/', packageUrl);
export const indexHtmlUrl = new URL('index.html', assetsUrl);
// ...
Replace @scope/package-name with your actual package name when adapting the template. Update constants.ts and rename the assets/@scope/package-name/ directory to match.
assets/
The template provides a PWA shell scaffold:
assets/
└── @scope/
└── package-name/
├── index.html ← SPA entry point; base href patched at request time
├── favicon.svg
├── manifest.webmanifest
└── images/
├── mobile-screenshot.png ← used in manifest for PWA install UI
└── wide-screenshot.png
assets/ is included in the files field of package.json and published with the package. The naming convention (assets/@scope/package-name/) follows the asset resolution contract from @temir.ra/create-ts-lib - scoped directories prevent naming collisions when multiple SPA packages coexist in the same server.
DevOps
bun update
bun install
bun run clean
bun run build
bun run tests
# see publish section for publish instructions
Change Management
- Create a new branch for the change.
- Make the changes and commit.
- Bump the version in
package.json. - Add an entry for the new version in
CHANGELOG.md. - Pull request the branch.
- Ensure package artifacts are current.
- Publish.
Publish
Publish to the public npm registry.
# authenticate
npm login
# publish
bun publish --registry https://registry.npmjs.org/ --access public