This repository ships a one-shot installer that scaffolds a SvelteKit app, adds common deps, optionally merges a template, sets up AI toolkit integration, tweaks .gitignore, can copy shared assets from the starter repo, sets a license, and optionally makes a local git commit.
npx github:bchainhub/dapp-starterWith a custom template:
npx github:bchainhub/dapp-starter -- --template https://github.com/your-org/your-template.gitOr clone and run locally:
git clone https://github.com/bchainhub/dapp-starter.git
cd dapp-starter
npm install
node start.mjsTemplate options: use --template URL or -t URL to point at a different template repo (default: mota-dapp). You can pin a version in the URL (jsDelivr-style): append @version (e.g. ...mota-dapp.git@1.2.3). Alternatively use --template-version REF or --tv REF when the URL has no @version (e.g. default mota-dapp or a custom URL without a tag). If no version is given, the repo’s default branch is used (from the remote).
To refresh an existing project from the template (overwrites files with the template’s version, except vite.config.ts):
cd /path/to/your-project
node /path/to/dapp-starter/start.mjs --update
# or: npx github:bchainhub/dapp-starter -- --updateWith a custom template or version in URL:
node start.mjs --update --template https://github.com/your-org/your-template.git
# version in URL (jsDelivr-style): ...mota-dapp.git@1.2.3
# or use --tv 1.2.3 when URL has no @version--update/-u— run in update mode (no new project; run from project root).--template-version REF/--tv REF— alternative toURL@version; use when the URL has no version (e.g. default mota-dapp or custom URL). Clone this branch or tag; if omitted, uses the repo’s default branch.- Before overwriting, the script asks: Create a git commit before updating (breakpoint)? (default Yes). If yes, it runs
git add -Aandgit commit -m "chore: checkpoint before template update". - Template is cloned to a temp dir; its contents are copied over your project (excluding
.gitandnode_modules). Yourvite.config.tsis backed up and restored so it is never replaced. - On success you get: Project updated from template. vite.config.ts was preserved.
- Node.js 18+
- git
- one package manager: npm, pnpm, yarn, or bun
The installer:
- runs
sv create - installs base dependencies
- installs addon tooling
- writes
bin/addon.mjs - maps the command name
addoninpackage.json - composes a project README
- optionally adds translations, skills, template merge, license, and first commit
After installation, projects can run:
npx addon <repo> <generator> <action> [options]Examples:
npx addon bchainhub@mota-support auth install
npx addon owner/repo auth uninstall
npx addon owner/repo auth install -c
npx addon owner/repo auth install -dVersioning: You can pin a release, branch, or commit by appending #<ref> to the repo (the addon uses tiged, which supports git refs). For example, for release 1.2.3 use a tag such as v1.2.3 or 1.2.3:
npx addon owner/repo#v1.2.3 auth install
npx addon owner/repo#1.2.3 auth installUse #branch for a branch or #<commit-hash> for a specific commit.
Options (short and long):
| Flag | Short | Effect |
|---|---|---|
--cache |
-c |
Use cache dir for repo (faster re-runs). |
--dry-run |
-d |
No writes; script/config/lang steps are skipped. |
--no-translations |
-nt |
Skip _lang (translations) processing. |
--no-scripts |
-ns |
Skip _scripts execution. |
--no-config |
-nc |
Skip _config merge. |
An addon repository contains generator/action folders. Hidden files _scripts, _config, and _lang can live either in the action root or inside optional subfolders of the same name:
<repo>/
<generator>/
<action>/
prompt.js
*.ejs.t
_scripts.ejs.sh or _scripts/_scripts.ejs.sh
_scripts.sh _scripts/_scripts.sh
_config.ejs.json5 or _config/_config.ejs.json5
_config.json5 _config/_config.json5
_lang.sk.json5 or _lang/sk.json5, _lang/en.ejs.json5, ...
_lang.en.json5 _lang/en.json5
A _migrations folder may be created or copied into your project (e.g. by addons or the template). It is intended for database migrations. It is listed in .gitignore, so it will not be committed to the git repository - but you can change it to commit it if you want.
You need to configure your app to use this folder. For example, with Drizzle ORM set out to ./_migrations in your config:
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./_migrations", // 👈 custom migrations folder
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});To execute migrations, add the intended migration command to your _scripts.ejs.sh or _scripts.sh scripts.
For example:
#!/usr/bin/env bash
set -euo pipefail
npx drizzle-kit pushOptional. Collects prompt values once.
Those same values are then reused by:
- Hygen templates
- hidden scripts
- hidden config
A prompt.js file can export either:
- an array of prompt definitions
- a function returning an answers object
Example array export:
export default [
{
type: 'text',
name: 'provider',
message: 'Auth provider'
},
{
type: 'text',
name: 'route',
message: 'Route name',
initial: 'auth'
}
];Example function export:
export default async ({ prompts, cwd, generator, action, repo }) => {
const answers = await prompts([
{
type: 'text',
name: 'provider',
message: 'Auth provider'
}
]);
return answers;
};Normal Hygen templates. These generate or inject project files.
Example:
---
to: src/routes/auth/+page.svelte
---
<h1>Hello</h1>
These are the only files in the addon action folder that generate normal project output.
Optional hidden control files.
They are:
- executed automatically after Hygen
- never copied into the user project
- useful for
npm install,pnpm add, formatting, cleanup, and post-generation actions
If you use _scripts.ejs.sh, prompt values are available through EJS:
#!/usr/bin/env bash
set -euo pipefail
npm install <%= packageName %>
echo "Configured route: <%= routeName %>"Prompt values are also exposed as environment variables:
ADDON_CONTEXT_JSONADDON_REPOADDON_GENERATORADDON_ACTIONADDON_VAR_<NAME>
Example:
#!/usr/bin/env bash
set -euo pipefail
echo "$ADDON_VAR_PROVIDER"
echo "$ADDON_CONTEXT_JSON"Use _scripts.sh when you do not need EJS interpolation.
Use _scripts.ejs.sh when you want prompt-driven values inserted directly into the script before execution.
Optional hidden config files.
They are:
- rendered automatically after Hygen
- never copied into the user project
- currently applied to the
modulesblock invite.config.ts
Important: This config is client-side. Never put secrets or server-only configuration here—it can end up in the client bundle.
For secrets and server config, use the official SvelteKit approach: $env/static/private, $env/dynamic/private, or Vite’s import.meta.env (e.g. VITE_* for public env vars only).
Use _config.ejs.json5 when you want prompt values interpolated before merge:
{
auth: {
enabled: true,
provider: "<%= provider %>",
route: "<%= route %>"
}
}Use _config.json5 when no interpolation is needed.
Optional. Language files are merged into src/i18n/<lang>/index.ts (e.g. src/i18n/en/index.ts). They can live in the action root or inside a _lang/ folder.
- In action root:
_lang.<code>.json5or_lang.<code>.<pathSuffix>.json5, e.g._lang.sk.json5,_lang.en.content.ejs.json5. - In
_lang/folder:<code>.json5or<code>.<pathSuffix>.json5, e.g.sk.json5,en.content.ejs.json5.
Use .ejs.json5 when you need prompt values interpolated (e.g. <%= routeName %>). Use $path to target a different object path in the i18n file (default is modules.<generator>). Use $remove to remove keys from the target before merging (see below).
To drop keys that are no longer used (e.g. when uninstalling an addon or deprecating strings), set $remove in the language file. It is applied to the target object before your new keys are merged. You can pass:
- Array — top-level keys to delete:
"$remove": ["oldTitle", "deprecatedLabel"] - String — single key:
"$remove": "oldTitle" - Object — nested removal: use
trueto delete a key, or a nested object to remove keys inside it
Example (remove two top-level keys and one nested key, then add/update others):
{
"$path": "modules.myAddon",
"$remove": {
"oldTitle": true,
"oldDescription": true,
"actions": { "legacySubmit": true }
},
"title": "New title",
"actions": { "submit": "Odoslať" }
}Example _lang.sk.json5 (or _lang/sk.json5):
{
"$path": "modules.myAddon",
"title": "Názov",
"description": "Popis",
"actions": {
"submit": "Odoslať",
"cancel": "Zrušiť"
}
}With EJS, e.g. _lang/en.content.ejs.json5:
{
"$path": "modules.myAddon",
"welcome": "Welcome to <%= featureName %>"
}Skip translation application with -nt or --no-translations.
The hidden config file is merged into the modules object in vite.config.ts. Remember: this is client-visible config—no secrets or server-only values (use SvelteKit $env/*/private or Vite import.meta.env instead).
Supported behavior:
- normal keys are merged into
modules $removeremoves keys$expr("...")injects a raw TypeScript expression instead of a quoted string
Example:
{
auth: {
enabled: true,
provider: "github"
},
$remove: {
legacyAuth: true
}
}That removes legacyAuth from modules and adds or updates auth.
Example:
{
auth: {
strategy: "$expr(resolveAuthStrategy())",
origin: "$expr(process.env.ORIGIN)"
}
}That is written into vite.config.ts as raw TypeScript expressions, not JSON strings.
These files are never copied into the target project:
prompt.js_scripts.ejs.sh_scripts.sh_config.ejs.json5_config.json5
Only normal Hygen templates like *.ejs.t produce project files.
auth/
install/
prompt.js
auth.config.ts.ejs.t
+page.svelte.ejs.t
_scripts.ejs.sh
_config.ejs.json5
Typical flow:
prompt.jscollects answers- Hygen renders normal templates
_scripts*runs automatically_config*is applied automatically
export default [
{
type: 'text',
name: 'provider',
message: 'Auth provider'
},
{
type: 'text',
name: 'route',
message: 'Route name',
initial: 'auth'
}
];---
to: src/lib/auth/auth.config.ts
---
export const authConfig = {
provider: "<%= provider %>",
route: "<%= route %>"
};
---
to: src/routes/<%= route %>/+page.svelte
---
<h1>Login via <%= provider %></h1>
#!/usr/bin/env bash
set -euo pipefail
npm install @auth/<%= provider %>{
auth: {
enabled: true,
provider: "<%= provider %>",
route: "<%= route %>"
},
$remove: {
legacyAuth: true
}
}Use:
npx addon owner/repo auth install --dry-runThis runs Hygen generation but skips hidden scripts and hidden config application.
Use:
npx addon owner/repo auth install --cacheThis keeps addon sources under .addon-cache/ so they do not need to be downloaded every time.
This starter is licensed under the CORE License.