From 8133faa6a165bc37240a3df64ad8f669ada7c5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez?= Date: Wed, 14 Aug 2024 19:32:30 +0200 Subject: [PATCH] chore: use `package-manager-detector` (#227) --- build.config.ts | 13 ++-- package.json | 3 +- pnpm-lock.yaml | 56 +++------------- src/agents.ts | 106 ++----------------------------- src/detect.ts | 54 ++++------------ src/parse.ts | 6 +- src/runner.ts | 6 +- taze.config.ts | 4 -- test/config/config.test.ts | 4 +- test/programmatic/detect.spec.ts | 2 +- test/programmatic/runCli.spec.ts | 2 +- tsconfig.json | 2 +- 12 files changed, 42 insertions(+), 216 deletions(-) diff --git a/build.config.ts b/build.config.ts index ff9886e7..8dcd97f8 100644 --- a/build.config.ts +++ b/build.config.ts @@ -3,16 +3,17 @@ import { defineBuildConfig } from 'unbuild' import fg from 'fast-glob' export default defineBuildConfig({ - entries: [ - ...fg.sync('src/commands/*.ts').map(i => ({ - input: i.slice(0, -3), - name: basename(i).slice(0, -3), - })), - ], + entries: fg.sync('src/commands/*.ts').map(i => ({ + input: i.slice(0, -3), + name: basename(i).slice(0, -3), + })), clean: true, declaration: true, rollup: { emitCJS: true, inlineDependencies: true, + commonjs: { + exclude: ['**/*.d.ts'], + }, }, }) diff --git a/package.json b/package.json index 8acaa528..da9d3561 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ }, "exports": { ".": { - "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs" } @@ -58,10 +57,10 @@ "bumpp": "^9.4.2", "eslint": "^9.8.0", "fast-glob": "^3.3.2", - "find-up": "^6.3.0", "fs-extra": "^11.2.0", "fzf": "^0.5.2", "ini": "^4.1.3", + "package-manager-detector": "^0.1.1", "picocolors": "^1.0.1", "taze": "^0.16.3", "terminal-link": "^3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3323e3aa..dd11614a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,9 +38,6 @@ importers: fast-glob: specifier: ^3.3.2 version: 3.3.2 - find-up: - specifier: ^6.3.0 - version: 6.3.0 fs-extra: specifier: ^11.2.0 version: 11.2.0 @@ -50,6 +47,9 @@ importers: ini: specifier: ^4.1.3 version: 4.1.3 + package-manager-detector: + specifier: ^0.1.1 + version: 0.1.1 picocolors: specifier: ^1.0.1 version: 1.0.1 @@ -1794,10 +1794,6 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-up@6.3.0: - resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -2147,10 +2143,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2359,10 +2351,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -2371,10 +2359,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} @@ -2386,6 +2370,9 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-manager-detector@0.1.1: + resolution: {integrity: sha512-KRfleQVD8jK1lIOo6fJxAtBH0fYo/r9q1+2Zs931cz0GLt1WeGYgIC0J+kETRZk8Ha2HghztDQSEqbeo00bzhw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2409,10 +2396,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2996,10 +2979,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} @@ -4718,11 +4697,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-up@6.3.0: - dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - flat-cache@4.0.1: dependencies: flatted: 3.3.1 @@ -5046,10 +5020,6 @@ snapshots: dependencies: p-locate: 5.0.0 - locate-path@7.2.0: - dependencies: - p-locate: 6.0.0 - lodash.merge@4.6.2: {} lodash@4.17.21: {} @@ -5294,10 +5264,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.0.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -5306,10 +5272,6 @@ snapshots: dependencies: p-limit: 3.1.0 - p-locate@6.0.0: - dependencies: - p-limit: 4.0.0 - p-map@4.0.0: dependencies: aggregate-error: 3.1.0 @@ -5318,6 +5280,8 @@ snapshots: package-json-from-dist@1.0.0: {} + package-manager-detector@0.1.1: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5347,8 +5311,6 @@ snapshots: path-exists@4.0.0: {} - path-exists@5.0.0: {} - path-key@3.1.1: {} path-key@4.0.0: {} @@ -5936,5 +5898,3 @@ snapshots: yargs-parser: 21.1.1 yocto-queue@0.1.0: {} - - yocto-queue@1.0.0: {} diff --git a/src/agents.ts b/src/agents.ts index 07becdb6..40d2728c 100644 --- a/src/agents.ts +++ b/src/agents.ts @@ -1,104 +1,6 @@ -function npmRun(agent: string) { - return (args: string[]) => { - if (args.length > 1) - return `${agent} run ${args[0]} -- ${args.slice(1).join(' ')}` - else return `${agent} run ${args[0]}` - } -} +import type { Agent, Command } from 'package-manager-detector/agents' +import { AGENTS, COMMANDS, INSTALL_PAGE, LOCKS } from 'package-manager-detector/agents' -const yarn = { - 'agent': 'yarn {0}', - 'run': 'yarn run {0}', - 'install': 'yarn install {0}', - 'frozen': 'yarn install --frozen-lockfile', - 'global': 'yarn global add {0}', - 'add': 'yarn add {0}', - 'upgrade': 'yarn upgrade {0}', - 'upgrade-interactive': 'yarn upgrade-interactive {0}', - 'execute': 'npx {0}', - 'uninstall': 'yarn remove {0}', - 'global_uninstall': 'yarn global remove {0}', -} -const pnpm = { - 'agent': 'pnpm {0}', - 'run': 'pnpm run {0}', - 'install': 'pnpm i {0}', - 'frozen': 'pnpm i --frozen-lockfile', - 'global': 'pnpm add -g {0}', - 'add': 'pnpm add {0}', - 'upgrade': 'pnpm update {0}', - 'upgrade-interactive': 'pnpm update -i {0}', - 'execute': 'pnpm dlx {0}', - 'uninstall': 'pnpm remove {0}', - 'global_uninstall': 'pnpm remove --global {0}', -} -const bun = { - 'agent': 'bun {0}', - 'run': 'bun run {0}', - 'install': 'bun install {0}', - 'frozen': 'bun install --frozen-lockfile', - 'global': 'bun add -g {0}', - 'add': 'bun add {0}', - 'upgrade': 'bun update {0}', - 'upgrade-interactive': 'bun update {0}', - 'execute': 'bun x {0}', - 'uninstall': 'bun remove {0}', - 'global_uninstall': 'bun remove -g {0}', -} +export { AGENTS, COMMANDS, INSTALL_PAGE, LOCKS } -export const AGENTS = { - 'npm': { - 'agent': 'npm {0}', - 'run': npmRun('npm'), - 'install': 'npm i {0}', - 'frozen': 'npm ci', - 'global': 'npm i -g {0}', - 'add': 'npm i {0}', - 'upgrade': 'npm update {0}', - 'upgrade-interactive': null, - 'execute': 'npx {0}', - 'uninstall': 'npm uninstall {0}', - 'global_uninstall': 'npm uninstall -g {0}', - }, - 'yarn': yarn, - 'yarn@berry': { - ...yarn, - 'frozen': 'yarn install --immutable', - 'upgrade': 'yarn up {0}', - 'upgrade-interactive': 'yarn up -i {0}', - 'execute': 'yarn dlx {0}', - // Yarn 2+ removed 'global', see https://github.com/yarnpkg/berry/issues/821 - 'global': 'npm i -g {0}', - 'global_uninstall': 'npm uninstall -g {0}', - }, - 'pnpm': pnpm, - // pnpm v6.x or below - 'pnpm@6': { - ...pnpm, - run: npmRun('pnpm'), - }, - 'bun': bun, -} - -export type Agent = keyof typeof AGENTS -export type Command = keyof typeof AGENTS.npm - -export const agents = Object.keys(AGENTS) as Agent[] - -// the order here matters, more specific one comes first -export const LOCKS: Record = { - 'bun.lockb': 'bun', - 'pnpm-lock.yaml': 'pnpm', - 'yarn.lock': 'yarn', - 'package-lock.json': 'npm', - 'npm-shrinkwrap.json': 'npm', -} - -export const INSTALL_PAGE: Record = { - 'bun': 'https://bun.sh', - 'pnpm': 'https://pnpm.io/installation', - 'pnpm@6': 'https://pnpm.io/6.x/installation', - 'yarn': 'https://classic.yarnpkg.com/en/docs/install', - 'yarn@berry': 'https://yarnpkg.com/getting-started/install', - 'npm': 'https://docs.npmjs.com/cli/v8/configuring-npm/install', -} +export type { Agent, Command } diff --git a/src/detect.ts b/src/detect.ts index 9e3cf8ef..acd90118 100644 --- a/src/detect.ts +++ b/src/detect.ts @@ -1,12 +1,9 @@ -import fs from 'node:fs' -import path from 'node:path' import process from 'node:process' import { async as ezspawn } from '@jsdevtools/ez-spawn' -import { findUp } from 'find-up' +import { detect as detectPM } from 'package-manager-detector' import terminalLink from 'terminal-link' import prompts from '@posva/prompts' -import type { Agent } from './agents' -import { AGENTS, INSTALL_PAGE, LOCKS } from './agents' +import { INSTALL_PAGE } from './agents' import { cmdExists } from './utils' export interface DetectOptions { @@ -16,46 +13,17 @@ export interface DetectOptions { } export async function detect({ autoInstall, programmatic, cwd }: DetectOptions = {}) { - let agent: Agent | null = null - let version: string | null = null - - const lockPath = await findUp(Object.keys(LOCKS), { cwd }) - let packageJsonPath: string | undefined - - if (lockPath) - packageJsonPath = path.resolve(lockPath, '../package.json') - else - packageJsonPath = await findUp('package.json', { cwd }) - - // read `packageManager` field in package.json - if (packageJsonPath && fs.existsSync(packageJsonPath)) { - try { - const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) - if (typeof pkg.packageManager === 'string') { - const [name, ver] = pkg.packageManager.replace(/^\^/, '').split('@') - version = ver - if (name === 'yarn' && Number.parseInt(ver) > 1) { - agent = 'yarn@berry' - // the version in packageManager isn't the actual yarn package version - version = 'berry' - } - else if (name === 'pnpm' && Number.parseInt(ver) < 7) { - agent = 'pnpm@6' - } - else if (name in AGENTS) { - agent = name - } - else if (!programmatic) { - console.warn('[ni] Unknown packageManager:', pkg.packageManager) - } + const pmDetection = await detectPM({ + cwd, + onUnknown: (packageManager) => { + if (!programmatic) { + console.warn('[ni] Unknown packageManager:', packageManager) } - } - catch {} - } + }, + }) - // detect based on lock - if (!agent && lockPath) - agent = LOCKS[path.basename(lockPath)] + const agent = pmDetection?.agent ?? null + const version = pmDetection?.version ?? null // auto install if (agent && !cmdExists(agent.split('@')[0]) && !programmatic) { diff --git a/src/parse.ts b/src/parse.ts index 5626deaf..3a9a19cc 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,5 +1,5 @@ import type { Agent, Command } from './agents' -import { AGENTS } from './agents' +import { AGENTS, COMMANDS } from './agents' import { exclude } from './utils' import type { Runner } from './runner' @@ -14,10 +14,10 @@ export function getCommand( command: Command, args: string[] = [], ) { - if (!(agent in AGENTS)) + if (!AGENTS.includes(agent)) throw new Error(`Unsupported agent "${agent}"`) - const c = AGENTS[agent][command] + const c = COMMANDS[agent][command] if (typeof c === 'function') return c(args) diff --git a/src/runner.ts b/src/runner.ts index e128ec55..3dd8127a 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -7,7 +7,7 @@ import { async as ezspawn } from '@jsdevtools/ez-spawn' import c from 'picocolors' import { version } from '../package.json' import type { Agent } from './agents' -import { agents } from './agents' +import { AGENTS } from './agents' import { getDefaultAgent, getGlobalAgent } from './config' import type { DetectOptions } from './detect' import { detect } from './detect' @@ -59,7 +59,7 @@ export async function getCliCommand( name: 'agent', type: 'select', message: 'Choose the agent', - choices: agents.filter(i => !i.includes('@')).map(value => ({ title: value, value })), + choices: AGENTS.filter(i => !i.includes('@')).map(value => ({ title: value, value })), }) ).agent if (!agent) @@ -85,7 +85,7 @@ export async function run(fn: Runner, args: string[], options: DetectOptions = { } if (args.length === 1 && (args[0]?.toLowerCase() === '-v' || args[0] === '--version')) { - const getCmd = (a: Agent) => agents.includes(a) ? getCommand(a, 'agent', ['-v']) : `${a} -v` + const getCmd = (a: Agent) => AGENTS.includes(a) ? getCommand(a, 'agent', ['-v']) : `${a} -v` const getV = (a: string, o: EzSpawnOptions = {}) => ezspawn(getCmd(a as Agent), o).then(e => e.stdout).then(e => e.startsWith('v') ? e : `v${e}`) const globalAgentPromise = getGlobalAgent() const globalAgentVersionPromise = globalAgentPromise.then(getV) diff --git a/taze.config.ts b/taze.config.ts index a23931b5..fcf42172 100644 --- a/taze.config.ts +++ b/taze.config.ts @@ -4,8 +4,4 @@ export default defineConfig({ ignorePaths: [ 'test/fixtures', ], - exclude: [ - // v7.0.0 has some bundle issue - 'find-up', - ], }) diff --git a/test/config/config.test.ts b/test/config/config.test.ts index 71a73738..40fa262b 100644 --- a/test/config/config.test.ts +++ b/test/config/config.test.ts @@ -9,8 +9,8 @@ beforeEach(() => { vi.resetModules() }) -vi.mock('find-up', () => ({ - findUp: vi.fn(), +vi.mock('../../src/detect', () => ({ + detect: vi.fn(), })) it('has correct defaults', async () => { diff --git a/test/programmatic/detect.spec.ts b/test/programmatic/detect.spec.ts index 4fcfcf21..3cb7351a 100644 --- a/test/programmatic/detect.spec.ts +++ b/test/programmatic/detect.spec.ts @@ -28,7 +28,7 @@ afterAll(() => { vi.resetAllMocks() }) -const agents = [...Object.keys(AGENTS), 'unknown'] +const agents = [...AGENTS, 'unknown'] const fixtures = ['lockfile', 'packager'] // matrix testing of: fixtures x agents diff --git a/test/programmatic/runCli.spec.ts b/test/programmatic/runCli.spec.ts index a95545ee..d6473371 100644 --- a/test/programmatic/runCli.spec.ts +++ b/test/programmatic/runCli.spec.ts @@ -58,7 +58,7 @@ afterAll(() => { vi.resetAllMocks() }) -const agents = [...Object.keys(AGENTS), 'unknown'] +const agents = [...AGENTS, 'unknown'] const fixtures = ['lockfile', 'packager'] // matrix testing of: fixtures x agents x commands diff --git a/tsconfig.json b/tsconfig.json index f50c6833..3725f5f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "es2017", "lib": ["esnext"], "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "Bundler", "resolveJsonModule": true, "strict": true, "strictNullChecks": true,