Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
bfabe0f to
821d5db
Compare
7cf64a7 to
ad4d161
Compare
ad4d161 to
9bdcacc
Compare
821d5db to
25e1f67
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/node/node-package-manager.d.ts@@ -27,6 +27,7 @@ export type DependencyType = 'dev' | 'prod' | 'peer';
*/
export declare const packageManager: readonly ["yarn", "npm", "pnpm", "bun", "homebrew", "unknown"];
export type PackageManager = (typeof packageManager)[number];
+export type ProjectPackageManager = Extract<PackageManager, 'yarn' | 'npm' | 'pnpm' | 'bun'>;
/**
* Returns an abort error that's thrown when the package manager can't be determined.
* @returns An abort error.
@@ -68,6 +69,58 @@ export declare function packageManagerFromUserAgent(env?: NodeJS.ProcessEnv): Pa
* @returns The dependency manager
*/
export declare function getPackageManager(fromDirectory: string): Promise<PackageManager>;
+/**
+ * Resolves the package manager for a directory already known to be the project root.
+ *
+ * Use this when the caller already knows the root directory and wants to inspect that root
+ * directly rather than walking upward from a child directory.
+ *
+ * @param rootDirectory - The known project root directory.
+ * @returns The package manager detected from root markers, defaulting to npm <command>
+
+Usage:
+
+npm install install all the dependencies in your project
+npm install <foo> add the <foo> dependency to your project
+npm test run this project's tests
+npm run <foo> run the script named <foo>
+npm <command> -h quick help on <command>
+npm -l display usage info for all commands
+npm help <term> search for help on <term>
+npm help npm more involved overview
+
+All commands:
+
+ access, adduser, audit, bugs, cache, ci, completion,
+ config, dedupe, deprecate, diff, dist-tag, docs, doctor,
+ edit, exec, explain, explore, find-dupes, fund, get, help,
+ help-search, init, install, install-ci-test, install-test,
+ link, ll, login, logout, ls, org, outdated, owner, pack,
+ ping, pkg, prefix, profile, prune, publish, query, rebuild,
+ repo, restart, root, run-script, sbom, search, set,
+ shrinkwrap, star, stars, start, stop, team, test, token,
+ undeprecate, uninstall, unpublish, unstar, update, version,
+ view, whoami
+
+Specify configs in the ini-formatted file:
+ /home/runner/work/_temp/.npmrc
+or on the command line via: npm <command> --key=value
+
+More configuration info: npm help config
+Configuration fields: npm help 7 config
+
+npm@11.3.0 /opt/hostedtoolcache/node/24.1.0/x64/lib/node_modules/npm when no marker exists.
+ * @throws PackageJsonNotFoundError if the provided directory does not contain a package.json.
+ */
+export declare function getPackageManagerForProjectRoot(rootDirectory: string): Promise<ProjectPackageManager>;
+/**
+ * Builds the command and argv needed to execute a local binary using the package manager
+ * detected from the provided directory or its ancestors.
+ */
+export declare function packageManagerBinaryCommandForDirectory(fromDirectory: string, binary: string, ...binaryArgs: string[]): Promise<{
+ command: string;
+ args: string[];
+}>;
interface InstallNPMDependenciesRecursivelyOptions {
/**
* The dependency manager to use to install the dependencies.
|
|
|
||
| readonly directory: string | ||
| readonly packageManager: PackageManager | ||
| readonly packageManager: ProjectPackageManager | 'unknown' |
There was a problem hiding this comment.
can you remove unknown and either make the operation sync or force the caller to narrow?
There was a problem hiding this comment.
Yeah, I looked at that. I kept 'unknown' here because Project is still just modeling what’s on disk, and “app root with no package.json” is a real state.
The sync narrowing is in the next PR which adds requirePackageManagerForOperations(), so install/mutation callers have to narrow before using it.
Happy to fold that in here instead if you’d prefer, but I split it to keep metadata vs operation-owned use separate.
| * @returns The package manager detected from root markers, defaulting to `npm` when no marker exists. | ||
| * @throws PackageJsonNotFoundError if the provided directory does not contain a package.json. | ||
| */ | ||
| export async function getPackageManagerForProjectRoot(rootDirectory: string): Promise<ProjectPackageManager> { |
There was a problem hiding this comment.
When you work with the CLI, I think you typically run it from the project root or from an extension. But both of them already have a package.json, so why using this instead of getPackageManager? Isn't it the same?
|
|
||
| export async function addResolutionOrOverride(directory: string, dependencies: Record<string, string>): Promise<void> { | ||
| const packageManager = await getPackageManager(directory) | ||
| const packageManager = await getPackageManagerForProjectRoot(directory) |
There was a problem hiding this comment.
How do you know that this directory is the root or not? If it must be, I'd make it explicit with the param name or documentation.

What
This follow-up continues the same package-manager stack as #7239.
Add a helper for callers that already know the project root, and use it for project metadata and root dependency update paths instead of depending on the broader upward-walk package-manager helper.
Why
This stack is not about reducing Shopify CLI's supported package managers.
The stack goal is to make callers choose intent, not implementation.
#7239handles directory-local execution for function typegen. This PR handles callers that already know the project root.Even if
getPackageManager(...)remains a broad upward-walk helper, callers that already know the project root should not depend on its broader root-finding and fallback behavior.How
Add
getPackageManagerForProjectRoot(...)to inspect a known project root directly.Use it from:
Project.load(...)addResolutionOrOverride(...)Also narrow
Project.packageManagerto project-valid values plus'unknown'when the app root has nopackage.json.