|
3 | 3 | import { stat } from 'node:fs/promises' |
4 | 4 | import path from 'node:path' |
5 | 5 |
|
6 | | -import chalk from 'chalk' |
7 | 6 | import meow from 'meow' |
8 | 7 | import ora from 'ora' |
9 | 8 | import { ErrorWithCause } from 'pony-cause' |
10 | 9 |
|
| 10 | +import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' |
11 | 11 | import { ChalkOrMarkdown, logSymbols } from '../../utils/chalk-markdown.js' |
12 | | -import { AuthError, InputError } from '../../utils/errors.js' |
| 12 | +import { InputError } from '../../utils/errors.js' |
13 | 13 | import { printFlagList } from '../../utils/formatting.js' |
14 | 14 | import { createDebugLogger } from '../../utils/misc.js' |
15 | 15 | import { setupSdk } from '../../utils/sdk.js' |
16 | 16 | import { isErrnoException } from '../../utils/type-helpers.js' |
17 | 17 |
|
18 | | -const description = 'Create a project report' |
| 18 | +/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */ |
| 19 | +export const create = { |
| 20 | + description: 'Create a project report', |
| 21 | + async run (argv, importMeta, { parentName }) { |
| 22 | + const name = parentName + ' view' |
| 23 | + |
| 24 | + const input = await setupCommand(name, create.description, argv, importMeta) |
| 25 | + |
| 26 | + if (input) { |
| 27 | + const { cwd, debugLog, dryRun, outputJson, outputMarkdown, packagePaths } = input |
| 28 | + const result = input && await createReport(packagePaths, { cwd, debugLog, dryRun }) |
| 29 | + |
| 30 | + if (result) { |
| 31 | + formatReportCreationOutput(result.data, { outputJson, outputMarkdown }) |
| 32 | + } |
| 33 | + } |
| 34 | + } |
| 35 | +} |
19 | 36 |
|
20 | | -/** @type {import('../../utils/meow-with-subcommands').CliSubcommandRun} */ |
21 | | -const run = async (argv, importMeta, { parentName }) => { |
22 | | - const name = parentName + ' create' |
| 37 | +// Internal functions |
23 | 38 |
|
| 39 | +/** |
| 40 | + * @param {string} name |
| 41 | + * @param {string} description |
| 42 | + * @param {readonly string[]} argv |
| 43 | + * @param {ImportMeta} importMeta |
| 44 | + * @returns {Promise<void|{ cwd: string, debugLog: typeof console.error, dryRun: boolean, outputJson: boolean, outputMarkdown: boolean, packagePaths: string[] }>} |
| 45 | + */ |
| 46 | +async function setupCommand (name, description, argv, importMeta) { |
24 | 47 | const cli = meow(` |
25 | 48 | Usage |
26 | 49 | $ ${name} <paths-to-package-folders-and-files> |
@@ -80,50 +103,59 @@ const run = async (argv, importMeta, { parentName }) => { |
80 | 103 | const cwd = process.cwd() |
81 | 104 | const packagePaths = await resolvePackagePaths(cwd, cli.input) |
82 | 105 |
|
| 106 | + return { |
| 107 | + cwd, |
| 108 | + debugLog, |
| 109 | + dryRun, |
| 110 | + outputJson, |
| 111 | + outputMarkdown, |
| 112 | + packagePaths |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +/** |
| 117 | + * @param {string[]} packagePaths |
| 118 | + * @param {{ cwd: string, debugLog: typeof console.error, dryRun: boolean }} context |
| 119 | + * @returns {Promise<void|import('@socketsecurity/sdk').SocketSdkReturnType<'createReport'>>} |
| 120 | + */ |
| 121 | +async function createReport (packagePaths, { cwd, debugLog, dryRun }) { |
83 | 122 | debugLog(`${logSymbols.info} Uploading:`, packagePaths.join(`\n${logSymbols.info} Uploading:`)) |
84 | 123 |
|
85 | 124 | if (dryRun) { |
86 | 125 | return |
87 | 126 | } |
88 | 127 |
|
89 | 128 | const socketSdk = await setupSdk() |
90 | | - |
91 | 129 | const spinner = ora(`Creating report with ${packagePaths.length} package files`).start() |
92 | | - |
93 | | - /** @type {Awaited<ReturnType<typeof socketSdk.createReportFromFilePaths>>} */ |
94 | | - let result |
95 | | - |
96 | | - try { |
97 | | - result = await socketSdk.createReportFromFilePaths(packagePaths, cwd) |
98 | | - } catch (cause) { |
99 | | - spinner.fail() |
100 | | - throw new ErrorWithCause('Failed creating report', { cause }) |
101 | | - } |
| 130 | + const result = await handleApiCall(socketSdk.createReportFromFilePaths(packagePaths, cwd), spinner, 'creating report') |
102 | 131 |
|
103 | 132 | if (result.success === false) { |
104 | | - if (result.status === 401 || result.status === 403) { |
105 | | - spinner.stop() |
106 | | - throw new AuthError(result.error.message) |
107 | | - } |
108 | | - spinner.fail(chalk.white.bgRed('API returned an error:') + ' ' + result.error.message) |
109 | | - process.exit(1) |
| 133 | + return handleUnsuccessfulApiResponse(result, spinner) |
110 | 134 | } |
111 | 135 |
|
| 136 | + // Conclude the status of the API call |
| 137 | + |
112 | 138 | spinner.succeed() |
113 | 139 |
|
| 140 | + return result |
| 141 | +} |
| 142 | + |
| 143 | +/** |
| 144 | + * @param {import('@socketsecurity/sdk').SocketSdkReturnType<'createReport'>["data"]} data |
| 145 | + * @param {{ outputJson: boolean, outputMarkdown: boolean }} context |
| 146 | + * @returns {void} |
| 147 | + */ |
| 148 | +function formatReportCreationOutput (data, { outputJson, outputMarkdown }) { |
114 | 149 | if (outputJson) { |
115 | | - console.log(JSON.stringify(result.data, undefined, 2)) |
| 150 | + console.log(JSON.stringify(data, undefined, 2)) |
116 | 151 | return |
117 | 152 | } |
118 | 153 |
|
119 | 154 | const format = new ChalkOrMarkdown(!!outputMarkdown) |
120 | 155 |
|
121 | | - console.log('\nNew report: ' + format.hyperlink(result.data.id, result.data.url, { fallbackToUrl: true })) |
| 156 | + console.log('\nNew report: ' + format.hyperlink(data.id, data.url, { fallbackToUrl: true })) |
122 | 157 | } |
123 | 158 |
|
124 | | -/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */ |
125 | | -export const create = { description, run } |
126 | | - |
127 | 159 | // TODO: Add globbing support with support for ignoring, as a "./**/package.json" in a project also traverses eg. node_modules |
128 | 160 | /** |
129 | 161 | * Takes paths to folders and/or package.json / package-lock.json files and resolves to package.json + package-lock.json pairs (where feasible) |
|
0 commit comments