@ilyeshdz/ts-create
v0.4.0
Published
type safe project scaffolding
Readme
@ilyeshdz/ts-create
Type safe project scaffolding. Define prompts, generate files, run commands, all with full type inference from end to end. Built on ts-treegen v1.
Install
npm install @ilyeshdz/ts-createQuick start
import { generator, text, confirm, select, packageJson } from "@ilyeshdz/ts-create";
import { file, dir } from "ts-treegen";
await generator({ name: "my-app" })
.prompt(text("name", "Project name", { default: "my-app" }))
.prompt(confirm("typescript", "Use TypeScript?", { default: true }))
.prompt(select("pkg", "Package manager", ["pnpm", "npm", "yarn"] as const))
.render(({ answers }) =>
dir(".", [
answers.typescript && file("tsconfig.json", "{ /* ... */ }"),
file("package.json", packageJson({ name: answers.name, devDependencies: ["typescript"] })),
file("README.md", `# ${answers.name}`),
]),
)
.cmd(({ answers }) => `${answers.pkg} install`)
.cmd("git init")
.run();answers is fully inferred: { name: string; typescript: boolean; pkg: "pnpm" | "npm" | "yarn" }. Conditional prompts become T | undefined.
API
generator({ name })
Start a builder chain.
.prompt(action, opts?)
Accumulate a prompt. Pass { when: (answers) => boolean } to make it conditional.
text("id", "Question?", { default: "value" }) // -> string
confirm("id", "Question?", { default: true }) // -> boolean
select("id", "Question?", ["a", "b"] as const) // -> "a" | "b"
multiselect("id", "Question?", ["a", "b", "c"] as const) // -> ("a" | "b" | "c")[].render(fn)
Attach file generation. Receives { answers }. Return file(), dir(), or packageJson() nodes from ts-treegen.
.cmd(command, opts?)
Register a post generation shell command.
.cmd("npm install")
.cmd(({ answers }) => `${answers.pkg} install`)
.cmd("npm install", { cwd: ({ answers }) => `./${answers.dir}` }).run(opts?)
Execute prompts, render, write files, and run commands. After completion, prints a summary with write and skip counts.
.run()
.run({ dryRun: true })
.run({ targetDir: "./output" })
.run({ overwrite: false }) // skip existing files
.run({ onSuccess: ({ answers }) => {} })
.run({ onError: ({ error, command }) => "continue" }) // skip failed commandWhen a command fails, the default behavior is to throw and abort. Pass onError returning 'continue' to skip the failed command and proceed to the next one.
packageJson(config)
Declare package.json with auto resolved dependency versions. String deps resolve to latest from the npm registry. Use { name, version } to pin.
packageJson({
name: "my-app",
dependencies: ["express"],
devDependencies: [{ name: "typescript", version: "5.7" }],
})CLI
Reverse engineer any project folder into a reusable generator.
npx ts-create <source-folder> [output-dir] [options]ts-create ./my-project ./scaffolds| Option | Description |
|--------|-------------|
| --lockfiles <list> | Comma separated lockfiles to ignore (default: pnpm-lock.yaml,yarn.lock,package-lock.json,bun.lock,bun.lockb,deno.lock) |
| --skip-hidden | Skip dotfiles and dot directories |
License
MIT
