From 4feb907f293db0b8ff697bed080526b4a90d8f1c Mon Sep 17 00:00:00 2001 From: neverland Date: Tue, 8 Sep 2026 10:12:54 +0800 Subject: [PATCH] feat: support template-specific package managers --- README.md | 23 +++++++++++++++-------- src/index.ts | 17 ++++++++++++++--- test/package-manager-files.test.ts | 30 +++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 72147f2..d7dcfb8 100644 --- a/README.md +++ b/README.md @@ -44,14 +44,21 @@ Omit the option to enable every built-in tool. Pass an array such as ### Package Manager Configuration -When a local template contains `pnpm-workspace.yaml`, the file is only copied -if the project is created with pnpm. Templates loaded from third-party npm -packages are copied without this filtering. - -The toolkit automatically passes the resolved `skipFiles` list to custom -`extraTools` actions. When an action uses `copyFolder` to copy a local tool -template, forward the received list so the same package-manager filtering is -applied: +Use `getPackageManager` to specify a package manager for a template: + +```ts +create({ + getPackageManager: ({ templateName }) => + templateName === 'turborepo' ? 'pnpm' : undefined, + // ...other options +}); +``` + +Return `undefined` to use the package manager detected from the user agent, falling back to npm. + +When a local template contains `pnpm-workspace.yaml`, the file is only copied if the resolved package manager is pnpm. Templates loaded from third-party npm packages are copied without this filtering. + +The toolkit automatically passes the resolved `skipFiles` list to custom `extraTools` actions. When an action uses `copyFolder` to copy a local tool template, forward the received list so the same package-manager filtering is applied: ```ts import { copyFolder, create } from '@rstackjs/create-toolkit'; diff --git a/src/index.ts b/src/index.ts index 15f009c..e226d7c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -463,6 +463,7 @@ type ExtraTool = { */ action?: (context: { templateName: string; + packageManager: string; distFolder: string; skipFiles: string[]; addAgentsMdSearchDirs: (dir: string) => void; @@ -678,6 +679,7 @@ export async function create({ templates, skipFiles, getTemplateName, + getPackageManager, mapESLintTemplate, mapRslintTemplate, version, @@ -694,6 +696,12 @@ export async function create({ skipFiles?: string[]; templates: string[]; getTemplateName: (argv: Argv) => Promise; + /** + * Specify the package manager for the selected template. + * Return undefined to use the package manager detected from the user agent, + * falling back to npm when no user agent is available. + */ + getPackageManager?: (context: { templateName: string }) => string | undefined; /** * Map the template name to the ESLint template name. * If not provided, defaults to 'vanilla-ts' for all templates. @@ -754,14 +762,13 @@ export async function create({ logger.greet(`\n◆ Create ${upperFirst(name)} Project`); const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); - const packageManager = pkgInfo ? pkgInfo.name : 'npm'; - const templateParameters = { packageManager }; + const detectedPackageManager = pkgInfo ? pkgInfo.name : 'npm'; const { isAgent } = await determineAgent(); if (isAgent) { console.log(''); logger.info( - `To create a project non-interactively, run: ${getAgentCreateCommand(name, packageManager)} --template