Skip to content

Commit 25359e6

Browse files
fix(sys): make NodeLazyRequire complain if package versions aren't right
This updates a bit of logic in `NodeLazyRequire.ensure` to check that the installed versions of packages are within the specified version range, i.e. that `minVersion <= installedVersion <= maxVersion`. This commit also adds tests for that module. STENCIL-391: bug: @stencil/core does not throw error when missing jest/jest-cli deps in a rush/pnpm monorepo
1 parent cb8eebc commit 25359e6

File tree

3 files changed

+91
-15
lines changed

3 files changed

+91
-15
lines changed

src/sys/node/node-lazy-require.ts

+31-13
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,61 @@
11
import type * as d from '../../declarations';
22
import { buildError } from '@utils';
33
import { NodeResolveModule } from './node-resolve-module';
4-
import semiver from 'semiver';
54
import fs from 'graceful-fs';
65
import path from 'path';
6+
import satisfies from 'semver/functions/satisfies';
7+
import major from 'semver/functions/major';
78

9+
/**
10+
* The version range that we support for a given package
11+
* [0] is the lower end, while [1] is the higher end.
12+
*
13+
* These strings should be standard semver strings.
14+
*/
15+
type NodeVersionRange = [string, string];
16+
17+
/**
18+
* A manifest for lazily-loaded dependencies, mapping dependency names
19+
* to version ranges.
20+
*/
21+
type LazyDependencies = Record<string, NodeVersionRange>;
22+
23+
/**
24+
* Lazy requirer for Node, with functionality for specifying version ranges
25+
* and returning diagnostic errors if requirements aren't met.
26+
*/
827
export class NodeLazyRequire implements d.LazyRequire {
928
private ensured = new Set<string>();
1029

11-
constructor(
12-
private nodeResolveModule: NodeResolveModule,
13-
private lazyDependencies: { [dep: string]: [string, string] }
14-
) {}
30+
constructor(private nodeResolveModule: NodeResolveModule, private lazyDependencies: LazyDependencies) {}
1531

1632
async ensure(fromDir: string, ensureModuleIds: string[]) {
1733
const diagnostics: d.Diagnostic[] = [];
18-
const missingDeps: string[] = [];
34+
const problemDeps: string[] = [];
1935

2036
ensureModuleIds.forEach((ensureModuleId) => {
2137
if (!this.ensured.has(ensureModuleId)) {
22-
const [minVersion, recommendedVersion] = this.lazyDependencies[ensureModuleId];
38+
const [minVersion, maxVersion] = this.lazyDependencies[ensureModuleId];
39+
2340
try {
2441
const pkgJsonPath = this.nodeResolveModule.resolveModule(fromDir, ensureModuleId);
25-
2642
const installedPkgJson: d.PackageJsonData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
2743

28-
if (semiver(installedPkgJson.version, minVersion) >= 0) {
44+
if (satisfies(installedPkgJson.version, `${minVersion} - ${major(maxVersion)}.x`)) {
2945
this.ensured.add(ensureModuleId);
3046
return;
3147
}
3248
} catch (e) {}
33-
missingDeps.push(`${ensureModuleId}@${recommendedVersion}`);
49+
// if we get here we didn't get to the `return` above, so either 1) there was some error
50+
// reading the package.json or 2) the version wasn't in our specified version range.
51+
problemDeps.push(`${ensureModuleId}@${maxVersion}`);
3452
}
3553
});
3654

37-
if (missingDeps.length > 0) {
55+
if (problemDeps.length > 0) {
3856
const err = buildError(diagnostics);
39-
err.header = `Please install missing dev dependencies with either npm or yarn.`;
40-
err.messageText = `npm install --save-dev ${missingDeps.join(' ')}`;
57+
err.header = `Please install supported versions of dev dependencies with either npm or yarn.`;
58+
err.messageText = `npm install --save-dev ${problemDeps.join(' ')}`;
4159
}
4260

4361
return diagnostics;

src/sys/node/node-stencil-version-checker.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Logger, PackageJsonData } from '../../declarations';
22
import { isString, noop } from '@utils';
33
import fs from 'graceful-fs';
44
import path from 'path';
5-
import semiver from 'semiver';
5+
import semverLt from 'semver/functions/lt';
66
import { tmpdir } from 'os';
77

88
const REGISTRY_URL = `https://registry.npmjs.org/@stencil/core`;
@@ -14,7 +14,7 @@ export async function checkVersion(logger: Logger, currentVersion: string): Prom
1414
const latestVersion = await getLatestCompilerVersion(logger);
1515
if (latestVersion != null) {
1616
return () => {
17-
if (semiver(currentVersion, latestVersion) < 0) {
17+
if (semverLt(currentVersion, latestVersion)) {
1818
printUpdateMessage(logger, currentVersion, latestVersion);
1919
} else {
2020
console.debug(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { NodeLazyRequire } from '../node-lazy-require';
2+
import { buildError } from '@utils';
3+
import { NodeResolveModule } from '../node-resolve-module';
4+
import fs from 'graceful-fs';
5+
6+
const mockPackageJson = (version: string) =>
7+
JSON.stringify({
8+
version,
9+
});
10+
11+
describe('node-lazy-require', () => {
12+
describe('NodeLazyRequire', () => {
13+
function setup() {
14+
const resolveModule = new NodeResolveModule();
15+
const readFSMock = jest.spyOn(fs, 'readFileSync').mockReturnValue(mockPackageJson('10.10.10'));
16+
17+
const nodeLazyRequire = new NodeLazyRequire(resolveModule, {
18+
jest: ['2.0.7', '38.0.1'],
19+
});
20+
return {
21+
nodeLazyRequire,
22+
readFSMock,
23+
};
24+
}
25+
26+
it.each(['2.0.7', '10.10.10', '38.0.1'])(
27+
'should not error if a package of suitable version (%p) is installed',
28+
async (testVersion) => {
29+
const { nodeLazyRequire, readFSMock } = setup();
30+
readFSMock.mockReturnValue(mockPackageJson(testVersion));
31+
let diagnostics = await nodeLazyRequire.ensure('.', ['jest']);
32+
expect(diagnostics.length).toBe(0);
33+
}
34+
);
35+
36+
it('should error if the installed version of a package is too low', async () => {
37+
const { nodeLazyRequire, readFSMock } = setup();
38+
readFSMock.mockReturnValue(mockPackageJson('1.1.1'));
39+
let [error] = await nodeLazyRequire.ensure('.', ['jest']);
40+
expect(error).toEqual({
41+
...buildError([]),
42+
header: 'Please install supported versions of dev dependencies with either npm or yarn.',
43+
messageText: 'npm install --save-dev jest@38.0.1',
44+
});
45+
});
46+
47+
it('should error if the installed version of a package is too high', async () => {
48+
const { nodeLazyRequire, readFSMock } = setup();
49+
readFSMock.mockReturnValue(mockPackageJson('100.1.1'));
50+
let [error] = await nodeLazyRequire.ensure('.', ['jest']);
51+
expect(error).toEqual({
52+
...buildError([]),
53+
header: 'Please install supported versions of dev dependencies with either npm or yarn.',
54+
messageText: 'npm install --save-dev jest@38.0.1',
55+
});
56+
});
57+
});
58+
});

0 commit comments

Comments
 (0)