126 lines
3.3 KiB
TypeScript
126 lines
3.3 KiB
TypeScript
import { resolve } from 'node:path';
|
|
import type { CliContext } from './types.js';
|
|
export type { CliContext } from './types.js';
|
|
|
|
import { existsSync, readFileSync } from 'node:fs';
|
|
|
|
function detectPackageManager(cwd: string): CliContext['packageManager'] {
|
|
// Check for lock files in cwd
|
|
if (existsSync(resolve(cwd, 'bun.lockb')) || existsSync(resolve(cwd, 'bun.lock'))) {
|
|
return 'bun';
|
|
}
|
|
if (existsSync(resolve(cwd, 'pnpm-lock.yaml'))) {
|
|
return 'pnpm';
|
|
}
|
|
if (existsSync(resolve(cwd, 'yarn.lock'))) {
|
|
return 'yarn';
|
|
}
|
|
if (existsSync(resolve(cwd, 'package-lock.json'))) {
|
|
return 'npm';
|
|
}
|
|
|
|
// Check package.json packageManager field
|
|
const packageJsonPath = resolve(cwd, 'package.json');
|
|
if (existsSync(packageJsonPath)) {
|
|
try {
|
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as { packageManager?: string };
|
|
const packageManager = packageJson.packageManager || '';
|
|
if (packageManager.startsWith('bun@')) return 'bun';
|
|
if (packageManager.startsWith('pnpm@')) return 'pnpm';
|
|
if (packageManager.startsWith('yarn@')) return 'yarn';
|
|
if (packageManager.startsWith('npm@')) return 'npm';
|
|
} catch {
|
|
// Ignore parse errors
|
|
}
|
|
}
|
|
|
|
// Check environment variables
|
|
if (process.env.npm_config_user_agent) {
|
|
const ua = process.env.npm_config_user_agent;
|
|
if (ua.includes('bun')) return 'bun';
|
|
if (ua.includes('pnpm')) return 'pnpm';
|
|
if (ua.includes('yarn')) return 'yarn';
|
|
if (ua.includes('npm')) return 'npm';
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
function detectCI(): boolean {
|
|
const ciEnvVars = [
|
|
'CI',
|
|
'GITHUB_ACTIONS',
|
|
'GITLAB_CI',
|
|
'CIRCLECI',
|
|
'TRAVIS',
|
|
'APPVEYOR',
|
|
'BUILDKITE',
|
|
'DRONE',
|
|
'JENKINS_URL',
|
|
'TF_BUILD',
|
|
'CODEBUILD_BUILD_ID',
|
|
'TEAMCITY_VERSION',
|
|
'SEMAPHORE',
|
|
'WERCKER',
|
|
'MAGNUM',
|
|
'SNAP_CI',
|
|
'BUDDY',
|
|
'BUILDBOX',
|
|
'AGOLA',
|
|
'WOODPECKER',
|
|
];
|
|
|
|
return ciEnvVars.some(varName => process.env[varName] !== undefined);
|
|
}
|
|
|
|
export function createContext(options: Record<string, unknown> = {}): CliContext {
|
|
// Detect cwd (respect --cwd override)
|
|
const cwd = typeof options.cwd === 'string'
|
|
? resolve(options.cwd)
|
|
: process.cwd();
|
|
|
|
// Detect environment
|
|
const nodeEnv = process.env.NODE_ENV;
|
|
const apophisEnv = process.env.APOPHIS_ENV;
|
|
|
|
// Detect TTY
|
|
const isTTY = process.stdout.isTTY === true;
|
|
|
|
// Detect CI
|
|
const isCI = detectCI();
|
|
|
|
// Package manager detection
|
|
const packageManager = detectPackageManager(cwd);
|
|
|
|
// Normalize options
|
|
const format = options.format === 'json' || options.format === 'ndjson'
|
|
? options.format
|
|
: 'human';
|
|
|
|
const color = options.color === 'always' || options.color === 'never'
|
|
? options.color
|
|
: 'auto';
|
|
|
|
return {
|
|
cwd,
|
|
env: {
|
|
nodeEnv,
|
|
apophisEnv,
|
|
},
|
|
isTTY,
|
|
isCI,
|
|
nodeVersion: process.version,
|
|
packageManager,
|
|
selfPath: process.argv[1],
|
|
options: {
|
|
config: typeof options.config === 'string' ? options.config : undefined,
|
|
profile: typeof options.profile === 'string' ? options.profile : undefined,
|
|
format,
|
|
color,
|
|
quiet: options.quiet === true,
|
|
verbose: options.verbose === true,
|
|
artifactDir: typeof options.artifactDir === 'string' ? options.artifactDir : undefined,
|
|
},
|
|
};
|
|
}
|