From cd05f34a3ea64b9a85e26fe662f3c469d17bb5ac Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 14 Aug 2025 12:45:14 +0300 Subject: [PATCH 1/7] test: add integration test for TypeScript declarations Signed-off-by: Andrey --- .github/workflows/ci.yaml | 4 ++++ Makefile | 9 +++++++++ test/typescript_declarations/.gitignore | 4 ++++ test/typescript_declarations/Makefile | 9 +++++++++ test/typescript_declarations/README.md | 4 ++++ test/typescript_declarations/gleam.toml | 10 ++++++++++ test/typescript_declarations/manifest.toml | 9 +++++++++ .../src/typescript_declarations.gleam | 20 +++++++++++++++++++ 8 files changed, 69 insertions(+) create mode 100644 test/typescript_declarations/.gitignore create mode 100644 test/typescript_declarations/Makefile create mode 100644 test/typescript_declarations/README.md create mode 100644 test/typescript_declarations/gleam.toml create mode 100644 test/typescript_declarations/manifest.toml create mode 100644 test/typescript_declarations/src/typescript_declarations.gleam diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 580b94dd2f6..ab8ccb6ba42 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -470,6 +470,10 @@ jobs: run: make working-directory: ./test/javascript_prelude + - name: Test generated TypeScript declarations + run: make test + working-directory: ./test/typescript_declarations + - name: Test export of hex tarball run: make test working-directory: ./test/hextarball diff --git a/Makefile b/Makefile index 228545d0864..c942f3a4e7a 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ test: ## Run the compiler unit tests cd test/project_javascript && cargo run clean && cargo run check && cargo run test cd test/project_deno && cargo run clean && cargo run check && cargo run test cd test/hextarball && make test + cd test/typescript_declarations && make test cd test/running_modules && make test cd test/subdir_ffi && make @@ -51,6 +52,14 @@ test-watch: ## Run compiler tests when files change export-hex-tarball-test: ## Run `gleam export hex-tarball` and verify it is created cd test/hextarball && make test +.PHONY: typescript-declarations-test +typescript-declarations-test: ## Check that generated TypeScript declaration compile + cd test/typescript_declarations && make test + +.PHONY: typescript-declarations-test-watch +typescript-declarations-test-watch: ## Check that generated TypeScript declaration compile when files change + watchexec "cd test/typescript_declarations && make test" + .PHONY: benchmark benchmark: ## Run the benchmarks cd benchmark/list && make diff --git a/test/typescript_declarations/.gitignore b/test/typescript_declarations/.gitignore new file mode 100644 index 00000000000..599be4eb929 --- /dev/null +++ b/test/typescript_declarations/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +/build +erl_crash.dump diff --git a/test/typescript_declarations/Makefile b/test/typescript_declarations/Makefile new file mode 100644 index 00000000000..e4b3acc8d18 --- /dev/null +++ b/test/typescript_declarations/Makefile @@ -0,0 +1,9 @@ +.PHONY: clean +clean: + rm -rf build + +.PHONY: build +test: + cargo run --quiet -- build + tsc ./build/dev/javascript/typescript_declarations/typescript_declarations.d.mts --noEmit --lib es2015 + diff --git a/test/typescript_declarations/README.md b/test/typescript_declarations/README.md new file mode 100644 index 00000000000..59040cbc5f4 --- /dev/null +++ b/test/typescript_declarations/README.md @@ -0,0 +1,4 @@ +# typescript_declarations + +Check that generated TypeScript declarartions are correct. This requires TypeScript installed and be in PATH. + diff --git a/test/typescript_declarations/gleam.toml b/test/typescript_declarations/gleam.toml new file mode 100644 index 00000000000..a3e170f734d --- /dev/null +++ b/test/typescript_declarations/gleam.toml @@ -0,0 +1,10 @@ +name = "typescript_declarations" +version = "1.0.0" +target = "javascript" + +[javascript] +typescript_declarations = true + +[dependencies] +gleam_stdlib = ">= 0.44.0 and < 2.0.0" + diff --git a/test/typescript_declarations/manifest.toml b/test/typescript_declarations/manifest.toml new file mode 100644 index 00000000000..88377adc600 --- /dev/null +++ b/test/typescript_declarations/manifest.toml @@ -0,0 +1,9 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "gleam_stdlib", version = "0.62.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "0080706D3A5A9A36C40C68481D1D231D243AF602E6D2A2BE67BA8F8F4DFF45EC" }, +] + +[requirements] +gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } diff --git a/test/typescript_declarations/src/typescript_declarations.gleam b/test/typescript_declarations/src/typescript_declarations.gleam new file mode 100644 index 00000000000..bacb9ba217b --- /dev/null +++ b/test/typescript_declarations/src/typescript_declarations.gleam @@ -0,0 +1,20 @@ +//// Test TS declarartions + +/// Answer for all questions in the world +pub const answer = 256 + +pub type User { + // Registered user + User(name: String, email: String) + /// Unregistered guest + Guest +} + +/// Get name of user. Returns "Guest" if user is guest +pub fn get_name(user) { + case user { + User(name, ..) -> name + Guest -> "Guest" + } +} + From c200261e6aec43d7788c4e959c879ed629ef7cdc Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 17 Aug 2025 13:49:34 +0300 Subject: [PATCH 2/7] style: test: reformat Gleam project for TS declarartions test Signed-off-by: Andrey --- test/typescript_declarations/src/typescript_declarations.gleam | 1 - 1 file changed, 1 deletion(-) diff --git a/test/typescript_declarations/src/typescript_declarations.gleam b/test/typescript_declarations/src/typescript_declarations.gleam index bacb9ba217b..d287130c87f 100644 --- a/test/typescript_declarations/src/typescript_declarations.gleam +++ b/test/typescript_declarations/src/typescript_declarations.gleam @@ -17,4 +17,3 @@ pub fn get_name(user) { Guest -> "Guest" } } - From 1ae7e887b45674f88e63690b466a1c4a30c6619a Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 17 Aug 2025 13:50:58 +0300 Subject: [PATCH 3/7] style: test: remove newlines at end in TS declarations test Signed-off-by: Andrey --- test/typescript_declarations/Makefile | 1 - test/typescript_declarations/README.md | 1 - test/typescript_declarations/gleam.toml | 1 - 3 files changed, 3 deletions(-) diff --git a/test/typescript_declarations/Makefile b/test/typescript_declarations/Makefile index e4b3acc8d18..c59317fbae6 100644 --- a/test/typescript_declarations/Makefile +++ b/test/typescript_declarations/Makefile @@ -6,4 +6,3 @@ clean: test: cargo run --quiet -- build tsc ./build/dev/javascript/typescript_declarations/typescript_declarations.d.mts --noEmit --lib es2015 - diff --git a/test/typescript_declarations/README.md b/test/typescript_declarations/README.md index 59040cbc5f4..6728af1eab5 100644 --- a/test/typescript_declarations/README.md +++ b/test/typescript_declarations/README.md @@ -1,4 +1,3 @@ # typescript_declarations Check that generated TypeScript declarartions are correct. This requires TypeScript installed and be in PATH. - diff --git a/test/typescript_declarations/gleam.toml b/test/typescript_declarations/gleam.toml index a3e170f734d..fc53a2d88c1 100644 --- a/test/typescript_declarations/gleam.toml +++ b/test/typescript_declarations/gleam.toml @@ -7,4 +7,3 @@ typescript_declarations = true [dependencies] gleam_stdlib = ">= 0.44.0 and < 2.0.0" - From f727803b2b3358925d9aafa55bc6302ac9d5bc86 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 17 Aug 2025 13:53:36 +0300 Subject: [PATCH 4/7] test: remove typescript-declarartions-test-watch from root Makefile Signed-off-by: Andrey --- Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile b/Makefile index c942f3a4e7a..8d6ea998163 100644 --- a/Makefile +++ b/Makefile @@ -56,10 +56,6 @@ export-hex-tarball-test: ## Run `gleam export hex-tarball` and verify it is crea typescript-declarations-test: ## Check that generated TypeScript declaration compile cd test/typescript_declarations && make test -.PHONY: typescript-declarations-test-watch -typescript-declarations-test-watch: ## Check that generated TypeScript declaration compile when files change - watchexec "cd test/typescript_declarations && make test" - .PHONY: benchmark benchmark: ## Run the benchmarks cd benchmark/list && make From 50b89cfd73fec133ca14e2c10c16f63a57e91304 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 17 Aug 2025 13:55:32 +0300 Subject: [PATCH 5/7] fix(test): wrong .PHONY directive in TS declarations test Makefile Signed-off-by: Andrey --- test/typescript_declarations/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/typescript_declarations/Makefile b/test/typescript_declarations/Makefile index c59317fbae6..2e192642eab 100644 --- a/test/typescript_declarations/Makefile +++ b/test/typescript_declarations/Makefile @@ -2,7 +2,7 @@ clean: rm -rf build -.PHONY: build +.PHONY: test test: cargo run --quiet -- build tsc ./build/dev/javascript/typescript_declarations/typescript_declarations.d.mts --noEmit --lib es2015 From 1b27e9563e8d8377d588042b308fcf33a425c1a2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 18 Aug 2025 11:29:48 +0300 Subject: [PATCH 6/7] test: update TS declarations test Signed-off-by: Andrey --- test/typescript_declarations/Makefile | 2 +- test/typescript_declarations/README.md | 2 +- test/typescript_declarations/main.ts | 79 ++++++++++++++ .../src/typescript_declarations.gleam | 103 ++++++++++++++++-- 4 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 test/typescript_declarations/main.ts diff --git a/test/typescript_declarations/Makefile b/test/typescript_declarations/Makefile index 2e192642eab..4ca028e4168 100644 --- a/test/typescript_declarations/Makefile +++ b/test/typescript_declarations/Makefile @@ -5,4 +5,4 @@ clean: .PHONY: test test: cargo run --quiet -- build - tsc ./build/dev/javascript/typescript_declarations/typescript_declarations.d.mts --noEmit --lib es2015 + tsc ./main.ts --noEmit --lib es2015,dom diff --git a/test/typescript_declarations/README.md b/test/typescript_declarations/README.md index 6728af1eab5..012d33f6529 100644 --- a/test/typescript_declarations/README.md +++ b/test/typescript_declarations/README.md @@ -1,3 +1,3 @@ # typescript_declarations -Check that generated TypeScript declarartions are correct. This requires TypeScript installed and be in PATH. +Check that generated TypeScript declarartions are correct. This requires `tsc` installed and be in PATH. diff --git a/test/typescript_declarations/main.ts b/test/typescript_declarations/main.ts new file mode 100644 index 00000000000..9d0a2404b42 --- /dev/null +++ b/test/typescript_declarations/main.ts @@ -0,0 +1,79 @@ +import type { Option$ } from "./build/dev/javascript/gleam_stdlib/gleam/option.d.mts"; +import { List, Ok, Result } from "./build/dev/javascript/typescript_declarations/gleam.mjs"; +import * as Gleam from "./build/dev/javascript/typescript_declarations/typescript_declarations.mjs" + +// Check add and add_alias +const sum: number = Gleam.add_alias(Gleam.add(1, 2), 908); + +// Check ID lists constants +const count_of_moderators: number = Gleam.moders.countLength(); +const count_of_administrators: number = Gleam.admins.countLength(); + +// Check add_one closure +const add_one: (_: number) => number = Gleam.add_one() +const two: number = add_one(1) + +// Check UserId type alias +const first_moderator: Gleam.UserId = Gleam.moders[ 0]; +const first_moderator_num: number = first_moderator; +const me: UserID = 84738; +type UserID = Gleam.UserId; + +// Check is_divisible_by +const is_five_divisible_by_two: boolean = Gleam.is_divisible_by(5, 2); + +// Check extern alert function if we are in browser target +if (typeof window != undefined) { + Gleam.js_alert("Hello!"); +} + +// Check generic twice function +const twice_number: number = Gleam.twice(45, add_one); + +// Check recursive sum_list +const sum_of_list: number = Gleam.sum_list(List.fromArray([10, 25, 65383, 8910, 1893]), 0); + +// Check results +const result_ok: Result = new Ok(10); +const is_ok: boolean = Gleam.is_ok_result(result_ok); + +// Check name_description tuple +const name: string = Gleam.name_description[0]; +const description: string = Gleam.name_description[1]; + +// Check User and related functions +const user: Gleam.User$ = new Gleam.User("King", 83874, new Gleam.PlainUser()); +const guest: Gleam.User$ = new Gleam.Guest(); +const user_username: Option$ = Gleam.user_name(user); +const guest_username: Option$ = Gleam.user_name(guest); +const plain_user_string: string = Gleam.role_string(user.withFields["role"]); + +// Check Either type +const left_either: Gleam.Either$ = new Gleam.Left("Hello!"); +const right_either: Gleam.Either$ = new Gleam.Right(3747); + +// This added since TypeScript will give warnings about unused things otherwise +void [ + sum, + count_of_administrators, + count_of_moderators, + add_one, + two, + first_moderator, + first_moderator_num, + me, + is_five_divisible_by_two, + twice_number, + sum_of_list, + result_ok, + is_ok, + name, + description, + user, + guest, + user_username, + guest_username, + plain_user_string, + left_either, + right_either, +] diff --git a/test/typescript_declarations/src/typescript_declarations.gleam b/test/typescript_declarations/src/typescript_declarations.gleam index d287130c87f..87b6ec07dbc 100644 --- a/test/typescript_declarations/src/typescript_declarations.gleam +++ b/test/typescript_declarations/src/typescript_declarations.gleam @@ -1,19 +1,102 @@ -//// Test TS declarartions +import gleam/option.{type Option} +import gleam/result -/// Answer for all questions in the world -pub const answer = 256 +/// Greeting for user +pub const greeting: String = "Hello, dear user!" +/// Add two numbers +pub fn add(first a: Int, second b: Int) -> Int { + a + b +} + +/// Returns true if number is divisible by another +pub fn is_divisible_by(number num: Int, divider div: Int) -> Bool { + case num % div { + 0 -> True + _ -> False + } +} + +/// ID of specific user +pub type UserId = + Int + +/// Alias to add function +pub const add_alias = add + +/// Return function that will add one to given number +pub fn add_one() -> fn(Int) -> Int { + let func = add(1, _) + func +} + +/// IDs of administrators +pub const admins: List(UserId) = [00_010, 00_000, 00_001, 00_002, 29_373] + +/// IDs of moderators +pub const moders = [01_013, 36_371, 74_839, 18_930, 36_373] + +/// Create alert on browser target +@external(javascript, "globalThis", "alert") +pub fn js_alert(text: String) -> Nil + +/// Twice value via given function +pub fn twice(val: a, function: fn(a) -> a) -> a { + function(function(val)) +} + +/// Recursively sum list of numbers +pub fn sum_list(list: List(Int), total: Int) -> Int { + case list { + [first, ..rest] -> sum_list(rest, total + first) + [] -> total + } +} + +/// Returns true if result is ok +pub fn is_ok_result(res: Result(a, b)) -> Bool { + result.is_ok(res) +} + +/// First value is name and second is description +pub const name_description = #("MyApp", "My Awesome Application") + +/// Role of user +pub type UserRole { + /// Administrator + Administrator + /// Moderator + Moderator + /// Just a user + PlainUser +} + +/// Represents user pub type User { - // Registered user - User(name: String, email: String) - /// Unregistered guest + /// Represents registered user + User(username: String, id: UserId, role: UserRole) + /// Represents guest without any data Guest } -/// Get name of user. Returns "Guest" if user is guest -pub fn get_name(user) { +/// Get name of user, None if user is guest +pub fn user_name(user: User) -> Option(String) { case user { - User(name, ..) -> name - Guest -> "Guest" + User(name, ..) -> option.Some(name) + Guest -> option.None + } +} + +/// Format user role as string +pub fn role_string(role: UserRole) -> String { + case role { + PlainUser -> "user" + Moderator -> "moderator" + Administrator -> "admin" } } + +pub type Either(a, b) { + Left(a) + Right(b) +} From 38026548f63f0b15030aea15c03d25a7d5426867 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 18 Aug 2025 18:08:23 +0300 Subject: [PATCH 7/7] test: use --strict flag for tsc Signed-off-by: Andrey --- test/typescript_declarations/Makefile | 2 +- test/typescript_declarations/main.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/typescript_declarations/Makefile b/test/typescript_declarations/Makefile index 4ca028e4168..4452639f0da 100644 --- a/test/typescript_declarations/Makefile +++ b/test/typescript_declarations/Makefile @@ -5,4 +5,4 @@ clean: .PHONY: test test: cargo run --quiet -- build - tsc ./main.ts --noEmit --lib es2015,dom + tsc ./main.ts --strict --noEmit --lib es2015,dom diff --git a/test/typescript_declarations/main.ts b/test/typescript_declarations/main.ts index 9d0a2404b42..8b7ba85c4e1 100644 --- a/test/typescript_declarations/main.ts +++ b/test/typescript_declarations/main.ts @@ -14,7 +14,7 @@ const add_one: (_: number) => number = Gleam.add_one() const two: number = add_one(1) // Check UserId type alias -const first_moderator: Gleam.UserId = Gleam.moders[ 0]; +const first_moderator: Gleam.UserId = Gleam.moders.toArray()[ 0]; const first_moderator_num: number = first_moderator; const me: UserID = 84738; type UserID = Gleam.UserId; @@ -46,7 +46,7 @@ const user: Gleam.User$ = new Gleam.User("King", 83874, new Gleam.PlainUser()); const guest: Gleam.User$ = new Gleam.Guest(); const user_username: Option$ = Gleam.user_name(user); const guest_username: Option$ = Gleam.user_name(guest); -const plain_user_string: string = Gleam.role_string(user.withFields["role"]); +const plain_user_string: string = Gleam.role_string((user as Gleam.User).role); // Check Either type const left_either: Gleam.Either$ = new Gleam.Left("Hello!");