Skip to content

Commit

Permalink
refactor(@molt/command): modular types (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Nov 4, 2023
1 parent ae2391a commit 2bae5c7
Show file tree
Hide file tree
Showing 42 changed files with 1,082 additions and 1,012 deletions.
11 changes: 8 additions & 3 deletions packages/@molt/command/src/CommandParameter/CommandParameter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
export * from './input.js'
export * from './output.js'
export * from './processor/process.js'
export * from './transform.js'
export * from './types.js'
export * from './validate.js'
import { stripeNegatePrefix } from '../helpers.js'
import type { Type } from '../Type/index.js'
import type { ValidationResult } from '../Type/Type.js'
import type { Output } from './output.js'
import { Either } from 'effect'

export const validate = <T>(parameter: Output.Basic, value: unknown): ValidationResult<T> => {
if (parameter.optionality._tag === `optional` && value === undefined) return Either.right(value as T)
return parameter.type.validate(value)
}

export const findByName = (name: string, specs: Output[]): null | Output => {
for (const spec of specs) {
Expand Down Expand Up @@ -57,7 +62,7 @@ export const hasName = (spec: Output, name: string): null | NameHit => {

export const isOrHasType = (spec: Output, typeTag: Type.Type['_tag']): boolean => {
return spec.type._tag === `TypeUnion`
? spec.type.members.find((_) => _._tag === typeTag) !== undefined
? (spec.type as Type.Union).members.find((_) => _._tag === typeTag) !== undefined
: spec.type._tag === typeTag
}

Expand Down
50 changes: 0 additions & 50 deletions packages/@molt/command/src/CommandParameter/transform.ts

This file was deleted.

26 changes: 0 additions & 26 deletions packages/@molt/command/src/CommandParameter/validate.ts

This file was deleted.

24 changes: 1 addition & 23 deletions packages/@molt/command/src/CommandParameter/zod.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import { ZodHelpers } from '../lib/zodHelpers/index.js'
import { stripOptionalAndDefault } from '../lib/zodHelpers/index_.js'
import { z } from 'zod'

export type SomeType = SomeBasicType | SomeUnionType

export type SomeBasicParameterType = SomeBasicType | SomeUnionType
import type { z } from 'zod'

type ZodEnumBase = z.ZodEnum<[string, ...string[]]>

Expand Down Expand Up @@ -33,19 +27,3 @@ export type SomeBasicTypeScalar =
| z.ZodLiteral<number>
| z.ZodLiteral<string>
| z.ZodLiteral<boolean>

export const isUnionType = (type: SomeBasicType | SomeUnionType): type is SomeUnionType => {
const type_ = stripOptionalAndDefault(type)
const isUnion = type_._def.typeName === z.ZodFirstPartyTypeKind.ZodUnion
return isUnion
}

export const getUnionScalar = (zodType: SomeUnionType): SomeUnionTypeScalar => {
const type = ZodHelpers.stripOptionalAndDefault(zodType)
return type
}

export const getBasicScalar = (zodType: SomeBasicType): SomeBasicTypeScalar => {
const type = ZodHelpers.stripOptionalAndDefault(zodType)
return type
}
20 changes: 10 additions & 10 deletions packages/@molt/command/src/Errors/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@ export namespace Global {

export class ErrorDuplicateLineArg extends Error {
public override name: 'ErrorDuplicateFlag' = `ErrorDuplicateFlag`
public spec: CommandParameter.Output
constructor(params: { spec: CommandParameter.Output; flagName: string }) {
public parameter: CommandParameter.Output
constructor(params: { parameter: CommandParameter.Output; flagName: string }) {
const message = `The parameter "${params.flagName}" was passed an argument multiple times via flags.`
super(message)
this.spec = params.spec
this.parameter = params.parameter
}
}

export class ErrorDuplicateEnvArg extends Error {
public override name: 'ErrorDuplicateEnvArg' = `ErrorDuplicateEnvArg`
public spec: CommandParameter.Output
public parameter: CommandParameter.Output
public instances: { value: string; name: string; prefix: string | null }[]
constructor(params: {
spec: CommandParameter.Output
parameter: CommandParameter.Output
instances: { value: string; name: string; prefix: string | null }[]
}) {
const message = `The parameter "${params.spec.name.canonical}" was passed an argument multiple times via different parameter aliases in the environment.`
const message = `The parameter "${params.parameter.name.canonical}" was passed an argument multiple times via different parameter aliases in the environment.`
super(message)
this.spec = params.spec
this.parameter = params.parameter
this.instances = params.instances
}
}
Expand All @@ -59,10 +59,10 @@ export class ErrorFailedToGetDefaultArgument extends Error {
export class ErrorMissingArgument extends Error {
public override name: 'ErrorMissingArgument' = `ErrorMissingArgument`
public spec: CommandParameter.Output
constructor(params: { spec: CommandParameter.Output }) {
const message = `Missing argument for flag "${params.spec.name.canonical}".`
constructor(params: { parameter: CommandParameter.Output }) {
const message = `Missing argument for flag "${params.parameter.name.canonical}".`
super(message)
this.spec = params.spec
this.spec = params.parameter
}
}

Expand Down
76 changes: 2 additions & 74 deletions packages/@molt/command/src/Help/Help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Tex } from '../lib/Tex/index.js'
import { Text } from '../lib/Text/index.js'
import type { Settings } from '../Settings/index.js'
import { Term } from '../term.js'
import type { Type } from '../Type/index.js'
import chalk from 'chalk'
import camelCase from 'lodash.camelcase'
import snakeCase from 'lodash.snakecase'
Expand Down Expand Up @@ -102,10 +101,7 @@ export const render = (
.rows([
...basicAndUnionSpecsWithoutHelp.map((spec) => [
parameterName(spec),
Tex.block(
{ maxWidth: 40, padding: { right: 9, bottom: 1 } },
parameterTypeAndDescription(settings, spec),
),
Tex.block({ maxWidth: 40, padding: { right: 9, bottom: 1 } }, spec.type.help(settings)),
Tex.block({ maxWidth: 24 }, parameterDefault(spec)),
...(isEnvironmentEnabled ? [parameterEnvironment(spec, settings)] : []),
]),
Expand All @@ -127,7 +123,7 @@ export const render = (
],
...Object.values(mexGroup.parameters).map((spec) => [
parameterName(spec),
parameterTypeAndDescription(settings, spec),
spec.type.help(settings),
parameterDefault(spec),
...(isEnvironmentEnabled ? [parameterEnvironment(spec, settings)] : []),
]),
Expand Down Expand Up @@ -276,48 +272,6 @@ const parameterName = (spec: CommandParameter.Output) => {
)
}

const parameterTypeAndDescription = (settings: Settings.Output, spec: CommandParameter.Output) => {
const t = spec.type
if (t._tag === `TypeUnion`) {
const unionMemberIcon = Term.colors.accent(`◒`)
const isOneOrMoreMembersWithDescription = t.members.some((_) => _.description !== null)
const isExpandedMode =
isOneOrMoreMembersWithDescription || settings.helpRendering.union.mode === `expandAlways`
const isExpandedModeViaForceSetting = isExpandedMode && !isOneOrMoreMembersWithDescription
if (isExpandedMode) {
const types = t.members.flatMap((m) => {
return Tex.block(
{
padding: { bottomBetween: isExpandedModeViaForceSetting ? 0 : 1 },
border: {
left: (index) =>
`${index === 0 ? unionMemberIcon : Term.colors.dim(Text.chars.borders.vertical)} `,
},
},
(__) => __.block(typeScalar(m)).block(m.description),
)
})
return Tex.block((__) =>
__.block(Term.colors.dim(Text.chars.borders.leftTop + Text.chars.borders.horizontal + `union`))
.block(
{ padding: { bottom: 1 }, border: { left: `${Term.colors.dim(Text.chars.borders.vertical)} ` } },
spec.description,
)
.block(types)
.block(Term.colors.dim(Text.chars.borders.leftBottom + Text.chars.borders.horizontal)),
)
} else {
const types = t.members.map((m) => typeTagsToTypeScriptName[m._tag]).join(` | `)
return Tex.block(($) => $.block(types).block(spec.description ?? null))
}
}

// const maybeZodEnum = ZodHelpers.getEnum(spec.zodType)
return Tex.block({ padding: { bottom: spec._tag === `Exclusive` ? 0 : 1 } }, ($) =>
$.block(typeScalar(spec.type)).block(spec.description),
)
}

const parameterEnvironment = (spec: CommandParameter.Output, settings: Settings.Output) => {
return spec.environment?.enabled
? Term.colors.secondary(Text.chars.check) +
Expand All @@ -338,32 +292,6 @@ const parameterEnvironment = (spec: CommandParameter.Output, settings: Settings.
: Term.colors.dim(Text.chars.x)
}

/**
* Render an enum type into a column.
*/
const typeEnum = (type: Type.Scalar.Enumeration) => {
const separator = Term.colors.accent(` ${Text.chars.pipe} `)
const members = Object.values(type.members)
const lines = members.map((member) => Term.colors.positive(String(member))).join(separator)

// eslint-disable-next-line
return members.length > 1 ? lines : `${lines} ${Term.colors.dim(`(enum)`)}`
}

const title = (string: string) => {
return Text.line(string.toUpperCase())
}

const typeScalar = (type: Type.Type): string => {
if (type._tag === `TypeEnum`) return typeEnum(type)
return Term.colors.positive(typeTagsToTypeScriptName[type._tag])
}

const typeTagsToTypeScriptName = {
TypeLiteral: `literal`,
TypeUnion: `union`,
TypeString: `string`,
TypeNumber: `number`,
TypeEnum: `enum`,
TypeBoolean: `boolean`,
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CommandParameter } from '../../CommandParameter/index.js'
import { Errors } from '../../Errors/index.js'
import type { Index, RequireField } from '../../lib/prelude.js'
import { parseRawInput } from '../helpers.js'
import { parseSerializedValue } from '../helpers.js'
import type { EnvironmentArgumentReport } from '../types.js'
import camelCase from 'lodash.camelcase'
import snakecase from 'lodash.snakecase'
Expand Down Expand Up @@ -32,15 +32,15 @@ export const parse = (environment: RawInputs, specs: CommandParameter.Output[]):
)

for (const envar of envars) {
for (const spec of specsWithEnvironmentSupport) {
const match = checkInputMatch(envar, spec)
for (const parameter of specsWithEnvironmentSupport) {
const match = checkInputMatch(envar, parameter)

// Case 1
if (!match) continue

// Case 2
// Check for multiple envars pointing to the same parameter.
const report = result.reports[spec.name.canonical]
const report = result.reports[parameter.name.canonical]
if (report) {
const instance = {
name: match.name,
Expand All @@ -53,7 +53,7 @@ export const parse = (environment: RawInputs, specs: CommandParameter.Output[]):
} else {
report.errors.push(
new Errors.ErrorDuplicateEnvArg({
spec,
parameter,
instances: [instance],
}),
)
Expand All @@ -62,9 +62,9 @@ export const parse = (environment: RawInputs, specs: CommandParameter.Output[]):
}

// Case 3
const value = parseRawInput(match.nameWithNegation, match.value, spec)
result.reports[spec.name.canonical] = {
spec,
const value = parseSerializedValue(match.nameWithNegation, match.value, parameter)
result.reports[parameter.name.canonical] = {
parameter,
value,
errors: [],
source: {
Expand Down
Loading

0 comments on commit 2bae5c7

Please sign in to comment.