Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 6x 6x 6x 6x 6x 25x 25x 25x 25x 25x 25x 25x 22x 22x 22x 3x 22x 20x 20x 2x 2x 18x 2x 2x 16x 7x 7x 9x 2x 2x 7x 6x 10x 6x 4x 4x 1x 6x 3x 3x 6x 84x 6x 20x 20x 26x 24x 16x 9x 13x 6x 6x 6x 20x 20x 20x 20x 10x 10x 10x 10x 10x 10x 10x 4x 6x 25x 25x 74x 20x 20x 25x 9x | import { InvalidArgumentError, InvalidNewlineTypeError, MissingArgumentValueError } from "./env/error"
type RawArgument = string
type ArgumentName = string
type ArgumentValue = string | boolean
type Argument = [ArgumentName, ArgumentValue]
type ArgumentMap = Record<ArgumentName, ArgumentValue>
type ProcessEnvVariable = [keyof NodeJS.ProcessEnv, NodeJS.ProcessEnv[keyof NodeJS.ProcessEnv]]
export enum NewlineType {
unix = 'unix',
windows = 'windows'
}
export interface Options {
distFilePath: string
localFilePath: string
prompts: boolean
allowDuplicates: boolean
newlineType: NewlineType
}
const defaultOptions: Options = {
distFilePath: '.env.dist',
localFilePath: '.env',
prompts: true,
allowDuplicates: false,
newlineType: NewlineType.unix
}
export type ProcessDependencies = Pick<NodeJS.Process, 'argv' | 'env' | 'platform'>
export const getOptionsFromEnvironment = ({ argv, env, platform }: ProcessDependencies): Options => {
const argumentMap = parseArguments(argv)
const isWindows = platform === 'win32'
const newlineType: NewlineType = isWindows ? NewlineType.windows : NewlineType.unix
const cliOptions: Partial<Options> = {}
const argumentList = Object.entries(argumentMap)
argumentList.forEach((argument) => mapArgumentToOptions(argument, cliOptions))
const processEnvOptions: Partial<Options> = {}
const processEnvVariableList = Object.entries(env)
processEnvVariableList.forEach(
(variable) => mapProcessEnvVariableToOptions(variable, processEnvOptions)
)
return { ...defaultOptions, newlineType, ...processEnvOptions, ...cliOptions }
}
const mapArgumentToOptions = ([name, value]: Argument, options: Partial<Options>) => {
if (isDistFileArgument(name)) {
options.distFilePath = String(value)
return
}
if (isLocalFileArgument(name)) {
options.localFilePath = String(value)
return
}
if (isPromptsArgument(name)) {
options.prompts = getBooleanFromArgumentValue(value)
return
}
if (isAllowDuplicatesArgument(name)) {
options.allowDuplicates = getBooleanFromArgumentValue(value)
return
}
if (isNewlineTypeArgument(name)) {
const validTypes = Object.values(NewlineType)
const isValid = validTypes.find(type => type === value)
if (!isValid) throw new InvalidNewlineTypeError()
options.newlineType = value as NewlineType
return
}
throw new InvalidArgumentError().setArgumentName(name)
}
const mapProcessEnvVariableToOptions = ([name, value]: ProcessEnvVariable, options: Partial<Options>) => {
const isContinuousIntegration = name === 'CI' && value === 'true'
if (isContinuousIntegration) options.prompts = false
}
const argumentNameAndInlineValueExpression = /^(--?\w+)(?:=(.*))?/
const isArgumentName = (value: RawArgument): boolean => argumentNameAndInlineValueExpression.test(value)
const getArgumentNameAndInlineValue = (rawArgument: string): [string, string?] => {
const [_, name, inlineValue] = argumentNameAndInlineValueExpression.exec(rawArgument)
return [name, inlineValue]
}
const isDistFileArgument = (name: string): boolean => name === '-d' || name === '--distFile'
const isLocalFileArgument = (name: string): boolean => name === '-l' || name === '--localFile'
const isPromptsArgument = (name: string): boolean => name === '-p' || name === '--prompts'
const isAllowDuplicatesArgument = (name: string): boolean => name === '-a' || name === '--allowDuplicates'
const isNewlineTypeArgument = (name: string): boolean => name === '-n' || name === '--newlineType'
const isArgumentValueRequired = (name: string): boolean =>
isDistFileArgument(name) || isLocalFileArgument(name) || isNewlineTypeArgument(name)
const getArgumentNameAndValue = (rawArguments: RawArgument[], i: number): Argument => {
const rawArgument = rawArguments[i]
const [name, inlineValue] = getArgumentNameAndInlineValue(rawArgument)
const hasInlineValue = typeof inlineValue !== 'undefined'
if (hasInlineValue) return [name, inlineValue]
const valueIndex = i + 1
const value = rawArguments[valueIndex]
const hasValue = !!value
Iif (!hasValue && isArgumentValueRequired(name)) throw new MissingArgumentValueError().setName(name)
const isEndOfRawArguments = rawArguments.length === valueIndex
const isValueAnArgumentName = isArgumentName(value)
if (isEndOfRawArguments || isValueAnArgumentName) return [name, true]
return [name, value]
}
const parseArguments = (rawArguments: RawArgument[]): ArgumentMap => {
const argumentMap: ArgumentMap = {}
rawArguments
.forEach((rawArgument: RawArgument, i: number) => {
if (!isArgumentName(rawArgument)) return
const [argumentName, argumentValue] = getArgumentNameAndValue(rawArguments, i)
argumentMap[argumentName] = argumentValue
})
return argumentMap
}
const getBooleanFromArgumentValue = (value: ArgumentValue): boolean => value === 'false' ? false : true
|