Skip to content
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
1 change: 1 addition & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,7 @@ fun ExportInfoDto.toEtsExportInfo(): EtsExportInfo {
from = exportFrom,
nameBeforeAs = nameBeforeAs,
modifiers = EtsModifiers(modifiers),
isTypeOnly = isTypeOnly,
)
}

Expand Down
1 change: 1 addition & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Model.kt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ data class ExportInfoDto(
val exportFrom: String? = null,
val nameBeforeAs: String? = null,
val modifiers: Int,
val isTypeOnly: Boolean = false,
// val decorators: List<DecoratorDto>,
)

Expand Down
2 changes: 2 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Export.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ package org.jacodb.ets.model
* @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.
* @property isTypeOnly Whether this export is only available in type positions.
*/
data class EtsExportInfo(
val name: String,
val type: EtsExportType,
val from: String? = null,
val nameBeforeAs: String? = null,
override val modifiers: EtsModifiers = EtsModifiers.EMPTY,
val isTypeOnly: Boolean = false,
) : Base {

// Note: Export statements do not have decorators in JS/TS.
Expand Down
30 changes: 30 additions & 0 deletions jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsExportTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

package org.jacodb.ets.test

import kotlinx.serialization.json.Json
import mu.KotlinLogging
import org.jacodb.ets.dto.ExportInfoDto
import org.jacodb.ets.dto.toEtsExportInfo
import org.jacodb.ets.model.EtsFile
import org.jacodb.ets.test.utils.getResourcePath
import org.jacodb.ets.utils.loadEtsFileAutoConvert
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
Expand Down Expand Up @@ -212,6 +216,32 @@ class EtsExportTest {
logger.info { "✓ Namespace export test passed: $namespaceExport" }
}

@Test
fun testExportInfoTypeOnlyMetadata() {
val typeOnlyExport = ExportInfoDto(
exportName = "*",
exportType = 9,
exportFrom = "./types",
modifiers = 0,
isTypeOnly = true,
).toEtsExportInfo()

assertTrue(typeOnlyExport.isTypeOnly)

val legacyExport = Json.decodeFromString<ExportInfoDto>(
"""
{
"exportName": "runtimeValue",
"exportType": 3,
"exportFrom": "./values",
"modifiers": 0
}
""".trimIndent()
).toEtsExportInfo()

assertFalse(legacyExport.isTypeOnly)
}

@Test
fun testExportInfoToString() {
logger.info { "Testing EtsExportInfo toString() method" }
Expand Down
1 change: 1 addition & 0 deletions jacodb-ets/ts-frontend/src/dto/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export interface ExportInfoDto {
exportFrom?: string; // Kotlin default: null
nameBeforeAs?: string; // Kotlin default: null
modifiers: number;
isTypeOnly: boolean;
}

export interface DecoratorDto {
Expand Down
18 changes: 16 additions & 2 deletions jacodb-ets/ts-frontend/src/lowering/fileBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,17 @@ class FileBuilder {
exportName: name,
exportType: exportTypeOfDeclaration(statement),
modifiers,
isTypeOnly: false,
});
} else if (ts.isVariableStatement(statement)) {
for (const decl of statement.declarationList.declarations) {
if (ts.isIdentifier(decl.name)) {
infos.push({ exportName: decl.name.text, exportType: ExportType.LOCAL, modifiers });
infos.push({
exportName: decl.name.text,
exportType: ExportType.LOCAL,
modifiers,
isTypeOnly: false,
});
}
}
}
Expand All @@ -241,7 +247,12 @@ class FileBuilder {
: undefined;
if (statement.exportClause === undefined) {
// `export * from "module"`.
const info: ExportInfoDto = { exportName: "*", exportType: ExportType.UNKNOWN, modifiers: 0 };
const info: ExportInfoDto = {
exportName: "*",
exportType: ExportType.UNKNOWN,
modifiers: 0,
isTypeOnly: statement.isTypeOnly,
};
if (exportFrom !== undefined) info.exportFrom = exportFrom;
infos.push(info);
} else if (ts.isNamedExports(statement.exportClause)) {
Expand All @@ -250,6 +261,7 @@ class FileBuilder {
exportName: element.name.text,
exportType: this.exportTypeOfSymbol(element.name),
modifiers: 0,
isTypeOnly: statement.isTypeOnly || element.isTypeOnly,
};
if (element.propertyName !== undefined) info.nameBeforeAs = element.propertyName.text;
if (exportFrom !== undefined) info.exportFrom = exportFrom;
Expand All @@ -263,6 +275,7 @@ class FileBuilder {
exportType: ExportType.NAMESPACE,
nameBeforeAs: "*",
modifiers: 0,
isTypeOnly: statement.isTypeOnly,
};
if (exportFrom !== undefined) info.exportFrom = exportFrom;
infos.push(info);
Expand All @@ -276,6 +289,7 @@ class FileBuilder {
exportName: name,
exportType: this.exportTypeOfSymbol(statement.expression),
modifiers: Modifier.DEFAULT,
isTypeOnly: false,
});
}
}
Expand Down
29 changes: 24 additions & 5 deletions jacodb-ets/ts-frontend/test/imports.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,27 @@ describe("export infos", () => {
export * as bundle from "./bundle";
`);
expect(file.exportInfos).toEqual([
{ exportName: "X", exportType: 9, modifiers: 0, exportFrom: "./other" },
{ exportName: "Z", exportType: 9, nameBeforeAs: "Y", modifiers: 0, exportFrom: "./other" },
{ exportName: "*", exportType: 9, modifiers: 0, exportFrom: "./star" },
{ exportName: "bundle", exportType: 0, nameBeforeAs: "*", modifiers: 0, exportFrom: "./bundle" },
{ exportName: "X", exportType: 9, modifiers: 0, exportFrom: "./other", isTypeOnly: false },
{ exportName: "Z", exportType: 9, nameBeforeAs: "Y", modifiers: 0, exportFrom: "./other", isTypeOnly: false },
{ exportName: "*", exportType: 9, modifiers: 0, exportFrom: "./star", isTypeOnly: false },
{ exportName: "bundle", exportType: 0, nameBeforeAs: "*", modifiers: 0, exportFrom: "./bundle", isTypeOnly: false },
]);
});

it("preserves type-only metadata for re-exports", () => {
const { file } = lower(`
export * from "./values";
export type * from "./types";
export { runtimeValue, type RuntimeType } from "./mixed";
export type { DeclaredType } from "./declared";
`);

expect(file.exportInfos).toEqual([
{ exportName: "*", exportType: 9, modifiers: 0, exportFrom: "./values", isTypeOnly: false },
{ exportName: "*", exportType: 9, modifiers: 0, exportFrom: "./types", isTypeOnly: true },
{ exportName: "runtimeValue", exportType: 9, modifiers: 0, exportFrom: "./mixed", isTypeOnly: false },
{ exportName: "RuntimeType", exportType: 9, modifiers: 0, exportFrom: "./mixed", isTypeOnly: true },
{ exportName: "DeclaredType", exportType: 9, modifiers: 0, exportFrom: "./declared", isTypeOnly: true },
]);
});

Expand All @@ -81,7 +98,7 @@ describe("export infos", () => {
export default Main;
`);
expect(file.exportInfos).toEqual([
{ exportName: "Main", exportType: 1, modifiers: Modifier.DEFAULT },
{ exportName: "Main", exportType: 1, modifiers: Modifier.DEFAULT, isTypeOnly: false },
]);
});

Expand All @@ -103,6 +120,7 @@ describe("export infos", () => {
exportName: "default",
exportType: 2,
modifiers: Modifier.EXPORT | Modifier.DEFAULT,
isTypeOnly: false,
});

const anonymousClass = lower(`export default class {}`).file;
Expand All @@ -113,6 +131,7 @@ describe("export infos", () => {
exportName: "default",
exportType: 1,
modifiers: Modifier.EXPORT | Modifier.DEFAULT,
isTypeOnly: false,
});
});
});
Loading