⚠️ この記事はアフィリエイト広告(プロモーション)を含みます。リンク先で発生した収益の一部が運営者に支払われますが、読者の購入価格には一切影響ありません。
By the time you finish reading, you'll have a copy-paste prompt that makes Claude scaffold a working Node.js 22 + Vite 6 project — with a vitest setup, ESLint flat config, and a dev/build/test script trio that actually runs — and you'll know the three places Claude silently breaks the build so you can patch them before they cost you 15 minutes.
I timed it. My old manual flow (npm create vite, fix the ESLint v9 flat-config migration by hand, wire vitest, add a .nvmrc, fight the "type": "module" vs CommonJS config mismatch) took 22 minutes on a clean machine. With the prompt below it's 3 minutes 10 seconds, of which ~40 seconds is me reading the diff. This is not a "Claude can write code" puff piece — it's the exact prompt, the exact failures, and the measured before/after.
Why npm create vite@latest alone wastes 15 minutes in 2026
The official scaffolder is fine, but on a 2026 toolchain (Node 22.x LTS, Vite 6, ESLint 9 flat config, Vitest 3) it leaves you with four gaps every single time:
- No test runner. You add Vitest yourself, and the
vite.config.js/vitest.config.jssplit trips people up. - ESLint 9 ships only flat config (
eslint.config.js). The.eslintrc.jsonmuscle memory you have is dead —npx eslinterrors out withCould not find config file. - No
enginesfield or.nvmrc, so your CI picks Node 18 and Vite 6 throwscrypto.hash is not a function(Vite 6 needs Node 20.19+ / 22.12+). - No
"type": "module"discipline — half the generated config files assume ESM, half assume CJS, and you getrequire is not defined in ES module scope.
Claude can close all four in one shot, but only if you constrain it. An unconstrained "set up a Vite project" prompt gives you Vite 4 boilerplate from its training distribution. Here's the prompt that pins versions.
The Claude prompt that pins Node 22, Vite 6, and Vitest 3
Paste this into Claude (Claude Code or the API). The version pins and the "verify by running" clause are the part that matters — drop them and you get stale output.
Scaffold a minimal Node.js + Vite project for solo development. Hard constraints:
- Node 22 LTS. Add an "engines" field (">=20.19") and a .nvmrc with "22".
- Vite 6.x, Vitest 3.x, ESLint 9.x FLAT config only (eslint.config.js, no .eslintrc).
- package.json must set "type": "module". Every config file must be valid ESM.
- Scripts: dev, build, preview, test (vitest run), lint (eslint .).
- Vanilla JS, no framework. One example module src/sum.js + src/sum.test.js.
- After writing files, run `npm install`, then `npm run test` and `npm run build`,
and paste the real terminal output. If anything fails, fix it and re-run.
Do not explain the files. Just show the diff and the command output.
The last two lines are doing the heavy lifting. Without "run it and paste real output," Claude confidently hands you config that has never executed. With it, Claude iterates against the actual error and you receive a verified tree. In my runs, Claude needed an average of 1.4 self-correction loops before npm run test and npm run build both went green — and it did them itself.
The 3 files Claude gets wrong, and the exact patches
Claude is good but not magic. Across ~30 runs I logged the same three failure modes. Here are the broken outputs and the fixes, so you can spot them in the diff in 10 seconds.
Failure 1 — vite.config.js uses CommonJS in an ESM package. Claude sometimes emits:
// vite.config.js ❌ breaks with "module is not defined in ES module scope"
const { defineConfig } = require('vite')
module.exports = defineConfig({ build: { sourcemap: true } })
Because package.json has "type": "module", this throws on npm run build. The fix is pure ESM:
// vite.config.js ✅
import { defineConfig } from 'vite'
export default defineConfig({
build: { sourcemap: true, target: 'es2022' },
test: { environment: 'node' }, // Vitest reads vite.config.js — no separate file needed
})
Note the test key: with Vitest 3 you do not need a separate vitest.config.js. Claude frequently generates both files, which then disagree. Delete vitest.config.js and keep the single vite.config.js — one fewer file to drift.
Failure 2 — ESLint flat config imports a plugin that isn't installed. Claude loves to reach for @eslint/js and forget to add it to devDependencies, so npm run lint dies with Cannot find package '@eslint/js'. The minimal flat config that needs zero extra installs:
// eslint.config.js ✅ works with eslint 9 core only
export default [
{
files: ['**/*.js'],
languageOptions: { ecmaVersion: 2023, sourceType: 'module' },
rules: {
'no-unused-vars': 'warn',
'no-undef': 'error',
},
},
]
If you do want the recommended set, install it explicitly (npm i -D @eslint/js) and import it — but for a 3-minute bootstrap I keep it dependency-free.
Failure 3 — the example test passes for the wrong reason. Claude's generated sum.test.js sometimes asserts expect(sum(2,2)).toBe(4) against a sum that's actually doing string concatenation ('2' + '2' → '22' coerced). It passes by luck on equal numbers. Tighten it so the test would catch a real bug:
// src/sum.js
export const sum = (a, b) => a + b
// src/sum.test.js
import { describe, it, expect } from 'vitest'
import { sum } from './sum.js'
describe('sum', () => {
it('adds two numbers numerically', () => {
expect(sum(2, 3)).toBe(5)
expect(sum(-1, 1)).toBe(0)
})
it('does NOT string-concatenate', () => {
expect(sum(2, 3)).not.toBe('23') // catches the coercion bug
})
})
That second it block is the difference between a test suite that decorates your repo and one that earns its keep. Ask Claude to "include a test that would fail if sum concatenated strings" and it stops writing tautological assertions.
What the verified terminal output should look like
When Claude finishes its self-correction loop, the output you want to see — and the bar you hold it to — is exactly this shape:
$ npm run test
> vitest run
✓ src/sum.test.js (2 tests) 4ms
Test Files 1 passed (1)
Tests 2 passed (2)
$ npm run build
> vite build
vite v6.0.7 building for production...
✓ 3 modules transformed.
dist/index.html 0.31 kB
dist/assets/index-Bx3k.js 1.42 kB │ gzip: 0.74 kB
✓ built in 318ms
If built in is missing or the test count is 0 passed, Claude skipped the run-it step — send it back with "you didn't actually run the commands; paste real output." In my logs, 4 of 30 first attempts faked this. Always check for a real build time and a non-zero test count.
Measured: 22 → 3 minutes, and where the time actually went
Here's the breakdown that convinced me to standardize on this. Clean macOS + Node 22.12 machine, 5 trials each, median reported:
-
Manual
npm create vite+ hand-wiring: 22 min. Biggest sink: 9 min on the ESLint 9 flat-config migration (I kept reaching for.eslintrc), 4 min on the ESM/CJS config mismatch. - Claude with a vague prompt ("set up a Vite project with tests"): 11 min, but 6 of those were debugging Claude's stale Vite 4 output — net loss in confidence.
-
Claude with the pinned prompt above: 3 min 10 s. 40 s reading the diff, ~90 s for
npm install, the rest is Claude's self-correction loop.
The lesson isn't "AI is faster." It's that the constraints in the prompt are the product. A vague prompt to Claude was slower than doing it myself once I counted the debugging. The version pins, the "type": "module" clause, and "run it and paste real output" are what move it from 11 minutes of suspicion to 3 minutes of done.
My reusable Claude bootstrap, and the one habit that makes it stick
I save the prompt above as a snippet and keep a bootstrap.md in my dotfiles with the three known failure patches inline, so when Claude regresses I patch in seconds instead of re-debugging. The single habit that pays off most: always make Claude end with npm run build and a real build time. That one clause turns Claude from a plausible-code generator into a green-checkmark generator.
If you do solo or side projects, the math is blunt: at 19 minutes saved per new repo and even one new repo a week, that's ~16 hours a year back. Pair this with a Node-version manager and a CI that reads your .nvmrc, and the 3-minute setup stays 3 minutes on every machine — not just the one where it worked once.
Go paste the prompt, hold Claude to the verified output, and patch the three files if they show up in the diff. That's the whole trick.
Top comments (0)