@twostepsai/create-app 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,17 +5,17 @@ Scaffold a new project from the [TwoSteps Turborepo template](https://github.com
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
- pnpm create @twosteps/app my-app
8
+ pnpm create @twostepsai/app my-app
9
9
  # or
10
- npm create @twosteps/app my-app
10
+ npm create @twostepsai/app my-app
11
11
  # or
12
- yarn create @twosteps/app my-app
12
+ yarn create @twostepsai/app my-app
13
13
  ```
14
14
 
15
15
  The CLI walks you through:
16
16
 
17
17
  1. **Project name** — kebab-case, used as the target directory.
18
- 2. **Apps to include** — pick any combination of `next`, `react`, `express`, `fastapi`, `storybook`. Shared `packages/*` are always kept.
18
+ 2. **Apps to include** — pick any combination of `next`, `react`, `express`, `fastapi`, `flask`, `storybook`. Shared `packages/*` are always kept.
19
19
  3. **npm scope** — used to rename `@twosteps/*` workspace packages (e.g. `@my-app/ui`).
20
20
  4. **Initialise git** — runs `git init` and creates a first commit.
21
21
 
@@ -40,7 +40,7 @@ After the prompts the scaffolder will:
40
40
  Example for CI:
41
41
 
42
42
  ```bash
43
- pnpm create @twosteps/app my-app --apps fastapi,next --scope @my-app --no-git --yes
43
+ pnpm create @twostepsai/app my-app --apps fastapi,next --scope @my-app --no-git --yes
44
44
  ```
45
45
 
46
46
  ## Add an app later
@@ -57,7 +57,7 @@ Powered by [Turborepo generators](https://turborepo.com/docs/guides/generating-c
57
57
 
58
58
  - Node.js >= 18
59
59
  - pnpm (the scaffolded project uses pnpm workspaces)
60
- - Python >= 3.10 + uv (only if you pick the FastAPI app)
60
+ - Python >= 3.10 + uv (only if you pick the FastAPI or Flask app)
61
61
 
62
62
  ## Links
63
63
 
package/dist/index.js CHANGED
@@ -74,13 +74,14 @@ import { checkbox, confirm, input } from "@inquirer/prompts";
74
74
  import path2 from "path";
75
75
 
76
76
  // ../../packages/app-metadata/src/index.ts
77
- var ALL_APPS = ["express", "fastapi", "next", "react", "storybook"];
77
+ var ALL_APPS = ["express", "fastapi", "flask", "next", "react", "storybook"];
78
78
  var SCRIPT_ALIAS = {
79
79
  next: "web",
80
80
  react: "react",
81
81
  express: "express",
82
82
  storybook: "storybook",
83
- fastapi: "fastapi"
83
+ fastapi: "fastapi",
84
+ flask: "flask"
84
85
  };
85
86
  var SCRIPT_VERBS = ["dev", "build", "lint", "format", "check-types", "test"];
86
87
  var DIVIDER_KEYS = {
@@ -93,6 +94,7 @@ var README_APP_TITLE = {
93
94
  react: "React",
94
95
  express: "Express",
95
96
  fastapi: "FastAPI",
97
+ flask: "Flask",
96
98
  storybook: "Storybook"
97
99
  };
98
100
  function scriptKeysForApp(app) {
@@ -158,6 +160,7 @@ async function runPrompts(flags) {
158
160
  { value: "react", name: "react - React 19 + Vite SPA" },
159
161
  { value: "express", name: "express - Express 5 backend" },
160
162
  { value: "fastapi", name: "fastapi - FastAPI Python backend", checked: true },
163
+ { value: "flask", name: "flask - Flask Python backend" },
161
164
  { value: "storybook", name: "storybook - Storybook component explorer" }
162
165
  ],
163
166
  required: true
@@ -432,7 +435,7 @@ function printHelp() {
432
435
  cyan("@twostepsai/create-app") + " \u2014 scaffold a Turborepo project from the TwoSteps template",
433
436
  "",
434
437
  "Usage:",
435
- " pnpm create @twosteps/app [name] [options]",
438
+ " pnpm create @twostepsai/app [name] [options]",
436
439
  "",
437
440
  "Options:",
438
441
  ` --name <name> project name (kebab-case)`,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/clone.ts","../src/postInstall.ts","../src/prompts.ts","../../../packages/app-metadata/src/index.ts","../src/prune.ts","../src/rename.ts"],"sourcesContent":["import { ExitPromptError } from '@inquirer/core';\nimport ora from 'ora';\nimport { cyan, green, red, yellow } from 'kolorist';\nimport { clone } from './clone.js';\nimport { postInstall } from './postInstall.js';\nimport { parseAppsCsv, runPrompts } from './prompts.js';\nimport { prune } from './prune.js';\nimport { rename } from './rename.js';\nimport { ALL_APPS, type App, type CliFlags } from './types.js';\n\nfunction parseArgv(argv: string[]): CliFlags {\n const flags: CliFlags = { yes: false };\n let positionalName: string | undefined;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (!arg) continue;\n\n if (arg === '--yes' || arg === '-y') {\n flags.yes = true;\n } else if (arg === '--no-git') {\n flags.gitInit = false;\n } else if (arg === '--name') {\n flags.name = argv[++i];\n } else if (arg.startsWith('--name=')) {\n flags.name = arg.slice('--name='.length);\n } else if (arg === '--apps') {\n const value = argv[++i];\n if (value) flags.apps = parseAppsCsv(value);\n } else if (arg.startsWith('--apps=')) {\n flags.apps = parseAppsCsv(arg.slice('--apps='.length));\n } else if (arg === '--scope') {\n flags.scope = argv[++i];\n } else if (arg.startsWith('--scope=')) {\n flags.scope = arg.slice('--scope='.length);\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n } else if (!arg.startsWith('-') && !positionalName) {\n positionalName = arg;\n }\n }\n\n if (!flags.name && positionalName) flags.name = positionalName;\n return flags;\n}\n\nfunction printHelp(): void {\n const lines = [\n '',\n cyan('@twostepsai/create-app') + ' — scaffold a Turborepo project from the TwoSteps template',\n '',\n 'Usage:',\n ' pnpm create @twosteps/app [name] [options]',\n '',\n 'Options:',\n ` --name <name> project name (kebab-case)`,\n ` --apps <csv> comma-separated apps (${ALL_APPS.join(', ')})`,\n ' --scope <scope> npm scope, e.g. @my-app',\n ' --no-git skip git init',\n ' --yes, -y skip prompts; use flags or defaults',\n ' --help, -h show this help',\n '',\n ];\n console.log(lines.join('\\n'));\n}\n\nasync function main(): Promise<void> {\n const flags = parseArgv(process.argv.slice(2));\n\n console.log('\\n' + cyan('@twostepsai/create-app') + '\\n');\n\n const opts = await runPrompts(flags);\n\n const cloneSpin = ora('Cloning template').start();\n await clone(opts);\n cloneSpin.succeed('Template cloned');\n\n const removed: App[] = ALL_APPS.filter((a) => !opts.apps.includes(a));\n const pruneSpin = ora(removed.length ? `Removing unselected apps (${removed.join(', ')})` : 'No apps to remove').start();\n const warnings = await prune(opts);\n pruneSpin.succeed('Pruned scripts, services, and README');\n\n const renameSpin = ora(`Renaming @twosteps/* to ${opts.scope}/*`).start();\n await rename(opts);\n renameSpin.succeed('Renamed workspace packages');\n\n await postInstall(opts);\n\n for (const w of warnings) {\n console.log(w);\n }\n\n const next = [\n '',\n `${green('Done.')} Next steps:`,\n ` ${cyan(`cd ${opts.projectName}`)}`,\n ` ${cyan('pnpm dev')}`,\n '',\n ];\n console.log(next.join('\\n'));\n}\n\nmain().catch((err: unknown) => {\n if (err instanceof ExitPromptError) {\n console.error(yellow('\\nCancelled.'));\n process.exit(0);\n }\n const message = err instanceof Error ? err.message : String(err);\n console.error(red(`\\nFailed to scaffold project: ${message}`));\n if (process.env['DEBUG']) {\n console.error(err);\n } else {\n console.error(yellow('Set DEBUG=1 for full stack trace.'));\n }\n process.exit(1);\n});\n","import degit from 'degit';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { ScaffoldOptions } from './types.js';\n\nconst TEMPLATE_REPO = 'github:two-steps-org/turbo-repo-templates';\n\nasync function ensureTargetDirAvailable(targetDir: string): Promise<void> {\n try {\n const entries = await fs.readdir(targetDir);\n if (entries.length > 0) {\n throw new Error(`Target directory '${targetDir}' already exists and is not empty`);\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n}\n\nexport async function clone(opts: ScaffoldOptions): Promise<void> {\n await ensureTargetDirAvailable(opts.targetDir);\n\n const emitter = degit(TEMPLATE_REPO, {\n cache: false,\n force: true,\n verbose: false,\n });\n\n await emitter.clone(opts.targetDir);\n\n const toolsDir = path.join(opts.targetDir, 'tools');\n await fs.rm(toolsDir, { recursive: true, force: true });\n}\n","import { execa } from 'execa';\nimport { yellow } from 'kolorist';\nimport type { ScaffoldOptions } from './types.js';\n\nasync function runPnpmInstall(targetDir: string): Promise<void> {\n try {\n await execa('pnpm', ['install'], {\n cwd: targetDir,\n stdio: 'inherit',\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(yellow(`pnpm install did not finish cleanly: ${message}`));\n console.warn(yellow(`run 'pnpm install' manually inside ${targetDir} to retry`));\n }\n}\n\nasync function initGit(targetDir: string): Promise<void> {\n try {\n await execa('git', ['init'], { cwd: targetDir, stdio: 'pipe' });\n await execa('git', ['add', '.'], { cwd: targetDir, stdio: 'pipe' });\n await execa('git', ['commit', '-m', 'chore: initial commit from @twostepsai/create-app'], {\n cwd: targetDir,\n stdio: 'pipe',\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(yellow(`git init / first commit failed: ${message}`));\n console.warn(yellow('run git init manually inside the project to retry'));\n }\n}\n\nexport async function postInstall(opts: ScaffoldOptions): Promise<void> {\n await runPnpmInstall(opts.targetDir);\n if (opts.gitInit) {\n await initGit(opts.targetDir);\n }\n}\n","import { checkbox, confirm, input } from '@inquirer/prompts';\nimport path from 'node:path';\nimport { ALL_APPS, type App, type CliFlags, type ScaffoldOptions } from './types.js';\n\nconst KEBAB_CASE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;\nconst SCOPE_PATTERN = /^@[a-z0-9][a-z0-9-]*$/;\n\nfunction bail(message: string): never {\n console.error(message);\n process.exit(0);\n}\n\nfunction validateProjectName(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) return 'Project name is required';\n if (trimmed.includes(' ')) return 'Project name cannot contain spaces';\n if (!KEBAB_CASE.test(trimmed)) return 'Project name must be kebab-case (lowercase, numbers, dashes)';\n return true;\n}\n\nfunction validateScope(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) return 'Scope is required';\n if (!trimmed.startsWith('@')) return 'Scope must start with @';\n if (trimmed.includes('/')) return 'Scope cannot contain /';\n if (!SCOPE_PATTERN.test(trimmed)) return 'Scope must be lowercase letters, numbers, dashes after the @';\n return true;\n}\n\nexport function parseAppsCsv(csv: string): App[] {\n const parts = csv\n .split(',')\n .map((p) => p.trim().toLowerCase())\n .filter(Boolean);\n const valid: App[] = [];\n for (const p of parts) {\n if ((ALL_APPS as readonly string[]).includes(p)) {\n valid.push(p as App);\n } else {\n bail(`Unknown app '${p}'. Valid options: ${ALL_APPS.join(', ')}`);\n }\n }\n if (valid.length === 0) bail('At least one app must be selected');\n return Array.from(new Set(valid));\n}\n\nexport async function runPrompts(flags: CliFlags): Promise<ScaffoldOptions> {\n let projectName = flags.name;\n if (!projectName && !flags.yes) {\n const result = await input({\n message: 'Project name',\n validate: validateProjectName,\n });\n projectName = result.trim();\n }\n if (!projectName) bail('Project name is required (pass --name or run interactively)');\n const nameError = validateProjectName(projectName);\n if (nameError !== true) bail(nameError);\n\n let apps = flags.apps;\n if (!apps && !flags.yes) {\n apps = await checkbox<App>({\n message: 'Pick apps to include',\n choices: [\n { value: 'next', name: 'next - Next.js 16 frontend', checked: true },\n { value: 'react', name: 'react - React 19 + Vite SPA' },\n { value: 'express', name: 'express - Express 5 backend' },\n { value: 'fastapi', name: 'fastapi - FastAPI Python backend', checked: true },\n { value: 'storybook', name: 'storybook - Storybook component explorer' },\n ],\n required: true,\n });\n }\n if (!apps || apps.length === 0) {\n apps = ['next', 'fastapi'];\n }\n\n let scope = flags.scope;\n if (!scope && !flags.yes) {\n const result = await input({\n message: 'npm scope',\n default: `@${projectName}`,\n validate: validateScope,\n });\n scope = result.trim();\n }\n if (!scope) scope = `@${projectName}`;\n const scopeError = validateScope(scope);\n if (scopeError !== true) bail(scopeError);\n\n let gitInit = flags.gitInit;\n if (gitInit === undefined && !flags.yes) {\n gitInit = await confirm({\n message: 'Initialise git repository?',\n default: true,\n });\n }\n if (gitInit === undefined) gitInit = true;\n\n const targetDir = path.resolve(process.cwd(), projectName);\n\n return {\n projectName,\n targetDir,\n apps,\n scope,\n gitInit,\n };\n}\n","// Shared by `tools/create-twosteps-app/src/prune.ts` (delete path) and\n// `turbo/generators/config.ts` (add path) so the two stay in lockstep.\n\nexport type App = 'express' | 'fastapi' | 'next' | 'react' | 'storybook';\n\nexport const ALL_APPS: readonly App[] = ['express', 'fastapi', 'next', 'react', 'storybook'];\n\n// Most apps use their own name; `next` is the historical exception (alias `web`).\nexport const SCRIPT_ALIAS: Record<App, string> = {\n\tnext: 'web',\n\treact: 'react',\n\texpress: 'express',\n\tstorybook: 'storybook',\n\tfastapi: 'fastapi',\n};\n\nexport const SCRIPT_VERBS = ['dev', 'build', 'lint', 'format', 'check-types', 'test'] as const;\nexport type ScriptVerb = (typeof SCRIPT_VERBS)[number];\n\n// When every script under a divider is removed, drop the divider too so\n// orphaned section headers don't accumulate in the root package.json.\nexport const DIVIDER_KEYS: Record<string, readonly ScriptVerb[]> = {\n\tlinters: ['lint', 'format'],\n\t'check-types-typescript': ['check-types'],\n\ttesting: ['test'],\n};\n\nexport const README_APP_TITLE: Record<App, string> = {\n\tnext: 'Next.js',\n\treact: 'React',\n\texpress: 'Express',\n\tfastapi: 'FastAPI',\n\tstorybook: 'Storybook',\n};\n\nexport interface ServiceFragment {\n\tbuild: {\n\t\tcontext: string;\n\t\tdockerfile: string;\n\t};\n\tports: string[];\n\tenv_file?: string[];\n\tdepends_on?: string[];\n\trestart?: string;\n}\n\n// `null` ⇒ the app has no Dockerfile in this template; the generator skips\n// the compose mutation entirely. Default ports match each app's Dockerfile;\n// the generator bumps the host port if a collision is detected.\nexport const DOCKER_SERVICES: Record<App, ServiceFragment | null> = {\n\tfastapi: {\n\t\tbuild: {\n\t\t\tcontext: './apps/fastapi',\n\t\t\tdockerfile: 'Dockerfile',\n\t\t},\n\t\tports: ['8000:8000'],\n\t\tenv_file: ['./apps/fastapi/.env'],\n\t\trestart: 'unless-stopped',\n\t},\n\tnext: {\n\t\tbuild: {\n\t\t\tcontext: './apps/next',\n\t\t\tdockerfile: 'Dockerfile',\n\t\t},\n\t\tports: ['3000:3000'],\n\t\trestart: 'unless-stopped',\n\t},\n\texpress: null,\n\treact: null,\n\tstorybook: null,\n};\n\nexport function scriptKeysForApp(app: App): string[] {\n\tconst alias = SCRIPT_ALIAS[app];\n\tconst keys: string[] = SCRIPT_VERBS.map((verb) => `${verb}:${alias}`);\n\tif (app === 'next') keys.push('lint:web:fix');\n\treturn keys;\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { yellow } from 'kolorist';\nimport { isMap, isScalar, isSeq, parseDocument, type Scalar, type YAMLMap, type YAMLSeq } from 'yaml';\nimport { ALL_APPS, DIVIDER_KEYS, README_APP_TITLE, SCRIPT_ALIAS, SCRIPT_VERBS, scriptKeysForApp, type App } from '@twosteps/app-metadata';\nimport type { ScaffoldOptions } from './types.js';\n\nasync function removeAppDir(targetDir: string, app: App): Promise<void> {\n await fs.rm(path.join(targetDir, 'apps', app), { recursive: true, force: true });\n}\n\nasync function prunePackageJson(targetDir: string, removed: App[], kept: App[]): Promise<void> {\n const pkgPath = path.join(targetDir, 'package.json');\n const raw = await fs.readFile(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { scripts?: Record<string, string>; dependencies?: Record<string, string>; pnpm?: { overrides?: Record<string, string> } };\n const scripts = pkg.scripts ?? {};\n\n for (const app of removed) {\n for (const key of scriptKeysForApp(app)) {\n delete scripts[key];\n }\n }\n\n for (const [dividerKey, verbs] of Object.entries(DIVIDER_KEYS)) {\n const sectionHasScripts = Object.keys(scripts).some(\n (k) => k !== dividerKey && verbs.some((v) => k === v || k.startsWith(`${v}:`)),\n );\n if (!sectionHasScripts) {\n delete scripts[dividerKey];\n }\n }\n\n const reactKept = kept.includes('react');\n const nextKept = kept.includes('next');\n const storybookKept = kept.includes('storybook');\n\n if (!nextKept && !storybookKept && pkg.pnpm?.overrides) {\n delete pkg.pnpm.overrides['next'];\n }\n\n if (!reactKept && !nextKept && !storybookKept) {\n if (pkg.dependencies) {\n delete pkg.dependencies['react'];\n delete pkg.dependencies['react-dom'];\n }\n if (pkg.pnpm?.overrides) {\n delete pkg.pnpm.overrides['react'];\n delete pkg.pnpm.overrides['react-dom'];\n }\n }\n\n pkg.scripts = scripts;\n await fs.writeFile(pkgPath, JSON.stringify(pkg, null, '\\t') + '\\n', 'utf8');\n}\n\nasync function pruneDockerCompose(targetDir: string, removed: App[]): Promise<void> {\n const composePath = path.join(targetDir, 'docker-compose.yml');\n let raw: string;\n try {\n raw = await fs.readFile(composePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n\n const doc = parseDocument(raw);\n const services = doc.get('services');\n if (!isMap(services)) return;\n const serviceMap = services as YAMLMap<unknown, YAMLMap>;\n\n for (const app of removed) {\n if (serviceMap.has(app)) serviceMap.delete(app);\n }\n\n for (const item of serviceMap.items) {\n const value = item.value;\n if (!value || !isMap(value)) continue;\n const dependsOn = value.get('depends_on');\n if (!isSeq(dependsOn)) continue;\n const deps = dependsOn as YAMLSeq<Scalar<string>>;\n deps.items = deps.items.filter((dep) => !(isScalar(dep) && removed.includes(dep.value as App)));\n if (deps.items.length === 0) value.delete('depends_on');\n }\n\n if (serviceMap.items.length === 0) {\n await fs.rm(composePath, { force: true });\n return;\n }\n\n await fs.writeFile(composePath, doc.toString(), 'utf8');\n}\n\nfunction lineMatchesApp(line: string, app: App): boolean {\n const alias = SCRIPT_ALIAS[app];\n const titleWord = README_APP_TITLE[app];\n const treeRow = new RegExp(`^[│├└─\\\\s]*[├└]──\\\\s+${app}/`);\n\n if (treeRow.test(line)) return true;\n if (line.includes(`apps/${app}/`)) return true;\n if (line.includes(`apps/${app} `)) return true;\n if (line.includes(`cd apps/${app}`)) return true;\n if (line.includes(`**${titleWord}**`)) return true;\n return SCRIPT_VERBS.some((verb) => line.includes(`pnpm ${verb}:${alias}`));\n}\n\nfunction pruneReadme(content: string, removed: App[]): string {\n let out = content;\n\n for (const app of removed) {\n out = out\n .split('\\n')\n .filter((line) => !lineMatchesApp(line, app))\n .join('\\n');\n }\n\n if (removed.includes('fastapi')) {\n out = stripBackendCommandsSection(out);\n out = out.replace(/^- \\*\\*Python\\*\\*[^\\n]*\\n/m, '');\n out = out.replace(/^- \\*\\*uv\\*\\*[^\\n]*\\n/m, '');\n out = out.replace(/^# Backend \\(Python\\)[\\s\\S]*?(?=\\n#|\\n```|$)/m, '');\n }\n\n return out.replace(/\\n{3,}/g, '\\n\\n');\n}\n\nfunction stripBackendCommandsSection(content: string): string {\n const heading = '### Backend Commands';\n const start = content.indexOf(heading);\n if (start === -1) return content;\n const after = content.slice(start + heading.length);\n const nextHeadingMatch = after.match(/\\n(##\\s|###\\s)/);\n const end = nextHeadingMatch && typeof nextHeadingMatch.index === 'number' ? start + heading.length + nextHeadingMatch.index : content.length;\n return content.slice(0, start) + content.slice(end + 1);\n}\n\nasync function pruneReadmeFile(targetDir: string, removed: App[]): Promise<void> {\n const readmePath = path.join(targetDir, 'README.md');\n let raw: string;\n try {\n raw = await fs.readFile(readmePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n const updated = pruneReadme(raw, removed);\n await fs.writeFile(readmePath, updated, 'utf8');\n}\n\nasync function deleteLockfile(targetDir: string): Promise<void> {\n await fs.rm(path.join(targetDir, 'pnpm-lock.yaml'), { force: true });\n}\n\nfunction orphanWarnings(kept: App[]): string[] {\n const warnings: string[] = [];\n const hasNext = kept.includes('next');\n const hasStorybook = kept.includes('storybook');\n if (!hasNext && !hasStorybook) {\n warnings.push('@twosteps/ui has no consumer in your selection');\n }\n if (!hasStorybook) {\n warnings.push('@twosteps/shared has no consumer in your selection');\n warnings.push('@twosteps/styles has no consumer in your selection');\n }\n return warnings;\n}\n\nexport async function prune(opts: ScaffoldOptions): Promise<string[]> {\n const removed = ALL_APPS.filter((a) => !opts.apps.includes(a));\n\n for (const app of removed) {\n await removeAppDir(opts.targetDir, app);\n }\n\n await prunePackageJson(opts.targetDir, removed, opts.apps);\n await pruneDockerCompose(opts.targetDir, removed);\n await pruneReadmeFile(opts.targetDir, removed);\n await deleteLockfile(opts.targetDir);\n\n const warnings = orphanWarnings(opts.apps).map((w) => yellow(`heads up: ${w} — remove it manually if you want a leaner repo`));\n return warnings;\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { ScaffoldOptions } from './types.js';\n\nconst RENAMABLE_EXTENSIONS = new Set(['.json', '.jsonc', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.md', '.yaml', '.yml']);\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', '.next', '.turbo', '.venv', '__pycache__', 'build']);\nconst OLD_NAMESPACE = '@twosteps';\n\nasync function* walk(dir: string): AsyncGenerator<string> {\n let entries: import('node:fs').Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n yield* walk(full);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n}\n\nasync function rewriteRootPackageName(targetDir: string, scope: string): Promise<void> {\n const pkgPath = path.join(targetDir, 'package.json');\n const raw = await fs.readFile(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { name?: string };\n pkg.name = `${scope}/root`;\n await fs.writeFile(pkgPath, JSON.stringify(pkg, null, '\\t') + '\\n', 'utf8');\n}\n\nexport async function rename(opts: ScaffoldOptions): Promise<void> {\n const newNamespace = opts.scope;\n for await (const file of walk(opts.targetDir)) {\n const ext = path.extname(file).toLowerCase();\n if (!RENAMABLE_EXTENSIONS.has(ext)) continue;\n const original = await fs.readFile(file, 'utf8');\n if (!original.includes(OLD_NAMESPACE)) continue;\n const updated = original.split(OLD_NAMESPACE).join(newNamespace);\n if (updated !== original) {\n await fs.writeFile(file, updated, 'utf8');\n }\n }\n\n await rewriteRootPackageName(opts.targetDir, newNamespace);\n}\n"],"mappings":"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","names":["yellow","path","path","fs","path","yellow","fs","path","yellow","fs","path","yellow"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/clone.ts","../src/postInstall.ts","../src/prompts.ts","../../../packages/app-metadata/src/index.ts","../src/prune.ts","../src/rename.ts"],"sourcesContent":["import { ExitPromptError } from '@inquirer/core';\nimport ora from 'ora';\nimport { cyan, green, red, yellow } from 'kolorist';\nimport { clone } from './clone.js';\nimport { postInstall } from './postInstall.js';\nimport { parseAppsCsv, runPrompts } from './prompts.js';\nimport { prune } from './prune.js';\nimport { rename } from './rename.js';\nimport { ALL_APPS, type App, type CliFlags } from './types.js';\n\nfunction parseArgv(argv: string[]): CliFlags {\n const flags: CliFlags = { yes: false };\n let positionalName: string | undefined;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (!arg) continue;\n\n if (arg === '--yes' || arg === '-y') {\n flags.yes = true;\n } else if (arg === '--no-git') {\n flags.gitInit = false;\n } else if (arg === '--name') {\n flags.name = argv[++i];\n } else if (arg.startsWith('--name=')) {\n flags.name = arg.slice('--name='.length);\n } else if (arg === '--apps') {\n const value = argv[++i];\n if (value) flags.apps = parseAppsCsv(value);\n } else if (arg.startsWith('--apps=')) {\n flags.apps = parseAppsCsv(arg.slice('--apps='.length));\n } else if (arg === '--scope') {\n flags.scope = argv[++i];\n } else if (arg.startsWith('--scope=')) {\n flags.scope = arg.slice('--scope='.length);\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n } else if (!arg.startsWith('-') && !positionalName) {\n positionalName = arg;\n }\n }\n\n if (!flags.name && positionalName) flags.name = positionalName;\n return flags;\n}\n\nfunction printHelp(): void {\n const lines = [\n '',\n cyan('@twostepsai/create-app') + ' — scaffold a Turborepo project from the TwoSteps template',\n '',\n 'Usage:',\n ' pnpm create @twostepsai/app [name] [options]',\n '',\n 'Options:',\n ` --name <name> project name (kebab-case)`,\n ` --apps <csv> comma-separated apps (${ALL_APPS.join(', ')})`,\n ' --scope <scope> npm scope, e.g. @my-app',\n ' --no-git skip git init',\n ' --yes, -y skip prompts; use flags or defaults',\n ' --help, -h show this help',\n '',\n ];\n console.log(lines.join('\\n'));\n}\n\nasync function main(): Promise<void> {\n const flags = parseArgv(process.argv.slice(2));\n\n console.log('\\n' + cyan('@twostepsai/create-app') + '\\n');\n\n const opts = await runPrompts(flags);\n\n const cloneSpin = ora('Cloning template').start();\n await clone(opts);\n cloneSpin.succeed('Template cloned');\n\n const removed: App[] = ALL_APPS.filter((a) => !opts.apps.includes(a));\n const pruneSpin = ora(removed.length ? `Removing unselected apps (${removed.join(', ')})` : 'No apps to remove').start();\n const warnings = await prune(opts);\n pruneSpin.succeed('Pruned scripts, services, and README');\n\n const renameSpin = ora(`Renaming @twosteps/* to ${opts.scope}/*`).start();\n await rename(opts);\n renameSpin.succeed('Renamed workspace packages');\n\n await postInstall(opts);\n\n for (const w of warnings) {\n console.log(w);\n }\n\n const next = [\n '',\n `${green('Done.')} Next steps:`,\n ` ${cyan(`cd ${opts.projectName}`)}`,\n ` ${cyan('pnpm dev')}`,\n '',\n ];\n console.log(next.join('\\n'));\n}\n\nmain().catch((err: unknown) => {\n if (err instanceof ExitPromptError) {\n console.error(yellow('\\nCancelled.'));\n process.exit(0);\n }\n const message = err instanceof Error ? err.message : String(err);\n console.error(red(`\\nFailed to scaffold project: ${message}`));\n if (process.env['DEBUG']) {\n console.error(err);\n } else {\n console.error(yellow('Set DEBUG=1 for full stack trace.'));\n }\n process.exit(1);\n});\n","import degit from 'degit';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { ScaffoldOptions } from './types.js';\n\nconst TEMPLATE_REPO = 'github:two-steps-org/turbo-repo-templates';\n\nasync function ensureTargetDirAvailable(targetDir: string): Promise<void> {\n try {\n const entries = await fs.readdir(targetDir);\n if (entries.length > 0) {\n throw new Error(`Target directory '${targetDir}' already exists and is not empty`);\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n}\n\nexport async function clone(opts: ScaffoldOptions): Promise<void> {\n await ensureTargetDirAvailable(opts.targetDir);\n\n const emitter = degit(TEMPLATE_REPO, {\n cache: false,\n force: true,\n verbose: false,\n });\n\n await emitter.clone(opts.targetDir);\n\n const toolsDir = path.join(opts.targetDir, 'tools');\n await fs.rm(toolsDir, { recursive: true, force: true });\n}\n","import { execa } from 'execa';\nimport { yellow } from 'kolorist';\nimport type { ScaffoldOptions } from './types.js';\n\nasync function runPnpmInstall(targetDir: string): Promise<void> {\n try {\n await execa('pnpm', ['install'], {\n cwd: targetDir,\n stdio: 'inherit',\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(yellow(`pnpm install did not finish cleanly: ${message}`));\n console.warn(yellow(`run 'pnpm install' manually inside ${targetDir} to retry`));\n }\n}\n\nasync function initGit(targetDir: string): Promise<void> {\n try {\n await execa('git', ['init'], { cwd: targetDir, stdio: 'pipe' });\n await execa('git', ['add', '.'], { cwd: targetDir, stdio: 'pipe' });\n await execa('git', ['commit', '-m', 'chore: initial commit from @twostepsai/create-app'], {\n cwd: targetDir,\n stdio: 'pipe',\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(yellow(`git init / first commit failed: ${message}`));\n console.warn(yellow('run git init manually inside the project to retry'));\n }\n}\n\nexport async function postInstall(opts: ScaffoldOptions): Promise<void> {\n await runPnpmInstall(opts.targetDir);\n if (opts.gitInit) {\n await initGit(opts.targetDir);\n }\n}\n","import { checkbox, confirm, input } from '@inquirer/prompts';\nimport path from 'node:path';\nimport { ALL_APPS, type App, type CliFlags, type ScaffoldOptions } from './types.js';\n\nconst KEBAB_CASE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;\nconst SCOPE_PATTERN = /^@[a-z0-9][a-z0-9-]*$/;\n\nfunction bail(message: string): never {\n console.error(message);\n process.exit(0);\n}\n\nfunction validateProjectName(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) return 'Project name is required';\n if (trimmed.includes(' ')) return 'Project name cannot contain spaces';\n if (!KEBAB_CASE.test(trimmed)) return 'Project name must be kebab-case (lowercase, numbers, dashes)';\n return true;\n}\n\nfunction validateScope(value: string): true | string {\n const trimmed = value.trim();\n if (!trimmed) return 'Scope is required';\n if (!trimmed.startsWith('@')) return 'Scope must start with @';\n if (trimmed.includes('/')) return 'Scope cannot contain /';\n if (!SCOPE_PATTERN.test(trimmed)) return 'Scope must be lowercase letters, numbers, dashes after the @';\n return true;\n}\n\nexport function parseAppsCsv(csv: string): App[] {\n const parts = csv\n .split(',')\n .map((p) => p.trim().toLowerCase())\n .filter(Boolean);\n const valid: App[] = [];\n for (const p of parts) {\n if ((ALL_APPS as readonly string[]).includes(p)) {\n valid.push(p as App);\n } else {\n bail(`Unknown app '${p}'. Valid options: ${ALL_APPS.join(', ')}`);\n }\n }\n if (valid.length === 0) bail('At least one app must be selected');\n return Array.from(new Set(valid));\n}\n\nexport async function runPrompts(flags: CliFlags): Promise<ScaffoldOptions> {\n let projectName = flags.name;\n if (!projectName && !flags.yes) {\n const result = await input({\n message: 'Project name',\n validate: validateProjectName,\n });\n projectName = result.trim();\n }\n if (!projectName) bail('Project name is required (pass --name or run interactively)');\n const nameError = validateProjectName(projectName);\n if (nameError !== true) bail(nameError);\n\n let apps = flags.apps;\n if (!apps && !flags.yes) {\n apps = await checkbox<App>({\n message: 'Pick apps to include',\n choices: [\n { value: 'next', name: 'next - Next.js 16 frontend', checked: true },\n { value: 'react', name: 'react - React 19 + Vite SPA' },\n { value: 'express', name: 'express - Express 5 backend' },\n { value: 'fastapi', name: 'fastapi - FastAPI Python backend', checked: true },\n { value: 'flask', name: 'flask - Flask Python backend' },\n { value: 'storybook', name: 'storybook - Storybook component explorer' },\n ],\n required: true,\n });\n }\n if (!apps || apps.length === 0) {\n apps = ['next', 'fastapi'];\n }\n\n let scope = flags.scope;\n if (!scope && !flags.yes) {\n const result = await input({\n message: 'npm scope',\n default: `@${projectName}`,\n validate: validateScope,\n });\n scope = result.trim();\n }\n if (!scope) scope = `@${projectName}`;\n const scopeError = validateScope(scope);\n if (scopeError !== true) bail(scopeError);\n\n let gitInit = flags.gitInit;\n if (gitInit === undefined && !flags.yes) {\n gitInit = await confirm({\n message: 'Initialise git repository?',\n default: true,\n });\n }\n if (gitInit === undefined) gitInit = true;\n\n const targetDir = path.resolve(process.cwd(), projectName);\n\n return {\n projectName,\n targetDir,\n apps,\n scope,\n gitInit,\n };\n}\n","// Shared by `tools/create-twosteps-app/src/prune.ts` (delete path) and\n// `turbo/generators/config.ts` (add path) so the two stay in lockstep.\n\nexport type App = 'express' | 'fastapi' | 'flask' | 'next' | 'react' | 'storybook';\n\nexport const ALL_APPS: readonly App[] = ['express', 'fastapi', 'flask', 'next', 'react', 'storybook'];\n\n// Most apps use their own name; `next` is the historical exception (alias `web`).\nexport const SCRIPT_ALIAS: Record<App, string> = {\n\tnext: 'web',\n\treact: 'react',\n\texpress: 'express',\n\tstorybook: 'storybook',\n\tfastapi: 'fastapi',\n\tflask: 'flask',\n};\n\nexport const SCRIPT_VERBS = ['dev', 'build', 'lint', 'format', 'check-types', 'test'] as const;\nexport type ScriptVerb = (typeof SCRIPT_VERBS)[number];\n\n// When every script under a divider is removed, drop the divider too so\n// orphaned section headers don't accumulate in the root package.json.\nexport const DIVIDER_KEYS: Record<string, readonly ScriptVerb[]> = {\n\tlinters: ['lint', 'format'],\n\t'check-types-typescript': ['check-types'],\n\ttesting: ['test'],\n};\n\nexport const README_APP_TITLE: Record<App, string> = {\n\tnext: 'Next.js',\n\treact: 'React',\n\texpress: 'Express',\n\tfastapi: 'FastAPI',\n\tflask: 'Flask',\n\tstorybook: 'Storybook',\n};\n\nexport interface ServiceFragment {\n\tbuild: {\n\t\tcontext: string;\n\t\tdockerfile: string;\n\t};\n\tports: string[];\n\tenv_file?: string[];\n\tdepends_on?: string[];\n\trestart?: string;\n}\n\n// `null` ⇒ the app has no Dockerfile in this template; the generator skips\n// the compose mutation entirely. Default ports match each app's Dockerfile;\n// the generator bumps the host port if a collision is detected.\nexport const DOCKER_SERVICES: Record<App, ServiceFragment | null> = {\n\tfastapi: {\n\t\tbuild: {\n\t\t\tcontext: './apps/fastapi',\n\t\t\tdockerfile: 'Dockerfile',\n\t\t},\n\t\tports: ['8000:8000'],\n\t\tenv_file: ['./apps/fastapi/.env'],\n\t\trestart: 'unless-stopped',\n\t},\n\tflask: {\n\t\tbuild: {\n\t\t\tcontext: './apps/flask',\n\t\t\tdockerfile: 'Dockerfile',\n\t\t},\n\t\tports: ['5001:5001'],\n\t\tenv_file: ['./apps/flask/.env'],\n\t\trestart: 'unless-stopped',\n\t},\n\tnext: {\n\t\tbuild: {\n\t\t\tcontext: './apps/next',\n\t\t\tdockerfile: 'Dockerfile',\n\t\t},\n\t\tports: ['3000:3000'],\n\t\trestart: 'unless-stopped',\n\t},\n\texpress: null,\n\treact: null,\n\tstorybook: null,\n};\n\nexport function scriptKeysForApp(app: App): string[] {\n\tconst alias = SCRIPT_ALIAS[app];\n\tconst keys: string[] = SCRIPT_VERBS.map((verb) => `${verb}:${alias}`);\n\tif (app === 'next') keys.push('lint:web:fix');\n\treturn keys;\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { yellow } from 'kolorist';\nimport { isMap, isScalar, isSeq, parseDocument, type Scalar, type YAMLMap, type YAMLSeq } from 'yaml';\nimport { ALL_APPS, DIVIDER_KEYS, README_APP_TITLE, SCRIPT_ALIAS, SCRIPT_VERBS, scriptKeysForApp, type App } from '@twosteps/app-metadata';\nimport type { ScaffoldOptions } from './types.js';\n\nasync function removeAppDir(targetDir: string, app: App): Promise<void> {\n await fs.rm(path.join(targetDir, 'apps', app), { recursive: true, force: true });\n}\n\nasync function prunePackageJson(targetDir: string, removed: App[], kept: App[]): Promise<void> {\n const pkgPath = path.join(targetDir, 'package.json');\n const raw = await fs.readFile(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { scripts?: Record<string, string>; dependencies?: Record<string, string>; pnpm?: { overrides?: Record<string, string> } };\n const scripts = pkg.scripts ?? {};\n\n for (const app of removed) {\n for (const key of scriptKeysForApp(app)) {\n delete scripts[key];\n }\n }\n\n for (const [dividerKey, verbs] of Object.entries(DIVIDER_KEYS)) {\n const sectionHasScripts = Object.keys(scripts).some(\n (k) => k !== dividerKey && verbs.some((v) => k === v || k.startsWith(`${v}:`)),\n );\n if (!sectionHasScripts) {\n delete scripts[dividerKey];\n }\n }\n\n const reactKept = kept.includes('react');\n const nextKept = kept.includes('next');\n const storybookKept = kept.includes('storybook');\n\n if (!nextKept && !storybookKept && pkg.pnpm?.overrides) {\n delete pkg.pnpm.overrides['next'];\n }\n\n if (!reactKept && !nextKept && !storybookKept) {\n if (pkg.dependencies) {\n delete pkg.dependencies['react'];\n delete pkg.dependencies['react-dom'];\n }\n if (pkg.pnpm?.overrides) {\n delete pkg.pnpm.overrides['react'];\n delete pkg.pnpm.overrides['react-dom'];\n }\n }\n\n pkg.scripts = scripts;\n await fs.writeFile(pkgPath, JSON.stringify(pkg, null, '\\t') + '\\n', 'utf8');\n}\n\nasync function pruneDockerCompose(targetDir: string, removed: App[]): Promise<void> {\n const composePath = path.join(targetDir, 'docker-compose.yml');\n let raw: string;\n try {\n raw = await fs.readFile(composePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n\n const doc = parseDocument(raw);\n const services = doc.get('services');\n if (!isMap(services)) return;\n const serviceMap = services as YAMLMap<unknown, YAMLMap>;\n\n for (const app of removed) {\n if (serviceMap.has(app)) serviceMap.delete(app);\n }\n\n for (const item of serviceMap.items) {\n const value = item.value;\n if (!value || !isMap(value)) continue;\n const dependsOn = value.get('depends_on');\n if (!isSeq(dependsOn)) continue;\n const deps = dependsOn as YAMLSeq<Scalar<string>>;\n deps.items = deps.items.filter((dep) => !(isScalar(dep) && removed.includes(dep.value as App)));\n if (deps.items.length === 0) value.delete('depends_on');\n }\n\n if (serviceMap.items.length === 0) {\n await fs.rm(composePath, { force: true });\n return;\n }\n\n await fs.writeFile(composePath, doc.toString(), 'utf8');\n}\n\nfunction lineMatchesApp(line: string, app: App): boolean {\n const alias = SCRIPT_ALIAS[app];\n const titleWord = README_APP_TITLE[app];\n const treeRow = new RegExp(`^[│├└─\\\\s]*[├└]──\\\\s+${app}/`);\n\n if (treeRow.test(line)) return true;\n if (line.includes(`apps/${app}/`)) return true;\n if (line.includes(`apps/${app} `)) return true;\n if (line.includes(`cd apps/${app}`)) return true;\n if (line.includes(`**${titleWord}**`)) return true;\n return SCRIPT_VERBS.some((verb) => line.includes(`pnpm ${verb}:${alias}`));\n}\n\nfunction pruneReadme(content: string, removed: App[]): string {\n let out = content;\n\n for (const app of removed) {\n out = out\n .split('\\n')\n .filter((line) => !lineMatchesApp(line, app))\n .join('\\n');\n }\n\n if (removed.includes('fastapi')) {\n out = stripBackendCommandsSection(out);\n out = out.replace(/^- \\*\\*Python\\*\\*[^\\n]*\\n/m, '');\n out = out.replace(/^- \\*\\*uv\\*\\*[^\\n]*\\n/m, '');\n out = out.replace(/^# Backend \\(Python\\)[\\s\\S]*?(?=\\n#|\\n```|$)/m, '');\n }\n\n return out.replace(/\\n{3,}/g, '\\n\\n');\n}\n\nfunction stripBackendCommandsSection(content: string): string {\n const heading = '### Backend Commands';\n const start = content.indexOf(heading);\n if (start === -1) return content;\n const after = content.slice(start + heading.length);\n const nextHeadingMatch = after.match(/\\n(##\\s|###\\s)/);\n const end = nextHeadingMatch && typeof nextHeadingMatch.index === 'number' ? start + heading.length + nextHeadingMatch.index : content.length;\n return content.slice(0, start) + content.slice(end + 1);\n}\n\nasync function pruneReadmeFile(targetDir: string, removed: App[]): Promise<void> {\n const readmePath = path.join(targetDir, 'README.md');\n let raw: string;\n try {\n raw = await fs.readFile(readmePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n const updated = pruneReadme(raw, removed);\n await fs.writeFile(readmePath, updated, 'utf8');\n}\n\nasync function deleteLockfile(targetDir: string): Promise<void> {\n await fs.rm(path.join(targetDir, 'pnpm-lock.yaml'), { force: true });\n}\n\nfunction orphanWarnings(kept: App[]): string[] {\n const warnings: string[] = [];\n const hasNext = kept.includes('next');\n const hasStorybook = kept.includes('storybook');\n if (!hasNext && !hasStorybook) {\n warnings.push('@twosteps/ui has no consumer in your selection');\n }\n if (!hasStorybook) {\n warnings.push('@twosteps/shared has no consumer in your selection');\n warnings.push('@twosteps/styles has no consumer in your selection');\n }\n return warnings;\n}\n\nexport async function prune(opts: ScaffoldOptions): Promise<string[]> {\n const removed = ALL_APPS.filter((a) => !opts.apps.includes(a));\n\n for (const app of removed) {\n await removeAppDir(opts.targetDir, app);\n }\n\n await prunePackageJson(opts.targetDir, removed, opts.apps);\n await pruneDockerCompose(opts.targetDir, removed);\n await pruneReadmeFile(opts.targetDir, removed);\n await deleteLockfile(opts.targetDir);\n\n const warnings = orphanWarnings(opts.apps).map((w) => yellow(`heads up: ${w} — remove it manually if you want a leaner repo`));\n return warnings;\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { ScaffoldOptions } from './types.js';\n\nconst RENAMABLE_EXTENSIONS = new Set(['.json', '.jsonc', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.md', '.yaml', '.yml']);\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', '.next', '.turbo', '.venv', '__pycache__', 'build']);\nconst OLD_NAMESPACE = '@twosteps';\n\nasync function* walk(dir: string): AsyncGenerator<string> {\n let entries: import('node:fs').Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw err;\n }\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n yield* walk(full);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n}\n\nasync function rewriteRootPackageName(targetDir: string, scope: string): Promise<void> {\n const pkgPath = path.join(targetDir, 'package.json');\n const raw = await fs.readFile(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { name?: string };\n pkg.name = `${scope}/root`;\n await fs.writeFile(pkgPath, JSON.stringify(pkg, null, '\\t') + '\\n', 'utf8');\n}\n\nexport async function rename(opts: ScaffoldOptions): Promise<void> {\n const newNamespace = opts.scope;\n for await (const file of walk(opts.targetDir)) {\n const ext = path.extname(file).toLowerCase();\n if (!RENAMABLE_EXTENSIONS.has(ext)) continue;\n const original = await fs.readFile(file, 'utf8');\n if (!original.includes(OLD_NAMESPACE)) continue;\n const updated = original.split(OLD_NAMESPACE).join(newNamespace);\n if (updated !== original) {\n await fs.writeFile(file, updated, 'utf8');\n }\n }\n\n await rewriteRootPackageName(opts.targetDir, newNamespace);\n}\n"],"mappings":"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","names":["yellow","path","path","fs","path","yellow","fs","path","yellow","fs","path","yellow"]}
package/package.json CHANGED
@@ -1,66 +1,66 @@
1
1
  {
2
- "name": "@twostepsai/create-app",
3
- "version": "0.0.1",
4
- "description": "Scaffold a new project from the TwoSteps Turborepo template — pick which apps to include.",
5
- "type": "module",
6
- "bin": {
7
- "create-app": "./dist/index.js"
8
- },
9
- "files": [
10
- "dist"
11
- ],
12
- "scripts": {
13
- "build": "tsup",
14
- "dev": "tsup --watch",
15
- "typecheck": "tsc --noEmit",
16
- "version:add": "bumpit add",
17
- "version:status": "bumpit status",
18
- "version:bump": "bumpit bump"
19
- },
20
- "engines": {
21
- "node": ">=18"
22
- },
23
- "keywords": [
24
- "create",
25
- "scaffold",
26
- "turborepo",
27
- "template",
28
- "monorepo",
29
- "next",
30
- "react",
31
- "express",
32
- "fastapi",
33
- "storybook"
34
- ],
35
- "license": "MIT",
36
- "author": "two-steps-org",
37
- "repository": {
38
- "type": "git",
39
- "url": "git+https://github.com/two-steps-org/turbo-repo-templates.git",
40
- "directory": "tools/create-twosteps-app"
41
- },
42
- "bugs": {
43
- "url": "https://github.com/two-steps-org/turbo-repo-templates/issues"
44
- },
45
- "homepage": "https://github.com/two-steps-org/turbo-repo-templates/tree/main/tools/create-twosteps-app#readme",
46
- "publishConfig": {
47
- "access": "public"
48
- },
49
- "dependencies": {
50
- "@inquirer/core": "^11.1.9",
51
- "@inquirer/prompts": "^8.4.2",
52
- "degit": "^2.8.4",
53
- "execa": "^9.5.1",
54
- "kolorist": "^1.8.0",
55
- "ora": "^9.4.0",
56
- "yaml": "^2.6.0"
57
- },
58
- "devDependencies": {
59
- "@twosteps/app-metadata": "workspace:*",
60
- "@twostepsai/bumpit": "^0.2.1",
61
- "@types/degit": "^2.8.6",
62
- "@types/node": "^22.10.0",
63
- "tsup": "^8.3.0",
64
- "typescript": "^5.6.0"
65
- }
66
- }
2
+ "name": "@twostepsai/create-app",
3
+ "version": "0.0.2",
4
+ "description": "Scaffold a new project from the TwoSteps Turborepo template — pick which apps to include.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-app": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "keywords": [
16
+ "create",
17
+ "scaffold",
18
+ "turborepo",
19
+ "template",
20
+ "monorepo",
21
+ "next",
22
+ "react",
23
+ "express",
24
+ "fastapi",
25
+ "storybook"
26
+ ],
27
+ "license": "MIT",
28
+ "author": "two-steps-org",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/two-steps-org/turbo-repo-templates.git",
32
+ "directory": "tools/create-twosteps-app"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/two-steps-org/turbo-repo-templates/issues"
36
+ },
37
+ "homepage": "https://github.com/two-steps-org/turbo-repo-templates/tree/main/tools/create-twosteps-app#readme",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "@inquirer/core": "^11.1.9",
43
+ "@inquirer/prompts": "^8.4.2",
44
+ "degit": "^2.8.4",
45
+ "execa": "^9.5.1",
46
+ "kolorist": "^1.8.0",
47
+ "ora": "^9.4.0",
48
+ "yaml": "^2.6.0"
49
+ },
50
+ "devDependencies": {
51
+ "@twostepsai/bumpit": "^0.2.1",
52
+ "@types/degit": "^2.8.6",
53
+ "@types/node": "^22.10.0",
54
+ "tsup": "^8.3.0",
55
+ "typescript": "^5.6.0",
56
+ "@twosteps/app-metadata": "0.0.0"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "typecheck": "tsc --noEmit",
62
+ "version:add": "bumpit add",
63
+ "version:status": "bumpit status",
64
+ "version:bump": "bumpit bump"
65
+ }
66
+ }