(input: string)
| 268 | protected abstract promptForValue(header?: string): Promise<string> | string; |
| 269 | |
| 270 | protected async replaceValueInString(input: string): Promise<string> { |
| 271 | let output: string = input; |
| 272 | |
| 273 | // Fast path: nothing to do. |
| 274 | if (!NAME_VALUE_REGEX.test(output)) return output; |
| 275 | |
| 276 | this.valuePromptContext = this.getValuePromptContext(output); |
| 277 | |
| 278 | // Preserve programmatic VALUE injection via reserved variable name `value`. |
| 279 | if (this.hasConcreteVariable("value")) { |
| 280 | const existingValue = this.variables.get("value"); |
| 281 | this.value = formatUnknownValue(existingValue); |
| 282 | } |
| 283 | |
| 284 | // Prompt only once per formatter run (empty string is a valid value). |
| 285 | if (this.value === undefined) { |
| 286 | this.value = await this.promptForValue(); |
| 287 | } |
| 288 | |
| 289 | // Replace all occurrences in a single non-recursive pass. |
| 290 | // Important: use a replacer function so `$` in user input is treated literally. |
| 291 | const regex = new RegExp(NAME_VALUE_REGEX.source, "gi"); |
| 292 | output = output.replace(regex, (...args) => { |
| 293 | const token = args[0]; |
| 294 | const offset = args[args.length - 2] as number; |
| 295 | const source = args[args.length - 1] as string; |
| 296 | const inner = token.slice(2, -2); |
| 297 | const optionsIndex = inner.indexOf("|"); |
| 298 | if (optionsIndex === -1) return this.value; |
| 299 | const rawOptions = inner.slice(optionsIndex); |
| 300 | const parsed = parseAnonymousValueOptions(rawOptions); |
| 301 | // Apply |default on an empty submission, mirroring the named path |
| 302 | // (ensureValueVariableResolved): the |default pre-fills the prompt, but |
| 303 | // a user who clears the box should still get the default unless the |
| 304 | // token is |optional (an optional empty is taken at face value). |
| 305 | const effectiveValue = |
| 306 | this.value === "" && parsed.defaultValue && !parsed.optional |
| 307 | ? parsed.defaultValue |
| 308 | : this.value; |
| 309 | const transformed = this.applyValueTextOptions(effectiveValue, parsed); |
| 310 | // |type:text on the anonymous {{VALUE|...}} form quotes the same way |
| 311 | // as the named form (see replaceVariableInString). |
| 312 | if ( |
| 313 | parsed.inputTypeOverride === "text" && |
| 314 | transformed !== "" && |
| 315 | shouldQuoteTextScalar(source, offset, offset + token.length) |
| 316 | ) { |
| 317 | return quoteYamlDouble(transformed); |
| 318 | } |
| 319 | return transformed; |
| 320 | }); |
| 321 | |
| 322 | return output; |
| 323 | } |
| 324 | |
| 325 | private applyValueTextOptions( |
| 326 | value: unknown, |
nothing calls this directly
no test coverage detected