Skip to content

Commit

Permalink
feat(cpm): add check-package-manager package (#48)
Browse files Browse the repository at this point in the history
Resolves #44
  • Loading branch information
mheob committed Sep 20, 2022
1 parent 86d4c40 commit 6adc64c
Show file tree
Hide file tree
Showing 14 changed files with 294 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/plenty-bugs-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mheob/check-package-manager': major
---

Initial release of the `check-package-manager` package
1 change: 1 addition & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
[ -n "$CI" ] && exit 0

pnpm exec changeset status --since main
pnpm exec check-package-manager
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@changesets/cli": "^2.24.4",
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"@mheob/check-package-manager": "workspace:*",
"@mheob/eslint-config": "workspace:*",
"@mheob/prettier-config": "workspace:*",
"@types/node": "^18.7.18",
Expand Down
16 changes: 16 additions & 0 deletions packages/check-package-manager/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"rules": {
"no-console": "off",
"unicorn/no-process-exit": "off"
},
"overrides": [
{
"files": ["bin/*"],
"rules": {
"no-undef": "off",
"unicorn/filename-case": ["error", { "case": "kebabCase" }],
"unicorn/prefer-module": "off"
}
}
]
}
86 changes: 86 additions & 0 deletions packages/check-package-manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Check for correct package manager

A simple check of the usage of the correct package manager.

## Install

The local installation is optional. It is only needed if you want to have the package inside your project. This could improve
performance, for example.

### With NPM

```sh
npm install -D @mheob/check-package-manager
```

### With YARN

```sh
yarn add -D @mheob/check-package-manager
```

### With PNPM

```sh
pnpm add -D @mheob/check-package-manager
```

## Usage

### As script in `package.json`

Use one of the examples.

```json
"scripts": {
"check-package-manager": "npx check-package-manager npm", // NPM
"check-package-manager": "yarn dlx check-package-manager yarn", // YARN
"check-package-manager": "yarn exec check-package-manager yarn", // YARN - locally installed
"check-package-manager": "pnpm dlx check-package-manager", // PNPM
"check-package-manager": "pnpm dlx check-package-manager pnpm", // PNPM - alternative
"check-package-manager": "pnpm exec check-package-manager", // PNPM - locally installed
"check-package-manager": "pnpm exec check-package-manager pnpm", // PNPM - alternative locally installed
},
```

### As shell script

#### Default (same as `PNPM`)

Use `npx` if you use `NPM` as package manager.\
Otherwise use `exec` if you have installed the package in your project, otherwise use `dlx`.

```sh
npx check-package-manager
```

#### `NPM`

```sh
npx check-package-manager npm
```

#### `YARN`

```sh
yarn dlx check-package-manager yarn
```

#### `PNPM`

```sh
pnpm dlx check-package-manager pnpm
```

### As git hook

For example used in combination with [husky](https://typicode.github.io/husky/).

```sh
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

[ -n "$CI" ] && exit 0

pnpm exec check-package-manager
```
2 changes: 2 additions & 0 deletions packages/check-package-manager/bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('../dist/index.js');
43 changes: 43 additions & 0 deletions packages/check-package-manager/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@mheob/check-package-manager",
"version": "0.0.0",
"description": "A simple check of the usage of the correct package manager.",
"keywords": [
"package-manager",
"node",
"config"
],
"homepage": "https://github.com/mheob/config/tree/main/packages/check-package-manager",
"bugs": "https://github.com/mheob/config/issues",
"repository": {
"type": "git",
"url": "https://github.com/mheob/config"
},
"license": "MIT",
"author": "Alexander Böhm <tools@boehm.work>",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"check-package-manager": "./bin/cli.js"
},
"files": [
"bin",
"dist",
"LICENSE",
"README.md"
],
"scripts": {
"build": "tsc",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
"lint": "eslint **/*.ts --fix",
"sort-package-json": "pnpm dlx sort-package-json"
},
"devDependencies": {
"@mheob/eslint-config": "workspace:*",
"@mheob/tsconfig": "workspace:*",
"eslint": "^8.23.1"
},
"publishConfig": {
"access": "public"
}
}
39 changes: 39 additions & 0 deletions packages/check-package-manager/src/check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { existsSync } from 'node:fs';

import {
getInvalidFileMessage,
getInvalidPackageManagerMessage,
getMissingLockFileMessage,
} from './messages';
import type { AvailablePackageManager } from './packageManager';
import { availablePackageManagers, packageManagerDetails } from './packageManager';
import { isCorrectPackageManager } from './utils';

/**
* Check if the lock file is the correct one for the package manager.
*
* @param selectedPackageManager The package manager to check for. Defaults to `PNPM`.
* @returns An array of error messages. If the array is empty, there are no errors.
*/
export function getCheckLockFilesErrors(
selectedPackageManager: string = packageManagerDetails.PNPM.name,
): string[] {
if (!isCorrectPackageManager(selectedPackageManager)) {
return [getInvalidPackageManagerMessage(selectedPackageManager)];
}

const selectedManager = selectedPackageManager.toUpperCase() as AvailablePackageManager;
const errors: string[] = [];

for (const manager of availablePackageManagers) {
const lockFile = packageManagerDetails[manager].lockFile;
if (manager === selectedManager) {
!existsSync(lockFile) && errors.push(getMissingLockFileMessage(lockFile));
} else {
const selectedLockFile = packageManagerDetails[selectedManager].lockFile;
existsSync(lockFile) && errors.push(getInvalidFileMessage(lockFile, selectedLockFile));
}
}

return errors;
}
13 changes: 13 additions & 0 deletions packages/check-package-manager/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getCheckLockFilesErrors } from './check';

const packageManagerArgument = process.argv[2];
const invalidLockFileErrors = getCheckLockFilesErrors(packageManagerArgument);
const hasErrors = invalidLockFileErrors.length > 0;

if (hasErrors) {
console.info('Invalid package manager or lock files found:');
for (const error of invalidLockFileErrors) {
console.warn(error);
}
process.exit(1);
}
38 changes: 38 additions & 0 deletions packages/check-package-manager/src/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { availablePackageManagers } from './packageManager';

/**
* Creates an error message for incorrect package manager.
*
* @param packageManager The package manager to check for.
* @returns The error message.
*/
export function getInvalidPackageManagerMessage(packageManager: string): string {
let message = `- Package manager "${packageManager}" is invalid or not supported.`;
message += ` Please use one of the following: ${availablePackageManagers.join(', ')}`;
return message;
}

/**
* Creates an error message for using an incorrect package manager lock file.
*
* @param lockFile The lock file of the package manager to check for.
* @param lockFileSelected The lock file of the package manager selected by the user.
* @returns The error message.
*/
export function getInvalidFileMessage(lockFile: string, lockFileSelected: string): string {
let message = `- Invalid occurrence of "${lockFile}" file.`;
message += ` Please remove it and use only "${lockFileSelected}".`;
return message;
}

/**
* Creates an error message for missing package manager lock file.
*
* @param lockFile The lock file of the package manager to check for.
* @returns The error message.
*/
export function getMissingLockFileMessage(lockFile: string): string {
let message = `- The "${lockFile}" does not exist or cannot be read.`;
message += ' Please run the installer of your package manager.';
return message;
}
12 changes: 12 additions & 0 deletions packages/check-package-manager/src/packageManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const availablePackageManagers = ['NPM', 'PNPM', 'YARN'] as const;
export type AvailablePackageManager = typeof availablePackageManagers[number];

type PackageManagerDetails = { name: AvailablePackageManager; lockFile: string };
type PackageManager = Record<AvailablePackageManager, PackageManagerDetails>;

/** The available package managers with their name and lock file. */
export const packageManagerDetails: PackageManager = {
NPM: { name: 'NPM', lockFile: 'package-lock.json' },
PNPM: { name: 'PNPM', lockFile: 'pnpm-lock.yaml' },
YARN: { name: 'YARN', lockFile: 'yarn.lock' },
};
12 changes: 12 additions & 0 deletions packages/check-package-manager/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type AvailablePackageManager, availablePackageManagers } from './packageManager';

/**
* Check if the given package manager is correct and available.
*
* @param packageManager The package manager to check for.
* @returns `true` if the package manager is correct and available, `false` otherwise.
*/
export function isCorrectPackageManager(packageManager: string): boolean {
const manager = packageManager.toUpperCase() as AvailablePackageManager;
return availablePackageManagers.includes(manager);
}
9 changes: 9 additions & 0 deletions packages/check-package-manager/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@mheob/tsconfig/commonjs.json",
"compilerOptions": {
"declaration": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"]
}
23 changes: 17 additions & 6 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 6adc64c

Please sign in to comment.