Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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.

About

Kick-off your next project fast πŸš€ One command, a few simple choices and you are ready to go.

Resources

Code of conduct

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages