diff --git a/README.md b/README.md index 5d1386e3c..164fc186f 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ When running `cargo test`, the TypeScript bindings will be exported to the file - generate necessary imports when exporting to multiple files - serde compatibility - generic types +- support for ESM imports ### limitations - generic fields cannot be inlined or flattened (#56) @@ -114,11 +115,17 @@ When running `cargo test`, the TypeScript bindings will be exported to the file When `serde-compat` is enabled, warnings are printed during build if unsupported serde attributes are encountered. Enabling this feature silences these warnings. +- `import-esm` + + `import` statements in the generated file will have the `.js` extension in the end of + the path to conform to the ES Modules spec. (e.g.: + `import { MyStruct } from "./my_struct.js"`) + If there's a type you're dealing with which doesn't implement `TS`, use `#[ts(type = "..")]` or open a PR. ### serde compatability -With the `serde-compat` feature (enabled by default), serde attributes can be parsed for enums and structs. +With the `serde-compat` feature (enabled by default), serde attributes can be parsed for enums and structs Supported serde attributes: - `rename` - `rename-all` diff --git a/ts-rs/Cargo.toml b/ts-rs/Cargo.toml index 12c6c04a2..58ff302ea 100644 --- a/ts-rs/Cargo.toml +++ b/ts-rs/Cargo.toml @@ -30,6 +30,7 @@ indexmap-impl = ["indexmap"] ordered-float-impl = ["ordered-float"] heapless-impl = ["heapless"] no-serde-warnings = ["ts-rs-macros/no-serde-warnings"] +import-esm = [] [dev-dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs index c4b9345e4..fdaf85164 100644 --- a/ts-rs/src/export.rs +++ b/ts-rs/src/export.rs @@ -110,12 +110,18 @@ fn generate_imports(out: &mut String) -> Result<(), Ex fn import_path(from: &Path, import: &Path) -> String { let rel_path = diff_paths(import, from.parent().unwrap()).expect("failed to calculate import path"); - match rel_path.components().next() { + let path = match rel_path.components().next() { Some(Component::Normal(_)) => format!("./{}", rel_path.to_string_lossy()), _ => rel_path.to_string_lossy().into(), + }; + + let path_without_extension = path.trim_end_matches(".ts"); + + if cfg!(feature = "import-esm") { + format!("{}.js", path_without_extension) + } else { + path_without_extension.to_owned() } - .trim_end_matches(".ts") - .to_owned() } // Construct a relative path from a provided base directory path to the provided path. diff --git a/ts-rs/tests/imports.rs b/ts-rs/tests/imports.rs index 7674e0991..7a72f0124 100644 --- a/ts-rs/tests/imports.rs +++ b/ts-rs/tests/imports.rs @@ -24,13 +24,26 @@ pub enum TestEnum { } #[test] -#[cfg(feature = "format")] +// #[cfg(feature = "format")] fn test_def() { // The only way to get access to how the imports look is to export the type and load the exported file TestEnum::export().unwrap(); let text = std::fs::read_to_string(TestEnum::EXPORT_TO.unwrap()).unwrap(); // Checks to make sure imports are ordered and deduplicated + #[cfg(feature = "import-esm")] + assert_eq!(text, + concat!( + "// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n", + "import type { TestTypeA } from \"./ts_rs_test_type_a.js\";\n", + "import type { TestTypeB } from \"./ts_rs_test_type_b.js\";\n", + "\n", + "export type TestEnum = { \"C\": { value: TestTypeB } } | {\n", + " \"A1\": { value: TestTypeA };\n", + "} | { \"A2\": { value: TestTypeA } };\n" + ) + ); + #[cfg(not(feature = "import-esm"))] assert_eq!(text, concat!( "// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n",