Skip to content

Commit 6694f4d

Browse files
committed
fix(create): preserve manifest template specifiers literally
Manifest entries like `{ template: '@your-org/template-web' }` were passing through `discoverTemplate` → `expandCreateShorthand` and being rewritten into `@your-org/create-template-web` — because the shorthand rule targets any `@scope/name` that doesn't already start with `create-`. The manifest author's literal package name was silently transformed, causing vp to fetch the wrong package. Fix: thread a `skipShorthand` flag through `discoverTemplate` that short- circuits the final `expandCreateShorthand` call. Set it when `resolveOrgManifestForCreate` returns `kind: 'replaced'` (the path responsible for manifest-resolved specifiers). The earlier branches in `discoverTemplate` (`vite:`, GitHub URLs, local workspace packages) still match first, so manifest entries using those specifier kinds behave unchanged. Unit test added to `discovery.spec.ts` pinning both halves of the contract: `@your-org/template-web` stays literal with the flag set, and still expands via shorthand without it (the existing behavior for non-manifest inputs). Reported by Cursor Bugbot on #1398.
1 parent beb49b3 commit 6694f4d

3 files changed

Lines changed: 43 additions & 1 deletion

File tree

packages/cli/src/create/__tests__/discovery.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,36 @@ describe('GitHub template helpers', () => {
108108
expect(template.command).toBe('degit');
109109
expect(template.args).toEqual(['your-org/fate-template', 'my-app']);
110110
});
111+
112+
it('should keep manifest-resolved specifiers literal when skipShorthand=true', () => {
113+
const workspace = {
114+
rootDir: '/tmp/workspace',
115+
isMonorepo: false,
116+
monorepoScope: '',
117+
workspacePatterns: [],
118+
parentDirs: [],
119+
packageManager: 'pnpm',
120+
packageManagerVersion: 'latest',
121+
downloadPackageManager: { binPrefix: '/tmp/bin', version: '10.0.0' } as never,
122+
packages: [],
123+
} as never;
124+
125+
// A manifest entry like `{ template: '@your-org/template-web' }` must
126+
// NOT be rewritten into `@your-org/create-template-web` by the create
127+
// shorthand expander — the manifest author already gave the exact
128+
// npm package name they want.
129+
const fromManifest = discoverTemplate(
130+
'@your-org/template-web',
131+
[],
132+
workspace,
133+
false,
134+
undefined,
135+
true,
136+
);
137+
expect(fromManifest.command).toBe('@your-org/template-web');
138+
139+
// But without the flag, the existing shorthand rules still apply.
140+
const withoutFlag = discoverTemplate('@your-org/template-web', [], workspace);
141+
expect(withoutFlag.command).toBe('@your-org/create-template-web');
142+
});
111143
});

packages/cli/src/create/bin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ async function main() {
441441
let remoteTargetDir: string | undefined;
442442
let shouldSetupHooks = false;
443443
let bundledLocalPath: string | undefined;
444+
let skipShorthandExpansion = false;
444445
const installArgs = process.env.CI ? ['--no-frozen-lockfile'] : undefined;
445446

446447
// Honor `create.defaultTemplate` from vite.config.ts when no CLI arg was passed.
@@ -460,6 +461,10 @@ async function main() {
460461
});
461462
if (resolved.kind === 'replaced') {
462463
selectedTemplateName = resolved.templateName;
464+
// Manifest entries are fully-qualified by their author.
465+
// Prevent `expandCreateShorthand` from rewriting e.g.
466+
// `@your-org/template-web` into `@your-org/create-template-web`.
467+
skipShorthandExpansion = true;
463468
} else if (resolved.kind === 'bundled') {
464469
bundledLocalPath = resolved.bundledLocalPath;
465470
} else if (resolved.kind === 'escape-hatch') {
@@ -790,6 +795,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
790795
workspaceInfo,
791796
options.interactive,
792797
bundledLocalPath,
798+
skipShorthandExpansion,
793799
);
794800

795801
if (selectedParentDir) {

packages/cli/src/create/discovery.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function discoverTemplate(
4747
workspaceInfo: WorkspaceInfo,
4848
interactive?: boolean,
4949
bundledLocalPath?: string,
50+
skipShorthand?: boolean,
5051
): TemplateInfo {
5152
const envs = prependToPathToEnvs(workspaceInfo.downloadPackageManager.binPrefix, {
5253
...process.env,
@@ -130,7 +131,10 @@ export function discoverTemplate(
130131
}
131132
}
132133

133-
const expandedName = expandCreateShorthand(templateName);
134+
// Manifest-resolved entries (`{ kind: 'replaced' }` from org-resolve.ts)
135+
// are already fully qualified by the manifest author — `@scope/template-web`
136+
// means exactly that package, NOT `@scope/create-template-web`.
137+
const expandedName = skipShorthand ? templateName : expandCreateShorthand(templateName);
134138
return {
135139
command: expandedName,
136140
args: [...templateArgs],

0 commit comments

Comments
 (0)