Templates and Package Snippets
Template Composition
Templates are composable layers. The base language template is always copied first, followed by the selected ORM base, database-specific ORM template, and optional feature templates.
templates/
template_ts/
template_js/
orm/
prisma/base/
prisma/postgresql/
prisma/mysql/
prisma/sqlite/
drizzle/base/
drizzle/postgresql/
drizzle/mysql/
drizzle/sqlite/
mongoose/base/
redis/
socket/
vitest/
eslint/
A template directory may contain source files, static assets, and one generic package.snippet.json.
Base Template Requirements
A language template should provide the common project surface expected by the E2E suite, including:
Readme.mdpublic/index.htmlpackage.snippet.json- TypeScript-only
tsconfig.jsonwhere applicable
Dynamic files are written by handleCopyTemplate, so do not duplicate those files in a template unless the overwrite behavior is intentional.
Package Snippets
A snippet is a JSON fragment merged into the generated root package. Example:
{
"dependencies": {
"example-driver": "^1.0.0"
},
"devDependencies": {
"example-cli": "^1.0.0"
},
"scripts": {
"example": "node scripts/example.js"
}
}
The merge rules are:
- Later sorted snippets override scalar keys from earlier snippets.
scripts,dependencies, anddevDependenciesare merged by key.- The generated package name is always derived from
ProjectConfig.projectName. - The generated package is always private.
- TypeScript projects receive
type: moduleunless a snippet explicitly provides a type. - Script extensions are adjusted to the selected language.
- Snippets are removed only after the final package write succeeds.
Use label-specific copied snippet names such as package.snippet.orm-prisma-base.json; the merge finder accepts the package.snippet*.json pattern.
Dynamic Content
src/utils/common_content.ts generates files whose contents depend on language, ORM, database, Redis, and WebSocket selections. Dynamic generators include:
mainFileContentserverFileContentuserRouterFileContentsubatomConfigContentenvConfigContentRelationalDbdotEnvFileContent
When adding a new interpolation branch:
- Add the branch to the generator.
- Add both TypeScript and JavaScript assertions where relevant.
- Add the corresponding files to
run-e2e.mjsexpected artifacts. - Add dependency/script expectations if the branch changes package metadata.
- Run generated-source validation.
Adding a Feature Template
For a feature named example:
- Create
templates/example/. - Add feature files using paths relative to the generated project root.
- Add
templates/example/package.snippet.jsonif dependencies or scripts are required. - Add a
useExamplefield toProjectConfig. - Add the prompt and cancellation handling.
- Add a conditional copy job in
handleCopyTemplate. - Add a post-copy configuration handler only if dynamic files are required.
- Update package and artifact expectations in the E2E runner.
- Add language permutations to unit tests.
Do not edit the interpolation mechanism or rename existing target paths without a migration plan.
Adding a Database Variant
For an existing ORM:
- Add
templates/orm/<orm>/<database>/. - Add its package snippet if dependencies/scripts differ.
- Update the ORM handler and dynamic content generators.
- Add database-specific generated files to
run-e2e.mjs. - Add source validation and package expectations.
- Test both JavaScript and TypeScript.
The prompt layer currently restricts Prisma and Drizzle to relational databases and Mongoose to MongoDB.
Symlinks and Paths
Template copy uses filesystem paths created with node:path. Keep template references relative to the configured template root; do not concatenate URL-style separators. The transaction rejects a symbolic-link target directory to avoid writing through an unexpected filesystem boundary. Symlinks inside copied template content should be tested explicitly before relying on them in published templates.