Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for modules that import other Pkl modules #25

Merged
merged 1 commit into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions codegen/snippet-tests/input/10-namedMod.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module n10.pkl.typescript.tests.namedModule

x: String
3 changes: 3 additions & 0 deletions codegen/snippet-tests/input/11-withImport.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import "support/moduleWithClass.pkl"

value: moduleWithClass.ExampleClass
4 changes: 4 additions & 0 deletions codegen/snippet-tests/input/support/moduleWithClass.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class ExampleClass {
x: String
y: Int
}
33 changes: 33 additions & 0 deletions codegen/snippet-tests/output/11_with_import.pkl.ts
Original file line number Diff line number Diff line change
@@ -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<N11WithImport> => {
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<N11WithImport> =>
evaluator.evaluateModule(source) as Promise<N11WithImport>;
Original file line number Diff line number Diff line change
@@ -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<NamedModule> => {
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<NamedModule> =>
evaluator.evaluateModule(source) as Promise<NamedModule>;
2 changes: 1 addition & 1 deletion codegen/src/Generator.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ local modules = allMappings
.mapValues((`_moduleName`: String?, _mappings: List<TypescriptMapping>) -> new TypescriptModule {
typescriptModule = _moduleName
`module` = _mappings.first.source.enclosingDeclaration
mappings = allMappings
mappings = _mappings
})

output {
Expand Down
23 changes: 8 additions & 15 deletions codegen/src/internal/ClassGen.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -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)`."

Expand All @@ -35,13 +30,12 @@ local superClass: TypescriptMapping.Class? = mappings.findOrNull((c) -> c is Typ

local fields: Map<String, TypescriptInterfaceProperty> = 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())

Expand All @@ -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.
Expand All @@ -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<ParentClass, 'overriddenField'>`
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)
""")
Expand Down
10 changes: 9 additions & 1 deletion codegen/src/internal/TypescriptModule.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,22 @@ local generated: List<Gen>(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 {
"""
// This file was generated by `pkl-typescript` from Pkl module `\(`module`.moduleClass.enclosingDeclaration.name)`.
// DO NOT EDIT.
"""

utils.renderImports(imports)
""

for (gen in generated) {
gen.contents
""
Expand Down
Loading