No description
Find a file
2026-03-24 22:56:56 +01:00
scripts update from template @temir.ra/ts-lib@0.5.0 2026-03-14 00:53:54 +01:00
src Fixed destinationPath resolution in cli.ts to use the full resolved path instead of just the basename. 2026-03-13 20:55:49 +01:00
template version 0.2.0 2026-03-24 22:30:09 +01:00
tests update from template @temir.ra/ts-lib@0.5.0 2026-03-14 00:53:54 +01:00
.gitignore cleanup *-template files, ignoring example/ and bun.lock 2026-03-11 23:55:56 +01:00
CHANGELOG.md version 0.2.0 2026-03-24 22:30:09 +01:00
package.json version 0.2.0 2026-03-24 22:30:09 +01:00
README.md version 0.1.0-pre.1 2026-03-22 13:21:56 +01:00
tsconfig.build.json adding files from template @temir.ra/ts-lib@0.3.0 2026-03-11 23:43:06 +01:00
tsconfig.json version 0.2.0 2026-03-24 22:30:09 +01:00

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

  1. Quick Start
  2. AI Assistant Context
  3. Documentation
    1. template/
    2. Architecture
    3. src/spa.ts
    4. src/constants.ts
    5. assets/
  4. DevOps
    1. Change Management
    2. Publish
      1. npmjs.org
      2. Custom registry

Quick Start

bun create caches the template package - a newer published version will not be picked up automatically. Pin the version or clear the cache to use the latest.

# placeholder:
    # <NEW_PACKAGE: <NEW_PACKAGE>
    # <@_VERSION: <@_VERSION>

# identify the latest version of the template package as <@_VERSION.
bun info "@temir.ra/create-hono-spa" version
# create a new library from the template version
bun create --no-install --no-git "@temir.ra/hono-spa<@_VERSION>" <NEW_PACKAGE>

# or

# clear package manager cache to ensure the latest template version is used
bun pm cache rm
# create a new library from the latest template version
bun create --no-install --no-git "@temir.ra/hono-spa" <NEW_PACKAGE>

# dependencies must be installed manually
cd <NEW_PACKAGE>
bun install

AI Assistant Context

To generate an AI coding assistant context file for this project:

Generate an AI coding assistant context file. Analyze the project structure and source files, using this README as the primary reference for architecture and conventions. Give particular attention to: the SPA-as-library abstraction and how assets are served via import.meta.url without any file copying on the consuming server, the ./spa export condition and createSpa() as the library's public API, and the base href patching mechanism that enables client-side routing regardless of mount path.

Documentation

The following sections explain the configurations and conventions baked into the generated package.

template/

The files in template/ were created from the @temir.ra/ts-lib@0.5.0 template and then updated to fit the needs of a Hono SPA template.

bun create --no-install --no-git --force "@temir.ra/ts-lib@0.5.0" "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

# remove dist/ and tsconfig.build.tsbuildinfo
bun run clean

# remove dist/ only
bun run clean:dist

# remove tsconfig.build.tsbuildinfo only
bun run clean:tsbuildinfo

# compile + bundle
bun run build

# create a new test hono spa in example/
bun run dist/cli.bundle.js -- example

Publish - see Publish.

Change Management

  1. Create a new branch for the change.
  2. Make the changes and commit.
  3. Bump the version in package.json.
  4. Add an entry for the new version in CHANGELOG.md.
  5. Pull request the branch.
  6. After merge, run bun run build - ensures artifacts are current before publish.
  7. Publish.

Publish

See the following sources to configure the target registry and authentication.

⚠️ Package Scope and the authentication for the target registry must be aligned.

npmjs.org

Publish to the public npm registry.

# authenticate
npm login
# publish
bun publish --registry https://registry.npmjs.org/ --access public

Custom registry

# placeholder:
    # <SCOPE_WITHOUT_AT: <SCOPE_WITHOUT_AT>
    # <REGISTRY_URL: <REGISTRY_URL>
    # <BUN_PUBLISH_AUTH_TOKEN: <BUN_PUBLISH_AUTH_TOKEN>

~/.bunfig.toml or bunfig.toml:

[install.scopes]
"<SCOPE_WITHOUT_AT>" = { url = "<REGISTRY_URL>", token = "$BUN_PUBLISH_AUTH_TOKEN" }
# authenticate
$env:BUN_PUBLISH_AUTH_TOKEN = "<BUN_PUBLISH_AUTH_TOKEN>"
# publish
bun publish