From bc325eba1f7818d987c99e77dc99bfead7ed4296 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 feat: make portal-client dependency optional feat: make portal-client dependency optional feat: make portal-client dependency optional --- packages/cli/README.md | 25 +++++++++++++++++++ .../cli/src/lib/autorun/autorun-command.ts | 6 +++-- packages/cli/src/lib/upload/upload-command.ts | 6 +++-- packages/core/package.json | 4 ++- packages/core/src/lib/compare.ts | 10 +++++--- packages/core/src/lib/load-portal-client.ts | 14 +++++++++++ packages/core/src/lib/upload.ts | 18 ++++++------- 7 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 packages/core/src/lib/load-portal-client.ts diff --git a/packages/cli/README.md b/packages/cli/README.md index 42dca2a5b..86af08a0e 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -44,6 +44,31 @@ _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). diff --git a/packages/cli/src/lib/autorun/autorun-command.ts b/packages/cli/src/lib/autorun/autorun-command.ts index 1e0572a3e..ecdc16d89 100644 --- a/packages/cli/src/lib/autorun/autorun-command.ts +++ b/packages/cli/src/lib/autorun/autorun-command.ts @@ -46,8 +46,10 @@ export function yargsAutorunCommandObject() { } if (options.upload) { - const { url } = await upload(options); - uploadSuccessfulLog(url); + const report = await upload(options); + if (report?.url) { + uploadSuccessfulLog(report.url); + } } else { ui().logger.warning('Upload skipped because configuration is not set.'); renderIntegratePortalHint(); diff --git a/packages/cli/src/lib/upload/upload-command.ts b/packages/cli/src/lib/upload/upload-command.ts index 89546f686..aeba2a609 100644 --- a/packages/cli/src/lib/upload/upload-command.ts +++ b/packages/cli/src/lib/upload/upload-command.ts @@ -22,8 +22,10 @@ export function yargsUploadCommandObject() { renderIntegratePortalHint(); throw new Error('Upload configuration not set'); } - const { url } = await upload(options); - uploadSuccessfulLog(url); + const report = await upload(options); + if (report?.url) { + uploadSuccessfulLog(report.url); + } }, } satisfies CommandModule; } 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..40e9a89a7 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, @@ -28,6 +24,7 @@ import { compareCategories, compareGroups, } from './implementation/compare-scorables'; +import { loadPortalClient } from './load-portal-client'; export async function compareReportFiles( inputPaths: Diff, @@ -119,6 +116,11 @@ async function fetchPortalComparisonLink( commits: NonNullable, ): Promise { const { server, apiKey, organization, project } = uploadConfig; + const portalClient = await loadPortalClient(); + if (!portalClient) { + return; + } + const { PortalOperationError, getPortalComparisonLink } = portalClient; try { return await getPortalComparisonLink({ server, diff --git a/packages/core/src/lib/load-portal-client.ts b/packages/core/src/lib/load-portal-client.ts new file mode 100644 index 000000000..2cd2a3840 --- /dev/null +++ b/packages/core/src/lib/load-portal-client.ts @@ -0,0 +1,14 @@ +import { ui } from '@code-pushup/utils'; + +export async function loadPortalClient(): Promise< + typeof import('@code-pushup/portal-client') | null +> { + try { + return await import('@code-pushup/portal-client'); + } catch { + ui().logger.error( + 'Optional dependency @code-pushup/portal-client is not available. Make sure it is installed to enable upload functionality.', + ); + return null; + } +} diff --git a/packages/core/src/lib/upload.ts b/packages/core/src/lib/upload.ts index a93a722a1..157e4c228 100644 --- a/packages/core/src/lib/upload.ts +++ b/packages/core/src/lib/upload.ts @@ -1,10 +1,8 @@ -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 { reportToGQL } from './implementation/report-to-gql'; +import { loadPortalClient } from './load-portal-client'; import type { GlobalOptions } from './types'; export type UploadOptions = { upload?: UploadConfig } & { @@ -16,13 +14,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 +39,5 @@ export async function upload( ...reportToGQL(report), }; - return uploadFn({ apiKey, server, data, timeout }); + return uploadToPortal({ apiKey, server, data, timeout }); }