|
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