From 75c9790acd510ad921492727a5a27b3f5f3256e2 Mon Sep 17 00:00:00 2001 From: prisis Date: Thu, 15 Jun 2023 14:43:02 +0200 Subject: [PATCH] feat: renamed some functions and added deprecated message for the old ones, added a resolvePackage function Signed-off-by: prisis --- .../__tests__/index.test.ts | 24 +++++- packages/package-json-utils/package.json | 14 ++-- packages/package-json-utils/src/index.ts | 82 ++++++++++++++----- 3 files changed, 87 insertions(+), 33 deletions(-) diff --git a/packages/package-json-utils/__tests__/index.test.ts b/packages/package-json-utils/__tests__/index.test.ts index 7dec18175..cdd4bf575 100644 --- a/packages/package-json-utils/__tests__/index.test.ts +++ b/packages/package-json-utils/__tests__/index.test.ts @@ -127,7 +127,12 @@ test("isPackageAvailable: returns false when the package is not available", () = assert.isFalse(isPackageAvailable("vitest2")); }); -test.each(["warn", "log", "error", "info"])("showMissingPackages: logs a %type message with the missing packages", (type: "error" | "info" | "log" | "warn") => { +test.each([ +"warn", +"log", +"error", +"info", +])("showMissingPackages: logs a %type message with the missing packages", (type: "error" | "info" | "log" | "warn") => { const consoleMock = vi.spyOn(console, type); showMissingPackages("example", ["package1", "package2"], { @@ -161,9 +166,20 @@ test("showMissingPackages: logs a warning message with the missing packages, pre }); test("unique: returns an array with unique values", () => { - const array = [1, 2, 2, 3, 3, 3]; - - assert.deepEqual(unique(array), [1, 2, 3]); + const array = [ +1, +2, +2, +3, +3, +3, +]; + + assert.deepEqual(unique(array), [ +1, +2, +3, +]); }); test("pkg: returns packageJson as NormalizedReadResult or undefined", () => { diff --git a/packages/package-json-utils/package.json b/packages/package-json-utils/package.json index c8bb04fdf..27fd110d5 100644 --- a/packages/package-json-utils/package.json +++ b/packages/package-json-utils/package.json @@ -58,12 +58,6 @@ "read-pkg-up": "^7.0.1" }, "devDependencies": { - "cross-env": "^7.0.3", - "rimraf": "^5.0.1", - "tsup": "^6.7.0", - "typescript": "^5.1.3", - "vitest": "^0.32.0", - "semantic-release": "^21.0.4", "@commitlint/cli": "^17.6.5", "@commitlint/config-conventional": "^17.6.5", "@commitlint/core": "^17.6.5", @@ -76,8 +70,14 @@ "@semantic-release/release-notes-generator": "^11.0.3", "commitizen": "^4.3.0", "conventional-changelog-conventionalcommits": "^6.0.0", + "cross-env": "^7.0.3", "cz-conventional-changelog": "^3.3.0", - "semantic-release-conventional-commits": "^3.0.0" + "rimraf": "^5.0.1", + "semantic-release": "^21.0.4", + "semantic-release-conventional-commits": "^3.0.0", + "tsup": "^6.7.0", + "typescript": "^5.1.3", + "vitest": "^0.32.0" }, "engines": { "node": ">=16" diff --git a/packages/package-json-utils/src/index.ts b/packages/package-json-utils/src/index.ts index a88154980..d97df882b 100644 --- a/packages/package-json-utils/src/index.ts +++ b/packages/package-json-utils/src/index.ts @@ -2,6 +2,7 @@ import { getByPath } from "dot-path-value"; import { existsSync, realpathSync } from "node:fs"; import module from "node:module"; import { dirname, join } from "node:path"; +import type { NormalizedPackageJson } from "read-pkg"; import readPkgUp from "read-pkg-up"; const { packageJson, path: packagePath } = readPkgUp.sync({ @@ -17,9 +18,23 @@ const atLatest = (name: string): string => { }; // eslint-disable-next-line max-len -export const hasPackageProperties = (properties: string[]): boolean => properties.some((property: string) => Boolean(packageJson !== undefined && getByPath(packageJson, property))); +export const hasPackageProperty = (property: string): boolean => Boolean(packageJson !== undefined && getByPath(packageJson, property)); -export const hasPackageSubProperties = (packageProperty: string) => (properties: string[]): boolean => hasPackageProperties(properties.map((p) => `${packageProperty}.${p}`)); +export const hasPackageSubProperty = (packageProperty: string) => (property: string): boolean => hasPackageProperty(`${packageProperty}.${property}`); + +// eslint-disable-next-line max-len +export const hasPackageProperties = (properties: string[], strict?: boolean): boolean => { + if (strict) { + return properties.every((property: string) => hasPackageProperty(property)); + } + + return properties.some((property: string) => hasPackageProperty(property)); +}; + +export const hasPackageSubProperties = (packageProperty: string) => (properties: string[], strict?: boolean): boolean => hasPackageProperties( + properties.map((p) => `${packageProperty}.${p}`), + strict, + ); export const environmentIsSet = (name: string): boolean => Boolean(process.env[name] && process.env[name] !== "undefined"); @@ -38,53 +53,76 @@ export const parseEnvironment = (name: string, defaultValue: unknown): any => { export const projectPath: string = packagePath ? dirname(packagePath) : ""; // @deprecated Use `projectPath` instead. export const appDirectory: string = projectPath; -export const fromRoot = (...p: string[]): string => join(appDirectory, ...p); +export const fromRoot = (...p: string[]): string => join(projectPath, ...p); export const hasFile = (...p: string[]): boolean => existsSync(fromRoot(...p)); export const hasScripts = hasPackageSubProperties("scripts"); -export const hasPeerDep = hasPackageSubProperties("peerDependencies"); -export const hasDep = hasPackageSubProperties("dependencies"); -export const hasDevelopmentDep = hasPackageSubProperties("devDependencies"); +export const hasPeerDependency = hasPackageSubProperty("peerDependencies"); +export const hasPeerDependencies = hasPackageSubProperties("peerDependencies"); +// @deprecated Use `hasPeerDependencies` instead. +export const hasPeerDep = hasPeerDependencies; +export const hasDependency = hasPackageSubProperty("dependencies"); +export const hasDependencies = hasPackageSubProperties("dependencies"); +// @deprecated Use `hasDependencies` instead. +export const hasDep = hasDependencies; +// eslint-disable-next-line unicorn/prevent-abbreviations +export const hasDevDependency = hasPackageSubProperty("devDependencies"); +// eslint-disable-next-line unicorn/prevent-abbreviations +export const hasDevDependencies = hasPackageSubProperties("devDependencies"); +// @deprecated Use `hasDevDependencies` instead. +export const hasDevelopmentDep = hasDevDependencies; // eslint-disable-next-line max-len -export const hasAnyDep = (arguments_: string[]): boolean => [hasDep, hasDevelopmentDep, hasPeerDep].some((function_: (arguments_: string[]) => boolean) => function_(arguments_)); +export const hasAnyDep = (arguments_: string[], options?: { peerDeps?: boolean; strict?: boolean }): boolean => [ +hasDependencies, +hasDevDependencies, +options?.peerDeps === false ? () => false : hasPeerDependencies, +].some( + (function_: (arguments_: string[], strict?: boolean) => boolean) => function_(arguments_, options?.strict), + ); -export const hasTypescript: boolean = hasAnyDep(["typescript"]) && hasFile("tsconfig.json"); +export const hasTypescript: boolean = (hasDependency("typescript") || hasDevDependency("typescript")) && hasFile("tsconfig.json"); export const packageIsTypeModule = hasPackageProperties(["type"]) && packageJson?.["type"] === "module"; -export const isPackageAvailable = (moduleName: string): boolean => { - const targetModule = import.meta.url; +export const resolvePackage = (packageName: string): string | undefined => { // See https://yarnpkg.com/advanced/pnpapi if (process.versions["pnp"]) { + const targetModule = import.meta.url; // @ts-expect-error TS2339: Property 'findPnpApi' does not exist on type 'typeof Module'. // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment const targetPnp = module.findPnpApi(targetModule); - // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access - if (targetPnp.resolveRequest(moduleName, targetModule)) { - return true; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment + const resolved = targetPnp.resolveRequest(packageName, targetModule); + + if (resolved) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return resolved; } } else if (packageIsTypeModule) { + const targetModule = import.meta.url; // See https://nodejs.org/api/esm.html#esm_resolver_algorithm try { - module.createRequire(targetModule).resolve(moduleName); - - return true; + return module.createRequire(targetModule).resolve(packageName); } catch { - return false; + return undefined; } } try { // eslint-disable-next-line unicorn/prefer-module - require.resolve(moduleName); - - return true; + return require.resolve(packageName); } catch { - return false; + return undefined; } }; +export const isPackageAvailable = (packageName: string): boolean => { + const resolved = resolvePackage(packageName); + + return resolved !== undefined; +}; + export const showMissingPackages = ( packageName: string, packages: string[], @@ -125,4 +163,4 @@ ${options.postMessage ?? ""}\n export const unique = (array: unknown[]): unknown[] => [...new Set(array)]; // eslint-disable-next-line unicorn/prevent-abbreviations -export const pkg = packageJson as readPkgUp.NormalizedReadResult | undefined; +export const pkg = packageJson as NormalizedPackageJson | undefined;