/** * TypeScript declaration file generator — produces index.d.ts with all resource classes. * * Output includes: * - Resource classes with typed constructors and readonly attributes * - Property type classes * - Enum union types * - Static content (ARM intrinsics, Azure pseudo-parameters, common interfaces) */ import type { ArmSchemaParseResult } from "../spec/parse"; import { armShortName } from "../spec/parse"; import type { NamingStrategy } from "./naming"; import { propertyTypeName, extractDefName } from "./naming"; import { writeResourceClass, writePropertyClass, writeEnumType, type DtsProperty, type DtsAttribute, } from "@intentius/chant/codegen/generate-typescript"; interface ResourceEntry { tsName: string; properties: DtsProperty[]; attributes: DtsAttribute[]; remap: Map; } interface PropertyEntry { tsName: string; properties: DtsProperty[]; remap: Map; } interface EnumEntry { tsName: string; values: string[]; } /** * Generate the complete .d.ts file content. */ export function generateTypeScriptDeclarations( results: ArmSchemaParseResult[], naming: NamingStrategy, ): string { const lines: string[] = []; lines.push("// Code generated by chant azure generate. DO NOT EDIT generated sections."); const resources: ResourceEntry[] = []; const properties: PropertyEntry[] = []; const enums: EnumEntry[] = []; for (const r of results) { const armType = r.resource.typeName; const tsName = naming.resolve(armType); if (!tsName) continue; // Build remap from parser property type names to resolved names const shortName = armShortName(armType); const remap = new Map(); for (const pt of r.propertyTypes) { const defName = extractDefName(pt.name, shortName); remap.set(pt.name, propertyTypeName(tsName, defName)); } for (const e of r.enums) { const defName = extractDefName(e.name, shortName); remap.set(e.name, propertyTypeName(tsName, defName)); } // Map ParsedProperty/ParsedAttribute → DtsProperty/DtsAttribute const dtsProps: DtsProperty[] = r.resource.properties.map((p) => ({ name: p.name, type: p.tsType, required: p.required, description: p.description, })); const dtsAttrs: DtsAttribute[] = r.resource.attributes.map((a) => ({ name: a.name, type: a.tsType, })); resources.push({ tsName, properties: dtsProps, attributes: dtsAttrs, remap }); // Alias entries for (const alias of naming.aliases(armType)) { resources.push({ tsName: alias, properties: dtsProps, attributes: dtsAttrs, remap }); } const ptAliases = naming.propertyTypeAliases(armType); for (const pt of r.propertyTypes) { const defName = extractDefName(pt.name, shortName); const ptName = propertyTypeName(tsName, defName); const ptProps: DtsProperty[] = pt.properties.map((p) => ({ name: p.name, type: p.tsType, required: p.required, description: p.description, })); properties.push({ tsName: ptName, properties: ptProps, remap }); if (ptAliases) { const aliasName = ptAliases.get(defName); if (aliasName) { properties.push({ tsName: aliasName, properties: ptProps, remap }); } } } for (const e of r.enums) { const defName = extractDefName(e.name, shortName); const eName = propertyTypeName(tsName, defName); enums.push({ tsName: eName, values: e.values }); } } // Sort all entries alphabetically resources.sort((a, b) => a.tsName.localeCompare(b.tsName)); properties.sort((a, b) => a.tsName.localeCompare(b.tsName)); enums.sort((a, b) => a.tsName.localeCompare(b.tsName)); // Section 1: Resource classes lines.push(""); lines.push("// --- Resource classes ---"); for (const re of resources) { writeResourceClass(lines, re.tsName, re.properties, re.attributes, re.remap, "ArmResourceAttributes"); } // Section 2: Property classes lines.push(""); lines.push("// --- Property classes ---"); for (const pe of properties) { writePropertyClass(lines, pe.tsName, pe.properties, pe.remap); } // Section 3: Enum types if (enums.length > 0) { lines.push(""); lines.push("// --- Enum types ---"); for (const ee of enums) { writeEnumType(lines, ee.tsName, ee.values); } } // Section 4: Static content lines.push(""); lines.push(staticTypeScript()); lines.push(""); return lines.join("\n"); } // --- Static content --- function staticTypeScript(): string { const lines: string[] = []; // ARM template functions lines.push("// --- ARM template functions ---"); lines.push(""); const intrinsics: [string, string][] = [ ["Concat", "(...values: unknown[]): any"], ["Format", "(format: string, ...args: unknown[]): any"], ["If", "(condition: string, trueValue: unknown, falseValue: unknown): any"], ["ListKeys", "(resourceId: unknown, apiVersion: string): any"], ["Reference", "(name: string, apiVersion?: string): any"], ["ResourceGroup", "(): any"], ["ResourceId", "(type: string, ...names: unknown[]): any"], ["Subscription", "(): any"], ["UniqueString", "(...values: unknown[]): any"], ]; intrinsics.sort((a, b) => a[0].localeCompare(b[0])); for (const [name, sig] of intrinsics) { lines.push(`export declare function ${name}${sig};`); } // Azure pseudo-parameters lines.push(""); lines.push("// --- Azure pseudo-parameters ---"); lines.push(""); lines.push("export declare const Azure: {"); const pseudoParams = [ "DeploymentName", "ResourceGroupId", "ResourceGroupLocation", "ResourceGroupName", "SubscriptionId", "TenantId", ]; for (const p of pseudoParams) { lines.push(` readonly ${p}: any;`); } lines.push("};"); // Type interfaces lines.push(""); lines.push("// --- Type interfaces ---"); lines.push(""); lines.push("export interface Declarable {"); lines.push(" readonly entityType: string;"); lines.push("}"); lines.push(""); lines.push("export interface ArmResourceAttributes {"); lines.push(" DependsOn?: Declarable | Declarable[] | string | string[];"); lines.push("}"); lines.push(""); lines.push("export interface Tag {"); lines.push(" key: string;"); lines.push(" value: string;"); lines.push("}"); return lines.join("\n"); }