diff --git a/bin/handlers/npm-handler.ts b/bin/handlers/npm-handler.ts index e4528e9..2c1f6ab 100644 --- a/bin/handlers/npm-handler.ts +++ b/bin/handlers/npm-handler.ts @@ -1,5 +1,158 @@ import type { PackageManagerCompletion } from '../package-manager-completion.js'; +import { stripAnsiEscapes, type ParsedOption } from '../utils/text-utils.js'; +import { + LazyCommand, + OptionHandlers, + commonOptionHandlers, + setupLazyOptionLoading, + setupCommandArguments, + safeExec, + safeExecSync, + createLogLevelHandler, +} from '../utils/shared.js'; + +const ALL_COMMANDS_RE = /^All commands:\s*$/i; +const OPTIONS_SECTION_RE = /^Options:\s*$/i; +const SECTION_END_RE = /^(aliases|run|more)/i; // marks end of Options: block +const COMMAND_VALIDATION_RE = /^[a-z][a-z0-9-]*$/; +const NPM_OPTION_RE = + /(?:\[)?(?:-([a-z])\|)?--([a-z][a-z0-9-]+)(?:\s+<[^>]+>)?(?:\])?/gi; +const ANGLE_VALUE_RE = /<[^>]+>/; +const INDENTED_LINE_RE = /^\s/; + +function toLines(helpText: string): string[] { + return stripAnsiEscapes(helpText).split(/\r?\n/); +} + +function readIndentedBlockAfter(lines: string[], headerRe: RegExp): string { + const start = lines.findIndex((l) => headerRe.test(l.trim())); + if (start === -1) return ''; + + let buf = ''; + for (let i = start + 1; i < lines.length; i++) { + const line = lines[i]; + if (!INDENTED_LINE_RE.test(line) && line.trim() && !line.includes(',')) + break; + if (INDENTED_LINE_RE.test(line)) buf += ' ' + line.trim(); + } + return buf; +} + +const listHandler = + (values: string[], describe: (v: string) => string = () => ' ') => + (complete: (value: string, description: string) => void) => + values.forEach((v) => complete(v, describe(v))); + +const npmOptionHandlers: OptionHandlers = { + ...commonOptionHandlers, + + loglevel: createLogLevelHandler([ + 'silent', + 'error', + 'warn', + 'notice', + 'http', + 'info', + 'verbose', + 'silly', + ]), + + 'install-strategy': listHandler( + ['hoisted', 'nested', 'shallow', 'linked'], + () => ' ' + ), + + omit: listHandler(['dev', 'optional', 'peer'], () => ' '), + + include: listHandler(['prod', 'dev', 'optional', 'peer'], () => ' '), +}; + +export function parseNpmHelp(helpText: string): Record { + const lines = toLines(helpText); + const commandsBlob = readIndentedBlockAfter(lines, ALL_COMMANDS_RE); + if (!commandsBlob) return {}; + + const commands: Record = {}; + + commandsBlob + .split(',') + .map((c) => c.trim()) + .filter((c) => c && COMMAND_VALIDATION_RE.test(c)) + .forEach((cmd) => { + // npm main help has no per-command descriptions + commands[cmd] = ' '; + }); + + // this is the most common used aliase that isn't in the main list + commands['run'] = ' '; + + return commands; +} + +// Get npm commands from the main help output +export async function getNpmCommandsFromMainHelp(): Promise< + Record +> { + const output = await safeExec('npm --help'); + return output ? parseNpmHelp(output) : {}; +} + +export function parseNpmOptions( + helpText: string, + { flagsOnly = true }: { flagsOnly?: boolean } = {} +): ParsedOption[] { + const lines = toLines(helpText); + + const start = lines.findIndex((l) => OPTIONS_SECTION_RE.test(l.trim())); + if (start === -1) return []; + + const out: ParsedOption[] = []; + + for (const line of lines.slice(start + 1)) { + const trimmed = line.trim(); + if (SECTION_END_RE.test(trimmed)) break; + + const matches = line.matchAll(NPM_OPTION_RE); + for (const m of matches) { + const short = m[1] || undefined; + const long = m[2]; + const takesValue = ANGLE_VALUE_RE.test(m[0]); + if (flagsOnly && takesValue) continue; + + out.push({ short, long, desc: ' ' }); + } + } + + return out; +} + +function loadNpmOptionsSync(cmd: LazyCommand, command: string): void { + const output = safeExecSync(`npm ${command} --help`); + if (!output) return; + + const allOptions = parseNpmOptions(output, { flagsOnly: false }); + + for (const { long, short, desc } of allOptions) { + const exists = cmd.optionsRaw?.get?.(long); + if (exists) continue; + + const handler = npmOptionHandlers[long]; + if (handler) cmd.option(long, desc, handler, short); + else cmd.option(long, desc, short); + } +} export async function setupNpmCompletions( completion: PackageManagerCompletion -): Promise {} +): Promise { + try { + const commands = await getNpmCommandsFromMainHelp(); + for (const [command, description] of Object.entries(commands)) { + const c = completion.command(command, description); + + setupCommandArguments(c, command, 'npm'); + + setupLazyOptionLoading(c, command, 'npm', loadNpmOptionsSync); + } + } catch {} +} diff --git a/bin/handlers/pnpm-handler.ts b/bin/handlers/pnpm-handler.ts index 770d173..b3502bf 100644 --- a/bin/handlers/pnpm-handler.ts +++ b/bin/handlers/pnpm-handler.ts @@ -1,22 +1,15 @@ -import { promisify } from 'node:util'; -import child_process from 'node:child_process'; - -const exec = promisify(child_process.exec); -const { execSync } = child_process; import type { PackageManagerCompletion } from '../package-manager-completion.js'; -import { Command, Option } from '../../src/t.js'; - -interface LazyCommand extends Command { - _lazyCommand?: string; - _optionsLoaded?: boolean; - optionsRaw?: Map; -} - -import { - packageJsonScriptCompletion, - packageJsonDependencyCompletion, -} from '../completions/completion-producers.js'; import { getWorkspacePatterns } from '../utils/filesystem-utils.js'; +import { + LazyCommand, + OptionHandlers, + commonOptionHandlers, + setupLazyOptionLoading, + setupCommandArguments, + safeExec, + safeExecSync, + createLogLevelHandler, +} from '../utils/shared.js'; import { stripAnsiEscapes, measureIndent, @@ -27,314 +20,314 @@ import { type ParsedOption, } from '../utils/text-utils.js'; -// regex to detect options section in help text const OPTIONS_SECTION_RE = /^\s*Options:/i; +const LEVEL_MATCH_RE = /(?:levels?|options?|values?)[^:]*:\s*([^.]+)/i; +const LINE_SPLIT_RE = /\r?\n/; +const COMMA_SPACE_SPLIT_RE = /[,\s]+/; +const OPTION_WITH_VALUE_RE = + /^\s*(?:-\w,?\s*)?--(\w+(?:-\w+)*)\s+(\w+(?:-\w+)*)\s+(.+)$/; +const OPTION_ALIAS_RE = + /^\s*-\w,?\s*--\w+(?:,\s*--(\w+(?:-\w+)*)\s+(\w+(?:-\w+)*))?\s+(.+)$/; +const CONTINUATION_LINE_RE = /^\s{20,}/; +const SECTION_HEADER_RE = /^\s*[A-Z][^:]*:\s*$/; + +function toLines(text: string): string[] { + return stripAnsiEscapes(text).split(LINE_SPLIT_RE); +} + +function findCommandDescColumn(lines: string[]): number { + let col = Number.POSITIVE_INFINITY; + for (const line of lines) { + const m = line.match(COMMAND_ROW_RE); + if (!m) continue; + const idx = line.indexOf(m[2]); + if (idx >= 0 && idx < col) col = idx; + } + return col; +} + +function findOptionDescColumn(lines: string[], flagsOnly: boolean): number { + let col = Number.POSITIVE_INFINITY; + for (const line of lines) { + const m = line.match(OPTION_ROW_RE); + if (!m) continue; + if (flagsOnly && m.groups?.val) continue; // skip value-taking options in flagsOnly mode + const idx = line.indexOf(m.groups!.desc); + if (idx >= 0 && idx < col) col = idx; + } + return col; +} function extractValidValuesFromHelp( helpText: string, optionName: string -): string[] { - const lines = stripAnsiEscapes(helpText).split(/\r?\n/); +): Array<{ value: string; desc: string }> { + const lines = toLines(helpText); + const results: Array<{ value: string; desc: string }> = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; - if (line.includes(`--${optionName}`) || line.includes(`${optionName}:`)) { - for (let j = i; j < Math.min(i + 3, lines.length); j++) { - const searchLine = lines[j]; - - const levelMatch = searchLine.match( - /(?:levels?|options?|values?)[^:]*:\s*([^.]+)/i - ); - if (levelMatch) { - return levelMatch[1] - .split(/[,\s]+/) - .map((v) => v.trim()) - .filter((v) => v && !v.includes('(') && !v.includes(')')); + + // Look for options with values in any section + const optionMatch = line.match(OPTION_WITH_VALUE_RE); + if (optionMatch) { + const [, option, value, initialDesc] = optionMatch; + if (option === optionName) { + // capture continuation lines for complete description + let fullDesc = initialDesc.trim(); + let j = i + 1; + + // Look ahead for continuation lines (indented lines that don't start new options) + while (j < lines.length) { + const nextLine = lines[j]; + const isIndented = CONTINUATION_LINE_RE.test(nextLine); + const isNewOption = + OPTION_WITH_VALUE_RE.test(nextLine) || + OPTION_ALIAS_RE.test(nextLine); + const isEmptyOrSection = + !nextLine.trim() || SECTION_HEADER_RE.test(nextLine); + + if (isIndented && !isNewOption && !isEmptyOrSection) { + fullDesc += ' ' + nextLine.trim(); + j++; + } else { + break; + } } - if (optionName === 'reporter') { - const reporterMatch = searchLine.match(/--reporter\s+(\w+)/); - if (reporterMatch) { - const reporterValues = new Set(); - for (const helpLine of lines) { - const matches = helpLine.matchAll(/--reporter\s+(\w+)/g); - for (const match of matches) { - reporterValues.add(match[1]); - } - } - return Array.from(reporterValues); + results.push({ value, desc: fullDesc }); + } + } + + const aliasMatch = line.match(OPTION_ALIAS_RE); + if (aliasMatch) { + const [, option, value, initialDesc] = aliasMatch; + if (option === optionName && value) { + // capture continuation lines for alias descriptions too + let fullDesc = initialDesc.trim(); + let j = i + 1; + + while (j < lines.length) { + const nextLine = lines[j]; + const isIndented = CONTINUATION_LINE_RE.test(nextLine); + const isNewOption = + OPTION_WITH_VALUE_RE.test(nextLine) || + OPTION_ALIAS_RE.test(nextLine); + const isEmptyOrSection = + !nextLine.trim() || SECTION_HEADER_RE.test(nextLine); + + if (isIndented && !isNewOption && !isEmptyOrSection) { + fullDesc += ' ' + nextLine.trim(); + j++; + } else { + break; } } + + results.push({ value, desc: fullDesc }); } } } + if (results.length) return results; + + for (let i = 0; i < lines.length; i++) { + const ln = lines[i]; + if (ln.includes(`--${optionName}`) || ln.includes(`${optionName}:`)) { + for (let j = i; j < Math.min(i + 3, lines.length); j++) { + const probe = lines[j]; + const m = probe.match(LEVEL_MATCH_RE); + if (m) { + return m[1] + .split(COMMA_SPACE_SPLIT_RE) + .map((v) => v.trim()) + .filter((v) => v && !v.includes('(') && !v.includes(')')) + .map((value) => ({ value, desc: `Log level: ${value}` })); + } + } + } + } return []; } -// completion handlers for pnpm options that take values -const pnpmOptionHandlers = { - // Let shell handle directory completions - it's much better at it - // dir, modules-dir, store-dir, lockfile-dir, virtual-store-dir removed +const pnpmOptionHandlers: OptionHandlers = { + ...commonOptionHandlers, - loglevel: function (complete: (value: string, description: string) => void) { - // Try to get values from help, fall back to known values - const helpValues = extractValidValuesFromHelp( - execSync('pnpm install --help', { encoding: 'utf8', timeout: 500 }), + loglevel(complete) { + const fromHelp = extractValidValuesFromHelp( + safeExecSync('pnpm install --help'), 'loglevel' ); - - if (helpValues.length > 0) { - helpValues.forEach((value) => complete(value, `Log level: ${value}`)); + if (fromHelp.length) { + fromHelp.forEach(({ value, desc }) => complete(value, desc)); } else { - // Fallback based on documented values - ['debug', 'info', 'warn', 'error', 'silent'].forEach((level) => - complete(level, `Log level: ${level}`) + createLogLevelHandler(['debug', 'info', 'warn', 'error', 'silent'])( + complete ); } }, - reporter: function (complete: (value: string, description: string) => void) { - // valid values from pnpm help - const reporters = [ - { value: 'default', desc: 'Default reporter when stdout is TTY' }, - { - value: 'append-only', - desc: 'Output always appended, no cursor manipulation', - }, - { value: 'ndjson', desc: 'Most verbose reporter in NDJSON format' }, - { value: 'silent', desc: 'No output logged to console' }, - ]; - - reporters.forEach(({ value, desc }) => complete(value, desc)); + reporter(complete) { + const out = extractValidValuesFromHelp( + safeExecSync('pnpm install --help'), + 'reporter' + ); + if (out.length) { + out.forEach(({ value, desc }) => complete(value, desc)); + } else { + createLogLevelHandler(['default', 'append-only', 'ndjson', 'silent'])( + complete + ); + } }, - filter: function (complete: (value: string, description: string) => void) { - // Based on pnpm documentation + filter(complete) { complete('.', 'Current working directory'); complete('!', 'Exclude packages matching selector'); - // Get actual workspace patterns from pnpm-workspace.yaml - const workspacePatterns = getWorkspacePatterns(); - workspacePatterns.forEach((pattern) => { - complete(pattern, `Workspace pattern: ${pattern}`); - complete(`${pattern}...`, `Include dependencies of ${pattern}`); + const patterns = getWorkspacePatterns(); + patterns.forEach((p) => { + complete(p, `Workspace pattern: ${p}`); + complete(`${p}...`, `Include dependencies of ${p}`); }); - // Common scope patterns complete('@*/*', 'All scoped packages'); complete('...', 'Include dependencies of pattern'); complete('...', 'Include dependents of pattern'); }, }; -// we parse the pnpm help text to extract commands and their descriptions! export function parsePnpmHelp(helpText: string): Record { - const helpLines = stripAnsiEscapes(helpText).split(/\r?\n/); - - // we find the earliest description column across command rows. - let descColumnIndex = Number.POSITIVE_INFINITY; - for (const line of helpLines) { - const rowMatch = line.match(COMMAND_ROW_RE); - if (!rowMatch) continue; - const descColumnIndexOnThisLine = line.indexOf(rowMatch[2]); - if ( - descColumnIndexOnThisLine >= 0 && - descColumnIndexOnThisLine < descColumnIndex - ) { - descColumnIndex = descColumnIndexOnThisLine; - } - } - if (!Number.isFinite(descColumnIndex)) return {}; - - // we fold rows, and join continuation lines aligned to descColumnIndex or deeper. - type PendingRow = { names: string[]; desc: string } | null; - let pendingRow: PendingRow = null; - - const commandMap = new Map(); - const flushPendingRow = () => { - if (!pendingRow) return; - const desc = pendingRow.desc.trim(); - for (const name of pendingRow.names) commandMap.set(name, desc); - pendingRow = null; + const lines = toLines(helpText); + + const descCol = findCommandDescColumn(lines); + if (!Number.isFinite(descCol)) return {}; + + type Pending = { names: string[]; desc: string } | null; + let pending: Pending = null; + + const out = new Map(); + + const flush = () => { + if (!pending) return; + const desc = pending.desc.trim(); + for (const n of pending.names) out.set(n, desc); + pending = null; }; - for (const line of helpLines) { - if (OPTIONS_SECTION_RE.test(line)) break; // we stop at options + for (const line of lines) { + if (OPTIONS_SECTION_RE.test(line)) break; // end of commands section - // we match the command row - const rowMatch = line.match(COMMAND_ROW_RE); - if (rowMatch) { - flushPendingRow(); - pendingRow = { - names: parseAliasList(rowMatch[1]), - desc: rowMatch[2].trim(), + const row = line.match(COMMAND_ROW_RE); + if (row) { + flush(); + pending = { + names: parseAliasList(row[1]), + desc: row[2].trim(), }; continue; } - // we join continuation lines aligned to descColumnIndex or deeper - if (pendingRow) { - const indentWidth = measureIndent(line); - if (indentWidth >= descColumnIndex && line.trim()) { - pendingRow.desc += ' ' + line.trim(); + if (pending) { + const indent = measureIndent(line); + if (indent >= descCol && line.trim()) { + pending.desc += ' ' + line.trim(); } } } - // we flush the pending row and return the command map - flushPendingRow(); + flush(); - return Object.fromEntries(commandMap); + return Object.fromEntries(out); } -// now we get the pnpm commands from the main help output export async function getPnpmCommandsFromMainHelp(): Promise< Record > { - try { - const { stdout } = await exec('pnpm --help', { - encoding: 'utf8', - timeout: 500, - maxBuffer: 4 * 1024 * 1024, - }); - return parsePnpmHelp(stdout); - } catch { - return {}; - } + const output = await safeExec('pnpm --help'); + return output ? parsePnpmHelp(output) : {}; } -// here we parse the pnpm options from the help text export function parsePnpmOptions( helpText: string, { flagsOnly = true }: { flagsOnly?: boolean } = {} ): ParsedOption[] { - // we strip the ANSI escapes from the help text - const helpLines = stripAnsiEscapes(helpText).split(/\r?\n/); - - // we find the earliest description column among option rows we care about - let descColumnIndex = Number.POSITIVE_INFINITY; - for (const line of helpLines) { - const optionMatch = line.match(OPTION_ROW_RE); - if (!optionMatch) continue; - if (flagsOnly && optionMatch.groups?.val) continue; - const descColumnIndexOnThisLine = line.indexOf(optionMatch.groups!.desc); - if ( - descColumnIndexOnThisLine >= 0 && - descColumnIndexOnThisLine < descColumnIndex - ) { - descColumnIndex = descColumnIndexOnThisLine; - } - } - if (!Number.isFinite(descColumnIndex)) return []; + const lines = toLines(helpText); + + const descCol = findOptionDescColumn(lines, flagsOnly); + if (!Number.isFinite(descCol)) return []; - // we fold the option rows and join the continuations - const optionsOut: ParsedOption[] = []; - let pendingOption: ParsedOption | null = null; + const out: ParsedOption[] = []; + let pending: ParsedOption | null = null; - const flushPendingOption = () => { - if (!pendingOption) return; - pendingOption.desc = pendingOption.desc.trim(); - optionsOut.push(pendingOption); - pendingOption = null; + const flush = () => { + if (!pending) return; + pending.desc = pending.desc.trim(); + out.push(pending); + pending = null; }; - // we match the option row - for (const line of helpLines) { - const optionMatch = line.match(OPTION_ROW_RE); - if (optionMatch) { - if (flagsOnly && optionMatch.groups?.val) continue; - flushPendingOption(); - pendingOption = { - short: optionMatch.groups?.short || undefined, - long: optionMatch.groups!.long, - desc: optionMatch.groups!.desc.trim(), + for (const line of lines) { + const m = line.match(OPTION_ROW_RE); + if (m) { + if (flagsOnly && m.groups?.val) continue; + flush(); + pending = { + short: m.groups?.short || undefined, + long: m.groups!.long, + desc: m.groups!.desc.trim(), }; continue; } - // we join the continuations - if (pendingOption) { - const indentWidth = measureIndent(line); - const startsNewOption = OPTION_HEAD_RE.test(line); - if (indentWidth >= descColumnIndex && line.trim() && !startsNewOption) { - pendingOption.desc += ' ' + line.trim(); + if (pending) { + const indent = measureIndent(line); + const startsNew = OPTION_HEAD_RE.test(line); + if (indent >= descCol && line.trim() && !startsNew) { + pending.desc += ' ' + line.trim(); } } } - // we flush the pending option - flushPendingOption(); + flush(); - return optionsOut; + return out; } -// we load the dynamic options synchronously when requested ( separated from the command loading ) -export function loadDynamicOptionsSync( - cmd: LazyCommand, - command: string -): void { - try { - const stdout = execSync(`pnpm ${command} --help`, { - encoding: 'utf8', - timeout: 500, - }); - - const allOptions = parsePnpmOptions(stdout, { flagsOnly: false }); - - for (const { long, short, desc } of allOptions) { - const alreadyDefined = cmd.optionsRaw?.get?.(long); - if (!alreadyDefined) { - const handler = - pnpmOptionHandlers[long as keyof typeof pnpmOptionHandlers]; - if (handler) { - cmd.option(long, desc, handler, short); - } else { - cmd.option(long, desc, short); - } - } - } - } catch (_err) {} -} +function loadPnpmOptionsSync(cmd: LazyCommand, command: string): void { + const output = safeExecSync(`pnpm ${command} --help`); + if (!output) return; -// we setup the lazy option loading for a command + const options = parsePnpmOptions(output, { flagsOnly: false }); -function setupLazyOptionLoading(cmd: LazyCommand, command: string): void { - cmd._lazyCommand = command; - cmd._optionsLoaded = false; + for (const { long, short, desc } of options) { + const exists = cmd.optionsRaw?.get?.(long); + if (exists) continue; - const optionsStore = cmd.options; - cmd.optionsRaw = optionsStore; + const handler = pnpmOptionHandlers[long]; + if (handler) cmd.option(long, desc, handler, short); + else cmd.option(long, desc, short); + } - Object.defineProperty(cmd, 'options', { - get() { - if (!this._optionsLoaded) { - this._optionsLoaded = true; - loadDynamicOptionsSync(this, this._lazyCommand); // block until filled + // Register options found by general algorithm but not in standard parsing + for (const [optionName, handler] of Object.entries(pnpmOptionHandlers)) { + if (!cmd.optionsRaw?.get?.(optionName)) { + const values = extractValidValuesFromHelp(output, optionName); + if (values.length > 0) { + cmd.option(optionName, ' ', handler); } - return optionsStore; - }, - configurable: true, - }); + } + } } export async function setupPnpmCompletions( completion: PackageManagerCompletion ): Promise { try { - const commandsWithDescriptions = await getPnpmCommandsFromMainHelp(); - - for (const [command, description] of Object.entries( - commandsWithDescriptions - )) { - const cmd = completion.command(command, description); - - if (['remove', 'rm', 'update', 'up'].includes(command)) { - cmd.argument('package', packageJsonDependencyCompletion); - } - if (command === 'run') { - cmd.argument('script', packageJsonScriptCompletion, true); - } + const commands = await getPnpmCommandsFromMainHelp(); - setupLazyOptionLoading(cmd, command); + for (const [command, description] of Object.entries(commands)) { + const c = completion.command(command, description); + setupCommandArguments(c, command, 'pnpm'); + setupLazyOptionLoading(c, command, 'pnpm', loadPnpmOptionsSync); } - } catch (_err) {} + } catch {} } diff --git a/bin/utils/shared.ts b/bin/utils/shared.ts new file mode 100644 index 0000000..30f749a --- /dev/null +++ b/bin/utils/shared.ts @@ -0,0 +1,104 @@ +import { promisify } from 'node:util'; +import child_process from 'node:child_process'; + +export const exec = promisify(child_process.exec); +export const { execSync } = child_process; + +import { Command, Option } from '../../src/t.js'; +import { + packageJsonScriptCompletion, + packageJsonDependencyCompletion, +} from '../completions/completion-producers.js'; +import { getWorkspacePatterns } from './filesystem-utils.js'; + +export interface LazyCommand extends Command { + _lazyCommand?: string; + _optionsLoaded?: boolean; + optionsRaw?: Map; +} + +export type CompletionHandler = ( + complete: (value: string, description: string) => void +) => void; +export type OptionHandlers = Record; + +export const commonOptionHandlers: OptionHandlers = { + workspace(complete) { + const patterns = getWorkspacePatterns(); + patterns.forEach((p) => complete(p, `Workspace pattern: ${p}`)); + }, +}; + +export function setupLazyOptionLoading( + cmd: LazyCommand, + command: string, + _packageManager: string, + loadOptionsSync: (cmd: LazyCommand, command: string) => void +): void { + cmd._lazyCommand = command; + cmd._optionsLoaded = false; + + const store = cmd.options; + cmd.optionsRaw = store; + + Object.defineProperty(cmd, 'options', { + get() { + if (!this._optionsLoaded) { + this._optionsLoaded = true; + loadOptionsSync(this, this._lazyCommand); + } + return store; + }, + configurable: true, + }); +} + +export function setupCommandArguments( + cmd: LazyCommand, + command: string, + _packageManager: string +): void { + if (['remove', 'rm', 'uninstall', 'un', 'update', 'up'].includes(command)) { + cmd.argument('package', packageJsonDependencyCompletion); + } + + if (['run', 'run-script'].includes(command)) { + cmd.argument('script', packageJsonScriptCompletion, true); + } +} + +export async function safeExec( + command: string, + options: any = {} +): Promise { + try { + const { stdout } = await exec(command, { + encoding: 'utf8' as const, + timeout: 500, + maxBuffer: 4 * 1024 * 1024, + ...options, + }); + return stdout as unknown as string; + } catch (error) { + if (error instanceof Error && 'stdout' in error) { + return (error as any).stdout as string; + } + return ''; + } +} + +export function safeExecSync(command: string, options: any = {}): string { + try { + return execSync(command, { + encoding: 'utf8' as const, + timeout: 500, + ...options, + }) as unknown as string; + } catch (error: any) { + return error?.stdout ? (error.stdout as string) : ''; + } +} + +export function createLogLevelHandler(levels: string[]): CompletionHandler { + return (complete) => levels.forEach((lvl) => complete(lvl, ' ')); +} diff --git a/completion-debug.log b/completion-debug.log new file mode 100644 index 0000000..7d5988a --- /dev/null +++ b/completion-debug.log @@ -0,0 +1,652 @@ + +========= starting completion logic ========== +CURRENT: 2, words[*]: pnpm +Truncated words[*]: pnpm , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- '' '' +completion output: add Installs a package and any packages that it depends on. By default, any new package is installed as a prod dependency +import Generates a pnpm-lock.yaml from an npm package-lock.json (or npm-shrinkwrap.json) file +i Install all dependencies for a project +install Install all dependencies for a project +it Runs a pnpm install followed immediately by a pnpm test +install-test Runs a pnpm install followed immediately by a pnpm test +ln Connect the local project to another one +link Connect the local project to another one +prune Removes extraneous packages +rb Rebuild a package +rebuild Rebuild a package +rm Removes packages from node_modules and from the project's package.json +remove Removes packages from node_modules and from the project's package.json +unlink Unlinks a package. Like yarn unlink but pnpm re-installs the dependency after removing the external link +up Updates packages to their latest version based on the specified range +update Updates packages to their latest version based on the specified range +audit Checks for known security issues with the installed packages +licenses Check licenses in consumed packages +ls Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure +list Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure +outdated Check for outdated packages +exec Executes a shell command in scope of a project +run Runs a defined package script +start Runs an arbitrary command specified in the package's "start" property of its "scripts" object +t Runs a package's "test" script, if one was provided +test Runs a package's "test" script, if one was provided +cat-file Prints the contents of a file based on the hash value stored in the index file +cat-index Prints the index file of a specific package from the store +find-hash Experimental! Lists the packages that include the file with the specified hash. +pack Create a tarball from a package +publish Publishes a package to the registry +root Prints the effective modules directory +store Adds new packages to the pnpm store directly. Does not modify any projects or files outside the store +:4 +last line: :4 +directive: 4 +completions: add Installs a package and any packages that it depends on. By default, any new package is installed as a prod dependency +import Generates a pnpm-lock.yaml from an npm package-lock.json (or npm-shrinkwrap.json) file +i Install all dependencies for a project +install Install all dependencies for a project +it Runs a pnpm install followed immediately by a pnpm test +install-test Runs a pnpm install followed immediately by a pnpm test +ln Connect the local project to another one +link Connect the local project to another one +prune Removes extraneous packages +rb Rebuild a package +rebuild Rebuild a package +rm Removes packages from node_modules and from the project's package.json +remove Removes packages from node_modules and from the project's package.json +unlink Unlinks a package. Like yarn unlink but pnpm re-installs the dependency after removing the external link +up Updates packages to their latest version based on the specified range +update Updates packages to their latest version based on the specified range +audit Checks for known security issues with the installed packages +licenses Check licenses in consumed packages +ls Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure +list Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure +outdated Check for outdated packages +exec Executes a shell command in scope of a project +run Runs a defined package script +start Runs an arbitrary command specified in the package's "start" property of its "scripts" object +t Runs a package's "test" script, if one was provided +test Runs a package's "test" script, if one was provided +cat-file Prints the contents of a file based on the hash value stored in the index file +cat-index Prints the index file of a specific package from the store +find-hash Experimental! Lists the packages that include the file with the specified hash. +pack Create a tarball from a package +publish Publishes a package to the registry +root Prints the effective modules directory +store Adds new packages to the pnpm store directly. Does not modify any projects or files outside the store +flagPrefix: +Adding completion: add:Installs a package and any packages that it depends on. By default, any new package is installed as a prod dependency +Adding completion: import:Generates a pnpm-lock.yaml from an npm package-lock.json (or npm-shrinkwrap.json) file +Adding completion: i:Install all dependencies for a project +Adding completion: install:Install all dependencies for a project +Adding completion: it:Runs a pnpm install followed immediately by a pnpm test +Adding completion: install-test:Runs a pnpm install followed immediately by a pnpm test +Adding completion: ln:Connect the local project to another one +Adding completion: link:Connect the local project to another one +Adding completion: prune:Removes extraneous packages +Adding completion: rb:Rebuild a package +Adding completion: rebuild:Rebuild a package +Adding completion: rm:Removes packages from node_modules and from the project's package.json +Adding completion: remove:Removes packages from node_modules and from the project's package.json +Adding completion: unlink:Unlinks a package. Like yarn unlink but pnpm re-installs the dependency after removing the external link +Adding completion: up:Updates packages to their latest version based on the specified range +Adding completion: update:Updates packages to their latest version based on the specified range +Adding completion: audit:Checks for known security issues with the installed packages +Adding completion: licenses:Check licenses in consumed packages +Adding completion: ls:Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure +Adding completion: list:Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure +Adding completion: outdated:Check for outdated packages +Adding completion: exec:Executes a shell command in scope of a project +Adding completion: run:Runs a defined package script +Adding completion: start:Runs an arbitrary command specified in the package's "start" property of its "scripts" object +Adding completion: t:Runs a package's "test" script, if one was provided +Adding completion: test:Runs a package's "test" script, if one was provided +Adding completion: cat-file:Prints the contents of a file based on the hash value stored in the index file +Adding completion: cat-index:Prints the index file of a specific package from the store +Adding completion: find-hash:Experimental! Lists the packages that include the file with the specified hash. +Adding completion: pack:Create a tarball from a package +Adding completion: publish:Publishes a package to the registry +Adding completion: root:Prints the effective modules directory +Adding completion: store:Adds new packages to the pnpm store directly. Does not modify any projects or files outside the store +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 2, words[*]: pnpm ins +Truncated words[*]: pnpm ins, +lastParam: ins, lastChar: s +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- ins +completion output: install Install all dependencies for a project +install-test Runs a pnpm install followed immediately by a pnpm test +:4 +last line: :4 +directive: 4 +completions: install Install all dependencies for a project +install-test Runs a pnpm install followed immediately by a pnpm test +flagPrefix: +Adding completion: install:Install all dependencies for a project +Adding completion: install-test:Runs a pnpm install followed immediately by a pnpm test +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install -- +Truncated words[*]: pnpm install --, +lastParam: --, lastChar: - +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install -- +completion output: --aggregate-output Aggregate output from child processes that are run in parallel, and only print output when child process is finished. It makes reading large logs after running `pnpm recursive` with `--parallel` or with `--workspace-concurrency` much easier (especially on CI). Only `--reporter=append-only` is supported. +--child-concurrency Controls the number of child processes run parallelly to build node modules +--dev Only `devDependencies` are installed +--dir Change to directory (default: /Users/amir/Desktop/projects/tab) +--fix-lockfile Fix broken lockfile entries automatically +--force Force reinstall dependencies: refetch packages modified in store, recreate a lockfile and/or modules directory created by a non-compatible version of pnpm. Install all optionalDependencies even they don't satisfy the current environment(cpu, os, arch) +--global-dir Specify a custom directory to store global packages +--help Output usage information +--hoist-pattern Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +--ignore-pnpmfile Disable pnpm hooks defined in .pnpmfile.cjs +--ignore-scripts Don't run lifecycle scripts +--ignore-workspace Ignore pnpm-workspace.yaml if exists in the parent directory, and treat the installation as normal non-workspace installation. +--lockfile-dir The directory in which the pnpm-lock.yaml of the package will be created. Several projects may share a single lockfile. +--lockfile-only Dependencies are not downloaded. Only `pnpm-lock.yaml` is updated +--loglevel What level of logs to report. Any logs at or higher than the given level will be shown. Levels (lowest to highest): debug, info, warn, error. Or use "--silent" to turn off all logging. +--merge-git-branch-lockfiles Merge lockfiles were generated on git branch +--modules-dir The directory in which dependencies will be installed (instead of node_modules) +--network-concurrency Maximum number of concurrent network requests +--no-hoist Dependencies inside the modules directory will have access only to their listed dependencies +--no-lockfile Don't read or generate a `pnpm-lock.yaml` file +--no-optional `optionalDependencies` are not installed +--offline Trigger an error if any required dependencies are not available in local store +--optimistic-repeat-install Skip reinstall if the workspace state is up-to-date selected method depends from the file system the store +--prefer-frozen-lockfile If the available `pnpm-lock.yaml` satisfies the `package.json` then perform a headless installation +--prefer-offline Skip staleness checks for cached data, but request missing data from the server +--prod Packages in `devDependencies` won't be installed +--public-hoist-pattern Hoist all dependencies matching the pattern to the root of the modules directory +--recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--shamefully-hoist All the subdeps will be hoisted into the root node_modules. Your code will have access to them +--side-effects-cache Use or cache the results of (pre/post)install hooks +--side-effects-cache-readonly Only use the side effects cache if present, do not create it for new packages +--store-dir The directory in which all the packages are saved on the disk +--stream Stream output from child processes immediately, prefixed with the originating package directory. This allows output from different packages to be interleaved. +--strict-peer-dependencies Fail on missing or invalid peer dependencies +--use-running-store-server Only allows installation with a store server. If no store server is running, installation will fail +--use-stderr Divert all output to stderr +--use-store-server Starts a store server in the background. The store server will keep running after installation is done. To stop the store server, run `pnpm server stop` +--virtual-store-dir The directory with links to the store (default is node_modules/.pnpm). All direct and indirect dependencies of the project are linked into this directory +--workspace-root Run the command on the root workspace project +--changed-files-ignore-pattern Defines files to ignore when filtering for changed projects since the specified commit/branch. Usage example: pnpm pattern="**/README.md" build +--fail-if-no-match If no projects are matched by the command, exit with exit code 1 (fail) \! in zsh), it means the packages matching the selector must be excluded. E.g., "pnpm packages except "foo" under the current working directory indirect dependents of the matched packages without including the matched packages themselves. ^ must be doubled at the Windows Command Prompt. E.g.: ...^foo (...^^foo in Command Prompt) dependents of the matched packages. E.g.: ...foo, "...@bar/*" inside a given subdirectory. E.g.: ./components since the specified commit/branch. E.g.: "[master]", "[HEAD~2]". It may be used together with "...". So, for instance, "...[HEAD~1]" selects all packages changed in the last commit and their dependents under the specified directory. It may be used with "..." to select dependents/dependencies as well. It also may be combined with "[]". For instance, all changed projects inside a directory: "{packages}[origin/master]" +--filter Restricts the scope to package names matching the given pattern. E.g.: foo, "@bar/*" dependencies of the matched packages. E.g.: foo... indirect dependencies of the matched packages without including the matched packages themselves. ^ must be doubled at the Windows Command Prompt. E.g.: foo^... (foo^^... in Command Prompt) +--filter-prod Restricts the scope to package names matching the given pattern similar to --filter, but it ignores devDependencies when searching for dependencies and dependents. +--test-pattern Defines files related to tests. Useful with the changed since filter. When selecting only changed packages and their dependent packages, the dependent packages will be ignored in case a package has changes only in tests. Usage example: pnpm +--reporter Output reporter for pnpm commands +:4 +last line: :4 +directive: 4 +completions: --aggregate-output Aggregate output from child processes that are run in parallel, and only print output when child process is finished. It makes reading large logs after running `pnpm recursive` with `--parallel` or with `--workspace-concurrency` much easier (especially on CI). Only `--reporter=append-only` is supported. +--child-concurrency Controls the number of child processes run parallelly to build node modules +--dev Only `devDependencies` are installed +--dir Change to directory (default: /Users/amir/Desktop/projects/tab) +--fix-lockfile Fix broken lockfile entries automatically +--force Force reinstall dependencies: refetch packages modified in store, recreate a lockfile and/or modules directory created by a non-compatible version of pnpm. Install all optionalDependencies even they don't satisfy the current environment(cpu, os, arch) +--global-dir Specify a custom directory to store global packages +--help Output usage information +--hoist-pattern Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +--ignore-pnpmfile Disable pnpm hooks defined in .pnpmfile.cjs +--ignore-scripts Don't run lifecycle scripts +--ignore-workspace Ignore pnpm-workspace.yaml if exists in the parent directory, and treat the installation as normal non-workspace installation. +--lockfile-dir The directory in which the pnpm-lock.yaml of the package will be created. Several projects may share a single lockfile. +--lockfile-only Dependencies are not downloaded. Only `pnpm-lock.yaml` is updated +--loglevel What level of logs to report. Any logs at or higher than the given level will be shown. Levels (lowest to highest): debug, info, warn, error. Or use "--silent" to turn off all logging. +--merge-git-branch-lockfiles Merge lockfiles were generated on git branch +--modules-dir The directory in which dependencies will be installed (instead of node_modules) +--network-concurrency Maximum number of concurrent network requests +--no-hoist Dependencies inside the modules directory will have access only to their listed dependencies +--no-lockfile Don't read or generate a `pnpm-lock.yaml` file +--no-optional `optionalDependencies` are not installed +--offline Trigger an error if any required dependencies are not available in local store +--optimistic-repeat-install Skip reinstall if the workspace state is up-to-date selected method depends from the file system the store +--prefer-frozen-lockfile If the available `pnpm-lock.yaml` satisfies the `package.json` then perform a headless installation +--prefer-offline Skip staleness checks for cached data, but request missing data from the server +--prod Packages in `devDependencies` won't be installed +--public-hoist-pattern Hoist all dependencies matching the pattern to the root of the modules directory +--recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--shamefully-hoist All the subdeps will be hoisted into the root node_modules. Your code will have access to them +--side-effects-cache Use or cache the results of (pre/post)install hooks +--side-effects-cache-readonly Only use the side effects cache if present, do not create it for new packages +--store-dir The directory in which all the packages are saved on the disk +--stream Stream output from child processes immediately, prefixed with the originating package directory. This allows output from different packages to be interleaved. +--strict-peer-dependencies Fail on missing or invalid peer dependencies +--use-running-store-server Only allows installation with a store server. If no store server is running, installation will fail +--use-stderr Divert all output to stderr +--use-store-server Starts a store server in the background. The store server will keep running after installation is done. To stop the store server, run `pnpm server stop` +--virtual-store-dir The directory with links to the store (default is node_modules/.pnpm). All direct and indirect dependencies of the project are linked into this directory +--workspace-root Run the command on the root workspace project +--changed-files-ignore-pattern Defines files to ignore when filtering for changed projects since the specified commit/branch. Usage example: pnpm pattern="**/README.md" build +--fail-if-no-match If no projects are matched by the command, exit with exit code 1 (fail) \! in zsh), it means the packages matching the selector must be excluded. E.g., "pnpm packages except "foo" under the current working directory indirect dependents of the matched packages without including the matched packages themselves. ^ must be doubled at the Windows Command Prompt. E.g.: ...^foo (...^^foo in Command Prompt) dependents of the matched packages. E.g.: ...foo, "...@bar/*" inside a given subdirectory. E.g.: ./components since the specified commit/branch. E.g.: "[master]", "[HEAD~2]". It may be used together with "...". So, for instance, "...[HEAD~1]" selects all packages changed in the last commit and their dependents under the specified directory. It may be used with "..." to select dependents/dependencies as well. It also may be combined with "[]". For instance, all changed projects inside a directory: "{packages}[origin/master]" +--filter Restricts the scope to package names matching the given pattern. E.g.: foo, "@bar/*" dependencies of the matched packages. E.g.: foo... indirect dependencies of the matched packages without including the matched packages themselves. ^ must be doubled at the Windows Command Prompt. E.g.: foo^... (foo^^... in Command Prompt) +--filter-prod Restricts the scope to package names matching the given pattern similar to --filter, but it ignores devDependencies when searching for dependencies and dependents. +--test-pattern Defines files related to tests. Useful with the changed since filter. When selecting only changed packages and their dependent packages, the dependent packages will be ignored in case a package has changes only in tests. Usage example: pnpm +--reporter Output reporter for pnpm commands +flagPrefix: +Adding completion: --aggregate-output:Aggregate output from child processes that are run in parallel, and only print output when child process is finished. It makes reading large logs after running `pnpm recursive` with `--parallel` or with `--workspace-concurrency` much easier (especially on CI). Only `--reporter=append-only` is supported. +Adding completion: --child-concurrency:Controls the number of child processes run parallelly to build node modules +Adding completion: --dev:Only `devDependencies` are installed +Adding completion: --dir:Change to directory (default: /Users/amir/Desktop/projects/tab) +Adding completion: --fix-lockfile:Fix broken lockfile entries automatically +Adding completion: --force:Force reinstall dependencies: refetch packages modified in store, recreate a lockfile and/or modules directory created by a non-compatible version of pnpm. Install all optionalDependencies even they don't satisfy the current environment(cpu, os, arch) +Adding completion: --global-dir:Specify a custom directory to store global packages +Adding completion: --help:Output usage information +Adding completion: --hoist-pattern:Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +Adding completion: --ignore-pnpmfile:Disable pnpm hooks defined in .pnpmfile.cjs +Adding completion: --ignore-scripts:Don't run lifecycle scripts +Adding completion: --ignore-workspace:Ignore pnpm-workspace.yaml if exists in the parent directory, and treat the installation as normal non-workspace installation. +Adding completion: --lockfile-dir:The directory in which the pnpm-lock.yaml of the package will be created. Several projects may share a single lockfile. +Adding completion: --lockfile-only:Dependencies are not downloaded. Only `pnpm-lock.yaml` is updated +Adding completion: --loglevel:What level of logs to report. Any logs at or higher than the given level will be shown. Levels (lowest to highest): debug, info, warn, error. Or use "--silent" to turn off all logging. +Adding completion: --merge-git-branch-lockfiles:Merge lockfiles were generated on git branch +Adding completion: --modules-dir:The directory in which dependencies will be installed (instead of node_modules) +Adding completion: --network-concurrency:Maximum number of concurrent network requests +Adding completion: --no-hoist:Dependencies inside the modules directory will have access only to their listed dependencies +Adding completion: --no-lockfile:Don't read or generate a `pnpm-lock.yaml` file +Adding completion: --no-optional:`optionalDependencies` are not installed +Adding completion: --offline:Trigger an error if any required dependencies are not available in local store +Adding completion: --optimistic-repeat-install:Skip reinstall if the workspace state is up-to-date selected method depends from the file system the store +Adding completion: --prefer-frozen-lockfile:If the available `pnpm-lock.yaml` satisfies the `package.json` then perform a headless installation +Adding completion: --prefer-offline:Skip staleness checks for cached data, but request missing data from the server +Adding completion: --prod:Packages in `devDependencies` won't be installed +Adding completion: --public-hoist-pattern:Hoist all dependencies matching the pattern to the root of the modules directory +Adding completion: --recursive:Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +Adding completion: --resolution-only:Re-runs resolution: useful for printing out peer dependency issues +Adding completion: --shamefully-hoist:All the subdeps will be hoisted into the root node_modules. Your code will have access to them +Adding completion: --side-effects-cache:Use or cache the results of (pre/post)install hooks +Adding completion: --side-effects-cache-readonly:Only use the side effects cache if present, do not create it for new packages +Adding completion: --store-dir:The directory in which all the packages are saved on the disk +Adding completion: --stream:Stream output from child processes immediately, prefixed with the originating package directory. This allows output from different packages to be interleaved. +Adding completion: --strict-peer-dependencies:Fail on missing or invalid peer dependencies +Adding completion: --use-running-store-server:Only allows installation with a store server. If no store server is running, installation will fail +Adding completion: --use-stderr:Divert all output to stderr +Adding completion: --use-store-server:Starts a store server in the background. The store server will keep running after installation is done. To stop the store server, run `pnpm server stop` +Adding completion: --virtual-store-dir:The directory with links to the store (default is node_modules/.pnpm). All direct and indirect dependencies of the project are linked into this directory +Adding completion: --workspace-root:Run the command on the root workspace project +Adding completion: --changed-files-ignore-pattern:Defines files to ignore when filtering for changed projects since the specified commit/branch. Usage example: pnpm pattern="**/README.md" build +Adding completion: --fail-if-no-match:If no projects are matched by the command, exit with exit code 1 (fail) \! in zsh), it means the packages matching the selector must be excluded. E.g., "pnpm packages except "foo" under the current working directory indirect dependents of the matched packages without including the matched packages themselves. ^ must be doubled at the Windows Command Prompt. E.g.: ...^foo (...^^foo in Command Prompt) dependents of the matched packages. E.g.: ...foo, "...@bar/*" inside a given subdirectory. E.g.: ./components since the specified commit/branch. E.g.: "[master]", "[HEAD~2]". It may be used together with "...". So, for instance, "...[HEAD~1]" selects all packages changed in the last commit and their dependents under the specified directory. It may be used with "..." to select dependents/dependencies as well. It also may be combined with "[]". For instance, all changed projects inside a directory: "{packages}[origin/master]" +Adding completion: --filter:Restricts the scope to package names matching the given pattern. E.g.: foo, "@bar/*" dependencies of the matched packages. E.g.: foo... indirect dependencies of the matched packages without including the matched packages themselves. ^ must be doubled at the Windows Command Prompt. E.g.: foo^... (foo^^... in Command Prompt) +Adding completion: --filter-prod:Restricts the scope to package names matching the given pattern similar to --filter, but it ignores devDependencies when searching for dependencies and dependents. +Adding completion: --test-pattern:Defines files related to tests. Useful with the changed since filter. When selecting only changed packages and their dependent packages, the dependent packages will be ignored in case a package has changes only in tests. Usage example: pnpm +Adding completion: --reporter:Output reporter for pnpm commands +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --h +Truncated words[*]: pnpm install --h, +lastParam: --h, lastChar: h +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --h +completion output: --help Output usage information +--hoist-pattern Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +:4 +last line: :4 +directive: 4 +completions: --help Output usage information +--hoist-pattern Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +flagPrefix: +Adding completion: --help:Output usage information +Adding completion: --hoist-pattern:Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --h +Truncated words[*]: pnpm install --h, +lastParam: --h, lastChar: h +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --h +completion output: --help Output usage information +--hoist-pattern Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +:4 +last line: :4 +directive: 4 +completions: --help Output usage information +--hoist-pattern Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +flagPrefix: +Adding completion: --help:Output usage information +Adding completion: --hoist-pattern:Hoist all dependencies matching the pattern to `node_modules/.pnpm/node_modules`. The default pattern is * and matches everything. Hoisted packages can be required by any dependencies, so it is an emulation of a flat node_modules +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 4, words[*]: pnpm install --hoist-pattern +Truncated words[*]: pnpm install --hoist-pattern , +lastParam: , lastChar: +Adding extra empty parameter +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern '' '' +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --hoist-pattern= +Truncated words[*]: pnpm install --hoist-pattern=, +lastParam: --hoist-pattern=, lastChar: = +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern= +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: -P --hoist-pattern= +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --hoist-pattern= +Truncated words[*]: pnpm install --hoist-pattern=, +lastParam: --hoist-pattern=, lastChar: = +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern= +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: -P --hoist-pattern= +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --hoist-pattern= +Truncated words[*]: pnpm install --hoist-pattern=, +lastParam: --hoist-pattern=, lastChar: = +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --hoist-pattern= +completion output: :4 +last line: :4 +directive: 4 +completions: +flagPrefix: -P --hoist-pattern= +Calling _describe +_describe did not find completions. +Checking if we should do file completion. +deactivating file completion + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --r +Truncated words[*]: pnpm install --r, +lastParam: --r, lastChar: r +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --r +completion output: --recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--reporter Output reporter for pnpm commands +:4 +last line: :4 +directive: 4 +completions: --recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--reporter Output reporter for pnpm commands +flagPrefix: +Adding completion: --recursive:Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +Adding completion: --resolution-only:Re-runs resolution: useful for printing out peer dependency issues +Adding completion: --reporter:Output reporter for pnpm commands +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --re +Truncated words[*]: pnpm install --re, +lastParam: --re, lastChar: e +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --re +completion output: --recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--reporter Output reporter for pnpm commands +:4 +last line: :4 +directive: 4 +completions: --recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--reporter Output reporter for pnpm commands +flagPrefix: +Adding completion: --recursive:Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +Adding completion: --resolution-only:Re-runs resolution: useful for printing out peer dependency issues +Adding completion: --reporter:Output reporter for pnpm commands +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm install --re +Truncated words[*]: pnpm install --re, +lastParam: --re, lastChar: e +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- install --re +completion output: --recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--reporter Output reporter for pnpm commands +:4 +last line: :4 +directive: 4 +completions: --recursive Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +--resolution-only Re-runs resolution: useful for printing out peer dependency issues +--reporter Output reporter for pnpm commands +flagPrefix: +Adding completion: --recursive:Run installation recursively in every package found in subdirectories. For options that may be used with `-r`, see "pnpm help recursive" +Adding completion: --resolution-only:Re-runs resolution: useful for printing out peer dependency issues +Adding completion: --reporter:Output reporter for pnpm commands +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm add --r +Truncated words[*]: pnpm add --r, +lastParam: --r, lastChar: r +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- add --r +completion output: --recursive Run installation recursively in every package found in subdirectories or in every workspace package, when executed inside a workspace. For options that may be used with `-r`, see "pnpm help recursive" +--reporter Output reporter for pnpm commands +:4 +last line: :4 +directive: 4 +completions: --recursive Run installation recursively in every package found in subdirectories or in every workspace package, when executed inside a workspace. For options that may be used with `-r`, see "pnpm help recursive" +--reporter Output reporter for pnpm commands +flagPrefix: +Adding completion: --recursive:Run installation recursively in every package found in subdirectories or in every workspace package, when executed inside a workspace. For options that may be used with `-r`, see "pnpm help recursive" +Adding completion: --reporter:Output reporter for pnpm commands +Calling _describe +_describe found some completions + +========= starting completion logic ========== +CURRENT: 3, words[*]: pnpm add --re +Truncated words[*]: pnpm add --re, +lastParam: --re, lastChar: e +About to call: eval node /Users/amir/Desktop/projects/tab/dist/bin/cli.js pnpm complete -- add --re +completion output: --recursive Run installation recursively in every package found in subdirectories or in every workspace package, when executed inside a workspace. For options that may be used with `-r`, see "pnpm help recursive" +--reporter Output reporter for pnpm commands +:4 +last line: :4 +directive: 4 +completions: --recursive Run installation recursively in every package found in subdirectories or in every workspace package, when executed inside a workspace. For options that may be used with `-r`, see "pnpm help recursive" +--reporter Output reporter for pnpm commands +flagPrefix: +Adding completion: --recursive:Run installation recursively in every package found in subdirectories or in every workspace package, when executed inside a workspace. For options that may be used with `-r`, see "pnpm help recursive" +Adding completion: --reporter:Output reporter for pnpm commands +Calling _describe +_describe found some completions