From afe394a0d31f0bfe3db7eb25f08c1f8658564ab2 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 17:28:38 +0300 Subject: [PATCH 01/10] Add modifiers property doc --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt index 5bde585dc..dc702881a 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt @@ -23,6 +23,7 @@ package org.jacodb.ets.model * @property type The [type][EtsExportType] of export. * @property from The module or path being exported from (null for direct exports). * @property nameBeforeAs The original name before 'as' aliasing (null if no aliasing). + * @property modifiers Export modifiers. */ data class EtsExportInfo( val name: String, From 5af083c4b751780dd949c988d6ec4c8616f9a902 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 18:13:32 +0300 Subject: [PATCH 02/10] Add docs for EtsExportType variants --- .../main/kotlin/org/jacodb/ets/dto/Convert.kt | 2 +- .../kotlin/org/jacodb/ets/model/Export.kt | 42 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt index 628aba2eb..75fb62326 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt @@ -800,7 +800,7 @@ private fun Int.toEtsClassCategory(): EtsClassCategory { private fun Int.toEtsExportType(): EtsExportType { return when (this) { - 0 -> EtsExportType.NAME_SPACE + 0 -> EtsExportType.NAMESPACE 1 -> EtsExportType.CLASS 2 -> EtsExportType.METHOD 3 -> EtsExportType.LOCAL diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt index dc702881a..43c4f7fc6 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt @@ -121,10 +121,50 @@ data class EtsExportInfo( * Type of export in TypeScript/JavaScript. */ enum class EtsExportType { - NAME_SPACE, + /** + * Namespace export: + * ```ts + * export namespace MyNamespace { ... } + * ``` + */ + NAMESPACE, + + /** + * Class export: + * ```ts + * export class MyClass { ... } + * ``` + */ CLASS, + + /** + * Function export: + * ```ts + * export function myFunction() { ... } + * ``` + */ METHOD, + + /** + * Local variable/constant export: + * ```ts + * export const myVariable = 42; + * export let myLet = 'hello'; + * export var myVar = true; + * ``` + */ LOCAL, + + /** + * Type export: + * ```ts + * export type MyType = string | number; + * ``` + */ TYPE, + + /** + * Unknown export type, fallback for unrecognized export patterns. + */ UNKNOWN; } From 91b4ec030354388122c49f910d5ab8d5bec1639c Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 18:42:14 +0300 Subject: [PATCH 03/10] Add isReExport property --- .../main/kotlin/org/jacodb/ets/model/Export.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt index 43c4f7fc6..b980985a4 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt @@ -66,15 +66,21 @@ data class EtsExportInfo( } /** - * Whether this export is a star re-export (re-exporting everything from another module). + * Whether this export is a re-export. + */ + val isReExport: Boolean + get() = from != null + + /** + * Whether this export is a star re-export. * * ```ts * export * from './module'; * export * as Utils from './utils'; * ``` */ - val isStarExport: Boolean - get() = from != null && originalName == "*" + val isStarReExport: Boolean + get() = isReExport && originalName == "*" /** * Whether this export is aliased. @@ -86,7 +92,7 @@ data class EtsExportInfo( * ``` */ val isAliased: Boolean - get() = nameBeforeAs != null && nameBeforeAs != name + get() = name != originalName override val isDefault: Boolean get() = isDefaultExport @@ -96,7 +102,7 @@ data class EtsExportInfo( // Re-exports from != null -> { val alias = if (isAliased) " as $name" else "" - if (isStarExport) { + if (isStarReExport) { "export *$alias from '$from'" } else { "export { $originalName$alias } from '$from'" From 024d0856a76868b102100b65f3bf7fa8eead9615 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 17:26:52 +0300 Subject: [PATCH 04/10] Fix getters in import/export infos --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt | 2 +- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt index b980985a4..4326844fb 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt @@ -34,7 +34,7 @@ data class EtsExportInfo( ) : Base { // Note: Export statements do not have decorators in JS/TS. - override val decorators: List = emptyList() + override val decorators: List get() = emptyList() /** * Export clause name without any aliasing. diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt index 2f1811589..1ce7009fb 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt @@ -34,12 +34,13 @@ data class EtsImportInfo( ) : Base { // Note: Import statements do not have decorators in JS/TS. - override val decorators: List = emptyList() + override val decorators: List get() = emptyList() /** * Import clause name without any aliasing. */ - val originalName: String = nameBeforeAs ?: name + val originalName: String + get() = nameBeforeAs ?: name /** * Whether this is a default import. From d71ea9e51b9c4f06359d3abef26d6e0f90e6c872 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 19:10:44 +0300 Subject: [PATCH 05/10] More examples --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt index 1ce7009fb..ed4a39948 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt @@ -47,6 +47,7 @@ data class EtsImportInfo( * * ```ts * import React from 'react'; + * import { default as React } from 'react'; * ``` */ val isDefaultImport: Boolean @@ -57,6 +58,7 @@ data class EtsImportInfo( * * ```ts * import { useState } from 'react'; + * import { useState as useReactState } from 'react'; * ``` */ val isNamedImport: Boolean @@ -86,7 +88,9 @@ data class EtsImportInfo( * Whether this import uses aliasing. * * ```ts - * import { Component as ReactComponent }; + * import { Component as ReactComponent } from 'react'; + * import { default as React } from 'react'; + * import React from 'react'; * ``` */ val isAliased: Boolean From d89513a21d100258c9c46cca51029905617b532f Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 19:11:01 +0300 Subject: [PATCH 06/10] Require that name is empty only for side-effect imports --- .../src/main/kotlin/org/jacodb/ets/model/Import.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt index ed4a39948..4351185f1 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt @@ -33,6 +33,15 @@ data class EtsImportInfo( override val modifiers: EtsModifiers = EtsModifiers.EMPTY, ) : Base { + init { + if (type == EtsImportType.SIDE_EFFECT) { + require(name.isEmpty()) { "Side-effect imports should have empty name" } + require(nameBeforeAs == null) { "Side-effect imports should not have nameBeforeAs" } + } else { + require(name.isNotEmpty()) { "Only side-effect imports can have empty name" } + } + } + // Note: Import statements do not have decorators in JS/TS. override val decorators: List get() = emptyList() From 3bfeff0fe8d42422de198f6f668722c769537ebf Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 19:44:19 +0300 Subject: [PATCH 07/10] Use isDefaultImport property --- jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsImportTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsImportTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsImportTest.kt index 61fe75492..66315eaa1 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsImportTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsImportTest.kt @@ -73,7 +73,7 @@ class EtsImportTest { it.name == "React" && it.from == "react" } assertNotNull(reactImport, "Should find React default import") - assertTrue(reactImport.isDefault, "React import should be marked as default") + assertTrue(reactImport.isDefaultImport, "React import should be marked as default") assertNull(reactImport.nameBeforeAs, "React import is not aliased") assertEquals("React", reactImport.name) logger.info { "✓ Default import test passed: $reactImport" } From 175ee56d70e664ffc2f102e86f693ec3a7a2e61f Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 25 Aug 2025 19:48:07 +0300 Subject: [PATCH 08/10] Rewrite EtsImportInfo.toString --- .../kotlin/org/jacodb/ets/model/Import.kt | 71 +++++++------------ 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt index 4351185f1..18c434bb7 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Import.kt @@ -60,14 +60,14 @@ data class EtsImportInfo( * ``` */ val isDefaultImport: Boolean - get() = type == EtsImportType.DEFAULT || originalName == "default" + get() = type == EtsImportType.DEFAULT /** * Whether this is a named import. * * ```ts * import { useState } from 'react'; - * import { useState as useReactState } from 'react'; + * import { Component as ReactComponent } from 'react'; * ``` */ val isNamedImport: Boolean @@ -81,7 +81,7 @@ data class EtsImportInfo( * ``` */ val isNamespaceImport: Boolean - get() = type == EtsImportType.NAMESPACE || nameBeforeAs == "*" + get() = type == EtsImportType.NAMESPACE /** * Whether this is a side-effect import. @@ -93,49 +93,28 @@ data class EtsImportInfo( val isSideEffectImport: Boolean get() = type == EtsImportType.SIDE_EFFECT - /** - * Whether this import uses aliasing. - * - * ```ts - * import { Component as ReactComponent } from 'react'; - * import { default as React } from 'react'; - * import React from 'react'; - * ``` - */ - val isAliased: Boolean - get() = nameBeforeAs != null && nameBeforeAs != "*" && nameBeforeAs != name - - override val isDefault: Boolean - get() = isDefaultImport || super.isDefault - - override fun toString(): String = buildString { - append("import ") - - when { - isSideEffectImport -> { - // Side effect import: import './styles.css' - append("'$from'") - } - - isNamespaceImport -> { - // Namespace import: import * as Utils from './utils' - append("* as $name from '$from'") - } - - isAliased -> { - // Aliased import: import { Component as ReactComponent } from 'react' - append("{ $originalName as $name } from '$from'") - } - - isNamedImport -> { - // Named import: import { useState } from 'react' - append("{ $name } from '$from'") - } - - isDefaultImport -> { - // Default import: import React from 'react' - append("$name from '$from'") - } + override fun toString(): String = when(type) { + EtsImportType.DEFAULT -> { + // Default import: import React from 'react' + "import $name from '$from'" + } + + EtsImportType.NAMED -> { + // Named import: + // import { useState } from 'react' + // import { Component as ReactComponent } from 'react' + val alias = if (name != originalName) " as $name" else "" + "import { $originalName$alias } from '$from'" + } + + EtsImportType.NAMESPACE -> { + // Namespace import: import * as Utils from './utils' + "import * as $name from '$from'" + } + + EtsImportType.SIDE_EFFECT -> { + // Side effect import: import './styles.css' + "import '$from'" } } } From 2df7d788c87a631c956118a17c113f88a858f57b Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 26 Aug 2025 13:55:50 +0300 Subject: [PATCH 09/10] Fix doc comment --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt index 4326844fb..423a1d2fd 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt @@ -67,6 +67,11 @@ data class EtsExportInfo( /** * Whether this export is a re-export. + * + * ```ts + * export { value } from './module'; + * export * from './module'; + * ``` */ val isReExport: Boolean get() = from != null From 3629f15faf1724c92c805eb8c19c76daf5d9aa48 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 26 Aug 2025 13:56:53 +0300 Subject: [PATCH 10/10] Remove isDefault override --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt index 423a1d2fd..7158beeb0 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt @@ -99,9 +99,6 @@ data class EtsExportInfo( val isAliased: Boolean get() = name != originalName - override val isDefault: Boolean - get() = isDefaultExport - override fun toString(): String { return when { // Re-exports