Skip to content
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f83b0b2
wip
cometkim Dec 22, 2025
feba309
wip advanced types
cometkim Dec 26, 2025
4268914
GADT + polishing outputs
cometkim Dec 27, 2025
d080453
polishing line formats
cometkim Dec 27, 2025
b14e2bb
fix clippy
cometkim Dec 27, 2025
7243733
format
cometkim Dec 27, 2025
d367cdc
fix output format
cometkim Dec 27, 2025
9b453d6
fix artifacts
cometkim Dec 27, 2025
6c11735
fix optional args
cometkim Dec 27, 2025
5e0282d
fix module aliases
cometkim Dec 27, 2025
76f8a7a
clean ts outputs
cometkim Dec 27, 2025
2f90e46
Revert "clean ts outputs"
cometkim Dec 27, 2025
392968d
fix namespaced modules
cometkim Dec 29, 2025
63c8c85
fix inline record definitions
cometkim Dec 29, 2025
0ff9113
fix bsb config
cometkim Dec 29, 2025
1234d85
fix opaque brand names
cometkim Dec 29, 2025
841c37c
make clean works
cometkim Dec 29, 2025
a66f116
package-specs[].module: typescript
cometkim Dec 29, 2025
6c89435
fix arrow function output
cometkim Dec 30, 2025
89724e7
fix module resolution
cometkim Dec 30, 2025
bb8d364
fix typechecking in build tests
cometkim Dec 30, 2025
5f86092
better opaque compatibility
cometkim Dec 30, 2025
820c40f
fix opaque formatting
cometkim Dec 30, 2025
8a7a331
rebuild test dependencies
cometkim Dec 30, 2025
b221b9b
ensure rebuild test deps
cometkim Dec 30, 2025
391fbc8
drop unused codes
cometkim Dec 31, 2025
e120e1d
fix phantom type params
cometkim Dec 31, 2025
1b9e917
fix runtime types
cometkim Dec 31, 2025
43989ca
fix output format
cometkim Dec 31, 2025
da3cd54
wip: support module type and first-class modules
cometkim Dec 31, 2025
d997889
Fix build (playground)
cknitt Jan 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ _build_playground
.idea
.DS_Store

node_modules
**/node_modules/
!tests/build_tests/*/node_modules/
*.dump
coverage

.ninja_log
.bsdeps
.bsbuild
lib/ocaml
/lib/
tests/build_tests/*/lib/
#ignore temporary directory
*.goog.js
Expand Down
21 changes: 19 additions & 2 deletions compiler/bsb/bsb_config_parse.ml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ let interpret_json ~(filename : string) ~(json : Ext_json_types.t)
| Dependency x -> ({jsx with version = x.jsx.version}, bsc_flags)
| Toplevel -> (jsx, bsc_flags)
in
let language : Bsb_spec_set.language =
match package_kind with
| Dependency x -> x.language
| Toplevel -> (
match map.?(Bsb_build_schemas.language) with
| Some (Str {str = "typescript"}) -> Typescript
| Some _ | None -> Javascript)
in
{
gentype_config;
package_name;
Expand All @@ -308,13 +316,15 @@ let interpret_json ~(filename : string) ~(json : Ext_json_types.t)
js_post_build_cmd = extract_js_post_build map per_proj_dir;
package_specs =
(match package_kind with
| Toplevel -> Bsb_package_specs.from_map ~cwd:per_proj_dir map
| Toplevel ->
Bsb_package_specs.from_map ~cwd:per_proj_dir ~language map
| Dependency x -> x.package_specs);
file_groups = groups;
files_to_install = Queue.create ();
jsx;
generators = extract_generators map;
cut_generators;
language;
filename;
}
| None -> Bsb_exception.invalid_spec ("no sources specified in " ^ filename)
Expand All @@ -323,8 +333,15 @@ let interpret_json ~(filename : string) ~(json : Ext_json_types.t)

let deps_from_bsconfig () =
let cwd = Bsb_global_paths.cwd in
let ( .?() ) = Map_string.find_opt in
match
Bsb_config_load.load_json ~per_proj_dir:cwd ~warn_legacy_config:false
with
| _, Obj {map} -> (Bsb_package_specs.from_map ~cwd map, Bsb_jsx.from_map map)
| _, Obj {map} ->
let language : Bsb_spec_set.language =
match map.?(Bsb_build_schemas.language) with
| Some (Str {str = "typescript"}) -> Typescript
| Some _ | None -> Javascript
in
(Bsb_package_specs.from_map ~cwd ~language map, Bsb_jsx.from_map map)
| _, _ -> assert false
2 changes: 2 additions & 0 deletions compiler/bsb/bsb_config_types.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type dependencies = dependency list
type gentype_config = bool
type command = string
type ppx = {name: string; args: string list}
type language = Bsb_spec_set.language = Javascript | Typescript

type t = {
package_name: string;
Expand Down Expand Up @@ -56,5 +57,6 @@ type t = {
cut_generators: bool;
(* note when used as a dev mode, we will always ignore it *)
gentype_config: gentype_config;
language: language;
filename: string;
}
4 changes: 3 additions & 1 deletion compiler/bsb/bsb_ninja_gen.ml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ let output_ninja_and_namespace_map ~per_proj_dir ~package_kind
namespace;
warning;
gentype_config;
language;
filename = _;
} :
Bsb_config_types.t) : unit =
let lib_artifacts_dir = Bsb_config.lib_bs in
Expand Down Expand Up @@ -197,7 +199,7 @@ let output_ninja_and_namespace_map ~per_proj_dir ~package_kind
~dpkg_incls (* dev dependencies *)
~lib_incls (* its own libs *)
~dev_incls (* its own devs *)
generators
~language generators
in

let oc = open_out_bin (cwd_lib_bs // Literals.build_ninja) in
Expand Down
6 changes: 5 additions & 1 deletion compiler/bsb/bsb_ninja_rule.ml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ let make_custom_rules ~(gentype_config : Bsb_config_types.gentype_config)
~(namespace : string option) ~package_name ~warnings
~(ppx_files : Bsb_config_types.ppx list) ~bsc_flags ~(dpkg_incls : string)
~(lib_incls : string) ~(dev_incls : string)
(custom_rules : command Map_string.t) : builtin =
~(language : Bsb_spec_set.language) (custom_rules : command Map_string.t) :
builtin =
let bs_dep = Ext_filename.maybe_quote Bsb_global_paths.vendor_bsdep in
let bsc = Ext_filename.maybe_quote Bsb_global_paths.vendor_bsc in
(* FIXME: We don't need set [-o ${out}] when building ast
Expand Down Expand Up @@ -122,6 +123,9 @@ let make_custom_rules ~(gentype_config : Bsb_config_types.gentype_config)
(match gentype_config with
| false -> ()
| true -> Ext_buffer.add_string buf " -bs-gentype");
(match language with
| Bsb_spec_set.Typescript -> Ext_buffer.add_string buf " -bs-typescript"
| Bsb_spec_set.Javascript -> ());
if read_cmi <> `is_cmi then (
Ext_buffer.add_string buf " -bs-package-name ";
Ext_buffer.add_string buf (Ext_filename.maybe_quote package_name);
Expand Down
1 change: 1 addition & 0 deletions compiler/bsb/bsb_ninja_rule.mli
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,6 @@ val make_custom_rules :
dpkg_incls:string ->
lib_incls:string ->
dev_incls:string ->
language:Bsb_spec_set.language ->
command Map_string.t ->
builtin
6 changes: 5 additions & 1 deletion compiler/bsb/bsb_package_kind.ml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type dep_payload = {package_specs: Bsb_package_specs.t; jsx: Bsb_jsx.t}
type dep_payload = {
package_specs: Bsb_package_specs.t;
jsx: Bsb_jsx.t;
language: Bsb_spec_set.language;
}

type t = Toplevel | Dependency of dep_payload
(* This package specs comes from the toplevel to
Expand Down
35 changes: 20 additions & 15 deletions compiler/bsb/bsb_package_specs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ let string_of_format (x : Ext_module_system.t) =
| Esmodule -> Literals.esmodule
| Es6_global -> Literals.es6_global

let js_suffix_regexp = Str.regexp "[A-Za-z0-9-_.]*\\.[cm]?js"
let suffix_regexp = Str.regexp "[A-Za-z0-9-_.]*\\.[cm]?[jt]s"

let validate_js_suffix suffix = Str.string_match js_suffix_regexp suffix 0
let validate_suffix suffix = Str.string_match suffix_regexp suffix 0

let rec from_array suffix (arr : Ext_json_types.t array) : Spec_set.t =
let spec = ref Spec_set.empty in
Expand Down Expand Up @@ -98,16 +98,15 @@ and from_json_single suffix (x : Ext_json_types.t) : Bsb_spec_set.spec =
in
let suffix =
match map.?(Bsb_build_schemas.suffix) with
| Some (Str {str = suffix; _}) when validate_js_suffix suffix -> suffix
| Some (Str {str = suffix; _}) when validate_suffix suffix -> suffix
| Some (Str {str; loc}) ->
Bsb_exception.errorf ~loc
"invalid suffix \"%s\". The suffix and may contain letters, \
digits, \"-\", \"_\" and \".\" and must end with .js, .mjs or \
.cjs."
"invalid suffix \"%s\". The suffix must end with .js, .mjs, .cjs, \
.ts, .mts, or .cts."
str
| Some _ ->
Bsb_exception.errorf ~loc:(Ext_json.loc_of x)
"expected a string extension like \".js\""
"expected a string extension like \".js\" or \".ts\""
| None -> suffix
in
{format = supported_format format loc; in_source; suffix}
Expand Down Expand Up @@ -192,20 +191,26 @@ let list_dirs_by (package_specs : t) (f : string -> unit) =

type json_map = Ext_json_types.t Map_string.t

let extract_js_suffix_exn (map : json_map) : string =
let extract_suffix_exn ~(language : Bsb_spec_set.language) (map : json_map) :
string =
let default_suffix =
match language with
| Bsb_spec_set.Javascript -> Literals.suffix_js
| Bsb_spec_set.Typescript -> Literals.suffix_ts
in
match map.?(Bsb_build_schemas.suffix) with
| None -> Literals.suffix_js
| Some (Str {str = suffix; _}) when validate_js_suffix suffix -> suffix
| None -> default_suffix
| Some (Str {str = suffix; _}) when validate_suffix suffix -> suffix
| Some (Str {str; _} as config) ->
Bsb_exception.config_error config
("invalid suffix \"" ^ str
^ "\". The suffix and may contain letters, digits, \"-\", \"_\" and \".\" \
and must end with .js, .mjs or .cjs.")
^ "\". The suffix must end with .js, .mjs, .cjs, .ts, .mts, or .cts.")
| Some config ->
Bsb_exception.config_error config "expected a string extension like \".js\""
Bsb_exception.config_error config
"expected a string extension like \".js\" or \".ts\""

let from_map ~(cwd : string) map =
let suffix = extract_js_suffix_exn map in
let from_map ~(cwd : string) ~(language : Bsb_spec_set.language) map =
let suffix = extract_suffix_exn ~language map in
let modules =
match map.?(Bsb_build_schemas.package_specs) with
| Some x -> from_json suffix x
Expand Down
6 changes: 5 additions & 1 deletion compiler/bsb/bsb_package_specs.mli
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@

type t

val from_map : cwd:string -> Ext_json_types.t Map_string.t -> t
val from_map :
cwd:string ->
language:Bsb_spec_set.language ->
Ext_json_types.t Map_string.t ->
t

val get_list_of_output_js : t -> string -> string list

Expand Down
2 changes: 2 additions & 0 deletions compiler/bsb/bsb_spec_set.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
(* TODO: sync up with {!Js_packages_info.module_system} *)
type format = Ext_module_system.t

type language = Javascript | Typescript

type spec = {format: format; in_source: bool; suffix: string}

type t = spec list
Expand Down
2 changes: 2 additions & 0 deletions compiler/bsb/bsb_spec_set.mli
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
type format = Ext_module_system.t

type language = Javascript | Typescript

type spec = {format: format; in_source: bool; suffix: string}

type t = private spec list
Expand Down
9 changes: 5 additions & 4 deletions compiler/bsb/bsb_world.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ let vendor_ninja = Bsb_global_paths.vendor_ninja

let make_world_deps cwd (config : Bsb_config_types.t option)
(ninja_args : string array) =
let package_specs, jsx =
let package_specs, jsx, language =
match config with
| None ->
(* When this running bsb does not read rescript.json,
we will read such json file to know which [package-specs]
it wants
*)
Bsb_config_parse.deps_from_bsconfig ()
| Some config -> (config.package_specs, config.jsx)
let package_specs, jsx = Bsb_config_parse.deps_from_bsconfig () in
(package_specs, jsx, Bsb_spec_set.Javascript)
| Some config -> (config.package_specs, config.jsx, config.language)
in
let args =
if Ext_array.is_empty ninja_args then [|vendor_ninja|]
Expand Down Expand Up @@ -64,7 +65,7 @@ let make_world_deps cwd (config : Bsb_config_types.t option)
Bsb_build_util.mkp lib_bs_dir;
let _config : _ option =
Bsb_ninja_regen.regenerate_ninja
~package_kind:(Dependency {package_specs; jsx})
~package_kind:(Dependency {package_specs; jsx; language})
~per_proj_dir:proj_dir ~forced:false ~warn_legacy_config:false
~warn_as_error:None
in
Expand Down
6 changes: 6 additions & 0 deletions compiler/bsc/rescript_compiler_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ let command_line_flags : (string * Bsc_args.spec * string) array =
string_call ignore,
"*internal* Set jsx mode, this is no longer used and is a no-op." );
("-bs-jsx-preserve", set Js_config.jsx_preserve, "*internal* Preserve jsx");
( "-bs-typescript",
unit_call (fun _ -> Js_config.ts_output := Js_config.Ts_typescript),
"*internal* Generate TypeScript output with type annotations" );
( "-bs-emit-dts",
unit_call (fun _ -> Js_config.emit_dts := true),
"*internal* Emit .d.ts declaration files alongside JavaScript output" );
( "-bs-package-output",
string_call Js_packages_state.update_npm_package_path,
"*internal* Set npm-output-path: [opt_module]:path, for example: \
Expand Down
10 changes: 10 additions & 0 deletions compiler/common/js_config.ml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,13 @@ let jsx_module_of_string = function

let as_pp = ref false
let self_stack : string Stack.t = Stack.create ()

(** TypeScript output mode *)
type ts_output_mode =
| Ts_none (** Plain JavaScript output (default) *)
| Ts_typescript (** Full TypeScript output (.ts) *)

let ts_output = ref Ts_none

(** Whether to emit .d.ts declaration files *)
let emit_dts = ref false
10 changes: 10 additions & 0 deletions compiler/common/js_config.mli
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,13 @@ val jsx_module_of_string : string -> jsx_module
val as_pp : bool ref

val self_stack : string Stack.t

(** TypeScript output mode *)
type ts_output_mode =
| Ts_none (** Plain JavaScript output (default) *)
| Ts_typescript (** Full TypeScript output (.ts) *)

val ts_output : ts_output_mode ref

val emit_dts : bool ref
(** Whether to emit .d.ts declaration files *)
13 changes: 12 additions & 1 deletion compiler/core/j.ml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ and expression_desc =
return_unit: bool;
async: bool;
directive: string option;
fn_type: Types.type_expr option;
}
| Str of {delim: delim; txt: string}
(* A string is UTF-8 encoded, and may contain
Expand Down Expand Up @@ -293,13 +294,23 @@ and variable_declaration = {
value: expression option;
property: property;
ident_info: ident_info;
ident_type: Types.type_expr option;
(** Type annotation for TypeScript output *)
}

(* TODO: For efficency: block should not be a list, it should be able to
be concatenated in both ways
*)
and block = statement list
and program = {block: block; exports: exports; export_set: Set_ident.t}
and program = {
block: block;
exports: exports;
export_set: Set_ident.t;
type_exports: Ts.type_decl list;
(** Exported type declarations for TypeScript output *)
value_exports: Ts.value_export list;
(** Exported values with types for .d.ts generation *)
}

and deps_program = {
program: program;
Expand Down
Loading
Loading