TNTStack
Architecture

CLI Package

Scaffolding CLI. Downloads, renames, and configures TNTStack projects.

Published to npm as @tntstack/create-app. Downloads the latest source, renames all identifiers to match your project, configures Android and iOS packaging, inits git, and optionally installs dependencies.

Usage

npm create @tntstack/app@latest
pnpm create @tntstack/app@latest
yarn create @tntstack/app
bunx @tntstack/create-app

The CLI runs in interactive mode by default. You can also pass flags for CI or scripted setups:

npm create @tntstack/app@latest --name <your-project-name> --github-user <your-github-username> --no-install
FlagDescription
-n, --name <name>Project name
-d, --directory <dir>Output directory (defaults to ./<name>)
-g, --github-user <user>GitHub username or org
-i, --identifier <id>App identifier, reverse-domain (defaults to com.<name>.app)
-v, --app-version <ver>Initial version (defaults to 0.1.0)
--no-installSkip pnpm install

Directory Structure

index.ts
scaffold.ts
prompts.ts
consts.ts
package.json
tsconfig.json

How It Works

The scaffold pipeline runs 5 steps in order:

actions/clone.ts
import { downloadTemplate } from "giget";
import { TEMPLATE_SOURCE } from "../consts.js";

export async function cloneTemplate(directory: string): Promise<string> {
  const { dir } = await downloadTemplate(TEMPLATE_SOURCE, {
    dir: directory,
    force: true,
  });
  return dir;
}

Downloads the latest source from github:odest/tntstack#master using giget. No git history is carried over.

The most complex step. Walks every text file in the project and applies find-replace for these identifiers:

Search termReplaced with
com.tntstack.appUser's app identifier
tntstack_lib<project_name_snake>_lib
TNTStackPascalCase project name
odestUser's GitHub username
tntstackUser's project name

After file contents, it also:

  • Renames Android Java package directories (gen/android/**/java/com/tntstack/ → user's identifier path)
  • Renames Apple generated directories and files (gen/apple/)
  • Updates version fields in all package.json files, Cargo.toml, Cargo.lock, tauri.conf.json, and .release-please-manifest.json

Binary files, lock files, and build artifacts are skipped via pattern matching.

Removes files that only belong to the source repo:

consts.ts
export const FILES_TO_CLEAN = [
  "CHANGELOG.md",
  "packages/cli",
  ".github/FUNDING.yml",
  ".github/workflows/publish-cli.yml",
] as const;

Also removes packages/cli references from release-please-config.json and .release-please-manifest.json.

Runs git init and creates an initial commit. Skipped gracefully if git is not available.

Runs pnpm install unless --no-install was passed. Falls back with a warning if pnpm is not found.

Interactive Prompts

In interactive mode, the CLI asks these questions:

PromptDefault
Project name<your-project-name>
GitHub username / org<your-github-username>
Initial version0.1.0
Install dependencies?Yes

After answering, a summary is shown and the user can confirm or re-enter their choices.

Build & Development

The CLI is built with tsup and published independently from the monorepo:

ScriptCommand
buildtsup (bundles to dist/index.js)
devtsup --watch
startnode ./dist/index.js

The CLI has its own version (vX.Y.Z) separate from the monorepo version. It's published to npm via the publish-cli.yml GitHub Actions workflow on new releases.

On this page