Skip to content

Latest commit

 

History

History
335 lines (243 loc) · 10.4 KB

File metadata and controls

335 lines (243 loc) · 10.4 KB

create-migracode-app

CLI scaffolder for MigraCode bootcamp final projects.

Scaffolds a fullstack monorepo with a Vite frontend and an Express backend, with optional TypeScript, React, TanStack Router, CSS Modules or Tailwind CSS, Vitest, PostgreSQL, and GitHub Actions — all wired up and ready to run.


Table of contents


Using the CLI (students)

Students run the tool with a single command — no cloning, no setup:

npx github:Migracode-Barcelona/create-migracode-app

The wizard asks a series of questions one at a time:

# Question Effect
1 Project name Creates a new folder with this name
2 TypeScript? All files use .ts/.tsx; tsconfig.json added to both apps
3 React? Adds React 19 and @vitejs/plugin-react
4 CSS approach? (only shown if React = yes) CSS Modules (scoped .module.css files) or Tailwind CSS (utility classes + @theme design tokens)
5 TanStack Router? (only shown if React = yes) File-based routing with __root, index, about, users routes
6 Vitest? Test setup and example tests for both frontend and backend
7 PostgreSQL? docker-compose.yml, connection pool, migration script, seeded demo data
8 GitHub Actions? .github/workflows/ci.yml that installs, lints, tests, and builds

After answering, a summary is shown and the student confirms before anything is written to disk.


Repository structure

create-migracode-app/
├── src/
│   ├── index.js              ← CLI entry point: asks questions, shows summary
│   ├── generator.js          ← Orchestrator: decides which files to write and where
│   ├── utils.js              ← write() helper, log() helper, version constants (V)
│   └── templates/
│       ├── packageJsons.js   ← root / frontend / backend package.json generators
│       ├── configs.js        ← vite.config, tsconfig (frontend + backend)
│       ├── frontend.js       ← all frontend source files (main, App, routes, components, tests)
│       ├── backend.js        ← all backend source files (index, routes, services, db, tests)
│       ├── infra.js          ← docker-compose, .env.example, oxfmtrc, oxlintrc, .gitignore, CI workflow
│       └── readme.js         ← the README that gets written into generated projects
├── scripts/
│   └── test-generate.js      ← generates all combos non-interactively into test-output/
├── test-output/              ← gitignored; created by npm run test:generate
├── .gitignore
├── package.json
└── README.md                 ← this file

Data flow:

index.js  →  prompts user  →  builds opts object
    ↓
generator.js  →  decides which files to create based on opts
    ↓
templates/*.js  →  return file content as strings
    ↓
utils.write()  →  writes string to disk (creates dirs automatically)

Local development

Running the CLI from source

git clone https://github.com/Migracode-Barcelona/create-migracode-app.git
cd create-migracode-app
npm install
npm run dev

npm run dev runs node src/index.js directly — no build step, no packing, no linking needed. The interactive wizard starts immediately. cd somewhere sensible before running so the generated project lands in the right place:

cd /tmp
node ~/code/create-migracode-app/src/index.js
# → project gets created at /tmp/<your-project-name>

Testing generated output without prompts

For rapid iteration — when you've changed a template and want to see the result immediately without answering questions — use the test script:

npm run test:generate

This generates 7 representative combinations into test-output/ (which is gitignored) and reports pass/fail for each:

🧪 create-migracode-app — test:generate

  full-ts-react-router-vitest-postgres-ci   ✔ 31 files
  ts-react-vitest-ci                        ✔ 23 files
  ts-react-router-postgres                  ✔ 25 files
  ts-react-router-tailwind-vitest           ✔ 27 files
  js-react-vitest                           ✔ 20 files
  js-minimal                                ✔ 12 files
  ts-minimal                                ✔ 17 files

Results: 7 passed

Add --files to also print every file path generated by each combo:

npm run test:generate -- --files

The combos are defined at the top of scripts/test-generate.js — add new ones there when you add new features that need coverage.

Inspecting a generated project

After npm run test:generate, the output sits in test-output/. You can open any file directly:

cat test-output/full-ts-react-router-vitest-postgres-ci/frontend/vite.config.ts
cat test-output/js-minimal/backend/src/services/users.js

Or install and actually run a generated project to confirm it boots:

cd test-output/ts-react-vitest-ci
npm run dev
# frontend → http://localhost:5173
# backend  → http://localhost:3000/api/health

(The generator already runs npm install in all workspaces during generation, so you can start immediately.)

Run the tests if Vitest was included:

cd frontend && npm test
cd ../backend && npm test

How to make changes

Changing a generated file's content

Every generated file is a template function in one of the src/templates/ files. Find the relevant function, edit the returned string, then run npm run test:generate to see the result.

Example: change something in the backend entry point

Open src/templates/backend.js, find backendIndex, edit the string it returns, then verify:

npm run test:generate
cat test-output/ts-react-vitest-ci/backend/src/index.ts

Adding a new question

Step 1 — add the prompts call in src/index.js after the existing questions. Use select type (not confirm) so the student must make an active choice:

const { useEslint } = await prompts(
  {
    type: 'select',
    name: 'useEslint',
    message: 'Add ESLint?',
    choices: [
      { title: 'Yes', value: true },
      { title: 'No',  value: false },
    ],
  },
  { onCancel }
)

Step 2 — add it to the opts object (still in src/index.js):

const opts = {
  // ...existing fields
  useEslint,
}

Step 3 — add a line to the summary block so it appears in the confirmation screen:

`  ${kleur.gray('ESLint:')}  ${flag(opts.useEslint)}`

Step 4 — use opts.useEslint in src/generator.js and relevant templates to conditionally write files or add dependencies.

Step 5 — add at least one combo that tests the new flag to scripts/test-generate.js, then confirm it passes:

npm run test:generate

Adding a new generated file

Step 1 — write a template function in the appropriate src/templates/ file:

export function myNewUtil(opts) {
  const { useTypeScript } = opts
  return `// src/utils/logger.${useTypeScript ? 'ts' : 'js'}
export function log(msg${useTypeScript ? ': string' : ''}) {
  console.log('[app]', msg)
}
`
}

Step 2 — import it in src/generator.js and call write() inside generateProject():

import { myNewUtil } from './templates/backend.js'

// inside generateProject():
const ext = ts ? 'ts' : 'js'
write(path.join(targetDir, 'backend', `src/utils/logger.${ext}`), myNewUtil(opts))
log(`backend/src/utils/logger.${ext}`)

write() creates parent directories automatically.

Step 3 — run npm run test:generate -- --files and confirm the file appears in the output.

Updating dependency versions

All version strings live in one place: the V object in src/utils.js:

export const V = {
  vite: '^8.0.16',
  react: '^19.2.7',
  // ...
}

Change the string here and it propagates to every package.json that gets generated.

To find what's outdated, check inside a generated project:

npm run test:generate
cd test-output/full-ts-react-router-vitest-postgres-ci/frontend
npm outdated

Releasing a new version

This package is installed directly from GitHub — no npm registry involved. Students always get the latest version of main when they run npx github:Migracode-Barcelona/create-migracode-app.

To release:

  1. Make your changes and verify them:

    npm run test:generate
  2. Commit and push to main:

    git add -A
    git commit -m "your change description"
    git push

That's it. The next time anyone runs npx github:Migracode-Barcelona/create-migracode-app, they get the updated version.

Note: npx caches packages locally. If a student gets stale output after an update, they can force a fresh fetch:

npx --yes github:Migracode-Barcelona/create-migracode-app

Or clear the npx cache entirely: rm -rf ~/.npm/_npx


Troubleshooting

The CLI runs but creates files in the wrong directory

src/index.js resolves the target dir relative to process.cwd(). Make sure you cd to where you want the project created before running.

npm run test:generate fails with "Directory already exists"

The script wipes test-output/ at the start of every run. If it still fails, delete the folder manually:

rm -rf test-output
npm run test:generate

Generated project fails to start — "Cannot find module"

The generator runs npm install in all workspaces automatically. If something went wrong, run it manually:

npm install
cd frontend && npm install && cd ..
cd backend && npm install && cd ..

TanStack Router: routeTree.gen.ts missing

This file is auto-generated the first time vite dev runs. It doesn't exist in the scaffold — that's expected. Running npm run dev creates it automatically.