Skip to content

Commit eb1f19f

Browse files
authored
Merge pull request #149 from modelix/typescript-generator-test
fix(model-api-gen): added tests for the typescript generator
2 parents 7c87e1f + a5a2a2f commit eb1f19f

File tree

10 files changed

+159
-32
lines changed

10 files changed

+159
-32
lines changed

model-api-gen-gradle-test/build.gradle.kts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import com.github.gradle.node.npm.task.NpmSetupTask
12

23
buildscript {
34
repositories {
@@ -24,6 +25,7 @@ plugins {
2425
kotlin("jvm") version "1.8.20"
2526
id("base")
2627
id("org.modelix.model-api-gen")
28+
id("com.github.node-gradle.node") version "3.4.0"
2729
}
2830

2931
val mps by configurations.creating
@@ -68,6 +70,7 @@ metamodel {
6870
mpsHome = mpsDir
6971
kotlinDir = kotlinGenDir
7072
kotlinProject = project
73+
typescriptDir = projectDir.resolve("typescript_src")
7174
includeNamespace("jetbrains")
7275
exportModules("jetbrains.mps.baseLanguage")
7376

@@ -78,3 +81,25 @@ metamodel {
7881
typedNodeImpl.suffix = "Impl"
7982
}
8083
}
84+
85+
node {
86+
version.set("18.3.0")
87+
npmVersion.set("8.11.0")
88+
download.set(true)
89+
}
90+
91+
tasks.withType<NpmSetupTask> {
92+
dependsOn("generateMetaModelSources")
93+
}
94+
95+
tasks.named("npm_run_build") {
96+
inputs.dir("typescript_src")
97+
inputs.file("package.json")
98+
inputs.file("package-lock.json")
99+
100+
outputs.dir("dist")
101+
}
102+
103+
tasks.named("assemble") {
104+
dependsOn("npm_run_build")
105+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "@modelix/model-api-gen-gradle-test",
3+
"version": "0.0.0",
4+
"description": "",
5+
"keywords": [
6+
"ts-model-api",
7+
"node",
8+
"typescript"
9+
],
10+
"homepage": "https://modelix.org/",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/modelix/modelix.core.git"
14+
},
15+
"license": "Apache 2.0",
16+
"files": [
17+
"dist/*.*js.map",
18+
"dist/*.*js",
19+
"dist/*.d.ts.map",
20+
"dist/*.d.ts"
21+
],
22+
"type": "commonjs",
23+
"main": "dist/index",
24+
"typings": "dist/index.d.ts",
25+
"types": "dist/index.d.ts",
26+
"scripts": {
27+
"build": "tsc -p tsconfig.json",
28+
"clean": "shx rm -rf dist/ .*cache *.log",
29+
"lint": "npm run lint:debug -- --config tslint.json",
30+
"lint:debug": "tslint --project tsconfig.json --format stylish",
31+
"prepublishOnly": "npm run lint && npm run build",
32+
"test": "shx echo 'Write your own tests'",
33+
"ts": "tsc",
34+
"watch": "tsc --watch"
35+
},
36+
"dependencies": {
37+
"@modelix/ts-model-api": "file:../ts-model-api"
38+
},
39+
"devDependencies": {
40+
"@reallyland/tsconfig": "^2.0.0",
41+
"@reallyland/tslint-config": "^1.1.1",
42+
"@types/node": "^13.9.8",
43+
"husky": "^4.2.3",
44+
"shx": "^0.3.2",
45+
"tslint": "^6.1.0",
46+
"typescript": "^4.7.4"
47+
},
48+
"engines": {
49+
"node": ">= 10.18.1",
50+
"npm": ">= 6.13.4"
51+
}
52+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": "@reallyland/tsconfig",
3+
"compilerOptions": {
4+
"rootDir": "typescript_src",
5+
"outDir": "dist",
6+
"declarationDir": "dist",
7+
"noUnusedLocals": false,
8+
"importsNotUsedAsValues": "remove",
9+
"module": "CommonJS",
10+
"resolveJsonModule": false,
11+
"target": "ES2020"
12+
},
13+
"include": ["typescript_src/**/*.ts"],
14+
"exclude": ["dist"]
15+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"extends": [
3+
"@reallyland/tslint-config"
4+
],
5+
"rules": {
6+
"object-curly-spacing": false,
7+
"quotemark": false,
8+
"ordered-imports": false,
9+
"function-name": false,
10+
"prefer-array-literal": false,
11+
"semicolon": false,
12+
"max-classes-per-file": false,
13+
"prefer-const": false,
14+
"prefer-template": false,
15+
"no-empty-interface": false,
16+
"interface-name": false,
17+
"ter-arrow-parens": false,
18+
"no-increment-decrement": false,
19+
"no-angle-bracket-type-assertion": false,
20+
"object-literal-shorthand": false,
21+
"trailing-comma": false,
22+
"max-line-length": false
23+
}
24+
}

model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/ConceptInLanguage.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,6 @@ class ConceptRef(val languageName: String, val conceptName: String) {
112112
override fun toString(): String = languageName + "." + conceptName
113113
}
114114

115-
private val reservedPropertyNames: Set<String> = setOf(
116-
"constructor", // already exists on JS objects
117-
) + IConcept::class.members.map { it.name }
118-
119115
data class FeatureInConcept(val concept: LanguageSet.ConceptInLanguage, val data: IConceptFeatureData) {
120116
val validName: String by lazy {
121117
if (reservedPropertyNames.contains(originalName) || hasNameConflict()) {

model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/GeneratorInput.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import org.modelix.model.data.LanguageData
2020
import org.modelix.model.data.PropertyType
2121
import kotlin.reflect.KClass
2222

23-
private val reservedPropertyNames: Set<String> = setOf(
23+
val reservedPropertyNames: Set<String> = setOf(
2424
"constructor", // already exists on JS objects
25+
"_node" // exists in TypedNode in ts-model-api
2526
) + IConcept::class.members.map { it.name }
2627

2728
interface IProcessedLanguageSet

model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/MetaModelGenerator.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ import org.modelix.model.data.PrimitivePropertyType
1010
import java.nio.file.Path
1111
import kotlin.reflect.KClass
1212

13-
private val reservedPropertyNames: Set<String> = setOf(
14-
"constructor", // already exists on JS objects
15-
) + IConcept::class.members.map { it.name }
16-
1713
class MetaModelGenerator(val outputDir: Path, val nameConfig: NameConfig = NameConfig()) {
1814
var alwaysUseNonNullableProperties: Boolean = true
1915

model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/TypescriptMMGenerator.kt

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,15 @@ class TypescriptMMGenerator(val outputDir: Path, val nameConfig: NameConfig = Na
9595
when (feature) {
9696
is ProcessedProperty -> {
9797
val rawValueName = feature.rawValueName()
98-
val defaultPropertyText = """
98+
val rawPropertyText = """
9999
public set $rawValueName(value: string | undefined) {
100-
this.node.setPropertyValue("${feature.originalName}", value)
100+
this._node.setPropertyValue("${feature.originalName}", value)
101101
}
102102
public get $rawValueName(): string | undefined {
103-
return this.node.getPropertyValue("${feature.originalName}")
103+
return this._node.getPropertyValue("${feature.originalName}")
104104
}
105105
""".trimIndent()
106-
if (feature.type is PrimitivePropertyType) {
106+
val typedPropertyText = if (feature.type is PrimitivePropertyType) {
107107
when((feature.type as PrimitivePropertyType).primitive) {
108108
Primitive.INT -> {
109109
"""
@@ -128,20 +128,33 @@ class TypescriptMMGenerator(val outputDir: Path, val nameConfig: NameConfig = Na
128128
129129
""".trimIndent()
130130
}
131-
else -> defaultPropertyText
131+
Primitive.STRING -> """
132+
public set ${feature.generatedName}(value: string) {
133+
this.${rawValueName} = value;
134+
}
135+
public get ${feature.generatedName}(): string {
136+
return this.${rawValueName} ?? "";
137+
}
138+
139+
""".trimIndent()
132140
}
133-
} else defaultPropertyText
141+
} else ""
142+
"""
143+
$rawPropertyText
144+
$typedPropertyText
145+
""".trimIndent()
134146
}
135147
is ProcessedReferenceLink -> {
136148
val typeRef = feature.type.resolved
137149
val languagePrefix = typeRef.languagePrefix(concept.language)
138150
val entityType = "$languagePrefix${typeRef.nodeWrapperInterfaceName()}"
139151
"""
140152
public set ${feature.generatedName}(value: $entityType | undefined) {
141-
this.node.setReferenceTargetNode("${feature.originalName}", value?.unwrap());
153+
this._node.setReferenceTargetNode("${feature.originalName}", value?.unwrap());
142154
}
143155
public get ${feature.generatedName}(): $entityType | undefined {
144-
return LanguageRegistry.INSTANCE.wrapNode(this.node.getReferenceTargetNode("${feature.originalName}"));
156+
let target = this._node.getReferenceTargetNode("${feature.originalName}");
157+
return target ? LanguageRegistry.INSTANCE.wrapNode(target) as $entityType : undefined;
145158
}
146159
""".trimIndent()
147160
}
@@ -150,7 +163,7 @@ class TypescriptMMGenerator(val outputDir: Path, val nameConfig: NameConfig = Na
150163
val typeRef = feature.type.resolved
151164
val languagePrefix = typeRef.languagePrefix(concept.language)
152165
"""
153-
public ${feature.generatedName}: $accessorClassName<$languagePrefix${typeRef.nodeWrapperInterfaceName()}> = new $accessorClassName(this.node, "${feature.originalName}")
166+
public ${feature.generatedName}: $accessorClassName<$languagePrefix${typeRef.nodeWrapperInterfaceName()}> = new $accessorClassName(this._node, "${feature.originalName}")
154167
""".trimIndent()
155168
}
156169
else -> ""
@@ -159,26 +172,35 @@ class TypescriptMMGenerator(val outputDir: Path, val nameConfig: NameConfig = Na
159172
val features = concept.getOwnRoles().joinToString("\n") { feature ->
160173
when (feature) {
161174
is ProcessedProperty -> {
162-
val defaultPropertyText = """
175+
val rawPropertyText = """
163176
${feature.rawValueName()}: string | undefined
164177
""".trimIndent()
165-
if (feature.type is PrimitivePropertyType) {
178+
val typedPropertyText = if (feature.type is PrimitivePropertyType) {
166179
when ((feature.type as PrimitivePropertyType).primitive) {
167180
Primitive.BOOLEAN -> {
168181
"""
169182
${feature.generatedName}: boolean
170183
171-
""".trimIndent()
184+
""".trimIndent()
172185
}
173186
Primitive.INT -> {
174187
"""
175188
${feature.generatedName}: number
176189
177-
""".trimIndent()
190+
""".trimIndent()
191+
}
192+
Primitive.STRING -> {
193+
"""
194+
${feature.generatedName}: string
195+
196+
""".trimIndent()
178197
}
179-
else -> defaultPropertyText
180198
}
181-
} else defaultPropertyText
199+
} else ""
200+
"""
201+
$rawPropertyText
202+
$typedPropertyText
203+
""".trimIndent()
182204
}
183205
is ProcessedReferenceLink -> {
184206
val typeRef = feature.type.resolved
@@ -275,8 +297,4 @@ internal fun ProcessedLanguage.languageDependencies(): List<ProcessedLanguage> {
275297
return languageSet.getLanguages().filter { languageNames.contains(it.name) }.minus(this)
276298
}
277299

278-
private fun ProcessedProperty.rawValueName() =
279-
if (type is PrimitivePropertyType
280-
&& ((type as PrimitivePropertyType).primitive == Primitive.STRING))
281-
generatedName
282-
else "raw_$generatedName"
300+
private fun ProcessedProperty.rawValueName() = "raw_$generatedName"

model-api/src/jsMain/kotlin/org/modelix/model/api/NodeAdapterJS.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object JSNodeConverter {
3232
fun toINode(node: Any): INode {
3333
if (node is INode) return node
3434
if (node is NodeAdapterJS) return node.node
35-
if (node is TypedNode) return toINode(node.node)
35+
if (node is TypedNode) return toINode(node._node)
3636

3737
// Workaround, because ts-model-api is loaded twice by webpack making the instanceof check on TypedNode fail.
3838
val unwrapped = node.asDynamic().node

ts-model-api/src/TypedNode.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type {INodeJS} from "./INodeJS.js";
22

33
export class TypedNode implements ITypedNode {
4-
constructor(public node: INodeJS) {
4+
constructor(public _node: INodeJS) {
55
}
66

77
unwrap(): INodeJS {
8-
return this.node;
8+
return this._node;
99
}
1010

1111
}

0 commit comments

Comments
 (0)