When scaffolding an internal tool with create-vue, what do the TypeScript, Router, Pinia and Vitest choices each add to the generated project?
answer
- one template layer per choice
- the entry file changes
- a router file and two views
- a counter store
- a spec file and a config
basics
~20 sTypeScript adds tsconfig files, env.d.ts and a type-checking build script; Router adds vue-router with src/router and two views; Pinia adds pinia and a counter store; Vitest adds vitest.config, a jsdom test setup and an example spec.
solid answer
~30 screate-vue renders a base Vite project and layers one template per choice. **TypeScript** renames sources to `.ts`, adds `tsconfig.json` referencing `tsconfig.app.json` and `tsconfig.node.json`, and makes `build` run `vue-tsc --build` alongside `vite build`. **Router** adds `vue-router` 5, `src/router/index.ts` with a `home` route and a lazily imported `about` route, and `HomeView`/`AboutView`. **Pinia** adds `pinia` 4 and `src/stores/counter.ts`, a setup store. With both, `main.ts` installs Pinia and then the router. **Vitest** adds `vitest.config.ts` merged from the Vite config with the `jsdom` environment, a `test:unit` script, Vue Test Utils and `src/components/__tests__/HelloWorld.spec.ts`, plus `tsconfig.vitest.json` under TypeScript.
code
ts · 14 linesimport './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')go deeper
Recall which folders each choice adds: src/router and views, src/stores, and tests with a spec file.
Explain the layering, the main.ts wiring order, the merged Vitest config with jsdom and the TypeScript build script.
Show you know what the scaffold does not do, such as file-based routing, and which generated files are load-bearing versus example code.
Decide which scaffold choices become the default for new internal tools and what an internal template adds on top.
## How the choices are applied create-vue builds the project in layers. It always renders a **base** template (a Vite project with `index.html`, `vite.config`, the Vue plugin, the Vue DevTools plugin and `dev`/`build`/`preview` scripts), then one **config** layer per feature, then a **code** layer (example components, with or without router views) and an **entry** layer that writes `src/main` for the combination chosen. `package.json` files from each layer are merged, so every choice adds its own dependencies and scripts. For an internal admin tool the usual selection is TypeScript, Router, Pinia and Vitest: ```bash npm create vue@latest admin-tool -- --ts --router --pinia --vitest ``` ## What each choice adds | Choice | Dependencies | Files | Scripts | |---|---|---|---| | TypeScript | `typescript`, `vue-tsc`, `@vue/tsconfig`, `npm-run-all2` | `tsconfig.json`, `tsconfig.app.json`, `tsconfig.node.json`, `env.d.ts`; `.js` sources become `.ts` | `build` runs `type-check` and `build-only` | | Router | `vue-router` ^5.3 | `src/router/index.ts`, `src/views/HomeView.vue`, `src/views/AboutView.vue` | none | | Pinia | `pinia` ^4.0 | `src/stores/counter.ts` | none | | Vitest | `vitest`, `@vue/test-utils`, `jsdom` | `vitest.config.ts`, `src/components/__tests__/HelloWorld.spec.ts`, `tsconfig.vitest.json` with TS | `test:unit` | ## Details worth knowing - **The entry file** differs by combination: with Router and Pinia, `main.ts` imports the CSS, creates the app, calls `app.use(createPinia())`, then `app.use(router)`, then mounts on `#app`. - **The router file** uses `createWebHistory(import.meta.env.BASE_URL)` and a hand-written `routes` array; the `about` route uses `() => import(...)` to show route-level code splitting. It does not set up Vue Router 5's file-based routing plugin. - **The store** is a setup store: `defineStore('counter', () => { ... })` with a `count` ref, a `doubleCount` computed and an `increment` function. - **The Vitest config** imports the Vite config and merges a `test` block into it with `environment: 'jsdom'`, and excludes `e2e/**` so end-to-end specs never run as unit tests. - **The TypeScript build** runs `vue-tsc --build` and `vite build` in parallel through `run-p`, because Vite itself only transpiles. ## Reading the generated test The Vitest layer ships one example, `src/components/__tests__/HelloWorld.spec.ts`: - it imports `describe`, `it` and `expect` from `vitest`, and `mount` from `@vue/test-utils`; - it mounts `HelloWorld` with a `msg` prop and asserts that the rendered text contains it; - it runs under the `jsdom` environment set in `vitest.config`, so DOM APIs exist without a browser. The `__tests__` folder beside the component is the convention the TypeScript and lint configs expect: `tsconfig.app.json` excludes it, `tsconfig.vitest.json` includes it, and the Vitest ESLint rules target it. Put the tool's own specs in the same kind of folder and all three configs pick them up without edits. `npm run test:unit` starts Vitest in watch mode; CI usually passes `--run` for a single pass. ## Choices outside this scenario - **JSX** adds `@vitejs/plugin-vue-jsx` to the Vite config. - **End-to-End Testing** asks for Playwright or Cypress; Cypress without Vitest also adds Cypress Component Testing. - **ESLint** and **Prettier** add lint and format tooling, covered separately. - **Barebone** (`--bare`) keeps the tooling but strips the example components: it keeps `main`, the `router` and `stores` folders, empties the routes array and replaces `App.vue` with a minimal page. ## Adding a feature you skipped create-vue cannot be re-run to add Pinia or Vitest to an existing project; it would ask to overwrite the folder. Adding a skipped feature later is manual, and the scaffold is a useful reference for what 'done' looks like: - install the same packages the layer would have added; - recreate its files, such as `vitest.config.ts` and `tsconfig.vitest.json`, and add the new reference to the root `tsconfig.json`; - add its scripts and, for ESLint, its config block. Scaffolding a throwaway project with the extra flag and diffing it against yours is the quickest way to see every file involved. ## Mistakes worth catching 1. Deleting `tsconfig.node.json` as clutter: it type-checks `vite.config.ts` and the other tool configs. 2. Adding a store file and forgetting it needs no registration: Pinia stores are created on first use, not listed in `main.ts`. 3. Expecting file-based routes because Vue Router 5 is installed: the scaffold writes an explicit routes array.
- Why does the generated about route use () => import() while home imports its view directly?It demonstrates route-level code splitting: the about view becomes its own chunk loaded on first visit, while the home view ships in the main bundle because almost every visit needs it. It is example code; you apply the same pattern to heavy screens of your tool.
- What does choosing Cypress without Vitest add that Cypress with Vitest does not?Cypress Component Testing. The scaffolder sets it up only when Vitest is not selected, so component tests still have a runner; with Vitest, component-level tests go to Vitest and Cypress is for end-to-end only.
saying these in an interview costs you the question
- Choosing Router sets up Vue Router 5's file-based routing plugin.
- The Pinia choice registers each store in main.ts.
- Vitest gets its own separate config unrelated to vite.config.
- With TypeScript, npm run build only runs vite build.
- tsconfig.node.json is leftover clutter that can be deleted.