Skip to content

Commit bc82771

Browse files
authored
Automate playground compiler (#695)
* first tests * automate playground versions * fix typo * refactor * refactor * refactor * format * remove log * remove json binding * add revalidate route * parse rc * order experimental versions * last adjusts * cleanup * move Semver to CompilerManagerHook module
1 parent c0a48a8 commit bc82771

File tree

11 files changed

+374
-180
lines changed

11 files changed

+374
-180
lines changed

pages/api/revalidate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { handler as default } from "src/others/Revalidate.mjs";

pages/try.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import dynamic from "next/dynamic";
22

3-
const Try = dynamic(() => import("src/Try.mjs"), {
3+
export { getStaticProps } from "src/Try.mjs";
4+
import Try from "src/Try.mjs";
5+
6+
const Playground = dynamic(() => import("src/Playground.mjs"), {
47
ssr: false,
5-
//loading: () => <div> Loading... </div>
8+
loading: () => <span>Loading...</span>
69
});
710

8-
function Comp() {
9-
return <Try />;
11+
function Comp(props) {
12+
return (
13+
<Try>
14+
<Playground {...props} />
15+
</Try>
16+
);
1017
}
1118

1219
export default Comp;

src/Playground.res

Lines changed: 103 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -915,30 +915,88 @@ module Settings = {
915915
<div className=titleClass> {React.string("ReScript Version")} </div>
916916
<DropdownSelect
917917
name="compilerVersions"
918-
value=readyState.selected.id
918+
value={CompilerManagerHook.Semver.toString(readyState.selected.id)}
919919
onChange={evt => {
920920
ReactEvent.Form.preventDefault(evt)
921-
let id = (evt->ReactEvent.Form.target)["value"]
922-
onCompilerSelect(id)
921+
let id: string = (evt->ReactEvent.Form.target)["value"]
922+
switch id->CompilerManagerHook.Semver.parse {
923+
| Some(v) => onCompilerSelect(v)
924+
| None => ()
925+
}
923926
}}>
924-
{switch readyState.experimentalVersions {
925-
| [] => React.null
926-
| experimentalVersions =>
927+
{
928+
let (experimentalVersions, stableVersions) =
929+
readyState.versions->Js.Array2.reduce((acc, item) => {
930+
let (lhs, rhs) = acc
931+
if item.preRelease->Belt.Option.isSome {
932+
Js.Array2.push(lhs, item)
933+
} else {
934+
Js.Array2.push(rhs, item)
935+
}->ignore
936+
acc
937+
}, ([], []))
938+
927939
<>
928-
<option disabled=true className="py-4"> {React.string("---Experimental---")} </option>
929-
{Belt.Array.map(experimentalVersions, version =>
930-
<option className="py-4" key=version value=version>
931-
{React.string(version)}
932-
</option>
933-
)->React.array}
934-
<option disabled=true className="py-4">
935-
{React.string("---Official Releases---")}
936-
</option>
940+
{switch experimentalVersions {
941+
| [] => React.null
942+
| experimentalVersions =>
943+
let versionByOrder = experimentalVersions->Js.Array2.sortInPlaceWith((a, b) => {
944+
let cmp = ({
945+
CompilerManagerHook.Semver.major: major,
946+
minor,
947+
patch,
948+
preRelease,
949+
}) => {
950+
let preRelease = switch preRelease {
951+
| Some(preRelease) =>
952+
switch preRelease {
953+
| Dev(id) => 0 + id
954+
| Alpha(id) => 10 + id
955+
| Beta(id) => 20 + id
956+
| Rc(id) => 30 + id
957+
}
958+
| None => 0
959+
}
960+
let number =
961+
[major, minor, patch]
962+
->Js.Array2.map(v => v->Belt.Int.toString)
963+
->Js.Array2.joinWith("")
964+
->Belt.Int.fromString
965+
->Belt.Option.getWithDefault(0)
966+
967+
number + preRelease
968+
}
969+
cmp(b) - cmp(a)
970+
})
971+
<>
972+
<option disabled=true className="py-4">
973+
{React.string("---Experimental---")}
974+
</option>
975+
{versionByOrder
976+
->Belt.Array.map(version => {
977+
let version = CompilerManagerHook.Semver.toString(version)
978+
<option className="py-4" key=version value=version>
979+
{React.string(version)}
980+
</option>
981+
})
982+
->React.array}
983+
<option disabled=true className="py-4">
984+
{React.string("---Official Releases---")}
985+
</option>
986+
</>
987+
}}
988+
{switch stableVersions {
989+
| [] => React.null
990+
| stableVersions =>
991+
Belt.Array.map(stableVersions, version => {
992+
let version = CompilerManagerHook.Semver.toString(version)
993+
<option className="py-4" key=version value=version>
994+
{React.string(version)}
995+
</option>
996+
})->React.array
997+
}}
937998
</>
938-
}}
939-
{Belt.Array.map(readyState.versions, version =>
940-
<option className="py-4" key=version value=version> {React.string(version)} </option>
941-
)->React.array}
999+
}
9421000
</DropdownSelect>
9431001
</div>
9441002
<div className="mt-6">
@@ -1350,29 +1408,29 @@ module App = {
13501408

13511409
let initialReContent = j`Js.log("Hello Reason 3.6!");`
13521410

1353-
/**
1354-
Takes a `versionStr` starting with a "v" and ending in major.minor.patch (e.g.
1355-
"v10.1.0") returns major, minor, patch as an integer tuple if it's actually in
1356-
a x.y.z format, otherwise will return `None`.
1357-
*/
1358-
let parseVersion = (versionStr: string): option<(int, int, int)> => {
1359-
switch versionStr->Js.String2.replace("v", "")->Js.String2.split(".") {
1360-
| [major, minor, patch] =>
1361-
switch (major->Belt.Int.fromString, minor->Belt.Int.fromString, patch->Belt.Int.fromString) {
1362-
| (Some(major), Some(minor), Some(patch)) => Some((major, minor, patch))
1363-
| _ => None
1364-
}
1365-
| _ => None
1366-
}
1367-
}
1368-
1369-
@react.component
1370-
let make = () => {
1411+
let default = (~props: Try.props) => {
13711412
let router = Next.Router.useRouter()
13721413

1414+
let versions =
1415+
props.versions
1416+
->Belt.Array.keepMap(v => v->CompilerManagerHook.Semver.parse)
1417+
->Js.Array2.sortInPlaceWith((a, b) => {
1418+
let cmp = ({CompilerManagerHook.Semver.major: major, minor, patch, _}) => {
1419+
[major, minor, patch]
1420+
->Js.Array2.map(v => v->Belt.Int.toString)
1421+
->Js.Array2.joinWith("")
1422+
->Belt.Int.fromString
1423+
->Belt.Option.getWithDefault(0)
1424+
}
1425+
cmp(b) - cmp(a)
1426+
})
1427+
1428+
let lastStableVersion =
1429+
versions->Js.Array2.find(version => version.preRelease->Belt.Option.isNone)
1430+
13731431
let initialVersion = switch Js.Dict.get(router.query, "version") {
1374-
| Some(version) => Some(version)
1375-
| None => CompilerManagerHook.CdnMeta.versions->Belt.Array.get(0)
1432+
| Some(version) => version->CompilerManagerHook.Semver.parse
1433+
| None => lastStableVersion
13761434
}
13771435

13781436
let initialLang = switch Js.Dict.get(router.query, "ext") {
@@ -1386,15 +1444,11 @@ let make = () => {
13861444
| (None, Res)
13871445
| (None, _) =>
13881446
switch initialVersion {
1389-
| Some(initialVersion) =>
1390-
switch parseVersion(initialVersion) {
1391-
| Some((major, minor, _)) =>
1392-
if major >= 10 && minor >= 1 {
1393-
InitialContent.since_10_1
1394-
} else {
1395-
InitialContent.original
1396-
}
1397-
| None => InitialContent.original
1447+
| Some({CompilerManagerHook.Semver.major: major, minor, _}) =>
1448+
if major >= 10 && minor >= 1 {
1449+
InitialContent.since_10_1
1450+
} else {
1451+
InitialContent.original
13981452
}
13991453
| None => InitialContent.original
14001454
}
@@ -1408,6 +1462,7 @@ let make = () => {
14081462
~initialVersion?,
14091463
~initialLang,
14101464
~onAction,
1465+
~versions,
14111466
(),
14121467
)
14131468

src/Playground.resi

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
@react.component
2-
let make: unit => React.element
1+
let default: (~props: Try.props) => React.element

src/Try.res

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
@react.component
2-
let default = () => {
1+
let default = (props: {"children": React.element}) => {
32
let overlayState = React.useState(() => false)
43

4+
let playground = props["children"]
5+
56
<>
67
<Meta title="ReScript Playground" description="Try ReScript in the browser" />
78
<Next.Head>
@@ -10,8 +11,39 @@ let default = () => {
1011
<div className="text-16">
1112
<div className="text-gray-40 text-14">
1213
<Navigation fixed=false overlayState />
13-
<Playground />
14+
playground
1415
</div>
1516
</div>
1617
</>
1718
}
19+
20+
type props = {versions: array<string>}
21+
22+
let getStaticProps: Next.GetStaticProps.t<props, _> = async _ => {
23+
let versions = {
24+
let response = await Webapi.Fetch.fetch("https://cdn.rescript-lang.org/")
25+
let text = await Webapi.Fetch.Response.text(response)
26+
text
27+
->Js.String2.split("\n")
28+
->Belt.Array.keepMap(line => {
29+
switch line->Js.String2.startsWith("<a href") {
30+
| true =>
31+
// Adapted from https://semver.org/
32+
let semverRe = %re(
33+
"/v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/"
34+
)
35+
switch Js.Re.exec_(semverRe, line) {
36+
| Some(result) =>
37+
switch Js.Re.captures(result)->Belt.Array.get(0) {
38+
| Some(str) => Js.Nullable.toOption(str)
39+
| None => None
40+
}
41+
| None => None
42+
}
43+
| false => None
44+
}
45+
})
46+
}
47+
48+
{"props": {versions: versions}}
49+
}

src/Try.resi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
let default: {"children": React.element} => React.element
2+
type props = {versions: array<string>}
3+
let getStaticProps: Next.GetStaticProps.t<props, 'a>

src/bindings/Webapi.res

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,12 @@ module Window = {
3636
@scope("window") @val external innerWidth: int = "innerWidth"
3737
@scope("window") @val external innerHeight: int = "innerHeight"
3838
}
39+
40+
module Fetch = {
41+
module Response = {
42+
type t
43+
@send external text: t => promise<string> = "text"
44+
}
45+
46+
@val external fetch: string => promise<Response.t> = "fetch"
47+
}

0 commit comments

Comments
 (0)