From 42c1f35203c4932bb4b6edbf4bbeb54ed9ee5869 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Fri, 11 Oct 2024 18:31:18 -0400 Subject: [PATCH] feat: make portal-client dependency optional --- packages/cli/README.md | 26 ++++++++++++++++++++ packages/core/package.json | 4 ++- packages/core/src/lib/compare.ts | 10 +++++--- packages/core/src/lib/upload.ts | 19 +++++++------- packages/utils/src/index.ts | 1 + packages/utils/src/lib/load-portal-client.ts | 14 +++++++++++ 6 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 packages/utils/src/lib/load-portal-client.ts diff --git a/packages/cli/README.md b/packages/cli/README.md index 42dca2a5b..6bff6f3d0 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -45,6 +45,32 @@ _If you're looking for programmatic usage, then refer to the underlying [@code-p +
+ Installing without optional dependencies + + To avoid installing optional dependencies like `@code-pushup/portal-client`, use the commands below. Please note that omitting this dependency will limit the upload functionality. + + **npm** + + ```sh + npm install @code-pushup/cli --omit=dev --omit=optional + ``` + + **pnpm** + + `pnpm` requires reinstalling all packages to skip optional dependencies. The `--no-optional` flag applies globally, omitting all optional dependencies across the workspace. For more granular control, you can customize dependency behavior using a [`.pnpmfile.cjs`](https://pnpm.io/pnpmfile) configuration file. + + ```sh + pnpm add --save-dev @code-pushup/cli + rm -rf pnpm-lock.yaml node_modules + pnpm install --no-optional + ``` + + **yarn** + + `yarn` does not support omitting optional dependencies. +
+ 2. Create a `code-pushup.config.ts` configuration file (`.js` or `.mjs` extensions are also supported). ```ts diff --git a/packages/core/package.json b/packages/core/package.json index 5ca51b4fa..af8e630b9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -6,7 +6,9 @@ "dependencies": { "@code-pushup/models": "0.51.0", "@code-pushup/utils": "0.51.0", - "@code-pushup/portal-client": "^0.9.0", "ansis": "^3.3.0" + }, + "optionalDependencies": { + "@code-pushup/portal-client": "^0.9.0" } } diff --git a/packages/core/src/lib/compare.ts b/packages/core/src/lib/compare.ts index 85e1693f3..ee05527f5 100644 --- a/packages/core/src/lib/compare.ts +++ b/packages/core/src/lib/compare.ts @@ -1,9 +1,5 @@ import { writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { - PortalOperationError, - getPortalComparisonLink, -} from '@code-pushup/portal-client'; import { type Format, type PersistConfig, @@ -17,6 +13,7 @@ import { calcDuration, ensureDirectoryExists, generateMdReportsDiff, + loadPortalClient, readJsonFile, scoreReport, ui, @@ -119,6 +116,11 @@ async function fetchPortalComparisonLink( commits: NonNullable, ): Promise { const { server, apiKey, organization, project } = uploadConfig; + const portalClient = await loadPortalClient(); + if (!portalClient) { + return undefined; + } + const { PortalOperationError, getPortalComparisonLink } = portalClient; try { return await getPortalComparisonLink({ server, diff --git a/packages/core/src/lib/upload.ts b/packages/core/src/lib/upload.ts index a93a722a1..4edcfe254 100644 --- a/packages/core/src/lib/upload.ts +++ b/packages/core/src/lib/upload.ts @@ -1,9 +1,6 @@ -import { - type SaveReportMutationVariables, - uploadToPortal, -} from '@code-pushup/portal-client'; +import type { SaveReportMutationVariables } from '@code-pushup/portal-client'; import type { PersistConfig, Report, UploadConfig } from '@code-pushup/models'; -import { loadReport } from '@code-pushup/utils'; +import { loadPortalClient, loadReport } from '@code-pushup/utils'; import { reportToGQL } from './implementation/report-to-gql'; import type { GlobalOptions } from './types'; @@ -16,13 +13,15 @@ export type UploadOptions = { upload?: UploadConfig } & { * @param options * @param uploadFn */ -export async function upload( - options: UploadOptions, - uploadFn: typeof uploadToPortal = uploadToPortal, -) { +export async function upload(options: UploadOptions) { if (options.upload == null) { throw new Error('Upload configuration is not set.'); } + const portalClient = await loadPortalClient(); + if (!portalClient) { + return; + } + const { uploadToPortal } = portalClient; const { apiKey, server, organization, project, timeout } = options.upload; const report: Report = await loadReport({ ...options.persist, @@ -39,5 +38,5 @@ export async function upload( ...reportToGQL(report), }; - return uploadFn({ apiKey, server, data, timeout }); + return uploadToPortal({ apiKey, server, data, timeout }); } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 6a15bb741..5d1a58300 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -120,3 +120,4 @@ export type { WithRequired, } from './lib/types'; export { verboseUtils } from './lib/verbose-utils'; +export { loadPortalClient } from './lib/load-portal-client'; diff --git a/packages/utils/src/lib/load-portal-client.ts b/packages/utils/src/lib/load-portal-client.ts new file mode 100644 index 000000000..a3a154aff --- /dev/null +++ b/packages/utils/src/lib/load-portal-client.ts @@ -0,0 +1,14 @@ +import { ui } from './logging'; + +export async function loadPortalClient(): Promise< + typeof import('@code-pushup/portal-client') | null +> { + try { + return await import('@code-pushup/portal-client'); + } catch { + ui().logger.warning( + 'Optional dependency @code-pushup/portal-client is not installed or failed to load.', + ); + return null; + } +}