Skip to content

Commit

Permalink
Simplify package manager detection
Browse files Browse the repository at this point in the history
  • Loading branch information
askoufis committed Mar 21, 2024
1 parent 01c0015 commit 96a454e
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 83 deletions.
16 changes: 16 additions & 0 deletions .changeset/shy-books-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'sku': major
---

Simplify package manager detection

By default, the package manager used to run a sku command will be used by sku for installing dependencies (during `sku init`) or suggesting commands.
This can be overridden via the `--packageManager` flag:

```bash
$ npx sku init --packageManager pnpm my-app
$ cd my-app
$ pnpm start
```

If a package manager cannot be detected, _and_ the `--packageManager` flag is not used, sku will default to using `npm`.
7 changes: 3 additions & 4 deletions docs/docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ Create a new project and start a local development environment:
```bash
$ npx sku init my-app
$ cd my-app
$ yarn start
$ npm start
```

By default, a new project's dependencies will be installed with the first supported package manager detected on your system.
Package managers are detected in the following order: `yarn` -> `pnpm` -> `npm`.
By default, a new project's dependencies will be installed using the package manager it was run with.
This can be overridden via the `--packageManager` flag:

```bash
$ pnpm dlx sku init --packageManager pnpm my-app
$ npx sku init --packageManager pnpm my-app
$ cd my-app
$ pnpm start
```
2 changes: 1 addition & 1 deletion fixtures/translations/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*.less.d.ts
.storybook/main.js
coverage/
dist-ssr/
dist-storybook/
dist/
report/
# end managed by sku
2 changes: 1 addition & 1 deletion fixtures/translations/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
.prettierrc
.storybook/main.js
coverage/
dist-ssr/
dist-storybook/
dist/
report/
tsconfig.json
# end managed by sku
2 changes: 1 addition & 1 deletion fixtures/translations/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ translations.ts
*.less.d.ts
.storybook/main.js
coverage/
dist-ssr/
dist-storybook/
dist/
report/
# end managed by sku
83 changes: 24 additions & 59 deletions packages/sku/lib/packageManager.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,45 @@
const { existsSync } = require('node:fs');
const { join } = require('node:path');
const { cwd } = require('../lib/cwd');
const { findRootSync } = require('@manypkg/find-root');
const { getCommand, INSTALL_PAGE, LOCKS } = require('@antfu/ni');
const { getCommand, INSTALL_PAGE } = require('@antfu/ni');

const { sync: which } = require('which');
const skuArgs = require('../config/args');

/** @typedef {'yarn' | 'pnpm' | 'npm'} SupportedPackageManager */

/** @type {Array<SupportedPackageManager>} */
const supportedPackageManagers = ['yarn', 'pnpm', 'npm'];
const getPackageManagerFromNpmUserAgent = () => {
const env = process.env.npm_config_user_agent || '';

/** @type {Record<SupportedPackageManager, string>} */
const lockfileForPackageManager = Object.fromEntries(
Object.entries(LOCKS)
.filter(([, packageManager]) =>
supportedPackageManagers.includes(packageManager),
)
.map(([lockfileName, packageManager]) => [packageManager, lockfileName]),
);

const supportedLockfiles = supportedPackageManagers.map(
(packageManager) => lockfileForPackageManager[packageManager],
);
if (env.includes('yarn')) {
return 'yarn';
}

/**
* @param {SupportedPackageManager} commandName
* @returns {SupportedPackageManager | null}
*/
const detectPackageManagerCommand = (commandName) =>
which(commandName, { nothrow: true }) ? commandName : null;
if (env.includes('pnpm')) {
return 'pnpm';
}

const detectPackageManager = () =>
detectPackageManagerCommand('yarn') ||
detectPackageManagerCommand('pnpm') ||
'npm';
return 'npm';
};

/**
* Get the package manager and root directory of the project. If the project does not have a
* package manager configured, a supported package manager will be detected in your `PATH`, and
* `rootDir` will be `null`.
* @returns {{packageManager: SupportedPackageManager, rootDir: string | null}}
* Get the package manager and root directory of the project. The package manager is derived from
* the `packageManager` CLI argument if present, falling back to the `npm_config_user_agent` envar.
* If the project does not have a root directory, `rootDir` will be `null`.
*/
const getPackageManager = () => {
let _packageManager = skuArgs?.packageManager;

// @manypkg/find-root only returns a tool if it finds a monorepo.
// If it finds a regular repo, it will return a 'root' tool, which is absolutely useless.
// So we need to detect the package manager ourselves. I'd use `detect` from from `@antfu/ni` or
// `detect-package-manager`, but they're async only and we can't make getPackageManager async.
try {
const { rootDir } = findRootSync(cwd());

let foundPackageManager;

for (const supportedLockfile of supportedLockfiles) {
if (existsSync(join(rootDir, supportedLockfile))) {
foundPackageManager = LOCKS[supportedLockfile];
break;
}
}

if (!supportedPackageManagers.includes(foundPackageManager)) {
throw new Error('Unsupported package manager found');
}
/** @type {SupportedPackageManager} */
const packageManager =
skuArgs?.packageManager || getPackageManagerFromNpmUserAgent();

_packageManager ||= foundPackageManager;
/** @type {string | null} */
let rootDir = null;

return { packageManager: _packageManager, rootDir };
try {
rootDir = findRootSync(cwd()).rootDir;
} catch {
_packageManager ||= detectPackageManager();

return { packageManager: _packageManager, rootDir: null };
// No root found (occurs during `sku init`), `rootDir` will stay `null`
}

return { packageManager, rootDir };
};

const { rootDir, packageManager } = getPackageManager();
Expand Down Expand Up @@ -162,7 +128,6 @@ const getWhyCommand = () => {
const getPackageManagerInstallPage = () => INSTALL_PAGE[packageManager];

module.exports = {
supportedPackageManagers,
rootDir,
packageManager,
isYarn,
Expand Down
1 change: 0 additions & 1 deletion packages/sku/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@
"webpack-dev-server": "^5.0.2",
"webpack-merge": "^5.8.0",
"webpack-node-externals": "^3.0.0",
"which": "^4.0.0",
"wrap-ansi": "^7.0.0",
"x-default-browser": "^0.5.0"
},
Expand Down
16 changes: 0 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 96a454e

Please sign in to comment.