Skip to content

Commit 4e511a3

Browse files
committed
feat(create): support @org default templates via createConfig.templates
Extend `vp create` so an organization can expose a curated set of templates through a single entry point: `vp create @your-org` opens an interactive picker over the org's manifest, `vp create @your-org:web` runs a specific entry directly, and `create.defaultTemplate` in `vite.config.ts` makes an org the default for bare `vp create`. The manifest lives in `@your-org/create`'s `package.json` under `createConfig.templates` (tool-neutral key, parallel to `publishConfig`). Each entry carries `{ name, description, template, monorepo? }`. The `template` field accepts npm specifiers, GitHub URLs, `vite:*` builtins, local workspace packages, or relative paths (`./templates/foo`) that resolve against the extracted `@org/create` tarball — letting an org ship N templates from one package. Highlights: - Spec syntax: `@scope` (picker), `@scope:name` (direct entry), `@scope@version` / `@scope:name@version` (pinned). The colon separator avoids collision with real `@org/package` npm specifiers and the existing `@org/create-name` shorthand. - Bundled subdirectory templates: tarball downloaded once over HTTPS, SHA-512 integrity-verified, extracted via nanotar to `$VP_HOME/tmp/create-org/<host>/<scope>/create/<version>/`. The `<host>` segment (sanitized for Windows-illegal characters) keeps cross-registry resolutions from sharing a slot. Extracts atomically via `<dir>.tmp-…` staging + rename; sibling staging older than 24h is pruned at the start of each fresh extract. - `.npmrc` registry/auth: walk-up parser that layers user → project → env, honoring `@scope:registry=...` overrides and resolving credentials via `_authToken` / `_auth` / username:_password. Retries with auth only on 401/403 — public registries never see a token. - `monorepo: true` entries are filtered from the picker inside an existing monorepo and rejected on direct selection there. When a bundled monorepo entry scaffolds successfully outside one, the generated `vite.config.ts` gains `create: { defaultTemplate: '@scope' }` so a bare `vp create` from inside the new workspace opens the same org's picker. - Builtin escape hatch: the org picker always appends a "Vite+ built-in templates" entry routing to the standard `getInitialTemplateOptions` flow. - `--no-interactive` prints the manifest as a fixed-column table so agents and CI scripts can recover available names. - Git-init prompt unified across the `vite:monorepo` and bundled-`@org`-monorepo paths in `bin.ts`. After `git init` succeeds, `ensureGitignoreNodeModules` writes / appends `node_modules` so bundled templates without their own `.gitignore` don't track installed deps on the first commit. - Schema rules: names are kebab-case, must be unique, and the `__vp_` prefix is reserved for internal sentinels (e.g. the picker's per-call escape-hatch UUID). Bundled paths that escape the package root are rejected at validation time, before any tarball fetch. - Tar entries preserve their stored mode (so `gradlew` and friends stay executable); setuid/setgid/sticky bits are stripped. Net change: 4132 insertions across 70 files. Ships `packages/cli/src/create/org-{manifest,resolve,picker,tarball}.ts`, `packages/cli/src/create/templates/bundled.ts`, the new `packages/cli/src/utils/npm-config.ts` module, the `UserConfig.create.defaultTemplate` augmentation, the `injectCreateDefaultTemplate` and `ensureGitignoreNodeModules` helpers, plus the integration in `bin.ts` and the `discoverTemplate` manifest branch. Tests: 12 new unit-test files (~230 specs) covering schema parsing, version pinning, scope/auth resolution, picker filtering + escape hatch, tarball cache key + extract atomicity, gitignore edge cases, and the migrator helper. Eight snap-test fixtures under `packages/cli/snap-tests/create-org-*` exercise the end-to-end flow through a shared mock npm registry, including the new `create-org-bundled-monorepo` case that verifies `create.defaultTemplate` injection and `git init` for bundled monorepo templates. Docs: new `docs/guide/create.md § Organization Templates`, new `docs/config/create.md` reference, README pointer to viteplus.dev, and a fully-revised RFC at `rfcs/create-org-default-templates.md` documenting the design as shipped.
1 parent fc3a8df commit 4e511a3

70 files changed

Lines changed: 4132 additions & 83 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ vp create
152152

153153
You can run `vp create` inside of a project to add new apps or libraries to your project.
154154

155+
Organizations can expose a curated set of templates under their npm scope by
156+
publishing `@org/create` with a `createConfig.templates` manifest in its `package.json`.
157+
Once published, `vp create @org` opens an interactive picker over those
158+
templates, and setting `create: { defaultTemplate: '@org' }` in
159+
`vite.config.ts` makes it the default for bare `vp create`. See the
160+
[Organization Templates guide](https://viteplus.dev/guide/create#organization-templates)
161+
for the authoring workflow and
162+
[`create.defaultTemplate`](https://viteplus.dev/config/create) for the
163+
config reference.
164+
155165
### Migrating an existing project
156166

157167
You can migrate an existing project to Vite+:

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ export default extendConfig(
168168
text: 'Configuration',
169169
items: [
170170
{ text: 'Configuring Vite+', link: '/config/' },
171+
{ text: 'Create', link: '/config/create' },
171172
{ text: 'Run', link: '/config/run' },
172173
{ text: 'Format', link: '/config/fmt' },
173174
{ text: 'Lint', link: '/config/lint' },

docs/config/create.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Create Config
2+
3+
`vp create` reads the `create` block in `vite.config.ts` to set per-repo defaults. See the [Creating a Project guide](/guide/create#organization-templates) for the full `@org` template workflow.
4+
5+
## `create.defaultTemplate`
6+
7+
When `vp create` is invoked with no `TEMPLATE` argument, Vite+ uses this value as if the user had typed it. Typically set to an npm scope whose `@scope/create` package publishes a `createConfig.templates` manifest — so bare `vp create` drops into the org picker.
8+
9+
```ts
10+
import { defineConfig } from 'vite-plus';
11+
12+
export default defineConfig({
13+
create: {
14+
defaultTemplate: '@your-org',
15+
},
16+
});
17+
```
18+
19+
Any value accepted by `vp create` as a first argument works here — `@your-org` for an org picker, `@your-org:web` for a direct manifest entry, `vite:application` for a built-in, etc.
20+
21+
## Precedence
22+
23+
CLI argument > `create.defaultTemplate` > the standard built-in picker.
24+
25+
Explicit specifiers always win, so scripts and CI can bypass the configured default:
26+
27+
```bash
28+
# Uses create.defaultTemplate
29+
vp create
30+
31+
# Explicitly ignores the default
32+
vp create vite:library
33+
```
34+
35+
The org picker also appends a trailing "Vite+ built-in templates" entry — selecting it routes to the `vite:monorepo` / `vite:application` / `vite:library` / `vite:generator` flow, so built-ins stay reachable interactively even when a default is configured.

docs/guide/create.md

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Vite+ ships with these built-in templates:
3535

3636
- Use shorthand templates like `vite`, `@tanstack/start`, `svelte`, `next-app`, `nuxt`, `react-router`, and `vue`
3737
- Use full package names like `create-vite` or `create-next-app`
38-
- Use local templates such as `./tools/create-ui-component` or `@acme/generator-*`
38+
- Use local templates such as `./tools/create-ui-component` or `@your-org/generator-*`
3939
- Use remote templates such as `github:user/repo` or `https://github.com/user/template-repo`
4040

4141
Run `vp create --list` to see the built-in templates and the common shorthand templates Vite+ recognizes.
@@ -86,3 +86,150 @@ vp create create-next-app
8686
vp create github:user/repo
8787
vp create https://github.com/user/template-repo
8888
```
89+
90+
## Organization Templates
91+
92+
An organization can publish a curated set of templates under a single npm scope by shipping an `@org/create` package whose `package.json` carries a `createConfig.templates` manifest. Once published, `vp create @org` opens an interactive picker over those templates.
93+
94+
### Pick from an org
95+
96+
```bash
97+
# Open an interactive picker over @your-org/create's manifest
98+
vp create @your-org
99+
100+
# Run a specific manifest entry directly
101+
vp create @your-org:web
102+
103+
# Pin to an exact version or a dist-tag
104+
vp create @your-org@1.2.3
105+
vp create @your-org:web@next
106+
107+
# Set the org as the default for a repo (see create.defaultTemplate config)
108+
vp create
109+
```
110+
111+
Behind the scenes, `vp create @org` maps to `@org/create` (the existing npm `create-*` convention). If that package has no `createConfig.templates` field, Vite+ falls back to running the package normally — so adopting the manifest is zero-risk for orgs that already publish `@org/create`.
112+
113+
Private registries work automatically: Vite+ reads `.npmrc` files from the project root and `~/`, honoring `@your-org:registry=...` scope mappings and `//host/:_authToken=...` credentials.
114+
115+
### Authoring `@org/create`
116+
117+
There are two common layouts. Pick the one that matches the org's template count and release cadence.
118+
119+
**Bundled (recommended for most orgs).** All templates live as subdirectories of `@org/create` itself. Manifest entries use relative `./path` values. One repo, one publish, one versioning story — the same pattern used by `create-vite` and `create-next-app`.
120+
121+
```
122+
@your-org/create/
123+
├── package.json # "createConfig": { "templates": [{ "template": "./templates/web" }, ...] }
124+
├── templates/
125+
│ ├── web/
126+
│ │ ├── package.json
127+
│ │ └── src/...
128+
│ └── library/...
129+
└── README.md
130+
```
131+
132+
**Manifest-only.** When the org already publishes independent `@org/template-*` packages (or hosts them on GitHub), `@org/create` stays a thin index.
133+
134+
```
135+
@your-org/create/
136+
├── package.json # "createConfig": { "templates": [{ "template": "@your-org/template-web" }, ...] }
137+
└── README.md
138+
```
139+
140+
The two layouts can be mixed — a manifest can point most entries at external packages and keep a few as bundled subdirectories.
141+
142+
Optionally, provide a `bin` script so `npm create @org` (the legacy path) keeps working for non-Vite+ users. `vp create @org` reads the manifest directly and never runs the `bin`.
143+
144+
### Manifest schema
145+
146+
The manifest lives at `createConfig.templates` in `@org/create`'s `package.json`:
147+
148+
```json
149+
{
150+
"name": "@your-org/create",
151+
"version": "1.0.0",
152+
"createConfig": {
153+
"templates": [
154+
{
155+
"name": "monorepo",
156+
"description": "Monorepo",
157+
"template": "@your-org/template-monorepo",
158+
"monorepo": true
159+
},
160+
{
161+
"name": "web",
162+
"description": "Web app template (Vite + React)",
163+
"template": "@your-org/template-web"
164+
},
165+
{
166+
"name": "demo",
167+
"description": "Bundled demo template",
168+
"template": "./templates/demo"
169+
}
170+
]
171+
}
172+
}
173+
```
174+
175+
Each entry supports:
176+
177+
| Field | Required | Notes |
178+
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
179+
| `name` | yes | Kebab-case identifier. Used by `vp create @org:<name>` for direct selection. Must be unique within the array. |
180+
| `description` | yes | One-line description shown in the picker. |
181+
| `template` | yes | An npm specifier (`@org/template-foo`, optionally `@version`), a GitHub URL (`github:user/repo`), a `vite:*` builtin, a local workspace package name, or a relative path (`./templates/foo`) that resolves against the `@org/create` root. |
182+
| `monorepo` | no | If `true`, marks this entry as a monorepo-creating template. Hidden from the picker when `vp create` runs inside an existing monorepo, mirroring the built-in `vite:monorepo` filter. |
183+
184+
An invalid manifest is a hard error, not a silent fall-through — a maintainer who shipped a manifest should hear about the offending field, e.g. `@your-org/create: createConfig.templates[2].template must be a non-empty string`.
185+
186+
### Bundled subdirectory templates
187+
188+
Relative `./...` paths resolve against the enclosing `@org/create` package root — **not** the user's cwd. The referenced directory is copied verbatim into the target project (no template-engine processing). Paths that escape the package root are rejected.
189+
190+
### Make the org the default in a repo
191+
192+
Commit this in `vite.config.ts` at the project root:
193+
194+
```ts
195+
import { defineConfig } from 'vite-plus';
196+
197+
export default defineConfig({
198+
create: { defaultTemplate: '@your-org' },
199+
});
200+
```
201+
202+
Now `vp create` (with no argument) drops straight into the `@your-org` picker. See [`create.defaultTemplate`](/config/create) for details.
203+
204+
The picker always appends a trailing **Vite+ built-in templates** entry so `vite:monorepo` / `vite:application` / `vite:library` / `vite:generator` stay reachable from the picker — selecting it routes to the standard built-in flow. For scripts and CI, explicit specifiers (`vp create vite:library`) bypass the configured default.
205+
206+
### Non-interactive inspection
207+
208+
`vp create @org --no-interactive` prints the manifest as a table and exits 1:
209+
210+
```
211+
A template name is required when running `vp create @your-org` in non-interactive mode.
212+
213+
Available templates in @your-org/create:
214+
215+
NAME DESCRIPTION TEMPLATE
216+
web Web app template (Vite + React) @your-org/template-web
217+
library TypeScript library template @your-org/template-library
218+
demo Bundled demo template ./templates/demo
219+
220+
Examples:
221+
# Scaffold a specific template from the org
222+
vp create @your-org:web --no-interactive
223+
224+
# Or use a Vite+ built-in template
225+
vp create vite:application --no-interactive
226+
```
227+
228+
### Publishing checklist
229+
230+
1. Create `@org/create` (scoped npm package) if you don't already have one.
231+
2. Add a `createConfig.templates` array to `package.json`. (Bundle the templates under `./templates/...` or point at external packages.)
232+
3. (Optional) Provide a `bin` launcher for `npm create @org` compatibility.
233+
4. Publish.
234+
5. Verify: `vp create @org --no-interactive` prints the manifest table; `vp create @org` opens the picker.
235+
6. (Optional) Commit `create: { defaultTemplate: '@org' }` in your internal template repos.

packages/cli/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,16 @@ vp create
150150

151151
You can run `vp create` inside of a project to add new apps or libraries to your project.
152152

153+
Organizations can expose a curated set of templates under their npm scope by
154+
publishing `@org/create` with a `createConfig.templates` manifest in its `package.json`.
155+
Once published, `vp create @org` opens an interactive picker over those
156+
templates, and setting `create: { defaultTemplate: '@org' }` in
157+
`vite.config.ts` makes it the default for bare `vp create`. See the
158+
[Organization Templates guide](https://viteplus.dev/guide/create#organization-templates)
159+
for the authoring workflow and
160+
[`create.defaultTemplate`](https://viteplus.dev/config/create) for the
161+
config reference.
162+
153163
### Migrating an existing project
154164

155165
You can migrate an existing project to Vite+:

packages/cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@
353353
"lint-staged": "catalog:",
354354
"minimatch": "catalog:",
355355
"mri": "catalog:",
356+
"nanotar": "catalog:",
356357
"picocolors": "catalog:",
357358
"rolldown-plugin-dts": "catalog:",
358359
"semver": "catalog:",

packages/cli/snap-tests-global/command-create-help/snap.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ Arguments:
99
- Remote: vite, @tanstack/start, create-next-app,
1010
create-nuxt, github:user/repo, https://github.com/user/template-repo, etc.
1111
- Local: @company/generator-*, ./tools/create-ui-component
12+
- Org scope: @your-org → picker from @your-org/create's createConfig.templates manifest
13+
- Org entry: @your-org:web → manifest entry "web" from @your-org/create
14+
When omitted, uses `create.defaultTemplate` from vite.config.ts if set.
1215

1316
Options:
1417
--directory DIR Target directory for the generated project.
@@ -49,6 +52,10 @@ Examples:
4952
vp create github:user/repo
5053
vp create https://github.com/user/template-repo
5154

55+
# Pick from an org that publishes @scope/create with createConfig.templates
56+
vp create @your-org # interactive picker
57+
vp create @your-org:web # direct manifest-entry selection
58+
5259
Documentation: https://viteplus.dev/guide/create
5360

5461

@@ -63,6 +70,9 @@ Arguments:
6370
- Remote: vite, @tanstack/start, create-next-app,
6471
create-nuxt, github:user/repo, https://github.com/user/template-repo, etc.
6572
- Local: @company/generator-*, ./tools/create-ui-component
73+
- Org scope: @your-org → picker from @your-org/create's createConfig.templates manifest
74+
- Org entry: @your-org:web → manifest entry "web" from @your-org/create
75+
When omitted, uses `create.defaultTemplate` from vite.config.ts if set.
6676

6777
Options:
6878
--directory DIR Target directory for the generated project.
@@ -103,6 +113,10 @@ Examples:
103113
vp create github:user/repo
104114
vp create https://github.com/user/template-repo
105115

116+
# Pick from an org that publishes @scope/create with createConfig.templates
117+
vp create @your-org # interactive picker
118+
vp create @your-org:web # direct manifest-entry selection
119+
106120
Documentation: https://viteplus.dev/guide/create
107121

108122

@@ -117,6 +131,9 @@ Arguments:
117131
- Remote: vite, @tanstack/start, create-next-app,
118132
create-nuxt, github:user/repo, https://github.com/user/template-repo, etc.
119133
- Local: @company/generator-*, ./tools/create-ui-component
134+
- Org scope: @your-org → picker from @your-org/create's createConfig.templates manifest
135+
- Org entry: @your-org:web → manifest entry "web" from @your-org/create
136+
When omitted, uses `create.defaultTemplate` from vite.config.ts if set.
120137

121138
Options:
122139
--directory DIR Target directory for the generated project.
@@ -157,5 +174,9 @@ Examples:
157174
vp create github:user/repo
158175
vp create https://github.com/user/template-repo
159176

177+
# Pick from an org that publishes @scope/create with createConfig.templates
178+
vp create @your-org # interactive picker
179+
vp create @your-org:web # direct manifest-entry selection
180+
160181
Documentation: https://viteplus.dev/guide/create
161182

packages/cli/snap-tests-global/new-check/snap.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ Arguments:
99
- Remote: vite, @tanstack/start, create-next-app,
1010
create-nuxt, github:user/repo, https://github.com/user/template-repo, etc.
1111
- Local: @company/generator-*, ./tools/create-ui-component
12+
- Org scope: @your-org → picker from @your-org/create's createConfig.templates manifest
13+
- Org entry: @your-org:web → manifest entry "web" from @your-org/create
14+
When omitted, uses `create.defaultTemplate` from vite.config.ts if set.
1215

1316
Options:
1417
--directory DIR Target directory for the generated project.
@@ -49,6 +52,10 @@ Examples:
4952
vp create github:user/repo
5053
vp create https://github.com/user/template-repo
5154

55+
# Pick from an org that publishes @scope/create with createConfig.templates
56+
vp create @your-org # interactive picker
57+
vp create @your-org:web # direct manifest-entry selection
58+
5259
Documentation: https://viteplus.dev/guide/create
5360

5461

0 commit comments

Comments
 (0)