Skip to content

Commit 90df7d4

Browse files
authored
Update TsLambdaBuilder to use existing ts config as a base (#3331)
1 parent a5ddd39 commit 90df7d4

File tree

2 files changed

+67
-25
lines changed

2 files changed

+67
-25
lines changed

jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsLambdaBuilder.kt

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@
33

44
package software.aws.toolkits.jetbrains.services.lambda.nodejs
55

6+
import com.fasterxml.jackson.core.JsonProcessingException
7+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
8+
import com.fasterxml.jackson.module.kotlin.readValue
69
import com.intellij.lang.annotation.HighlightSeverity
710
import com.intellij.lang.javascript.DialectDetector
8-
import com.intellij.lang.typescript.compiler.TypeScriptCompilerService
11+
import com.intellij.lang.typescript.compiler.TypeScriptService
912
import com.intellij.lang.typescript.tsconfig.TypeScriptConfig
1013
import com.intellij.openapi.application.runReadAction
1114
import com.intellij.openapi.module.Module
1215
import com.intellij.openapi.project.Project
1316
import com.intellij.openapi.vfs.VfsUtil
1417
import com.intellij.psi.PsiElement
18+
import com.intellij.util.castSafelyTo
1519
import software.aws.toolkits.core.utils.exists
20+
import software.aws.toolkits.core.utils.inputStream
1621
import software.aws.toolkits.core.utils.writeText
1722
import software.aws.toolkits.jetbrains.services.lambda.Lambda
1823
import software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder
@@ -52,35 +57,64 @@ class NodeJsLambdaBuilder : LambdaBuilder() {
5257
val tsOutput = sourceRoot.resolve(TS_BUILD_DIR).normalize().toAbsolutePath().toString()
5358
// relative to existing tsconfig because there is no other option https://github.com/microsoft/TypeScript/issues/25430
5459
val tsConfig = sourceRoot.resolve(TS_CONFIG_FILE)
60+
61+
// read base ts config into mutable map
62+
fun loadBaseConfig(tsConfig: Path): MutableMap<String, Any>? {
63+
if (!tsConfig.exists()) {
64+
return null
65+
}
66+
67+
try {
68+
val tsConfigMap: MutableMap<String, Any> = MAPPER.readValue(tsConfig.inputStream())
69+
stepEmitter.emitMessageLine(message("lambda.build.typescript.compiler.using_base", tsConfig), false)
70+
return tsConfigMap
71+
} catch (e: JsonProcessingException) {
72+
stepEmitter.emitMessageLine(message("lambda.build.typescript.compiler.using_base_error", tsConfig), true)
73+
}
74+
75+
return null
76+
}
77+
val tsConfigMap = loadBaseConfig(tsConfig)
78+
?: loadBaseConfig(sourceRoot.resolve(TS_CONFIG_INITIAL_BASE_FILE))
79+
?: mutableMapOf()
80+
81+
// will create config from scratch if no base config has been loaded
82+
if (tsConfigMap.isEmpty()) {
83+
stepEmitter.emitMessageLine(message("lambda.build.typescript.compiler.creating_config"), false)
84+
}
85+
86+
// use initial skeleton for compilerOptions if it does not exist
87+
val compilerOptions = tsConfigMap[TypeScriptConfig.COMPILER_OPTIONS_PROPERTY].castSafelyTo<MutableMap<String, Any>>()
88+
?: mutableMapOf<String, Any>(
89+
TypeScriptConfig.TARGET_OPTION to TypeScriptConfig.LanguageTarget.ES6.libName,
90+
TypeScriptConfig.MODULE to TypeScriptConfig.MODULE_COMMON_JS,
91+
TypeScriptConfig.SOURCE_MAP to true
92+
)
93+
tsConfigMap[TypeScriptConfig.COMPILER_OPTIONS_PROPERTY] = compilerOptions
94+
95+
// overwrite outDir, rootDir, sourceRoot
96+
compilerOptions[TypeScriptConfig.OUT_DIR] = tsOutput
97+
compilerOptions[TypeScriptConfig.ROOT_DIR] = "."
98+
compilerOptions["sourceRoot"] = sourceRoot.toString()
99+
100+
// ensure typeRoots has resolved path
101+
val typeRoots = compilerOptions[TypeScriptConfig.TYPE_ROOTS].castSafelyTo<MutableList<String>>() ?: mutableListOf()
102+
typeRoots.add(sourceRoot.resolve(TypeScriptConfig.DEFAULT_TYPES_DIRECTORY).toString())
103+
compilerOptions[TypeScriptConfig.TYPE_ROOTS] = typeRoots.toSet().toList()
104+
105+
// ensure types has node
106+
val types = compilerOptions[TypeScriptConfig.TYPES].castSafelyTo<MutableList<String>>() ?: mutableListOf()
107+
types.add("node")
108+
compilerOptions[TypeScriptConfig.TYPES] = types.toSet().toList()
109+
110+
// pretty print the merged result
55111
if (!tsConfig.exists()) {
56112
Files.createFile(tsConfig)
57113
}
58-
59-
// TODO: if there's an existing tsconfig file, should we use it as a base?
60-
tsConfig.writeText(
61-
// language=JSON
62-
"""
63-
{
64-
"compilerOptions": {
65-
"${TypeScriptConfig.TYPE_ROOTS}": [
66-
"${sourceRoot.resolve(TypeScriptConfig.DEFAULT_TYPES_DIRECTORY)}"
67-
],
68-
"${TypeScriptConfig.TYPES}": [
69-
"node"
70-
],
71-
"${TypeScriptConfig.TARGET_OPTION}": "${TypeScriptConfig.LanguageTarget.ES6.libName}",
72-
"${TypeScriptConfig.MODULE}": "${TypeScriptConfig.MODULE_COMMON_JS}",
73-
"${TypeScriptConfig.OUT_DIR}": "$tsOutput",
74-
"${TypeScriptConfig.ROOT_DIR}": ".",
75-
"sourceRoot": "$sourceRoot",
76-
"${TypeScriptConfig.SOURCE_MAP}": true
77-
}
78-
}
79-
""".trimIndent()
80-
)
114+
tsConfig.writeText(MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(tsConfigMap))
81115

82116
val tsConfigVirtualFile = VfsUtil.findFile(tsConfig, true) ?: throw RuntimeException("Could not find temporary tsconfig file using VFS")
83-
val tsService = TypeScriptCompilerService.getServiceForFile(project, handlerElement.containingFile.virtualFile)
117+
val tsService = TypeScriptService.getCompilerServiceForFile(project, handlerElement.containingFile.virtualFile)
84118

85119
stepEmitter.emitMessageLine(message("lambda.build.typescript.compiler.running", tsConfig), false)
86120
val compilerFuture = tsService?.compileConfigProjectAndGetErrors(tsConfigVirtualFile)
@@ -141,6 +175,11 @@ class NodeJsLambdaBuilder : LambdaBuilder() {
141175
private const val TS_BUILD_DIR = "aws-toolkit-ts-output"
142176
private const val TS_CONFIG_FILE = "aws-toolkit-tsconfig.json"
143177

178+
// use project tsconfig.json as initial base - if unable to parse existing config
179+
private const val TS_CONFIG_INITIAL_BASE_FILE = "tsconfig.json"
180+
181+
private val MAPPER = jacksonObjectMapper()
182+
144183
private fun getSourceRoot(handlerElement: PsiElement): Path {
145184
val handlerVirtualFile = runReadAction { handlerElement.containingFile?.virtualFile }
146185
?: throw IllegalArgumentException("Handler file must be backed by a VirtualFile")

resources/resources/software/aws/toolkits/resources/MessagesBundle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,11 +627,14 @@ iam.create.role.trust.editor.name=Trust Relationships:
627627
lambda.build.java.unsupported_build_system=Module ''{0}'' is not managed by Maven or Gradle
628628
lambda.build.module_with_no_content_root=Module ''{0}'' lacks a content root
629629
lambda.build.typescript.compiler.annotation_results=Annotation Results:
630+
lambda.build.typescript.compiler.creating_config=Creating TypeScript config from scratch
630631
lambda.build.typescript.compiler.emitted_files=Emitted Files:
631632
lambda.build.typescript.compiler.ide_error=TypeScript compiler failed to start
632633
lambda.build.typescript.compiler.processed_files=Processed Files:
633634
lambda.build.typescript.compiler.running=Running TypeScript compiler against temporary config file ''{0}''
634635
lambda.build.typescript.compiler.step=Compile TypeScript
636+
lambda.build.typescript.compiler.using_base=Using TypeScript config ''{0}'' as a base
637+
lambda.build.typescript.compiler.using_base_error=Unable to read existing TypeScript config ''{0}''
635638
lambda.build.unable_to_locate_handler_root=Unable to locate root directory of the handler
636639
lambda.build.unable_to_locate_project_root=Unable to locate project directory for Module ''{0}''
637640
lambda.create.description.label=Description:

0 commit comments

Comments
 (0)