Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support checking workspaces #118

Merged
merged 3 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
*.d.ts
*.d.ts.map
!/lib/**/*-types.d.ts
!/index.d.ts

# Library specific ones
116 changes: 61 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ The rich version range check that `installed-check` itself uses.
#### Syntax

```ts
checkVersionRange(mainPackage, key, installedDependencies, [options]) => VersionRangeResult
checkVersionRange(pkg, key, installed, [options]) => VersionRangeResult
```

#### Arguments

* `mainPackage`: Type `PackageJsonLike` – the content of the `package.json` file to check, see [`getInstalledData()`](#getinstalleddata)
* `pkg`: Type [`PackageJsonLike`](#packagejsonlike) – the content of the `package.json` file to check
* `key`: Type `string` – the key of the version range to check, eg `engines.node`
* `installedDependencies`: Type `InstalledDependencies` – the installed dependencies to use when checking, see [`getInstalledData()`](#getinstalleddata)
* `installed`: Type [`InstalledDependencies`](#installeddependencies) – the `package.json` files of the installed dependencies
* `options`: Type `VersionRangeOptions` – optional options

#### Types
Expand Down Expand Up @@ -80,14 +80,21 @@ type VersionRangeResult = VersionRangeItem & {
#### Example

```javascript
import { checkVersionRange, getInstalledData } from 'installed-check-core';
import { checkVersionRange } from 'installed-check-core';
import { listInstalled } from 'list-installed';
import { readPackage } from 'read-pkg';

const { installedDependencies, mainPackage } = await getInstalledData(path);
const cwd = '.';

const [pkg, installed] = await Promise.all([
readPackage({ cwd }),
listInstalled(cwd),
]);

const result = await checkVersionRange(
mainPackage,
pkg,
'engines.node',
installedDependencies,
installed,
{
expectedInDependencies: true,
noDev: true,
Expand Down Expand Up @@ -127,70 +134,45 @@ Wrapper around as [`checkVersionRange()`](#checkversionrange) that differs from
#### Syntax

```ts
checkVersionRangeCollection(mainPackage, key, installedDependencies, [options]) => VersionRangesResult
checkVersionRangeCollection(pkg, key, installed, [options]) => VersionRangesResult
```

#### Arguments

See main description of [`checkVersionRangeCollection()`](#checkversionrangecollection) and full docs for [`checkVersionRange()`](#checkversionrange).

### getInstalledData()

Companion method to eg. `checkVersionRange()` that which makes it easy to get the correct data required. Not meant for any other use.

Is a simple wrapper around [`read-pkg`](https://github.com/sindresorhus/read-pkg) and [`list-installed`](https://github.com/voxpelli/list-installed) – those or similar modules can be used directly just as well.

#### Syntax

```ts
getInstalledData(path = '.') => Promise<InstalledData>
```

#### Arguments

* `path` – specifies the path to the package to be checked, with its `package.json` expected to be there and its installed `node_modules` as well.

#### Types

```ts
// Subset of import('type-fest').PackageJson / import('read-pkg').NormalizedPackageJson
export type PackageJsonLike = {
name?: string | undefined;
version?: string | undefined;
engines?: Record<string, string | undefined>;
dependencies?: Record<string, string | undefined>;
devDependencies?: Record<string, string | undefined>;
optionalDependencies?: Record<string, string | undefined>;
peerDependencies?: Record<string, string | undefined>;
};

// A map is allowed since that's what import('list-installed).listInstalled returns
export type InstalledDependencies = Map<string, PackageJsonLike> | Record<string, PackageJsonLike>;
```

#### Example

See example of [`checkVersionRange()`](#checkversionrange)

### installedCheck()

The full on `installed-check` experience, returning error and warning strings only.

#### Syntax

```ts
installedCheck(checks, options) => Promise<InstalledCheckResult>
installedCheck(checks, [lookupOptions], [options]) => Promise<InstalledCheckResult>
```

#### Arguments

* `checks`: Type `InstalledChecks[]` – the checks to run, an array of one or more of: `'engine'`, `'peer'`, `'version'`
* `options`: Type `InstalledCheckOptions`
* `lookupOptions`: Type `LookupOptions` – optional – defaults to `cwd='.'` and `includeWorkspaceRoot: true`
* `options`: Type `InstalledCheckOptions` – optional

#### Types

```ts
type LookupOptions = {
includeWorkspaceRoot?: boolean;
cwd?: string;
skipWorkspaces?: boolean;
workspace?: string[];
};
type InstalledChecks = 'engine' | 'peer' | 'version'
type InstalledCheckOptions = {
ignore?: string[] | undefined;
noDev?: boolean | undefined;
prefix?: string | undefined;
strict?: boolean | undefined;
};
type InstalledCheckResult = { errors: string[], warnings: string[] }
```

Expand All @@ -202,7 +184,7 @@ type InstalledCheckResult = { errors: string[], warnings: string[] }

#### Options

* `path = '.'` – specifies the path to the package to be checked, with its `package.json` expected to be there and its installed `node_modules` as well.
* `cwd = '.'` – specifies the path to the package to be checked, with its `package.json` expected to be there and its installed `node_modules` as well.
* `ignores = string[]` – names of modules to exclude from checks. Supports [`picomatch`](https://www.npmjs.com/package/picomatch) globbing syntax, eg. `@types/*`. (Not supported by `version` checks)
* `noDev = false` – exclude `devDependencies` from checks. `devDependencies` that are also in `peerDependencies` will not be ignored. (Not supported by `version` checks)
* `strict = false` – converts most warnings into failures
Expand All @@ -213,28 +195,52 @@ type InstalledCheckResult = { errors: string[], warnings: string[] }
import { installedCheck } from 'installed-check-core';

const { errors, warnings } = await installedCheck(['engine', 'version'], {
path: 'path/to/module',
cwd: 'path/to/module',
ignore: ['foo'],
noDev: true,
});
```

### performInstalledCheck()

Same as [`installedCheck()`](#installedcheck) but without looking up any data on its own but instead expects the data from [`getInstalledData()`](#getinstalleddata) or similar to be given to it.
Similar to [`installedCheck()`](#installedcheck) but expects to be given package data instead of looking it up itself..

#### Syntax

```ts
performInstalledCheck(checks, mainPackage, installedDependencies, options) => Promise<InstalledCheckResult>
performInstalledCheck(checks, pkg, installed, options) => Promise<InstalledCheckResult>
```

#### Arguments

* `checks`: Type `InstalledChecks[]` – same as for [`installedCheck()`](#installedcheck)
* `mainPackage`: Type `PackageJsonLike` – the content of the `package.json` file to check, see [`getInstalledData()`](#getinstalleddata)
* `installedDependencies`: Type `InstalledDependencies` – the installed dependencies to use when checking, see [`getInstalledData()`](#getinstalleddata)
* `options`: Type `InstalledCheckOptions` – same as for [`installedCheck()`](#installedcheck), but without the `path` option
* `pkg`: Type [`PackageJsonLike`](#packagejsonlike) – the content of the `package.json` file to check
* `installed`: Type [`InstalledDependencies`](#installeddependencies) – the `package.json` files of the installed dependencies
* `options`: Type `InstalledCheckOptions` – same as for [`installedCheck()`](#installedcheck), but without the `cwd` option

## Types

### PackageJsonLike

```ts
// Subset of import('type-fest').PackageJson / import('read-pkg').NormalizedPackageJson
export type PackageJsonLike = {
name?: string | undefined;
version?: string | undefined;
engines?: Record<string, string | undefined>;
dependencies?: Record<string, string | undefined>;
devDependencies?: Record<string, string | undefined>;
optionalDependencies?: Record<string, string | undefined>;
peerDependencies?: Record<string, string | undefined>;
};
```

### InstalledDependencies

```ts
// A map is allowed since that's what import('list-installed).listInstalled returns
export type InstalledDependencies = Map<string, PackageJsonLike> | Record<string, PackageJsonLike>;
```

## Used by

Expand Down
1 change: 1 addition & 0 deletions declaration.tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

{
"extends": "./tsconfig",
"files": [],
"exclude": [
"test/**/*"
],
Expand Down
9 changes: 9 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type * from './lib/lookup-types.d.ts';

export type { VersionRangeItem, VersionRangeOptions, VersionRangeResult, VersionRangesOptions } from './lib/check-version-range.js';
export type { LookupOptions } from './lib/installed-check.js';
export type { InstalledChecks, InstalledCheckOptions, InstalledCheckResult } from './lib/perform-installed-check.js';

export { checkVersionRange, checkVersionRangeCollection } from './lib/check-version-range.js';
export { installedCheck } from './lib/installed-check.js';
export { performInstalledCheck } from './lib/perform-installed-check.js';
13 changes: 0 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
/** @typedef {import('./lib/check-version-range.js').VersionRangeItem} VersionRangeItem */
/** @typedef {import('./lib/check-version-range.js').VersionRangeOptions} VersionRangeOptions */
/** @typedef {import('./lib/check-version-range.js').VersionRangeResult} VersionRangeResult */
/** @typedef {import('./lib/check-version-range.js').VersionRangeOptions} VersionRangesOptions */

/** @typedef {import('./lib/get-installed-data.js').PackageJsonLike} PackageJsonLike */
/** @typedef {import('./lib/get-installed-data.js').InstalledDependencies} InstalledDependencies */

/** @typedef {import('./lib/perform-installed-check.js').InstalledChecks} InstalledChecks */
/** @typedef {import('./lib/perform-installed-check.js').InstalledCheckOptions} InstalledCheckOptions */
/** @typedef {import('./lib/perform-installed-check.js').InstalledCheckResult} InstalledCheckResult */

export { checkVersionRange, checkVersionRangeCollection } from './lib/check-version-range.js';
export { getInstalledData } from './lib/get-installed-data.js';
export { installedCheck } from './lib/installed-check.js';
export { performInstalledCheck } from './lib/perform-installed-check.js';
10 changes: 5 additions & 5 deletions lib/check-package-versions.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import semver from 'semver';

/**
* @param {import('./get-installed-data.js').PackageJsonLike} mainPackage
* @param {import('./get-installed-data.js').InstalledDependencies} installedDependencies
* @param {import('./lookup-types.d.ts').PackageJsonLike} pkg
* @param {import('./lookup-types.d.ts').InstalledDependencies} installedDependencies
* @returns {{ errors: string[], warnings: string[] }}
*/
export function checkPackageVersions (mainPackage, installedDependencies) {
export function checkPackageVersions (pkg, installedDependencies) {
/** @type {string[]} */
const errors = [];
/** @type {string[]} */
const warnings = [];

const requiredDependencies = { ...mainPackage.dependencies, ...mainPackage.devDependencies };
const optionalDependencies = { ...mainPackage.optionalDependencies };
const requiredDependencies = { ...pkg.dependencies, ...pkg.devDependencies };
const optionalDependencies = { ...pkg.optionalDependencies };

for (const dependency of Object.keys(requiredDependencies)) {
let targetVersion = requiredDependencies[dependency];
Expand Down
30 changes: 15 additions & 15 deletions lib/check-version-range.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import { getObjectValueByPath, getStringValueByPath } from './utils.js';
/** @typedef {VersionRangeItem & { packageNotes: Array<VersionRangeItem & { name: string }> }} VersionRangeResult */

/**
* @param {import('./get-installed-data.js').PackageJsonLike} mainPackage
* @param {import('./lookup-types.d.ts').PackageJsonLike} pkg
* @param {string} key
* @param {import('./get-installed-data.js').InstalledDependencies} installedDependencies
* @param {import('./lookup-types.d.ts').InstalledDependencies} installed
* @param {VersionRangeOptions} [options]
* @returns {VersionRangeResult}
*/
export function checkVersionRange (mainPackage, key, installedDependencies, options) {
export function checkVersionRange (pkg, key, installed, options) {
const {
ignore,
noDev = false,
Expand All @@ -32,23 +32,23 @@ export function checkVersionRange (mainPackage, key, installedDependencies, opti

const ignoreMatcher = ignore && !Array.isArray(ignore) ? ignore : undefined;

const rawReferenceRange = getStringValueByPath(mainPackage, key);
const rawReferenceRange = getStringValueByPath(pkg, key);
const referenceRange = rawReferenceRange || '*';

/** @type {Array<VersionRangeItem & { name: string }>} */
const packageNotes = [];

const requiredDependencies = omit({
...mainPackage.dependencies,
...pkg.dependencies,
...(
noDev
// Always include peer dependency dev deps
? pick(mainPackage.devDependencies || {}, Object.keys(mainPackage.peerDependencies || {}))
: mainPackage.devDependencies
? pick(pkg.devDependencies || {}, Object.keys(pkg.peerDependencies || {}))
: pkg.devDependencies
),
}, Array.isArray(ignore) ? ignore : []);

const optionalDependencies = { ...mainPackage.optionalDependencies };
const optionalDependencies = { ...pkg.optionalDependencies };

/** @type {string | false} */
let intersection = referenceRange;
Expand All @@ -58,7 +58,7 @@ export function checkVersionRange (mainPackage, key, installedDependencies, opti
continue;
}

const dependencyPackage = installedDependencies instanceof Map ? installedDependencies.get(dependency) : installedDependencies[dependency];
const dependencyPackage = installed instanceof Map ? installed.get(dependency) : installed[dependency];
const isOptional = !!optionalDependencies[dependency];

const dependencyNotes = checkDependencyRange(referenceRange, key, dependencyPackage, isOptional, options);
Expand Down Expand Up @@ -93,16 +93,16 @@ export function checkVersionRange (mainPackage, key, installedDependencies, opti
/** @typedef {VersionRangeOptions & { defaultKeys?: string[] }} VersionRangesOptions */

/**
* @param {import('./get-installed-data.js').PackageJsonLike} mainPackage
* @param {import('./lookup-types.d.ts').PackageJsonLike} pkg
* @param {string} topKey
* @param {import('./get-installed-data.js').InstalledDependencies} installedDependencies
* @param {import('./lookup-types.d.ts').InstalledDependencies} installed
* @param {VersionRangesOptions} [options]
* @returns {{ [key: string]: VersionRangeResult }}
*/
export function checkVersionRangeCollection (mainPackage, topKey, installedDependencies, options) {
export function checkVersionRangeCollection (pkg, topKey, installed, options) {
const { defaultKeys, ...restOptions } = options || {};

let foundKeys = Object.keys(getObjectValueByPath(mainPackage, topKey) || {});
let foundKeys = Object.keys(getObjectValueByPath(pkg, topKey) || {});

if (foundKeys.length === 0 && defaultKeys) {
foundKeys = defaultKeys;
Expand All @@ -114,7 +114,7 @@ export function checkVersionRangeCollection (mainPackage, topKey, installedDepen
for (const childKey of foundKeys) {
const key = `${topKey}.${childKey}`;

result[key] = checkVersionRange(mainPackage, key, installedDependencies, restOptions);
result[key] = checkVersionRange(pkg, key, installed, restOptions);
}

return result;
Expand All @@ -123,7 +123,7 @@ export function checkVersionRangeCollection (mainPackage, topKey, installedDepen
/**
* @param {string} referenceRange
* @param {string} key
* @param {import('./get-installed-data.js').PackageJsonLike|undefined} dependencyPackage
* @param {import('./lookup-types.d.ts').PackageJsonLike|undefined} dependencyPackage
* @param {boolean} [isOptional]
* @param {Omit<VersionRangeOptions, 'noDev'>} [options]
* @returns {(VersionRangeItem & { valid: false | undefined }) | undefined}
Expand Down
8 changes: 4 additions & 4 deletions lib/check-versions.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { checkVersionRangeCollection } from './check-version-range.js';

/**
* @param {import('./get-installed-data.js').PackageJsonLike} mainPackage
* @param {import('./lookup-types.d.ts').PackageJsonLike} pkg
* @param {string} key
* @param {import('./get-installed-data.js').InstalledDependencies} installedDependencies
* @param {import('./lookup-types.d.ts').InstalledDependencies} installed
* @param {import('./check-version-range.js').VersionRangesOptions} options
* @returns {{ errors: string[], warnings: string[] }}
*/
export function checkVersions (mainPackage, key, installedDependencies, options) {
export function checkVersions (pkg, key, installed, options) {
/** @type {string[]} */
const errors = [];
/** @type {string[]} */
const warnings = [];
/** @type {string[]} */
const summaries = [];

const rangesResult = checkVersionRangeCollection(mainPackage, key, installedDependencies, options);
const rangesResult = checkVersionRangeCollection(pkg, key, installed, options);

for (const [name, rangeResult] of Object.entries(rangesResult)) {
for (const result of [rangeResult, ...rangeResult.packageNotes]) {
Expand Down
Loading
Loading