@typespec/compiler
Version:
TypeSpec Compiler Preview
134 lines • 5.37 kB
JavaScript
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