Skip to content

Commit cf94640

Browse files
Developer playground: genType and sourcemap support (#8448)
* Developer playground: genType support and sourcemap scaffolding * Omit bumping the playground version as long as the old one is not deployed on rescript-lang.org * Fix PR compiler versions not showing up in selection box * Sourcemap tab * Changelog * Fix event order * Format & review fixes * Different approach: use JSOO vfs * Review * One more review * And another * genType: derive module name from the configured source filename * Bump version
1 parent d4699e9 commit cf94640

16 files changed

Lines changed: 1298 additions & 60 deletions

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,10 @@ jobs:
260260
if: matrix.build_playground
261261
run: yarn workspace playground test
262262

263+
- name: Test dev playground
264+
if: matrix.build_playground
265+
run: yarn workspace dev-playground test
266+
263267
- name: Stage PR dev playground compiler bundle
264268
if: ${{ matrix.build_playground && github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'master' && github.event.pull_request.head.repo.full_name == github.repository }}
265269
env:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#### :house: Internal
7070

7171
- Merge the duplicate Lam intermediate representation into Lambda, removing the conversion layer and obsolete supporting infrastructure. Lambda is now a single private, normalized representation, with generated JavaScript remaining semantically unchanged. https://github.com/rescript-lang/rescript/pull/8608
72+
- Add genType and source map controls and output to the developer playground. https://github.com/rescript-lang/rescript/pull/8448
7273
- Rework the object-type representation end to end: object rows are plain field chains carrying a per-field mutability state (no phantom setter members), object literals are typed directly and property access and assignment are first-class AST and Lambda nodes shared between the Lambda and JS pipelines, and dead class-system remnants (the field-presence lattice, the class-abbreviation memo on object types, method-send typing) are removed. https://github.com/rescript-lang/rescript/pull/8597
7374
- Upgrade the development toolchain and primary CI builds to OCaml 5.5 while retaining OCaml 5.0 as the minimum supported version. https://github.com/rescript-lang/rescript/pull/8589
7475
- Upgrade the vendored Flow parser from 0.267.0 to 0.320.0, the final release of the OCaml implementation. https://github.com/rescript-lang/rescript/pull/8588

compiler/jsoo/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
(= %{profile} browser))
88
(flags
99
(:standard -w +a-4-9-40-42-44-45))
10-
(libraries core syntax ml js_of_ocaml))
10+
(libraries core syntax ml gentype js_of_ocaml))

compiler/jsoo/jsoo_playground_main.ml

Lines changed: 238 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@
5252
* v5: Removed .ml support.
5353
* v6: Added `config.experimental_features` and `config.jsx_preserve_mode` to the BundleConfig.
5454
* v7: Added debug dump output APIs for developer playground tooling.
55+
* v8: Added genType and source map configuration and compilation outputs.
5556
* *)
56-
let api_version = "7"
57+
let api_version = "8"
5758

5859
module Js = Js_of_ocaml.Js
5960

@@ -78,6 +79,10 @@ module Bundle_config = struct
7879
mutable open_modules: string list;
7980
mutable experimental_features: string list;
8081
mutable jsx_preserve_mode: bool;
82+
mutable gentype_enabled: bool;
83+
mutable source_map_mode: Js_config.source_map;
84+
mutable source_map_sources_content: bool;
85+
mutable source_map_root: string;
8186
}
8287

8388
let make () =
@@ -88,6 +93,10 @@ module Bundle_config = struct
8893
open_modules = [];
8994
experimental_features = [];
9095
jsx_preserve_mode = false;
96+
gentype_enabled = false;
97+
source_map_mode = No_source_map;
98+
source_map_sources_content = false;
99+
source_map_root = "";
91100
}
92101

93102
let default_filename (lang : Lang.t) = "playground." ^ Lang.to_string lang
@@ -96,6 +105,21 @@ module Bundle_config = struct
96105
match m with
97106
| Ext_module_system.Commonjs -> "commonjs"
98107
| Esmodule -> "esmodule"
108+
109+
let source_map_of_string value =
110+
match String.lowercase_ascii value with
111+
| "linked" -> Some Js_config.Linked
112+
| "inline" -> Some Inline
113+
| "hidden" -> Some Hidden
114+
| "false" | "none" | "disabled" -> Some No_source_map
115+
| _ -> None
116+
117+
let string_of_source_map mode =
118+
match mode with
119+
| Js_config.Linked -> "linked"
120+
| Inline -> "inline"
121+
| Hidden -> "hidden"
122+
| No_source_map -> "false"
99123
end
100124

101125
type loc_err_info = {
@@ -473,6 +497,127 @@ module Compile = struct
473497
List.iter Iter.iter_structure_item structure.str_items;
474498
Js.array (!acc |> Array.of_list)
475499

500+
let gentype_output ~module_system ~modulename ~sourcefile structure env =
501+
let cmt_annots = Cmt_format.Implementation structure in
502+
let input_cmt =
503+
{
504+
Cmt_format.cmt_modname = modulename;
505+
cmt_annots;
506+
cmt_value_dependencies = [];
507+
cmt_comments = [];
508+
cmt_args = [||];
509+
cmt_sourcefile = Some sourcefile;
510+
cmt_builddir = Sys.getcwd ();
511+
cmt_loadpath = !Config.load_path;
512+
cmt_source_digest = None;
513+
cmt_initial_env = env;
514+
cmt_imports = [];
515+
cmt_interface_digest = None;
516+
cmt_use_summaries = false;
517+
cmt_extra_info = {Cmt_utils.deprecated_used = []};
518+
}
519+
in
520+
let has_gentype_annotations =
521+
Gentype_main.cmt_check_annotations input_cmt
522+
~check_annotation:(fun ~loc:_ attributes ->
523+
attributes
524+
|> Annotation.get_attribute_payload
525+
Annotation.tag_is_one_of_the_gentype_annotations
526+
<> None)
527+
in
528+
if has_gentype_annotations then
529+
let module_ =
530+
match module_system with
531+
| Ext_module_system.Commonjs -> Gentype_config.CommonJS
532+
| Esmodule -> ESModule
533+
in
534+
let project_root = Sys.getcwd () in
535+
let config =
536+
{
537+
Gentype_config.default with
538+
module_;
539+
platform_lib = "rescript";
540+
project_root;
541+
bsb_project_root = project_root;
542+
suffix = Literals.suffix_js;
543+
}
544+
in
545+
let source_file = sourcefile in
546+
let output_file_relative =
547+
source_file |> Paths.get_output_file_relative ~config
548+
in
549+
let file_name =
550+
sourcefile |> Filename.basename |> Filename.remove_extension
551+
|> Module_name.from_string_unsafe
552+
in
553+
let resolver =
554+
Module_resolver.create_lazy_resolver ~config
555+
~extensions:[".res"; ".shim.ts"] ~exclude_file:(fun _ -> false)
556+
in
557+
let code_text =
558+
input_cmt
559+
|> Gentype_main.translate_cmt ~config ~output_file_relative ~resolver
560+
|> Emit_js.emit_translation_as_string ~config ~file_name
561+
~output_file_relative ~resolver
562+
~input_cmt_translate_type_declarations:
563+
Gentype_main.input_cmt_translate_type_declarations
564+
in
565+
Emit_type.file_header ~source_file:(Filename.basename source_file)
566+
^ "\n" ^ code_text ^ "\n"
567+
else "No @gentype annotations found."
568+
569+
let render_javascript ~module_system ~filename ~source ~source_map_mode
570+
~source_map_sources_content ~source_map_root lambda_output =
571+
let buffer = Buffer.create 1000 in
572+
let generated_file =
573+
Filename.concat (Sys.getcwd ())
574+
(Filename.remove_extension (Filename.basename filename)
575+
^ Literals.suffix_js)
576+
in
577+
let source_map_builder =
578+
match source_map_mode with
579+
| Js_config.No_source_map -> None
580+
| Linked | Inline | Hidden ->
581+
Some
582+
(Js_source_map.make ~generated_file ~source_root:source_map_root
583+
~sources_content:source_map_sources_content)
584+
in
585+
let print_javascript () =
586+
Js_dump_program.pp_deps_program ~output_prefix:"" module_system
587+
lambda_output
588+
(Ext_pp.from_buffer buffer)
589+
in
590+
(match source_map_builder with
591+
| None -> print_javascript ()
592+
| Some builder ->
593+
let source_file =
594+
if Filename.is_relative filename then
595+
Filename.concat (Sys.getcwd ()) filename
596+
else filename
597+
in
598+
let source_dir = Filename.dirname source_file in
599+
Js_of_ocaml.Sys_js.mount ~path:source_dir (fun ~prefix:_ ~path ->
600+
if path = Filename.basename source_file then Some source else None);
601+
Ext_pervasives.finally ()
602+
~clean:(fun () -> Js_of_ocaml.Sys_js.unmount ~path:source_dir)
603+
(fun () -> Js_source_map.with_builder builder print_javascript));
604+
let source_map =
605+
match source_map_builder with
606+
| None -> None
607+
| Some builder ->
608+
let json = Js_source_map.json builder in
609+
(match source_map_mode with
610+
| Linked ->
611+
Buffer.add_string buffer
612+
(Js_source_map.linked_comment ~map_file:(generated_file ^ ".map"))
613+
| Inline ->
614+
Buffer.add_string buffer (Js_source_map.inline_comment ~json)
615+
| Hidden -> ()
616+
| No_source_map -> assert false);
617+
Some json
618+
in
619+
(Buffer.contents buffer, source_map)
620+
476621
let implementation ?(include_debug_outputs = false)
477622
~(config : Bundle_config.t) ~lang str =
478623
let {
@@ -481,6 +626,10 @@ module Compile = struct
481626
open_modules;
482627
experimental_features;
483628
jsx_preserve_mode;
629+
gentype_enabled;
630+
source_map_mode;
631+
source_map_sources_content;
632+
source_map_root;
484633
} =
485634
config
486635
in
@@ -502,6 +651,9 @@ module Compile = struct
502651
let types_signature = ref [] in
503652
Js_config.jsx_version := Some Js_config.Jsx_v4;
504653
Js_config.jsx_preserve := jsx_preserve_mode;
654+
Js_config.source_map := source_map_mode;
655+
Js_config.source_map_sources_content := source_map_sources_content;
656+
Js_config.source_map_root := source_map_root;
505657
experimental_features
506658
|> List.iter Experimental_features.enable_from_string;
507659
(* default *)
@@ -519,27 +671,34 @@ module Compile = struct
519671
let {Translmod.lambda; exports; hoisted_functions} =
520672
Translmod.transl_implementation modulename typed_tree
521673
in
522-
let buffer = Buffer.create 1000 in
523-
let () =
524-
Js_dump_program.pp_deps_program ~output_prefix:""
525-
(* does not matter here *) module_system
526-
(Lam_compile_main.compile "" exports hoisted_functions lambda)
527-
(Ext_pp.from_buffer buffer)
674+
let lambda_output =
675+
Lam_compile_main.compile "" exports hoisted_functions lambda
676+
in
677+
let js_code, source_map =
678+
render_javascript ~module_system ~filename ~source:str ~source_map_mode
679+
~source_map_sources_content ~source_map_root lambda_output
528680
in
529-
let v = Buffer.contents buffer in
530681
let type_hints = collect_type_hints typed_tree in
682+
let source_map_attrs =
683+
match source_map with
684+
| None -> [||]
685+
| Some source_map ->
686+
Js.Unsafe.[|("source_map", inject @@ Js.string source_map)|]
687+
in
531688
let attrs =
532-
Js.Unsafe.
533-
[|
534-
("js_code", inject @@ Js.string v);
535-
( "warnings",
536-
inject
537-
@@ (!warning_infos
538-
|> Array.map Error_ret.make_warning
539-
|> Js.array |> inject) );
540-
("type_hints", inject @@ type_hints);
541-
("type", inject @@ Js.string "success");
542-
|]
689+
Array.append
690+
Js.Unsafe.
691+
[|
692+
("js_code", inject @@ Js.string js_code);
693+
( "warnings",
694+
inject
695+
@@ (!warning_infos
696+
|> Array.map Error_ret.make_warning
697+
|> Js.array |> inject) );
698+
("type_hints", inject @@ type_hints);
699+
("type", inject @@ Js.string "success");
700+
|]
701+
source_map_attrs
543702
in
544703
if include_debug_outputs then
545704
let parsetree = Printer.to_string Printast.implementation ast in
@@ -548,6 +707,19 @@ module Compile = struct
548707
in
549708
let lambda_output = Printer.to_string Printlambda.lambda lambda in
550709
let lam = Printlambda.lambda_to_string lambda in
710+
let gentype_attrs =
711+
if gentype_enabled then
712+
let structure, _ = typed_tree in
713+
Js.Unsafe.
714+
[|
715+
( "gentype",
716+
inject
717+
@@ Js.string
718+
(gentype_output ~module_system ~modulename
719+
~sourcefile:filename structure env) );
720+
|]
721+
else [||]
722+
in
551723
let debug_attrs =
552724
Js.Unsafe.
553725
[|
@@ -557,7 +729,7 @@ module Compile = struct
557729
("lam", inject @@ Js.string lam);
558730
|]
559731
in
560-
Js.Unsafe.obj (Array.append attrs debug_attrs)
732+
Js.Unsafe.obj (Array.concat [attrs; debug_attrs; gentype_attrs])
561733
else Js.Unsafe.obj attrs
562734
with e -> (
563735
match e with
@@ -658,6 +830,25 @@ module Export = struct
658830
config.jsx_preserve_mode <- value;
659831
true
660832
in
833+
let set_gentype_enabled value =
834+
config.gentype_enabled <- value;
835+
true
836+
in
837+
let set_source_map_mode value =
838+
match Bundle_config.source_map_of_string value with
839+
| Some source_map_mode ->
840+
config.source_map_mode <- source_map_mode;
841+
true
842+
| None -> false
843+
in
844+
let set_source_map_sources_content value =
845+
config.source_map_sources_content <- value;
846+
true
847+
in
848+
let set_source_map_root value =
849+
config.source_map_root <- value;
850+
true
851+
in
661852
let convert_syntax ~(from_lang : string) ~(to_lang : string) (src : string)
662853
=
663854
let open Lang in
@@ -716,6 +907,22 @@ module Export = struct
716907
inject
717908
@@ Js.wrap_meth_callback (fun _ value ->
718909
Js.bool (set_jsx_preserve_mode (Js.to_bool value))) );
910+
( "setGentypeEnabled",
911+
inject
912+
@@ Js.wrap_meth_callback (fun _ value ->
913+
Js.bool (set_gentype_enabled (Js.to_bool value))) );
914+
( "setSourceMapMode",
915+
inject
916+
@@ Js.wrap_meth_callback (fun _ value ->
917+
Js.bool (set_source_map_mode (Js.to_string value))) );
918+
( "setSourceMapSourcesContent",
919+
inject
920+
@@ Js.wrap_meth_callback (fun _ value ->
921+
Js.bool (set_source_map_sources_content (Js.to_bool value))) );
922+
( "setSourceMapRoot",
923+
inject
924+
@@ Js.wrap_meth_callback (fun _ value ->
925+
Js.bool (set_source_map_root (Js.to_string value))) );
719926
( "getConfig",
720927
inject
721928
@@ Js.wrap_meth_callback (fun _ ->
@@ -730,6 +937,17 @@ module Export = struct
730937
("warn_flags", inject @@ Js.string config.warn_flags);
731938
( "jsx_preserve_mode",
732939
inject @@ (config.jsx_preserve_mode |> Js.bool) );
940+
( "gentype_enabled",
941+
inject @@ (config.gentype_enabled |> Js.bool) );
942+
( "source_map_mode",
943+
inject
944+
@@ (config.source_map_mode
945+
|> Bundle_config.string_of_source_map |> Js.string) );
946+
( "source_map_sources_content",
947+
inject @@ (config.source_map_sources_content |> Js.bool)
948+
);
949+
( "source_map_root",
950+
inject @@ Js.string config.source_map_root );
733951
( "experimental_features",
734952
inject
735953
@@ (config.experimental_features |> Array.of_list

packages/dev-playground/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
"prepare-pages-site": "node scripts/prepare-pages-site.mjs",
1010
"res:build": "rescript",
1111
"res:watch": "rescript -w",
12+
"test": "rescript && node --test scripts/source-map-navigation.test.mjs",
1213
"dev": "vite --host 127.0.0.1",
1314
"build": "rescript && vite build",
1415
"preview": "vite preview --host 127.0.0.1"
1516
},
1617
"dependencies": {
18+
"@jridgewell/trace-mapping": "^0.3.31",
1719
"@rescript/runtime": "12.3.0",
1820
"rescript": "12.3.0",
1921
"vite": "^8.0.14",

0 commit comments

Comments
 (0)