├── .editorconfig
├── .eslintrc
├── .github
├── FUNDING.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── dependency-review.yml
│ ├── exit-silently-on-unsupported.yml
│ ├── lint.yml
│ └── nodejs.yml
├── .gitignore
├── .husky
└── pre-push
├── .npmrc
├── LICENSE
├── README.md
├── cli-wrapper.cjs
├── cli.js
├── installed-check.svg
├── package.json
├── renovate.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_style = space
7 | indent_size = 2
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@voxpelli/eslint-config/esm",
3 | "root": true
4 | }
5 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [voxpelli]
4 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 | schedule:
9 | - cron: '44 1 * * 3'
10 |
11 | permissions:
12 | actions: read
13 | contents: read
14 | security-events: write
15 |
16 | jobs:
17 | analyze:
18 | uses: voxpelli/ghatemplates/.github/workflows/codeql-analysis.yml@main
19 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yml:
--------------------------------------------------------------------------------
1 | name: 'Dependency Review'
2 |
3 | on: [pull_request]
4 |
5 | permissions:
6 | contents: read
7 |
8 | jobs:
9 | dependency-review:
10 | uses: voxpelli/ghatemplates/.github/workflows/dependency-review.yml@main
11 |
12 |
--------------------------------------------------------------------------------
/.github/workflows/exit-silently-on-unsupported.yml:
--------------------------------------------------------------------------------
1 | name: Exit silently on unsupported
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | tags:
8 | - '*'
9 | pull_request:
10 | branches:
11 | - main
12 |
13 | permissions:
14 | contents: read
15 |
16 | jobs:
17 | lint:
18 | uses: voxpelli/ghatemplates/.github/workflows/exit-silently-on-unsupported.yml@main
19 | with:
20 | files: './cli-wrapper.cjs'
21 | node-versions: '12'
22 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Linting
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | tags:
8 | - '*'
9 | pull_request:
10 | branches:
11 | - main
12 |
13 | permissions:
14 | contents: read
15 |
16 | jobs:
17 | lint:
18 | uses: voxpelli/ghatemplates/.github/workflows/lint.yml@main
19 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | tags:
8 | - '*'
9 | pull_request:
10 | branches:
11 | - main
12 |
13 | permissions:
14 | contents: read
15 |
16 | jobs:
17 | test:
18 | uses: voxpelli/ghatemplates/.github/workflows/test.yml@main
19 | with:
20 | node-versions: '18,20,22'
21 | os: 'ubuntu-latest,windows-latest'
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Basic ones
2 | /coverage
3 | /docs
4 | /node_modules
5 | /.env
6 | /.nyc_output
7 |
8 | # We're a library, so please, no lock files
9 | /package-lock.json
10 | /yarn.lock
11 |
12 | # Generated types
13 | *.d.ts
14 | *.d.ts.map
15 |
16 | # Library specific ones
17 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | npm test
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Pelle Wessman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

8 |
9 |
10 |
11 |
12 | [](https://www.npmjs.com/package/installed-check)
13 | [](https://www.npmjs.com/package/installed-check)
14 | [](https://libraries.io/npm/installed-check)
15 | [](https://github.com/voxpelli/eslint-config)
16 | [](https://mastodon.social/@voxpelli)
17 |
18 |
19 |
20 | Verifies that installed modules comply with the requirements specified in `package.json`.
21 |
22 | By default checks engine ranges, peer dependency ranges and installed versions and, in mono-repos using workspaces, by default checks all workspaces as well as the workspace root.
23 |
24 | ## Usage
25 |
26 | ### Command line
27 |
28 | ```sh
29 | npm install -g installed-check
30 | ```
31 |
32 | Then run it at the root of your project to validate the installed dependencies:
33 |
34 | ```sh
35 | installed-check
36 | ```
37 |
38 | ### As npm script
39 |
40 | ```sh
41 | npm install --save-dev installed-check
42 | ```
43 |
44 | ```json
45 | "scripts": {
46 | "test": "installed-check"
47 | }
48 | ```
49 |
50 | ### Programmatic use
51 |
52 | Use [installed-check-core](https://github.com/voxpelli/node-installed-check-core)
53 |
54 | ## Checks
55 |
56 | * `--engine-check` / `-e` – if set `installed-check` will check that the installed modules doesn't have stricter [`engines`](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines) ranges than those in the `package.json` and suggests an alternative requirement if they do. If set, the default checks will be overriden.
57 | * `--peer-check` / `-p` – if set `installed-check` will check that the installed modules doesn't have stricter [`peerDependencies`](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#peerdependencies) ranges than those in the `package.json` and suggests an alternative requirement if they do. If set, the default checks will be overriden.
58 | * `--version-check` / `-c` – if set `installed-check` will check that the installed modules comply with the version requirements set for them the `package.json`. If set, the default checks will be overriden.
59 |
60 | ## Check options
61 |
62 | * `--ignore ARG` / `-i ARG` – excludes the named dependency from non-version checks. Supports [`picomatch`](https://www.npmjs.com/package/picomatch) globbing syntax, eg. `@types/*` (but be sure to provide the pattern in a way that avoids your shell from matching it against files first)
63 | * `--ignore-dev` / `-d` – if set then dev dependencies won't be included in the non-version checks.
64 | * `--strict` / `-s` – treats warnings as errors
65 |
66 | ## Fix options
67 | * `--fix` – tries to apply all suggestions and write them back to disk
68 |
69 | ## Workspace options
70 |
71 | * `--no-include-workspace-root` – excludes the workspace root package. Negated equivalent of npm's [`--include-workspace-root`](https://docs.npmjs.com/cli/v10/commands/npm-run-script#include-workspace-root)
72 | * `--no-workspaces` – excludes workspace packages. Negated equivalent of npm's [`--workspaces`](https://docs.npmjs.com/cli/v10/commands/npm-run-script#workspaces)
73 | * `--workspace=ARG` / `-w ARG` – excludes all workspace packages not matching these names / paths. Equivalent to npm's [`--workspace` / `-w`](https://docs.npmjs.com/cli/v10/commands/npm-run-script#workspace)
74 | * `--workspace-ignore=ARG` – xcludes the specified paths from workspace lookup. (Supports globs)
75 |
76 | ### Additional command line options
77 |
78 | * `--debug` – prints debug info
79 | * `--verbose` / `-v` – prints warnings and notices
80 | * `--help` / `-h` – prints help and exits
81 | * `--version` – prints current version and exits
82 |
83 | ## Similar modules
84 |
85 | * [`knip`](https://github.com/webpro/knip) – finds unused files, dependencies and exports in your JavaScript and TypeScript projects – a great companion module to `installed-check`
86 |
--------------------------------------------------------------------------------
/cli-wrapper.cjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | 'use strict';
4 |
5 | require('version-guard')('./cli.js', 18, 6);
6 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console, unicorn/no-process-exit */
2 |
3 | import chalk from 'chalk';
4 | import meow from 'meow';
5 | import { messageWithCauses, stackWithCauses } from 'pony-cause';
6 | import { installedCheck, ROOT } from 'installed-check-core';
7 |
8 | const EXIT_CODE_ERROR_RESULT = 1;
9 | const EXIT_CODE_INVALID_INPUT = 2;
10 | const EXIT_CODE_UNEXPECTED_ERROR = 4;
11 |
12 | const cli = meow(`
13 | Usage
14 | $ installed-check
15 |
16 | Defaults to current folder and to perform all checks.
17 |
18 | Checks
19 | -e, --engine-check Override default checks and explicitly request an engine range check
20 | -p, --peer-check Override default checks and explicitly request a peer dependency range check
21 | -c, --version-check Override default checks and explicitly request a check of installed versions
22 |
23 | Check options
24 | -i ARG, --ignore=ARG Excludes the named dependency from non-version checks.(Supports globs)
25 | -d, --ignore-dev Excludes dev dependencies from non-version checks
26 | -s, --strict Treat warnings as errors
27 |
28 | Fix options
29 | --fix Tries to apply all suggestions and write them back to disk
30 |
31 | Workspace options
32 | --no-include-workspace-root Will exclude the workspace root package
33 | --no-workspaces Will exclude workspace packages
34 | -w ARG, --workspace=ARG Excludes all workspace packages not matching these names / paths
35 | --workspace-ignore=ARG Excludes the specified paths from workspace lookup. (Supports globs)
36 |
37 | Options
38 | --debug Prints debug info
39 | --help Print this help and exits
40 | --version Prints current version and exits
41 | -v, --verbose Shows warnings
42 |
43 | Examples
44 | $ installed-check
45 | `, {
46 | flags: {
47 | debug: { type: 'boolean' },
48 | engineCheck: { shortFlag: 'e', type: 'boolean' },
49 | engineIgnore: { type: 'string', isMultiple: true },
50 | engineNoDev: { type: 'boolean' },
51 | fix: { type: 'boolean' },
52 | ignore: { shortFlag: 'i', type: 'string', isMultiple: true },
53 | ignoreDev: { shortFlag: 'd', type: 'boolean' },
54 | includeWorkspaceRoot: { type: 'boolean', 'default': true },
55 | peerCheck: { shortFlag: 'p', type: 'boolean' },
56 | strict: { shortFlag: 's', type: 'boolean' },
57 | verbose: { shortFlag: 'v', type: 'boolean' },
58 | versionCheck: { shortFlag: 'c', type: 'boolean' },
59 | workspace: { shortFlag: 'w', type: 'string', isMultiple: true },
60 | workspaceIgnore: { type: 'string', isMultiple: true },
61 | workspaces: { type: 'boolean', 'default': true },
62 | },
63 | importMeta: import.meta,
64 | });
65 |
66 | if (cli.input.length > 1) {
67 | console.error(chalk.bgRed('Invalid input:') + ` Can only handle a single folder path, but received ${cli.input.length} paths: "${cli.input.join('", "')}"` + '\n');
68 | process.exit(EXIT_CODE_INVALID_INPUT);
69 | }
70 |
71 | const {
72 | debug,
73 | engineCheck,
74 | engineIgnore, // deprecated
75 | engineNoDev, // deprecated
76 | fix = false,
77 | includeWorkspaceRoot,
78 | peerCheck,
79 | strict,
80 | verbose,
81 | versionCheck,
82 | workspace,
83 | workspaceIgnore,
84 | workspaces,
85 | } = cli.flags;
86 |
87 | let {
88 | ignore,
89 | ignoreDev,
90 | } = cli.flags;
91 |
92 | // Handle deprecated flags
93 | if (engineIgnore?.length) {
94 | ignore = [...ignore || [], ...engineIgnore];
95 | console.error(chalk.bgRed.black('DEPRECATED:') + ' --engine-ignore is replace by --ignore');
96 | }
97 | if (engineNoDev) {
98 | ignoreDev = engineNoDev;
99 | console.error(chalk.bgRed.black('DEPRECATED:') + ' --engine-no-dev is replace by --ignore-dev');
100 | }
101 |
102 | /** @type {import('installed-check-core').InstalledChecks[]} */
103 | let checks = [
104 | ...engineCheck ? /** @type {const} */ (['engine']) : [],
105 | ...peerCheck ? /** @type {const} */ (['peer']) : [],
106 | ...versionCheck ? /** @type {const} */ (['version']) : [],
107 | ];
108 |
109 | /** @type {import('installed-check-core').LookupOptions} */
110 | const lookupOptions = {
111 | cwd: cli.input[0],
112 | ignorePaths: workspaceIgnore,
113 | includeWorkspaceRoot,
114 | skipWorkspaces: !workspaces,
115 | workspace,
116 | };
117 |
118 | /** @type {import('installed-check-core').InstalledCheckOptions} */
119 | const checkOptions = {
120 | noDev: ignoreDev,
121 | ignore,
122 | strict,
123 | };
124 |
125 | if (checks.length === 0) {
126 | checks = ['engine', 'peer', 'version'];
127 | }
128 |
129 | if (debug) {
130 | const { inspect } = await import('node:util');
131 | console.log(chalk.blue('Checks:') + ' ' + inspect(checks, { colors: true, compact: true }));
132 | console.log(chalk.blue('Lookup options:') + ' ' + inspect(lookupOptions, { colors: true, compact: true }));
133 | console.log(chalk.blue('Check options:') + ' ' + inspect(checkOptions, { colors: true, compact: true }));
134 | }
135 |
136 | try {
137 | const result = await installedCheck(checks, lookupOptions, { ...checkOptions, fix });
138 |
139 | if (verbose && result.warnings.length) {
140 | console.log('\n' + chalk.bgYellow.black('Warnings:') + '\n\n' + result.warnings.join('\n') + '\n');
141 | } else if (result.errors.length) {
142 | console.log('');
143 | }
144 |
145 | if (result.errors.length) {
146 | console.error(chalk.bgRed.black('Errors:') + '\n\n' + result.errors.join('\n') + '\n');
147 | }
148 |
149 | if (result.suggestions.length) {
150 | console.error(chalk.bgCyanBright.black('Suggestions:') + '\n\n' + result.suggestions.join('\n') + '\n');
151 | }
152 |
153 | const workspaceSuccess = /** @type {const} */ ([
154 | ...Object.entries(result.workspaceSuccess),
155 | ...(result.workspaceSuccess[ROOT] === undefined ? [] : /** @type {const} */ ([['root', result.workspaceSuccess[ROOT]]])),
156 | ]);
157 |
158 | if (verbose && workspaceSuccess.length) {
159 | if (result.errors.length === 0 && workspaceSuccess.length === 1 && result.workspaceSuccess[ROOT]) {
160 | console.log(chalk.bgGreen.black('Successful!') + '\n');
161 | } else {
162 | const success = workspaceSuccess.filter(([, value]) => value);
163 | const failure = workspaceSuccess.filter(([, value]) => !value);
164 |
165 | if (success.length) {
166 | console.log(chalk.bgGreen.black('Successful workspaces:') + ' ' + success.map(([key]) => key).join(', ') + '\n');
167 | }
168 | if (failure.length) {
169 | console.log(chalk.bgRed.black('Unsuccessful workspaces:') + ' ' + failure.map(([key]) => key).join(', ') + '\n');
170 | }
171 | }
172 | }
173 |
174 | if (result.errors.length) {
175 | process.exit(EXIT_CODE_ERROR_RESULT);
176 | }
177 | } catch (err) {
178 | console.error(chalk.bgRed('Unexpected error:') + ' ' + (err instanceof Error ? messageWithCauses(err) + '\n\n' + stackWithCauses(err) : err) + '\n');
179 | process.exit(EXIT_CODE_UNEXPECTED_ERROR);
180 | }
181 |
--------------------------------------------------------------------------------
/installed-check.svg:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "installed-check",
3 | "version": "9.3.0",
4 | "description": "Verifies that installed modules comply with the requirements specified in package.json",
5 | "homepage": "http://github.com/voxpelli/node-installed-check",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/voxpelli/node-installed-check.git"
9 | },
10 | "type": "module",
11 | "bin": {
12 | "installed-check": "cli-wrapper.cjs"
13 | },
14 | "files": [
15 | "cli-wrapper.cjs",
16 | "cli.js"
17 | ],
18 | "scripts": {
19 | "check:lint": "eslint --report-unused-disable-directives .",
20 | "check:installed-check": "node cli-wrapper.cjs -i eslint-plugin-jsdoc -i knip",
21 | "check:knip": "knip",
22 | "check:tsc": "tsc",
23 | "check:type-coverage": "type-coverage --detail --strict --at-least 95",
24 | "check": "run-p check:*",
25 | "prepare": "husky",
26 | "test-ci": "run-s check:installed-check",
27 | "test": "run-s check"
28 | },
29 | "keywords": [
30 | "cli",
31 | "dependencies",
32 | "devDependencies",
33 | "peerDependencies",
34 | "lint",
35 | "maintenance",
36 | "monorepo",
37 | "scan",
38 | "workspace"
39 | ],
40 | "author": "Pelle Wessman (http://kodfabrik.se/)",
41 | "license": "MIT",
42 | "engines": {
43 | "node": ">=18.6.0"
44 | },
45 | "dependencies": {
46 | "chalk": "^5.3.0",
47 | "installed-check-core": "^8.3.0",
48 | "meow": "^13.0.0",
49 | "pony-cause": "^2.1.10",
50 | "version-guard": "^1.1.1"
51 | },
52 | "devDependencies": {
53 | "@types/node": "^18.19.29",
54 | "@voxpelli/eslint-config": "^19.0.0",
55 | "@voxpelli/tsconfig": "^11.0.0",
56 | "eslint": "^8.57.0",
57 | "eslint-plugin-es-x": "^7.6.0",
58 | "eslint-plugin-import": "^2.29.1",
59 | "eslint-plugin-jsdoc": "^46.10.1",
60 | "eslint-plugin-mocha": "^10.4.3",
61 | "eslint-plugin-n": "^16.6.2",
62 | "eslint-plugin-promise": "^6.1.1",
63 | "eslint-plugin-security": "^1.7.1",
64 | "eslint-plugin-sort-destructure-keys": "^1.6.0",
65 | "eslint-plugin-unicorn": "^48.0.1",
66 | "husky": "^9.0.11",
67 | "knip": "^5.13.0",
68 | "npm-run-all2": "^6.1.2",
69 | "type-coverage": "^2.28.2",
70 | "typescript": "~5.4.5"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "github>voxpelli/renovate-config:default"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@voxpelli/tsconfig/node18.json",
3 | "files": [
4 | "cli-wrapper.cjs",
5 | "cli.js"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------