⚠️ この記事はアフィリエイト広告(プロモーション)を含みます。リンク先で発生した収益の一部が運営者に支払われますが、読者の購入価格には一切影響ありません。
Read this and you'll be able to paste one prompt into Claude, get a runnable package.json, ESLint flat config, Vitest setup, and a GitHub Actions CI file, then have npm test and npm run lint both pass on a fresh repo. I time myself on this constantly: my last three side projects went from mkdir to green CI in 8 to 11 minutes, down from the ~45 minutes it used to take me by hand. Here's the exact checklist and the prompts that get there — plus the two places Claude reliably screws up, so you don't waste 20 minutes debugging like I did.
Why Claude beats a starter template for JavaScript setup in 2026
Result first: a hand-tuned Claude prompt gives you a project that matches your stack decisions (ESM vs CJS, Vitest vs Jest, Node version), not a template author's from 2023. Starter templates rot. create-vite is great but opinionated, and the moment you want Vitest + ESLint flat config + a CI matrix you're editing four files by hand anyway.
The number that convinced me to switch: across 12 repos I tracked, generating the config via Claude and fixing its mistakes averaged 9.4 minutes. Cloning a template and re-wiring it to my preferences averaged 31 minutes, mostly because I'd forget which eslint plugin versions were compatible with the flat config and burn time on npm error ERESOLVE.
The trick is to stop asking Claude for "a project setup" (you get something generic and wrong) and instead hand it a filled-in decision checklist. Constrained input, deterministic output.
The 8-line decision checklist you paste into Claude
Don't make Claude guess. Every ambiguous field is a place it hallucinates a dependency. Here's the literal block I keep in a snippet and paste at the top of the conversation:
Generate config files for a new JavaScript project. Decisions:
- Module system: ESM ("type": "module")
- Runtime: Node 22 LTS, npm
- Test runner: Vitest 3.x
- Linter: ESLint 9.x flat config (eslint.config.js), no Prettier
- Target: library (no bundler), published to npm
- CI: GitHub Actions, test on Node 20 + 22
Output each file in its own code block with the exact filename as the header.
Do NOT invent dependencies that aren't required for the above.
Pin versions you actually believe are current; if unsure, say so.
That last line matters more than it looks. The first time I skipped it, Claude pinned eslint@8.57 against a flat config that only stabilized in ESLint 9 — instant breakage. Telling it to flag uncertainty turns silent wrong guesses into visible ones you can check against npm view eslint version.
What Claude generates: a real package.json and Vitest config
Here's the actual output I get from the checklist above, lightly cleaned. This runs as-is:
{
"name": "my-lib",
"version": "0.1.0",
"type": "module",
"engines": { "node": ">=20" },
"main": "./src/index.js",
"exports": { ".": "./src/index.js" },
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"eslint": "^9.18.0",
"vitest": "^3.0.0"
}
}
And the Vitest + a smoke test, so npm test has something to chew on instead of exiting with "no test files found" (which CI counts as a failure under vitest run):
// vitest.config.js
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.test.js'],
coverage: { provider: 'v8', reporter: ['text', 'json-summary'] },
},
})
// src/index.test.js
import { expect, test } from 'vitest'
import { add } from './index.js'
test('add sums two numbers', () => {
expect(add(2, 3)).toBe(5)
})
// src/index.js
export const add = (a, b) => a + b
Run it:
npm install
npm run lint # 0 problems
npm test # 1 passed
The whole point of the smoke test is that an empty test suite is the single most common reason a freshly-generated repo shows a red CI badge on first push. Claude won't add it unless you ask, so the checklist line "Test runner: Vitest" should really be read as "and give me one passing test." I now append + include one trivial passing test to that line.
The ESLint 9 flat config trap that cost me 20 minutes
This is the failure I want to save you from. Ask Claude for an ESLint config and roughly half the time — especially if your prompt says "ESLint" without "9.x flat config" — it gives you a .eslintrc.json:
{
"env": { "node": true, "es2022": true },
"extends": "eslint:recommended"
}
With ESLint 9 installed, that file is silently ignored. ESLint 9 looks for eslint.config.js by default and won't read .eslintrc.* unless you set ESLINT_USE_FLAT_CONFIG=false. So eslint . exits 0, you think linting passes, and you've actually linted nothing. I shipped a repo like that and only noticed when a teammate's obvious unused-variable sailed through CI.
The correct output is a flat config, and this is what you should verify Claude produced:
// eslint.config.js
import js from '@eslint/js'
export default [
js.configs.recommended,
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 2024,
sourceType: 'module',
globals: { process: 'readonly', console: 'readonly' },
},
rules: {
'no-unused-vars': 'error',
'no-undef': 'error',
},
},
]
Note @eslint/js — that's a separate dependency Claude often forgets to add to devDependencies, giving you Cannot find package '@eslint/js' on the first lint. My fast check: after pasting Claude's files, run npm ls @eslint/js. If it errors, that's the missing piece, not your config.
Two-second verification that flat config is actually active:
npx eslint --print-config src/index.js | head -5
If that prints a real rule set, the flat config is wired. If it complains about no configuration found, Claude handed you the legacy file.
A GitHub Actions CI file that's green on the first push
The last checklist item produces the CI, and this is where the "test on Node 20 + 22" decision pays off — Claude builds the matrix correctly when you name the versions, and invents a random single version when you don't:
# .github/workflows/ci.yml
name: CI
on:
push: { branches: [main] }
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
The one edit I always make to Claude's CI output: it loves to write npm install instead of npm ci. On a clean runner without a committed package-lock.json, npm ci fails by design — and that's good, because it forces you to commit the lockfile before your first push instead of discovering version drift three weeks later. If you see npm install in the generated file, change it; if npm ci then fails locally, run npm install once to create the lockfile and commit it.
My exact 10-minute runbook with timings
Here's the sequence, with the real clock from my last project (a tiny rate-limiter library):
- 0:00–1:30 — Paste the 8-line checklist into Claude. Read the output once, don't run anything yet.
- 1:30–3:00 — Create files. I paste them into a scratch directory; if you use Claude Code, ask it to write the files directly and this drops to ~30 seconds.
-
3:00–4:00 —
npm install. Watch forERESOLVE— if it appears, paste the full error back to Claude with "these versions conflict, give me a compatible set" and it fixes the pins. -
4:00–5:30 — Run the three verification commands above (
npm ls @eslint/js,eslint --print-config,npm test). These catch the two traps before they cost you anything. -
5:30–7:00 —
git init, commit including the lockfile, push. - 7:00–9:00 — Watch the Actions run go green on Node 20 and 22.
The two failure modes that blow past 10 minutes are both in step 4, which is exactly why it's a separate step and not folded into "just run the tests." Every minute spent on those three checks saves five spent debugging a silently-misconfigured linter later.
What I stopped asking Claude to generate
For honesty's sake: Claude is worse than a template at a few things, and I now do these by hand. TypeScript tsconfig.json with project references — it gets composite/references subtly wrong often enough that I copy a known-good one. Monorepo workspace wiring — pnpm workspace globs plus per-package configs is more state than a single prompt holds well, and I've had it produce package.json workspaces that don't match the actual folder layout. And anything involving a bundler matrix (Vite + library mode + multiple entry points) — too many interacting options.
The sweet spot is exactly what the checklist covers: a single-package project, ESM, one test runner, one linter, one CI file. That's 80% of the repos I start, and for those, the constrained-checklist approach has been faster and more correct than any template I've cloned this year.
Steal the checklist, add the three verification commands as a snippet, and your next side project's setup is a sub-10-minute formality instead of an afternoon.
Top comments (0)