diff --git a/codegen/snippet-tests/input/10-namedMod.pkl b/codegen/snippet-tests/input/10-namedMod.pkl new file mode 100644 index 0000000..1621094 --- /dev/null +++ b/codegen/snippet-tests/input/10-namedMod.pkl @@ -0,0 +1,3 @@ +module n10.pkl.typescript.tests.namedModule + +x: String diff --git a/codegen/snippet-tests/input/11-withImport.pkl b/codegen/snippet-tests/input/11-withImport.pkl new file mode 100644 index 0000000..e0532d4 --- /dev/null +++ b/codegen/snippet-tests/input/11-withImport.pkl @@ -0,0 +1,3 @@ +import "support/moduleWithClass.pkl" + +value: moduleWithClass.ExampleClass diff --git a/codegen/snippet-tests/input/support/moduleWithClass.pkl b/codegen/snippet-tests/input/support/moduleWithClass.pkl new file mode 100644 index 0000000..4ed0e5e --- /dev/null +++ b/codegen/snippet-tests/input/support/moduleWithClass.pkl @@ -0,0 +1,4 @@ +class ExampleClass { + x: String + y: Int +} diff --git a/codegen/snippet-tests/output/11_with_import.pkl.ts b/codegen/snippet-tests/output/11_with_import.pkl.ts new file mode 100644 index 0000000..f5162dd --- /dev/null +++ b/codegen/snippet-tests/output/11_with_import.pkl.ts @@ -0,0 +1,33 @@ +// This file was generated by `pkl-typescript` from Pkl module `11-withImport`. +// DO NOT EDIT. +import * as pklTypescript from "@pkl-community/pkl-typescript" + +// Ref: Module root. +export interface N11WithImport { + value: ExampleClass +} + +// Ref: Pkl class `moduleWithClass.ExampleClass`. +export interface ExampleClass { + x: string + + y: number +} + +// Ref: Module root. +export interface ModuleWithClass { +} + +// LoadFromPath loads the pkl module at the given path and evaluates it into a N11WithImport +export const loadFromPath = async (path: string): Promise => { + const evaluator = await pklTypescript.newEvaluator(pklTypescript.PreconfiguredOptions); + try { + const result = await load(evaluator, pklTypescript.FileSource(path)); + return result + } finally { + evaluator.close() + } +}; + +export const load = (evaluator: pklTypescript.Evaluator, source: pklTypescript.ModuleSource): Promise => + evaluator.evaluateModule(source) as Promise; diff --git a/codegen/snippet-tests/output/n10_pkl_typescript_tests_named_module.pkl.ts b/codegen/snippet-tests/output/n10_pkl_typescript_tests_named_module.pkl.ts new file mode 100644 index 0000000..130f3be --- /dev/null +++ b/codegen/snippet-tests/output/n10_pkl_typescript_tests_named_module.pkl.ts @@ -0,0 +1,22 @@ +// This file was generated by `pkl-typescript` from Pkl module `n10.pkl.typescript.tests.namedModule`. +// DO NOT EDIT. +import * as pklTypescript from "@pkl-community/pkl-typescript" + +// Ref: Module root. +export interface NamedModule { + x: string +} + +// LoadFromPath loads the pkl module at the given path and evaluates it into a NamedModule +export const loadFromPath = async (path: string): Promise => { + const evaluator = await pklTypescript.newEvaluator(pklTypescript.PreconfiguredOptions); + try { + const result = await load(evaluator, pklTypescript.FileSource(path)); + return result + } finally { + evaluator.close() + } +}; + +export const load = (evaluator: pklTypescript.Evaluator, source: pklTypescript.ModuleSource): Promise => + evaluator.evaluateModule(source) as Promise; diff --git a/codegen/src/Generator.pkl b/codegen/src/Generator.pkl index 52f0db2..0e91763 100644 --- a/codegen/src/Generator.pkl +++ b/codegen/src/Generator.pkl @@ -51,7 +51,7 @@ local modules = allMappings .mapValues((`_moduleName`: String?, _mappings: List) -> new TypescriptModule { typescriptModule = _moduleName `module` = _mappings.first.source.enclosingDeclaration - mappings = allMappings + mappings = _mappings }) output { diff --git a/codegen/src/internal/ClassGen.pkl b/codegen/src/internal/ClassGen.pkl index 8e9afe8..d44175f 100644 --- a/codegen/src/internal/ClassGen.pkl +++ b/codegen/src/internal/ClassGen.pkl @@ -15,11 +15,6 @@ classInfo: TypescriptMapping.Class = mapping as TypescriptMapping.Class local isModule: Boolean = clazz.enclosingDeclaration.moduleClass == clazz contents = new Listing { - when (!imports.isEmpty) { - utils.renderImports(imports) - "" - } - when (interface != null) { if (isModule) "// Ref: Module root." else "// Ref: Pkl class `\(clazz.enclosingDeclaration.name).\(clazz.name)`." @@ -35,13 +30,12 @@ local superClass: TypescriptMapping.Class? = mappings.findOrNull((c) -> c is Typ local fields: Map = getFields(clazz, mappings) -local imports = +imports = fields.values .flatMap((f) -> f.type.imports) .filter((i) -> i != classInfo.typescriptModule).distinct + (if (superClass != null && superClass.typescriptModule != classInfo.typescriptModule) List(superClass.typescriptModule) else List()) + (if (isModule && !isAbstract) - // List("../src") List("@pkl-community/pkl-typescript") else List()) @@ -61,11 +55,11 @@ local function isSameType(typeA: reflect.Type, typeB: reflect.Type) = true else if (typeA is reflect.StringLiteralType && typeB is reflect.StringLiteralType) typeA.value == typeB.value - // union types turn into Go's `any`, so we can say that this is always fine. - // TODO(Jason): this was true in Go, what does it mean here? - // TODO(Jason): need to figure out how to deep-check union types structures?? else if (typeA is reflect.UnionType && typeB is reflect.UnionType) - true + typeA.members.length == typeB.members.length && + typeA.members + .zip(typeB.members) + .every((pair) -> isSameType(pair.first, pair.second)) // remaining types: `FunctionType`, `TypeParameter`, `ModuleType`. // we can actually check if `ModuleType` refers to the same type by checking if the enclosing declaration is the same, // but we will pretend it is always false for now. @@ -88,11 +82,10 @@ function getFields( // Okay if the property is overridden but does not define a type, but don't render as its own field. // E.g. `class Foo extends Bar { bar = "mybar" }` else if (prop.type is reflect.UnknownType) !isSuperOpen - // Otherwise, the property's type has been overridden and this is impossible to - // represent in Go. - // TODO(Jason): I think this _can_ be handled by how TypeScript does interface inheritance. + // Otherwise, the property's type has been overridden, and this is currently + // not supported - would require something like `extends Omit` else throw(""" - Illegal: Class `\(clazz.reflectee)` overrides property `\(propName)`. This is not supported when generating Go. + Illegal: Class `\(clazz.reflectee)` overrides property `\(propName)`. This is not supported when generating TypeScript. \(prop.location.displayUri) """) diff --git a/codegen/src/internal/TypescriptModule.pkl b/codegen/src/internal/TypescriptModule.pkl index d9d34fa..52e27be 100644 --- a/codegen/src/internal/TypescriptModule.pkl +++ b/codegen/src/internal/TypescriptModule.pkl @@ -60,7 +60,12 @@ local generated: List(hasUniqueNames()) = mapping = it } ) - // If not a class, what then? + +local imports = generated + .filter((it) -> it is ClassGen) + .map((it) -> it.imports) + .flatten() + .distinct contents: String = new Listing { """ @@ -68,6 +73,9 @@ contents: String = new Listing { // DO NOT EDIT. """ + utils.renderImports(imports) + "" + for (gen in generated) { gen.contents ""