diff --git a/NOTICE.md b/NOTICE.md index fea65f7b614..9abf4d1e9d5 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1472,6 +1472,30 @@ Homepage: https://lodash.com/ -------- +## `lru-cache` + +License: ISC + +Author: Isaac Z. Schlueter + +> The ISC License +> +> Copyright (c) Isaac Z. Schlueter and Contributors +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-------- + ## `magic-string` License: MIT @@ -2822,21 +2846,25 @@ Homepage: https://rollupjs.org/ -------- -## `semiver` +## `semver` -License: MIT - -Author: [Luke Edwards](lukeed.com) +License: ISC -> MIT License -> -> Copyright (c) Luke Edwards (lukeed.com) +> The ISC License > -> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> Copyright (c) Isaac Z. Schlueter and Contributors > -> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. > -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------- @@ -3263,3 +3291,27 @@ Homepage: https://github.com/websockets/ws > SOFTWARE. -------- + +## `yallist` + +License: ISC + +Author: Isaac Z. Schlueter (http://blog.izs.me/) + +> The ISC License +> +> Copyright (c) Isaac Z. Schlueter and Contributors +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-------- diff --git a/package-lock.json b/package-lock.json index b555dbe95a3..3e5bb62ff95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,6 @@ "puppeteer": "~10.0.0", "rollup": "2.42.3", "rollup-plugin-sourcemaps": "^0.6.3", - "semiver": "^1.1.0", "semver": "7.3.4", "sizzle": "^2.3.6", "terser": "5.6.1", @@ -9979,15 +9978,6 @@ "node": ">= 4" } }, - "node_modules/semiver": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", - "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -20161,12 +20151,6 @@ "ajv-keywords": "^3.1.0" } }, - "semiver": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", - "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", - "dev": true - }, "semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", diff --git a/package.json b/package.json index d16ad868491..31bc28464ca 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,6 @@ "puppeteer": "~10.0.0", "rollup": "2.42.3", "rollup-plugin-sourcemaps": "^0.6.3", - "semiver": "^1.1.0", "semver": "7.3.4", "sizzle": "^2.3.6", "terser": "5.6.1", diff --git a/scripts/license.ts b/scripts/license.ts index ff7a85ba756..9482bd609f5 100644 --- a/scripts/license.ts +++ b/scripts/license.ts @@ -29,7 +29,7 @@ const entryDeps = [ 'postcss', 'prompts', 'rollup', - 'semiver', + 'semver', 'sizzle', 'source-map', 'terser', diff --git a/src/sys/node/node-lazy-require.ts b/src/sys/node/node-lazy-require.ts index 865084fab91..bd82eed98d3 100644 --- a/src/sys/node/node-lazy-require.ts +++ b/src/sys/node/node-lazy-require.ts @@ -1,43 +1,78 @@ import type * as d from '../../declarations'; import { buildError } from '@utils'; import { NodeResolveModule } from './node-resolve-module'; -import semiver from 'semiver'; import fs from 'graceful-fs'; import path from 'path'; +import satisfies from 'semver/functions/satisfies'; +import major from 'semver/functions/major'; +/** + * The version range that we support for a given package + * [0] is the lower end, while [1] is the higher end. + * + * These strings should be standard semver strings. + */ +type NodeVersionRange = [string, string]; + +/** + * A manifest for lazily-loaded dependencies, mapping dependency names + * to version ranges. + */ +type LazyDependencies = Record; + +/** + * Lazy requirer for Node, with functionality for specifying version ranges + * and returning diagnostic errors if requirements aren't met. + */ export class NodeLazyRequire implements d.LazyRequire { private ensured = new Set(); - constructor( - private nodeResolveModule: NodeResolveModule, - private lazyDependencies: { [dep: string]: [string, string] } - ) {} + /** + * Create a NodeLazyRequire instance + * + * @param nodeResolveModule an object which wraps up module resolution functionality + * @param lazyDependencies the dependency requirements we want to enforce here + */ + constructor(private nodeResolveModule: NodeResolveModule, private lazyDependencies: LazyDependencies) {} - async ensure(fromDir: string, ensureModuleIds: string[]) { + /** + * Ensure that a dependency within our supported range is installed in the current + * environment. This function will check all the dependency requirements passed in when + * the class is instantiated and return diagnostics if there are any issues. + * + * @param fromDir the directory from which we'll attempt to resolve the dependencies, typically + * this will be project's root directory. + * @param ensureModuleIds an array of module names whose versions we're going to check + * @returns a Promise holding diagnostics if any of the dependencies either were not + * resolved _or_ did not meet our version requirements. + */ + async ensure(fromDir: string, ensureModuleIds: string[]): Promise { const diagnostics: d.Diagnostic[] = []; - const missingDeps: string[] = []; + const problemDeps: string[] = []; ensureModuleIds.forEach((ensureModuleId) => { if (!this.ensured.has(ensureModuleId)) { - const [minVersion, recommendedVersion] = this.lazyDependencies[ensureModuleId]; + const [minVersion, maxVersion] = this.lazyDependencies[ensureModuleId]; + try { const pkgJsonPath = this.nodeResolveModule.resolveModule(fromDir, ensureModuleId); - const installedPkgJson: d.PackageJsonData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); - if (semiver(installedPkgJson.version, minVersion) >= 0) { + if (satisfies(installedPkgJson.version, `${minVersion} - ${major(maxVersion)}.x`)) { this.ensured.add(ensureModuleId); return; } } catch (e) {} - missingDeps.push(`${ensureModuleId}@${recommendedVersion}`); + // if we get here we didn't get to the `return` above, so either 1) there was some error + // reading the package.json or 2) the version wasn't in our specified version range. + problemDeps.push(`${ensureModuleId}@${maxVersion}`); } }); - if (missingDeps.length > 0) { + if (problemDeps.length > 0) { const err = buildError(diagnostics); - err.header = `Please install missing dev dependencies with either npm or yarn.`; - err.messageText = `npm install --save-dev ${missingDeps.join(' ')}`; + err.header = `Please install supported versions of dev dependencies with either npm or yarn.`; + err.messageText = `npm install --save-dev ${problemDeps.join(' ')}`; } return diagnostics; diff --git a/src/sys/node/node-stencil-version-checker.ts b/src/sys/node/node-stencil-version-checker.ts index 0ddb70bd172..903b6022b31 100644 --- a/src/sys/node/node-stencil-version-checker.ts +++ b/src/sys/node/node-stencil-version-checker.ts @@ -2,7 +2,7 @@ import type { Logger, PackageJsonData } from '../../declarations'; import { isString, noop } from '@utils'; import fs from 'graceful-fs'; import path from 'path'; -import semiver from 'semiver'; +import semverLt from 'semver/functions/lt'; import { tmpdir } from 'os'; const REGISTRY_URL = `https://registry.npmjs.org/@stencil/core`; @@ -14,7 +14,7 @@ export async function checkVersion(logger: Logger, currentVersion: string): Prom const latestVersion = await getLatestCompilerVersion(logger); if (latestVersion != null) { return () => { - if (semiver(currentVersion, latestVersion) < 0) { + if (semverLt(currentVersion, latestVersion)) { printUpdateMessage(logger, currentVersion, latestVersion); } else { console.debug( diff --git a/src/sys/node/test/node-lazy-require.spec.ts b/src/sys/node/test/node-lazy-require.spec.ts new file mode 100644 index 00000000000..d38e0aeaa0e --- /dev/null +++ b/src/sys/node/test/node-lazy-require.spec.ts @@ -0,0 +1,68 @@ +import { NodeLazyRequire } from '../node-lazy-require'; +import { buildError } from '@utils'; +import { NodeResolveModule } from '../node-resolve-module'; +import fs from 'graceful-fs'; + +const mockPackageJson = (version: string) => + JSON.stringify({ + version, + }); + +describe('node-lazy-require', () => { + describe('NodeLazyRequire', () => { + describe('ensure', () => { + let readFSMock: jest.SpyInstance, Parameters>; + + beforeEach(() => { + readFSMock = jest.spyOn(fs, 'readFileSync').mockReturnValue(mockPackageJson('10.10.10')); + }); + + afterEach(() => { + readFSMock.mockClear(); + }); + + function setup() { + const resolveModule = new NodeResolveModule(); + const nodeLazyRequire = new NodeLazyRequire(resolveModule, { + jest: ['2.0.7', '38.0.1'], + }); + return nodeLazyRequire; + } + + it.each(['2.0.7', '10.10.10', '38.0.1', '38.0.2', '38.5.17'])( + 'should not error if installed package has a suitable major version (%p)', + async (testVersion) => { + const nodeLazyRequire = setup(); + readFSMock.mockReturnValue(mockPackageJson(testVersion)); + let diagnostics = await nodeLazyRequire.ensure('.', ['jest']); + expect(diagnostics.length).toBe(0); + } + ); + + it('should error if the installed version of a package is too low', async () => { + const nodeLazyRequire = setup(); + readFSMock.mockReturnValue(mockPackageJson('1.1.1')); + let [error] = await nodeLazyRequire.ensure('.', ['jest']); + expect(error).toEqual({ + ...buildError([]), + header: 'Please install supported versions of dev dependencies with either npm or yarn.', + messageText: 'npm install --save-dev jest@38.0.1', + }); + }); + + it.each(['100.1.1', '38.0.1-alpha.0'])( + 'should error if the installed version of a package is too high (%p)', + async (version) => { + const nodeLazyRequire = setup(); + readFSMock.mockReturnValue(mockPackageJson(version)); + let [error] = await nodeLazyRequire.ensure('.', ['jest']); + expect(error).toEqual({ + ...buildError([]), + header: 'Please install supported versions of dev dependencies with either npm or yarn.', + messageText: 'npm install --save-dev jest@38.0.1', + }); + } + ); + }); + }); +});