|
| 1 | +import utils, { promisify } from 'node:util' |
1 | 2 | import { stat } from 'node:fs/promises' |
2 | 3 | import { execFile } from 'node:child_process' |
3 | | -import { promisify } from 'node:util' |
4 | 4 | import { platform } from 'node:os' |
| 5 | +import http from 'node:http' |
| 6 | +import https from 'node:https' |
| 7 | +import { parse, format } from 'node:url' |
5 | 8 | import { getAllRegistries } from './config.mjs' |
6 | 9 |
|
| 10 | +/** |
| 11 | + * @param {import('http').RequestOptions | import('https').RequestOptions | string | URL} options |
| 12 | + * @param {(res: import('http').IncomingMessage) => void} callback |
| 13 | + * @returns {import('http').ClientRequest} |
| 14 | + */ |
| 15 | +export function request(options, callback) { |
| 16 | + const url = parse(format(options), false, true) |
| 17 | + const module = url.protocol === 'https:' ? https : http |
| 18 | + return module.request(options, callback) |
| 19 | +} |
| 20 | + |
| 21 | +export const parseArgs = utils.parseArgs || _parseArgs |
| 22 | + |
| 23 | +/** |
| 24 | + * Polyfill for Node.js util.parseArgs |
| 25 | + * @param {import('util').ParseArgsConfig} config |
| 26 | + * @returns {ReturnType<import('util').parseArgs>} |
| 27 | + */ |
| 28 | +function _parseArgs(config = {}) { |
| 29 | + const { |
| 30 | + options = {}, |
| 31 | + strict = false, |
| 32 | + allowPositionals = false, |
| 33 | + args = process.argv.slice(2), |
| 34 | + } = config |
| 35 | + |
| 36 | + /** @type {{ [longOption: string]: undefined | string | boolean | Array<string | boolean> }} */ |
| 37 | + const values = {} |
| 38 | + /** @type {string[]} */ |
| 39 | + const positionals = [] |
| 40 | + |
| 41 | + // Initialize default values |
| 42 | + for (const [name, opt] of Object.entries(options)) { |
| 43 | + if ('default' in opt) { |
| 44 | + values[name] = opt.default |
| 45 | + } else if (opt.multiple) { |
| 46 | + values[name] = [] |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + for (let i = 0; i < args.length; i++) { |
| 51 | + const arg = args[i] |
| 52 | + |
| 53 | + // Handle positional arguments |
| 54 | + if (!arg.startsWith('-')) { |
| 55 | + if (!allowPositionals && strict) { |
| 56 | + throw new Error(`Unexpected positional argument: ${arg}`) |
| 57 | + } |
| 58 | + positionals.push(arg) |
| 59 | + continue |
| 60 | + } |
| 61 | + |
| 62 | + // Handle long options (--option) |
| 63 | + if (arg.startsWith('--')) { |
| 64 | + const optName = arg.slice(2) |
| 65 | + const option = options[optName] |
| 66 | + |
| 67 | + if (!option) { |
| 68 | + if (strict) { |
| 69 | + throw new Error(`Unknown option: ${arg}`) |
| 70 | + } |
| 71 | + continue |
| 72 | + } |
| 73 | + |
| 74 | + if (option.type === 'boolean') { |
| 75 | + if (option.multiple) { |
| 76 | + if (!Array.isArray(values[optName])) values[optName] = [] |
| 77 | + values[optName].push(true) |
| 78 | + } else { |
| 79 | + values[optName] = true |
| 80 | + } |
| 81 | + } else if (option.type === 'string') { |
| 82 | + const value = args[++i] |
| 83 | + if (value === undefined) { |
| 84 | + throw new Error(`Option ${arg} requires a value`) |
| 85 | + } |
| 86 | + if (option.multiple) { |
| 87 | + if (!Array.isArray(values[optName])) values[optName] = [] |
| 88 | + values[optName].push(value) |
| 89 | + } else { |
| 90 | + values[optName] = value |
| 91 | + } |
| 92 | + } |
| 93 | + continue |
| 94 | + } |
| 95 | + |
| 96 | + // Handle short options (-o) |
| 97 | + if (arg.startsWith('-')) { |
| 98 | + const shortOpts = arg.slice(1).split('') |
| 99 | + |
| 100 | + for (let j = 0; j < shortOpts.length; j++) { |
| 101 | + const shortOpt = shortOpts[j] |
| 102 | + let optName = null |
| 103 | + |
| 104 | + // Find option by short name |
| 105 | + for (const [name, opt] of Object.entries(options)) { |
| 106 | + if (opt.short === shortOpt) { |
| 107 | + optName = name |
| 108 | + break |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + if (!optName) { |
| 113 | + if (strict) { |
| 114 | + throw new Error(`Unknown option: -${shortOpt}`) |
| 115 | + } |
| 116 | + continue |
| 117 | + } |
| 118 | + |
| 119 | + const option = options[optName] |
| 120 | + |
| 121 | + if (option.type === 'boolean') { |
| 122 | + if (option.multiple) { |
| 123 | + if (!Array.isArray(values[optName])) |
| 124 | + values[optName] = [] |
| 125 | + //@ts-ignore |
| 126 | + values[optName].push(true) |
| 127 | + } else { |
| 128 | + values[optName] = true |
| 129 | + } |
| 130 | + } else if (option.type === 'string') { |
| 131 | + let value |
| 132 | + // If not last char in group, remaining chars are the value |
| 133 | + if (j < shortOpts.length - 1) { |
| 134 | + value = shortOpts.slice(j + 1).join('') |
| 135 | + j = shortOpts.length // End loop |
| 136 | + } else { |
| 137 | + value = args[++i] |
| 138 | + if (value === undefined) { |
| 139 | + throw new Error( |
| 140 | + `Option -${shortOpt} requires a value`, |
| 141 | + ) |
| 142 | + } |
| 143 | + } |
| 144 | + if (option.multiple) { |
| 145 | + if (!Array.isArray(values[optName])) |
| 146 | + values[optName] = [] |
| 147 | + //@ts-ignore |
| 148 | + values[optName].push(value) |
| 149 | + } else { |
| 150 | + values[optName] = value |
| 151 | + } |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + return { values, positionals } |
| 158 | +} |
| 159 | + |
7 | 160 | /** |
8 | 161 | * ANSI escape codes mapping |
9 | 162 | * @typedef {keyof typeof styles} Format |
@@ -51,13 +204,15 @@ const styles = { |
51 | 204 | bgWhite: '\x1b[47m', |
52 | 205 | } |
53 | 206 |
|
| 207 | +export const styleText = utils.styleText || _styleText |
| 208 | + |
54 | 209 | /** |
55 | 210 | * Basic implementation of util.styleText for formatting text with ANSI colors |
56 | 211 | * @param {Format | Format[]} format - A text format or an Array of text formats |
57 | 212 | * @param {string} text - The text to be formatted |
58 | 213 | * @returns {string} The formatted text with ANSI escape codes |
59 | 214 | */ |
60 | | -export function styleText(format, text) { |
| 215 | +function _styleText(format, text) { |
61 | 216 | const formats = Array.isArray(format) ? format : [format] |
62 | 217 | // Build the opening escape sequences |
63 | 218 | let openCodes = '' |
@@ -103,10 +258,13 @@ export async function printRegistries( |
103 | 258 | timeoutLimit, |
104 | 259 | ) { |
105 | 260 | const registries = await getAllRegistries() |
106 | | - registriesInfo ||= Array.from(registries.entries()).map(([name, url]) => ({ |
107 | | - name, |
108 | | - url, |
109 | | - })) |
| 261 | + if (!registriesInfo) |
| 262 | + registriesInfo = Array.from(registries.entries()).map( |
| 263 | + ([name, url]) => ({ |
| 264 | + name, |
| 265 | + url, |
| 266 | + }), |
| 267 | + ) |
110 | 268 |
|
111 | 269 | const maxNameLength = Math.max( |
112 | 270 | ...registriesInfo.map(({ name }) => name.length), |
|
0 commit comments