|
|
||
|---|---|---|
| scripts | ||
| src | ||
| template | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
Introduction
A template for monorepos using workspaces. Provides a shared root configuration with unified dependency management and cross-workspace imports by package name.
The following templates are available in this ecosystem:
| Template | Purpose |
|---|---|
create-ts-lib |
TypeScript library distributed via registry |
create-hono-server |
Bun/Hono HTTP server |
create-hono-spa |
Frontend SPA as a Hono sub-app library |
create-monorepo |
Monorepo root (this template) |
Table of Contents
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:
# <MONOREPO: <MONOREPO>
# <@_VERSION: <@_VERSION>
# identify the latest version of the template package as <@_VERSION.
bun info "@temir.ra/create-monorepo" version
# create a new library from the template version
bun create --no-install --no-git "@temir.ra/monorepo<@_VERSION>" <MONOREPO>
# 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/monorepo" <MONOREPO>
# dependencies must be installed manually
cd <MONOREPO>
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 workspace symlink mechanism and how cross-package imports resolve by package name without path aliases, the workspace:* protocol and its publish-time version replacement behaviour, and the scaffolding patterns and which templates are combined for each scenario.
Documentation
The following sections explain the configurations and conventions baked into the generated package. Useful when adapting the generated monorepo to fit specific needs.
package.json
{
"name": "",
"version": "0.0.0",
"description": "",
"author": "",
"license": "",
// prevents publishing the root to a registry; only workspace packages are published
"private": true,
// glob patterns matching directories that each contain a package.json;
// the execution environment installs all workspace dependencies into the shared root node_modules/
// and symlinks each package by its name field for cross-workspace imports
"workspaces": [
"packages/*"
],
"scripts": {
// runs the named script in every workspace package that defines it,
// in dependency order (dependents run after the packages they depend on)
"clean": "bun run --filter '*' --parallel clean",
"build": "bun run --filter '*' --parallel build",
"typecheck": "bun run --filter '*' --parallel typecheck",
"tests": "bun run --filter '*' --parallel tests"
},
}
Each workspace package has its own package.json with its specific dependencies and scripts.
<MONOREPO>/
├── package.json ← root
└── packages/
├── pkg-a/
│ ├── package.json
│ └── ...
└── pkg-b/
├── package.json
└── ...
The execution environment installs all workspace packages into a single shared node_modules/ at the root and symlinks each workspace package by its name field. Cross-workspace imports resolve by package name without path aliases or module mapping.
Cross-workspace Dependencies
To use one workspace package from another, declare it as a dependency using the workspace: protocol:
// packages/pkg-a/package.json
{
"dependencies": {
// workspace: resolves to the local package instead of fetching from a registry;
// workspace:* tracks the exact local version;
// workspace:^ is replaced by ^version on publish, tracking the semver range in the registry
"@scope/pkg-b": "workspace:*"
}
}
Run bun install after adding the entry. The execution environment creates the symlink in node_modules/ so imports resolve by package name at runtime and at the type level - no path alias or paths mapping required.
Scaffolding Patterns
Scaffold the monorepo first, then add packages inside it. After scaffolding each package, update its package.json with the correct name, version, description, author, license, and repository fields. To wire cross-workspace dependencies, add the workspace:* entry to the consuming package's package.json and run bun install from the monorepo root.
Library
<monorepo>/
└── packages/
└── my-lib/ ← create-ts-lib
bun create --no-install --no-git "@temir.ra/monorepo" <monorepo>
cd <monorepo>
bun create --no-install --no-git "@temir.ra/ts-lib" packages/my-lib
bun install
create-ts-lib
Starting point for any package distributed via a registry. See the full documentation at @temir.ra/create-ts-lib.
- Dual
tsconfig:tsconfig.json(dev,bundlerresolution, includestests/+scripts/) andtsconfig.build.json(prod,nodenext,src/only, emits JS +.d.ts) exportsconditions:entrypoint(custom, source entry for the bundle script),types,browser(bundled output),import(compiled ESM)importsfield:#src/*.js→./src/*.ts- runtime alias fordev.ts,tests/,scripts/only- Build metadata:
scripts/buildinfo.tsgeneratesbuildinfo.txt(version + git hash) as aprebuildhook - Bundle script:
scripts/build-lib-bundle.tsbundles via Bun; supports CDN specifier rewriting viacdn-rewrite-map.json - Asset resolution contract: assets under
assets/@scope/lib-name/, accessed viaimport.meta.url+fetch() - Optional CLI entry point via
binfield andbuild:cli-bundlescript
Server
<monorepo>/
└── packages/
└── server/ ← create-hono-server
bun create --no-install --no-git "@temir.ra/monorepo" <monorepo>
cd <monorepo>
bun create --no-install --no-git "@temir.ra/hono-server" packages/server
bun install
create-hono-server
Starting point for any HTTP backend. See the full documentation at @temir.ra/create-hono-server.
createAppHost(options)- configures and returns aHono<AppEnv>instance- Built-in middleware pipeline:
Logging,requestId,RequestLogger,compress,secureHeaders - Built-in endpoints:
/health,/buildinfo, OpenAPI spec, Scalar UI endpointGroups- mounts additionalHonosub-apps; integration point for SPA packagesAppEnv- exported, extensible context type; extend it to carry custom variables through the contextstartServerHost(options)- wrapsBun.serve(), default port 7200, graceful shutdown on SIGINT/SIGTERM
Server + SPA
<monorepo>/
└── packages/
├── server/ ← create-hono-server
└── spa/ ← create-hono-spa
bun create --no-install --no-git "@temir.ra/monorepo" <monorepo>
cd <monorepo>
bun create --no-install --no-git "@temir.ra/hono-server" packages/server
bun create --no-install --no-git "@temir.ra/hono-spa" packages/spa
bun install
Add the SPA as a dependency of the server, then reinstall:
// packages/server/package.json
{
"dependencies": {
"@scope/spa": "workspace:*"
}
}
bun install
create-hono-spa
Starting point for a frontend SPA distributed as a Hono sub-app library. See the full documentation at @temir.ra/create-hono-spa.
createSpa(options)- returns aHonosub-app serving the SPA shell/app/*wildcard handles client-side routing;<base href>patched at request time frombasePathandpath- Assets resolved via
import.meta.url; no file copying or static serving config needed on the consuming server - PWA scaffold in
assets/:index.html,favicon.svg,manifest.webmanifest, screenshots - Distributed as a library (
exports+dist/+assets/); consumed viaworkspace:*in the server
Full-Stack with Shared Types
<monorepo>/
└── packages/
├── server/ ← create-hono-server
├── spa/ ← create-hono-spa
└── types/ ← create-ts-lib
bun create --no-install --no-git "@temir.ra/monorepo" <monorepo>
cd <monorepo>
bun create --no-install --no-git "@temir.ra/hono-server" packages/server
bun create --no-install --no-git "@temir.ra/hono-spa" packages/spa
bun create --no-install --no-git "@temir.ra/ts-lib" packages/types
bun install
Add the types package as a dependency of both the server and the SPA, then reinstall:
// packages/server/package.json and packages/spa/package.json
{
"dependencies": {
"@scope/types": "workspace:*"
}
}
bun install
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 monorepo in example/
bun run dist/cli.bundle.js -- example
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.
- After merge, run
bun run build- ensures artifacts are current before publish. - 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