Skip to content

Commit 140440b

Browse files
Playground toggles for jsx preserve and experimental features
1 parent 4e4fabe commit 140440b

File tree

5 files changed

+176
-27
lines changed

5 files changed

+176
-27
lines changed

src/Playground.res

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,38 @@ module Api = RescriptCompilerApi
1313

1414
type layout = Column | Row
1515
type tab = JavaScript | Output | Problems | Settings
16+
17+
module JsxCompilation = {
18+
type t =
19+
| Plain
20+
| PreserveJsx
21+
22+
let getLabel = (mode: t): string =>
23+
switch mode {
24+
| Plain => "Plain JS functions"
25+
| PreserveJsx => "Preserve JSX"
26+
}
27+
28+
let toBool = (mode: t): bool =>
29+
switch mode {
30+
| Plain => false
31+
| PreserveJsx => true
32+
}
33+
34+
let fromBool = (bool): t => bool ? PreserveJsx : Plain
35+
}
36+
37+
module ExperimentalFeatures = {
38+
type t = LetUnwrap
39+
40+
let getLabel = (feature: t): string =>
41+
switch feature {
42+
| LetUnwrap => "let?"
43+
}
44+
45+
let list = [LetUnwrap]
46+
}
47+
1648
let breakingPoint = 1024
1749

1850
module DropdownSelect = {
@@ -31,23 +63,23 @@ module DropdownSelect = {
3163
}
3264
}
3365

34-
module ToggleSelection = {
35-
module SelectionOption = {
36-
@react.component
37-
let make = (~label, ~isActive, ~disabled, ~onClick) => {
38-
<button
39-
className={"mr-1 px-2 py-1 rounded inline-block " ++ if isActive {
40-
"bg-fire text-white font-bold"
41-
} else {
42-
"bg-gray-80 opacity-50 hover:opacity-80"
43-
}}
44-
onClick
45-
disabled>
46-
{React.string(label)}
47-
</button>
48-
}
66+
module SelectionOption = {
67+
@react.component
68+
let make = (~label, ~isActive, ~disabled, ~onClick) => {
69+
<button
70+
className={"mr-1 px-2 py-1 rounded inline-block " ++ if isActive {
71+
"bg-fire text-white font-bold"
72+
} else {
73+
"bg-gray-80 opacity-50 hover:opacity-80"
74+
}}
75+
onClick
76+
disabled>
77+
{React.string(label)}
78+
</button>
4979
}
80+
}
5081

82+
module ToggleSelection = {
5183
@react.component
5284
let make = (
5385
~onChange: 'a => unit,
@@ -867,6 +899,25 @@ module Settings = {
867899
setConfig(config)
868900
}
869901

902+
let onJsxPreserveModeUpdate = compilation => {
903+
let jsx_preserve_mode = JsxCompilation.toBool(compilation)
904+
let config = {...config, jsx_preserve_mode}
905+
setConfig(config)
906+
}
907+
908+
let onExperimentalFeaturesUpdate = feature => {
909+
let features = config.experimental_features->Option.getOr([])
910+
911+
let experimental_features = if features->Array.includes(feature) {
912+
features->Array.filter(x => x !== feature)
913+
} else {
914+
[...features, feature]
915+
}
916+
917+
let config = {...config, experimental_features}
918+
setConfig(config)
919+
}
920+
870921
let warnFlagTokens = WarningFlagDescription.Parser.parse(warn_flags)->Result.getOr([])
871922

872923
let onWarningFlagsResetClick = _evt => {
@@ -1000,6 +1051,40 @@ module Settings = {
10001051
onChange=onModuleSystemUpdate
10011052
/>
10021053
</div>
1054+
{switch readyState.selected.apiVersion {
1055+
| V1 | V2 | V3 | V4 | V5 | UnknownVersion(_) => React.null
1056+
| V6 =>
1057+
<>
1058+
<div className="mt-6">
1059+
<div className=titleClass> {React.string("JSX")} </div>
1060+
<ToggleSelection
1061+
values=[JsxCompilation.Plain, PreserveJsx]
1062+
toLabel=JsxCompilation.getLabel
1063+
selected={config.jsx_preserve_mode->Option.getOr(false)->JsxCompilation.fromBool}
1064+
onChange=onJsxPreserveModeUpdate
1065+
/>
1066+
</div>
1067+
<div className="mt-6">
1068+
<div className=titleClass> {React.string("Experimental Features")} </div>
1069+
{ExperimentalFeatures.list
1070+
->Array.map(feature => {
1071+
let key = (feature :> string)
1072+
1073+
<SelectionOption
1074+
key
1075+
disabled=false
1076+
label={feature->ExperimentalFeatures.getLabel}
1077+
isActive={config.experimental_features
1078+
->Option.getOr([])
1079+
->Array.includes(key)}
1080+
onClick={_evt => onExperimentalFeaturesUpdate(key)}
1081+
/>
1082+
})
1083+
->React.array}
1084+
</div>
1085+
</>
1086+
}}
1087+
10031088
<div className="mt-6">
10041089
<div className=titleClass> {React.string("Loaded Libraries")} </div>
10051090
<ul>
@@ -1440,7 +1525,7 @@ let make = (~bundleBaseUrl: string, ~versions: array<string>) => {
14401525
| [v] => Some(v) // only single version available. maybe local dev.
14411526
| versions => {
14421527
let lastStableVersion = versions->Array.find(version => version.preRelease->Option.isNone)
1443-
switch Dict.get(router.query, "version") {
1528+
switch Dict.get(router.query, (CompilerManagerHook.Version :> string)) {
14441529
| Some(version) => version->Semver.parse
14451530
| None =>
14461531
switch Url.getVersionFromStorage(Playground) {
@@ -1451,14 +1536,20 @@ let make = (~bundleBaseUrl: string, ~versions: array<string>) => {
14511536
}
14521537
}
14531538

1454-
let initialLang = switch Dict.get(router.query, "ext") {
1539+
let initialLang = switch Dict.get(router.query, (CompilerManagerHook.Ext :> string)) {
14551540
| Some("re") => Api.Lang.Reason
14561541
| _ => Api.Lang.Res
14571542
}
14581543

1459-
let initialModuleSystem = Dict.get(router.query, "module")
1544+
let initialModuleSystem = Dict.get(router.query, (Module :> string))
1545+
let initialJsxPreserveMode = Dict.get(router.query, (JsxPreserve :> string))->Option.isSome
1546+
1547+
let initialExperimentalFeatures =
1548+
Dict.get(router.query, (Experiments :> string))->Option.mapOr([], str =>
1549+
str->String.split(",")->Array.map(String.trim)
1550+
)
14601551

1461-
let initialContent = switch (Dict.get(router.query, "code"), initialLang) {
1552+
let initialContent = switch (Dict.get(router.query, (Code :> string)), initialLang) {
14621553
| (Some(compressedCode), _) => LzString.decompressToEncodedURIComponent(compressedCode)
14631554
| (None, Reason) => initialReContent
14641555
| (None, Res) =>
@@ -1477,6 +1568,8 @@ let make = (~bundleBaseUrl: string, ~versions: array<string>) => {
14771568
~bundleBaseUrl,
14781569
~initialVersion?,
14791570
~initialModuleSystem?,
1571+
~initialJsxPreserveMode,
1572+
~initialExperimentalFeatures,
14801573
~initialLang,
14811574
~onAction,
14821575
~versions,

src/bindings/RescriptCompilerApi.res

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ module Version = {
3434
| V3
3535
| V4
3636
| V5
37+
| V6
3738
| UnknownVersion(string)
3839

3940
// Helps finding the right API version
@@ -57,6 +58,7 @@ module Version = {
5758
| list{"3"} => V3
5859
| list{"4"} => V4
5960
| list{"5"} => V5
61+
| list{"6"} => V6
6062
| _ => UnknownVersion(apiVersion)
6163
}
6264

@@ -67,6 +69,7 @@ module Version = {
6769
| V3 => "3.0"
6870
| V4 => "4.0"
6971
| V5 => "5.0"
72+
| V6 => "6.0"
7073
| UnknownVersion(version) => version
7174
}
7275

@@ -75,7 +78,7 @@ module Version = {
7578
let availableLanguages = t =>
7679
switch t {
7780
| V1 => [Lang.Reason, Res]
78-
| V2 | V3 | V4 | V5 => [Lang.Res]
81+
| V2 | V3 | V4 | V5 | V6 => [Lang.Res]
7982
| UnknownVersion(_) => [Res]
8083
}
8184
}
@@ -396,6 +399,8 @@ module Config = {
396399
warn_flags: string,
397400
uncurried?: bool,
398401
open_modules?: array<string>,
402+
experimental_features?: array<string>,
403+
jsx_preserve_mode?: bool,
399404
}
400405
}
401406

@@ -469,6 +474,10 @@ module Compiler = {
469474

470475
@send external setOpenModules: (t, array<string>) => bool = "setOpenModules"
471476

477+
@send external setExperimentalFeatures: (t, array<string>) => bool = "setExperimentalFeatures"
478+
479+
@send external setJsxPreserveMode: (t, bool) => bool = "setJsxPreserveMode"
480+
472481
let setConfig = (t: t, config: Config.t): unit => {
473482
let moduleSystem = switch config.module_system {
474483
| "commonjs" => #nodejs->Some
@@ -478,6 +487,10 @@ module Compiler = {
478487

479488
Option.forEach(moduleSystem, moduleSystem => t->setModuleSystem(moduleSystem)->ignore)
480489
Option.forEach(config.open_modules, modules => t->setOpenModules(modules)->ignore)
490+
Option.forEach(config.experimental_features, features =>
491+
t->setExperimentalFeatures(features)->ignore
492+
)
493+
Option.forEach(config.jsx_preserve_mode, toggle => t->setJsxPreserveMode(toggle)->ignore)
481494

482495
t->setWarnFlags(config.warn_flags)->ignore
483496
}

src/bindings/RescriptCompilerApi.resi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module Version: {
2525
| V3
2626
| V4
2727
| V5
28+
| V6
2829
| UnknownVersion(string)
2930

3031
// Helps finding the right API version
@@ -163,6 +164,9 @@ module Config: {
163164
/** Only available in apiVersion > 3 (= ReScript 11+) */
164165
uncurried?: bool,
165166
open_modules?: array<string>,
167+
/** Only available in apiVersion >= 6 (= ReScript 12+) */
168+
experimental_features?: array<string>,
169+
jsx_preserve_mode?: bool,
166170
}
167171
}
168172

src/common/CompilerManagerHook.res

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ let getLibrariesForVersion = (~version: Semver.t): array<string> => {
8484
let getOpenModules = (~apiVersion: Version.t, ~libraries: array<string>): option<array<string>> =>
8585
switch apiVersion {
8686
| V1 | V2 | V3 | UnknownVersion(_) => None
87-
| V4 | V5 => libraries->Array.some(el => el === "@rescript/core") ? Some(["RescriptCore"]) : None
87+
| V4 | V5 | V6 =>
88+
libraries->Array.some(el => el === "@rescript/core") ? Some(["RescriptCore"]) : None
8889
}
8990

9091
/*
@@ -191,15 +192,37 @@ type action =
191192
| ToggleAutoRun
192193
| RunCode
193194

195+
type queryParams =
196+
| @as("ext") Ext
197+
| @as("version") Version
198+
| @as("module") Module
199+
| @as("jsxPreserve") JsxPreserve
200+
| @as("experiments") Experiments
201+
| @as("code") Code
202+
194203
let createUrl = (pathName, ready) => {
195204
let params = switch ready.targetLang {
196205
| Res => []
197-
| lang => [("ext", RescriptCompilerApi.Lang.toExt(lang))]
206+
| lang => [(Ext, RescriptCompilerApi.Lang.toExt(lang))]
207+
}
208+
209+
Array.push(params, (Version, "v" ++ ready.selected.compilerVersion))
210+
Array.push(params, (Module, ready.selected.config.module_system))
211+
212+
if ready.selected.config.jsx_preserve_mode->Option.getOr(false) {
213+
Array.push(params, (JsxPreserve, "true"))
198214
}
199-
Array.push(params, ("version", "v" ++ ready.selected.compilerVersion))
200-
Array.push(params, ("module", ready.selected.config.module_system))
201-
Array.push(params, ("code", ready.code->LzString.compressToEncodedURIComponent))
202-
let querystring = params->Array.map(((key, value)) => key ++ "=" ++ value)->Array.join("&")
215+
216+
switch ready.selected.config.experimental_features {
217+
| Some([]) | None => ()
218+
| Some(features) => Array.push(params, (Experiments, features->Array.join(",")))
219+
}
220+
221+
// Put code last as it is the longest param.
222+
Array.push(params, (Code, ready.code->LzString.compressToEncodedURIComponent))
223+
224+
let querystring =
225+
params->Array.map(((key, value)) => (key :> string) ++ "=" ++ value)->Array.join("&")
203226
let url = pathName ++ "?" ++ querystring
204227
url
205228
}
@@ -221,6 +244,8 @@ let useCompilerManager = (
221244
~bundleBaseUrl: string,
222245
~initialVersion: option<Semver.t>=?,
223246
~initialModuleSystem=defaultModuleSystem,
247+
~initialJsxPreserveMode=false,
248+
~initialExperimentalFeatures=[],
224249
~initialLang: Lang.t=Res,
225250
~onAction: option<action => unit>=?,
226251
~versions: array<Semver.t>,
@@ -421,6 +446,8 @@ let useCompilerManager = (
421446
let config = {
422447
...instance->Compiler.getConfig,
423448
module_system: initialModuleSystem,
449+
experimental_features: initialExperimentalFeatures,
450+
jsx_preserve_mode: initialJsxPreserveMode,
424451
?open_modules,
425452
}
426453
instance->Compiler.setConfig(config)
@@ -529,7 +556,7 @@ let useCompilerManager = (
529556
)
530557
| Lang.Res => instance->Compiler.resCompile(code)
531558
}
532-
| V5 =>
559+
| V5 | V6 =>
533560
switch lang {
534561
| Lang.Res => instance->Compiler.resCompile(code)
535562
| _ => CompilationResult.UnexpectedError(`Can't handle with lang: ${lang->Lang.toString}`)
@@ -601,6 +628,8 @@ let useCompilerManager = (
601628
dispatchError,
602629
initialVersion,
603630
initialModuleSystem,
631+
initialJsxPreserveMode,
632+
initialExperimentalFeatures,
604633
initialLang,
605634
versions,
606635
router.route,

src/common/CompilerManagerHook.resi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ module FinalResult: {
88
| Nothing
99
}
1010

11+
type queryParams =
12+
| @as("ext") Ext
13+
| @as("version") Version
14+
| @as("module") Module
15+
| @as("jsxPreserve") JsxPreserve
16+
| @as("experiments") Experiments
17+
| @as("code") Code
18+
1119
type selected = {
1220
id: Semver.t, // The id used for loading the compiler bundle (ideally should be the same as compilerVersion)
1321
apiVersion: Version.t, // The playground API version in use
@@ -56,6 +64,8 @@ let useCompilerManager: (
5664
~bundleBaseUrl: string,
5765
~initialVersion: Semver.t=?,
5866
~initialModuleSystem: string=?,
67+
~initialJsxPreserveMode: bool=?,
68+
~initialExperimentalFeatures: array<string>=?,
5969
~initialLang: Lang.t=?,
6070
~onAction: action => unit=?,
6171
~versions: array<Semver.t>,

0 commit comments

Comments
 (0)