Skip to content

Commit

Permalink
sku v13: Simplify package manager detection (#982)
Browse files Browse the repository at this point in the history
  • Loading branch information
askoufis authored Jul 5, 2024
1 parent 5be6978 commit 0f091a2
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 85 deletions.
15 changes: 15 additions & 0 deletions .changeset/shy-books-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'sku': major
---

`sku init`: Install dependencies with the package manager that was used to run the command

**BREAKING CHANGE**:

The `sku init` command will now install dependencies with the package manager that was used to run the command. This can be overridden via the `--packageManager` flag:

```sh
npx sku init my-app # Will install dependencies using npm
pnpm dlx sku init my-app # Will install dependencies using pnpm
npx sku init --packageManager yarn my-app # Will install dependencies using yarn
```
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
```
13 changes: 10 additions & 3 deletions fixtures/sku-init/sku-init.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ describe('sku init', () => {
force: true,
});

const { child } = await runSkuScriptInDir('init', fixtureDirectory, [
projectName,
]);
const { child, stdout, stderr } = await runSkuScriptInDir(
'init',
fixtureDirectory,
[projectName],
);

console.log('sku init stdout');
console.log(stdout);
console.log('sku init stderr');
console.error(stderr);

expect(child.exitCode).toBe(0);

Expand Down
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 getPackageManagerFromUserAgent = () => {
const userAgent = 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 (userAgent.includes('yarn')) {
return 'yarn';
}

/**
* @param {SupportedPackageManager} commandName
* @returns {SupportedPackageManager | null}
*/
const detectPackageManagerCommand = (commandName) =>
which(commandName, { nothrow: true }) ? commandName : null;
if (userAgent.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 || getPackageManagerFromUserAgent();

_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
2 changes: 0 additions & 2 deletions packages/sku/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,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 All @@ -137,7 +136,6 @@
"@types/minimist": "^1.2.5",
"@types/picomatch": "^2.3.3",
"@types/react-dom": "^18.2.3",
"@types/which": "^3.0.0",
"@vanilla-extract/css": "^1.0.0",
"@vocab/react": "^1.1.11",
"braid-design-system": "^32.0.0",
Expand Down
26 changes: 9 additions & 17 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 0f091a2

Please sign in to comment.