diff --git a/.gitignore b/.gitignore index 1265784..da6853c 100644 --- a/.gitignore +++ b/.gitignore @@ -82,4 +82,4 @@ typings/ # Compile Output lib/ coverage/ -docs/ \ No newline at end of file +docs/ diff --git a/README.md b/README.md index 7b1c26e..f6296f0 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Full documentation of available functions and configuration can be found on: ### 1. Disparity in supported flags -At the current moment `yarn` has no equivalent flags for `--save-bundle` or `--no-save`. These will be ignored when `yarn` has been detected as package manager. +At the current moment `yarn` and `pnpm` have no equivalent flags for `--save-bundle` or `--no-save`. These will be ignored when `yarn` or `pnpm` has been detected as package manager. The flags that were ignored in the run are returned as the `ignoredFlags` property. diff --git a/package.json b/package.json index 55c0f7d..7268c7c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "keywords": [ "npm", "yarn", + "pnpm", "install", "cli", "script" @@ -37,7 +38,7 @@ "typedoc": "^0.14.2", "typedoc-plugin-external-module-name": "^2.0.0", "typedoc-plugin-internal-external": "^2.0.1", - "typescript": "^3.3.3" + "typescript": "^5.4.3" }, "bugs": { "url": "https://github.com/dkundel/pkg-install/issues", diff --git a/src/install.ts b/src/install.ts index 55749c9..b10418e 100644 --- a/src/install.ts +++ b/src/install.ts @@ -9,6 +9,7 @@ import { import { getExecaConfig, getPackageList } from './helpers'; import { constructNpmArguments, npmProjectInstallArgs } from './npm'; import { getPackageManager, getPackageManagerSync } from './package-manager'; +import { constructPnpmArguments, pnpmProjectInstallArgs } from "./pnpm"; import { PackageList, Packages } from './types'; import { constructYarnArguments, yarnProjectInstallArgs } from './yarn'; @@ -36,7 +37,11 @@ export async function install( const packageList = getPackageList(packages); const getArguments = - pkgManager === 'npm' ? constructNpmArguments : constructYarnArguments; + pkgManager === "npm" + ? constructNpmArguments + : pkgManager === "yarn" + ? constructYarnArguments + : constructPnpmArguments; const { args, ignoredFlags } = getArguments(packageList, config); const result = await execa(pkgManager, args, getExecaConfig(config)); @@ -68,7 +73,11 @@ export function installSync( const packageList = getPackageList(packages); const getArguments = - pkgManager === 'npm' ? constructNpmArguments : constructYarnArguments; + pkgManager === "npm" + ? constructNpmArguments + : pkgManager === "yarn" + ? constructYarnArguments + : constructPnpmArguments; const { args, ignoredFlags } = getArguments(packageList, config); const result = execa.sync(pkgManager, args, getExecaConfig(config)); @@ -97,7 +106,11 @@ export async function projectInstall( const pkgManager = await getPackageManager(config); const args = - pkgManager === 'npm' ? npmProjectInstallArgs : yarnProjectInstallArgs; + pkgManager === "npm" + ? npmProjectInstallArgs + : pkgManager === "yarn" + ? yarnProjectInstallArgs + : pnpmProjectInstallArgs; return execa(pkgManager, args, getExecaConfig(config)); } @@ -121,7 +134,11 @@ export function projectInstallSync( const pkgManager = getPackageManagerSync(config); const args = - pkgManager === 'npm' ? npmProjectInstallArgs : yarnProjectInstallArgs; + pkgManager === "npm" + ? npmProjectInstallArgs + : pkgManager === "yarn" + ? yarnProjectInstallArgs + : pnpmProjectInstallArgs; return execa.sync(pkgManager, args, getExecaConfig(config)); } diff --git a/src/package-manager.ts b/src/package-manager.ts index 9e502dd..37bf75e 100644 --- a/src/package-manager.ts +++ b/src/package-manager.ts @@ -12,7 +12,7 @@ import { /** * Determine what package manager to use based on what preference is set, - * and whether it's currently running in a yarn/npm script + * and whether it's currently running in a yarn/npm/pnpm script * * @export * @param {InstallConfig} config diff --git a/src/pnpm.ts b/src/pnpm.ts new file mode 100644 index 0000000..5b7b394 --- /dev/null +++ b/src/pnpm.ts @@ -0,0 +1,48 @@ +import { InstallConfig, PackageManagerFlag } from "./config"; +import { getFlagsToSet } from "./flags"; +import { UnreachableCaseError } from "./helpers"; +import { ConstructArgumentsResult, PackageList } from "./types"; + +export function constructPnpmArguments( + packageList: PackageList, + config: InstallConfig +): ConstructArgumentsResult { + const flagsToSet = getFlagsToSet(config); + const globalCommand = config.global ? ["--global"] : []; + const cwdCommand = config.forceCwd ? ["--pnpm-prefix", config.cwd] : []; + const args: string[] = [ + "install", + ...globalCommand, + ...cwdCommand, + "add", + ...packageList, + ]; + + const ignoredFlags: PackageManagerFlag[] = []; + flagsToSet.forEach((flag) => { + switch (flag) { + case "dev": + if (!config.global) { + args.push("--save-dev"); + } else { + ignoredFlags.push(flag); + } + break; + case "exact": + args.push("--save-exact"); + break; + case "verbose": + case "bundle": + case "noSave": + ignoredFlags.push(flag); + break; + /* istanbul ignore next */ + default: + throw new UnreachableCaseError(flag); + } + }); + + return { args, ignoredFlags }; +} + +export const pnpmProjectInstallArgs = ["install"]; diff --git a/src/types.ts b/src/types.ts index 409a1ba..d4ce277 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,7 +8,7 @@ export type PackageMap = { export type PackageList = string[]; export type Packages = PackageMap | PackageList; -export type SupportedPackageManagers = 'yarn' | 'npm'; +export type SupportedPackageManagers = 'yarn' | 'npm' | 'pnpm'; /** * What to do with I/O. This is passed to `execa` diff --git a/src/utils/package-manager-utils.ts b/src/utils/package-manager-utils.ts index 9d707e2..25bc9fc 100644 --- a/src/utils/package-manager-utils.ts +++ b/src/utils/package-manager-utils.ts @@ -62,6 +62,10 @@ export function getCurrentPackageManager(): SupportedPackageManagers | null { return 'yarn'; } + if (userAgent.startsWith('pnpm')) { + return 'pnpm'; + } + return null; } @@ -77,6 +81,7 @@ export async function getPackageManagerFromLockfile( ): Promise { const pkgLockPath = path.join(config.cwd, 'package-lock.json'); const yarnLockPath = path.join(config.cwd, 'yarn.lock'); + const pnpmLockPath = path.join(config.cwd, 'pnpm-lock.yaml'); try { await access(pkgLockPath); return 'npm'; @@ -85,7 +90,12 @@ export async function getPackageManagerFromLockfile( await access(yarnLockPath); return 'yarn'; } catch (err) { - return null; + try { + await access(pnpmLockPath); + return 'pnpm'; + } catch (err) { + return null; + } } } } @@ -102,6 +112,7 @@ export function getPackageManagerFromLockfileSync( ): SupportedPackageManagers | null { const pkgLockPath = path.join(config.cwd, 'package-lock.json'); const yarnLockPath = path.join(config.cwd, 'yarn.lock'); + const pnpmLockPath = path.join(config.cwd, 'pnpm-lock.yaml'); try { accessSync(pkgLockPath); return 'npm'; @@ -110,7 +121,12 @@ export function getPackageManagerFromLockfileSync( accessSync(yarnLockPath); return 'yarn'; } catch (err) { - return null; + try { + accessSync(pnpmLockPath); + return 'pnpm'; + } catch (err) { + return null; + } } } } diff --git a/tsconfig.json b/tsconfig.json index 3607e31..d62a5bb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,7 +44,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ @@ -56,5 +56,7 @@ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + "useUnknownInCatchVariables": false } } diff --git a/typedoc.json b/typedoc.json index 9a57dfd..3ee1d4d 100644 --- a/typedoc.json +++ b/typedoc.json @@ -4,6 +4,6 @@ "mode": "modules", "out": "docs", "theme": "default", - "exclude": ["**/__tests__/*.ts", "**/helpers.ts", "**/npm.ts", "**/yarn.ts"], + "exclude": ["**/__tests__/*.ts", "**/helpers.ts", "**/npm.ts", "**/yarn.ts", "**/pnpm.ts"], "gaID": "UA-40008341-3" }