(name: string, inputDefsByProperty: InputDefsByProperty<T>, options?: ObjectDefOptions<T>)
| 117 | * Calling `updateIfNeeded` on rolled up properties is not yet implemented. |
| 118 | */ |
| 119 | export function objectDef<T extends object>(name: string, inputDefsByProperty: InputDefsByProperty<T>, |
| 120 | options?: ObjectDefOptions<T>): InputDefinition<T> { |
| 121 | const buildFromUserInput = async (context: unknown[] = []): Promise<T | CancelAction> => { |
| 122 | // Since we're just going to run through all the options and ask for their value, we don't |
| 123 | // have a menu or any place to put help for the whole object so we'll just display it |
| 124 | // before asking the questions. |
| 125 | if (options?.helpText) { |
| 126 | console.log(`\n${options.helpText}\n`) |
| 127 | } |
| 128 | const retVal = {} as Partial<T> |
| 129 | for (const propertyName in inputDefsByProperty) { |
| 130 | const propertyInputDefinition = inputDefsByProperty[propertyName] |
| 131 | // Allow `undefined` definitions, but do nothing with them. This allows callers of |
| 132 | // `objectDef` to have a field skipped by setting it to `undefined` as well as by leaving |
| 133 | // it out. |
| 134 | if (!propertyInputDefinition) { |
| 135 | continue |
| 136 | } |
| 137 | const value = await propertyInputDefinition.buildFromUserInput([{ ...retVal }, ...context]) |
| 138 | if (value !== cancelAction) { |
| 139 | retVal[propertyName] = value |
| 140 | } else { |
| 141 | return cancelAction |
| 142 | } |
| 143 | } |
| 144 | return retVal as T |
| 145 | } |
| 146 | |
| 147 | const summarizeForEdit = options?.summarizeForEdit ?? defaultSummarizeForEditFn(name) |
| 148 | |
| 149 | const updateFromUserInput = async (original: T, context: unknown[] = []): Promise<T | CancelAction> => { |
| 150 | const updated = { ...original } |
| 151 | // eslint-disable-next-line no-constant-condition |
| 152 | while (true) { |
| 153 | const contextForChildren = [{ ...updated }, ...context] |
| 154 | const choices = buildPropertyChoices(inputDefsByProperty, updated, contextForChildren) |
| 155 | choices.push(new Separator()) |
| 156 | if (options?.helpText) { |
| 157 | choices.push(helpOption) |
| 158 | } |
| 159 | choices.push(finishOption(name)) |
| 160 | choices.push(cancelOption) |
| 161 | |
| 162 | const action = await select({ message: name, choices, default: finishAction, pageSize: inquirerPageSize }) |
| 163 | |
| 164 | if (action === helpAction) { |
| 165 | console.log(`\n${options?.helpText}\n`) |
| 166 | } else if (action === cancelAction) { |
| 167 | return cancelAction |
| 168 | } else if (action === finishAction) { |
| 169 | return updated |
| 170 | } else { |
| 171 | const props = action.split('.') |
| 172 | |
| 173 | const updatedPropertyName = props[0] as keyof T |
| 174 | const propertyInputDefinition = inputDefsByProperty[updatedPropertyName] |
| 175 | if (props.length === 1) { |
| 176 | // top-level property |
no test coverage detected