UNPKG

@typespec/compiler

Version:

TypeSpec Compiler Preview

134 lines 5.37 kB
import { createDiagnosticCollector, ignoreDiagnostics } from "../core/diagnostics.js"; import { createDiagnostic } from "../core/messages.js"; import { NoTarget } from "../core/types.js"; export function expandConfigVariables(config, expandOptions) { const diagnostics = createDiagnosticCollector(); const builtInVars = { "project-root": config.projectRoot, cwd: expandOptions.cwd, }; const resolvedArgsParameters = diagnostics.pipe(resolveArgs(config.parameters, expandOptions.args, builtInVars)); const commonVars = { ...builtInVars, ...resolvedArgsParameters, ...diagnostics.pipe(resolveArgs(config.options, {}, resolvedArgsParameters)), env: diagnostics.pipe(resolveArgs(config.environmentVariables, expandOptions.env, builtInVars, true)), }; const outputDir = diagnostics.pipe(resolveValue(expandOptions.outputDir ?? config.outputDir, commonVars)); const result = { ...config, outputDir }; if (config.options) { const options = {}; for (const [name, emitterOptions] of Object.entries(config.options)) { const emitterVars = { ...commonVars, "output-dir": outputDir, "emitter-name": name }; options[name] = diagnostics.pipe(resolveValues(emitterOptions, emitterVars)); } result.options = options; } return diagnostics.wrap(result); } function resolveArgs(declarations, args, predefinedVariables, allowUnspecified = false) { function tryGetValue(value) { return typeof value === "string" ? value : undefined; } const unmatchedArgs = new Set(Object.keys(args ?? {})); const result = {}; function resolveNestedArgs(parentName, declarations) { for (const [declarationName, definition] of declarations) { const name = parentName ? `${parentName}.${declarationName}` : declarationName; if (hasNestedValues(definition)) { resolveNestedArgs(name, Object.entries(definition ?? {})); } unmatchedArgs.delete(name); result[name] = ignoreDiagnostics(resolveValue(args?.[name] ?? tryGetValue(definition.default) ?? tryGetValue(definition) ?? "", predefinedVariables)); } } if (declarations !== undefined) { resolveNestedArgs("", Object.entries(declarations ?? {})); } if (!allowUnspecified) { const diagnostics = [...unmatchedArgs].map((unmatchedArg) => { return createDiagnostic({ code: "config-invalid-argument", format: { name: unmatchedArg }, target: NoTarget, }); }); return [result, diagnostics]; } return [result, []]; } function hasNestedValues(value) { return (value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0); } const VariableInterpolationRegex = /{([a-zA-Z-_.]+)}/g; function resolveValue(value, predefinedVariables) { const [result, diagnostics] = resolveValues({ value }, predefinedVariables); return [result.value, diagnostics]; } export function resolveValues(values, predefinedVariables = {}) { const diagnostics = []; const resolvedValues = {}; const resolvingValues = new Set(); function resolveValue(keys) { resolvingValues.add(keys[0]); let value = values; value = keys.reduce((acc, key) => acc?.[key], value); if (typeof value !== "string") { if (hasNestedValues(value)) { value = value; const resultObject = {}; for (const [nestedKey] of Object.entries(value)) { resolvingValues.add(nestedKey); resultObject[nestedKey] = resolveValue(keys.concat(nestedKey)); } return resultObject; } return value; } return value.replace(VariableInterpolationRegex, (match, expression) => { return resolveExpression(expression) ?? `{${expression}}`; }); } function resolveExpression(expression) { if (expression in resolvedValues) { return resolvedValues[expression]; } if (resolvingValues.has(expression)) { diagnostics.push(createDiagnostic({ code: "config-circular-variable", target: NoTarget, format: { name: expression }, })); return undefined; } if (expression in values) { return resolveValue([expression]); } let resolved = predefinedVariables; if (expression in resolved) { return resolved[expression]; } const segments = expression.split("."); for (const segment of segments) { resolved = resolved[segment]; if (resolved === undefined) { return undefined; } } if (typeof resolved === "string") { return resolved; } else { return undefined; } } for (const key of Object.keys(values)) { resolvingValues.clear(); if (key in resolvedValues) { continue; } resolvedValues[key] = resolveValue([key]); } return [resolvedValues, diagnostics]; } //# sourceMappingURL=config-interpolation.js.map