import type { Props } from "@alloy-js/core"; import { code, List, memberRefkey, namekey, Output, refkey, render, StatementList, } from "@alloy-js/core"; import { describe, expect, it } from "vitest"; import { ClassField } from "../src/components/stc/index.js"; import { ClassDeclaration, FunctionDeclaration, InterfaceDeclaration, InterfaceMember, SourceFile, TypeDeclaration, VarDeclaration, } from "../src/index.js"; import type { ParameterDescriptor } from "../src/parameter-descriptor.js"; import { TestFile } from "./utils.js"; it("works", () => { expect( , ).toRenderTo(` function foo() {} `); }); it("can be exported", () => { expect( , ).toRenderTo(` export function foo() {} `); }); it("can be a default export", () => { expect( , ).toRenderTo(` export default function foo() {} `); }); it("can be an async function", () => { expect( , ).toRenderTo(` export async function foo() {} `); }); it("can be an async function with returnType", () => { expect( , ).toRenderTo(` export async function foo(): Promise {} `); }); it("can be an async function with returnType element", () => { function Foo(_props?: Props) { return <>Foo; } expect( } /> , ).toRenderTo(` export async function foo(): Promise {} `); }); it("supports parameters by element", () => { const decl = ( return a + b; a, b ); expect({decl}).toRenderTo(` function foo(a, b) { return a + b; } `); }); it("supports type parameters by descriptor object", () => { const decl = ( ); expect({decl}).toRenderTo(` function foo() {} `); }); it("supports type parameters with namekeys", () => { const decl = ( ); expect({decl}).toRenderTo(` function foo() {} `); }); it("supports type parameters by descriptor array", () => { const decl = ( ); expect({decl}).toRenderTo(` function foo() {} `); }); it("supports type parameters by element", () => { const decl = ( a, b ); expect({decl}).toRenderTo(` function foo() {} `); }); it("resolves references to function-local symbols from deeply indented code blocks (#429)", () => { const value = refkey(); expect( {code` if (value) { if (!${value}) { return ${value} as any; } } return ${value}; `} , ).toRenderTo(` export function serialize(value: string | null): string { if (value) { if (!value) { return value as any; } } return value; } `); }); it("do not add an extra comma when there is no parameters", () => { expect( , ).toRenderTo(` function thisFunctionNameIsTooLongSoTheFormatterWillInsertLineBreakAndIfBreakNodes() { } `); }); it("resolves references to function-local symbols from indented code blocks (#429)", () => { const value = refkey(); expect( {code` if (!${value}) { return ${value} as any; } return ${value}; `} , ).toRenderTo(` export function serialize(value: string | null): string { if (!value) { return value as any; } return value; } `); }); describe("symbols", () => { it("creates a nested scope", () => { const innerRefkey = refkey(); const outerRefkey = refkey(); const decl = ( {innerRefkey} 1 2 {outerRefkey} ); expect({decl}).toRenderTo(` function foo() { refme; const refme = 1; }; const refme = 2; refme; `); }); it("throws an error when trying to access a symbol in a nested function scope", () => { const innerRefkey = refkey(); const decl = ( <> 1 ;{innerRefkey} ); expect(() => render({decl}, { insertFinalNewLine: false }), ).toThrow(/Cannot reference a symbol/); }); it("creates symbols for parameters", () => { const rk = refkey(); const decl = ( {rk} ); expect({decl}).toRenderTo(` function foo(sym: any) { function bar() { sym } } `); }); it("creates symbols for parameters and addresses conflicts", () => { const decl = ( 1; ); expect({decl}).toRenderTo(` function foo(conflict: any) { const conflict_2 = 1; } `); }); it("create optional parameters", () => { const paramDesc: ParameterDescriptor = { name: "foo", refkey: refkey(), type: "any", optional: true, }; const decl = ( console.log(foo); ); expect({decl}).toRenderTo(` function foo(foo?: any) { console.log(foo); } `); }); it("create optional parameters", () => { const paramDesc: ParameterDescriptor = { name: "foo", refkey: refkey(), type: "any[]", rest: true, }; const decl = ( console.log(foo); ); expect({decl}).toRenderTo(` function foo(...foo: any[]) { console.log(foo); } `); }); it("creates parameters with default values", () => { const paramDesc: ParameterDescriptor = { name: "foo", refkey: refkey(), type: "string", default: '"bar"', }; const decl = ( console.log(foo); ); expect({decl}).toRenderTo(` function foo(foo: string = "bar") { console.log(foo); } `); }); it("correctly renders mixed parameters", () => { const params: ParameterDescriptor[] = [ { name: "a", refkey: refkey(), type: "string" }, { name: "b", refkey: refkey(), type: "number", optional: true }, { name: "c", refkey: refkey(), type: "boolean", default: "false" }, { name: "d", refkey: refkey(), type: "any[]", rest: true }, ]; const decl = ( console.log(a, b, c, d); ); expect({decl}).toRenderTo(` function foo(a: string, b?: number, c: boolean = false, ...d: any[]) { console.log(a, b, c, d); } `); }); it("adds symbols for members of parameters when a type is provided", () => { const ifaceRk = refkey(); const ifaceMemberRk = refkey(); const clsRk = refkey(); const clsMemberRk = refkey(); const ifaceParamRk = refkey(); const clsParamRk = refkey(); const ifaceParam: ParameterDescriptor = { name: "ifaceParam", refkey: ifaceParamRk, type: ifaceRk, optional: true, }; const clsParam: ParameterDescriptor = { name: "classParam", refkey: clsParamRk, type: clsRk, optional: true, }; const decl = ( string 42 <>console.log({memberRefkey(ifaceParamRk, ifaceMemberRk)}) <>console.log({memberRefkey(clsParamRk, clsMemberRk)}); ); expect({decl}).toRenderTo(` interface IFace { ifaceProp: string } class Class { classProp = 42 } function fn(ifaceParam?: IFace, classParam?: Class) { console.log(ifaceParam?.ifaceProp); console.log(classParam?.classProp);; } `); }); it("adds symbols for members of parameters when a type and default are provided", () => { const ifaceRk = refkey(); const ifaceMemberRk = refkey(); const paramRk = refkey(); const param: ParameterDescriptor = { name: "myParam", refkey: paramRk, type: ifaceRk, default: "undefined", }; const decl = ( string {memberRefkey(paramRk, ifaceMemberRk)} ); expect({decl}).toRenderTo(` interface IFace { myProp: string } function fn(myParam: IFace = undefined) { myParam.myProp } `); }); }); it("has parameters and return type in type ref context", () => { const i1 = namekey("iface1"); const i2 = namekey("iface2"); const template = ( any any ); expect(template).toRenderTo({ "test1.ts": ` type iface1 = any; type iface2 = any; `, "test2.ts": ` import type { iface1, iface2 } from "./test1.js"; function foo(a: iface1): iface2 {} `, }); });