Merge branch 'dev' of gitlab.com:ligolang/ligo into feature/doc-pascaligo-loop
This commit is contained in:
commit
e6dc4371ee
@ -3,9 +3,11 @@ variables:
|
|||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
build_binary_script: "./scripts/distribution/generic/build.sh"
|
build_binary_script: "./scripts/distribution/generic/build.sh"
|
||||||
package_binary_script: "./scripts/distribution/generic/package.sh"
|
package_binary_script: "./scripts/distribution/generic/package.sh"
|
||||||
|
LIGO_REGISTRY_IMAGE_BASE_NAME: "${CI_PROJECT_PATH}/${CI_PROJECT_NAME}"
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
|
- ide
|
||||||
- build_and_package_binaries
|
- build_and_package_binaries
|
||||||
- build_docker
|
- build_docker
|
||||||
- build_and_deploy_docker
|
- build_and_deploy_docker
|
||||||
@ -75,9 +77,9 @@ dont-merge-to-master:
|
|||||||
- public
|
- public
|
||||||
|
|
||||||
.docker: &docker
|
.docker: &docker
|
||||||
image: docker:1.11
|
image: docker:19
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:19-dind
|
||||||
|
|
||||||
|
|
||||||
.before_script: &before_script
|
.before_script: &before_script
|
||||||
@ -130,7 +132,7 @@ build-and-publish-latest-docker-image:
|
|||||||
- sh scripts/build_docker_image.sh
|
- sh scripts/build_docker_image.sh
|
||||||
- sh scripts/test_cli.sh
|
- sh scripts/test_cli.sh
|
||||||
- docker login -u $LIGO_REGISTRY_USER -p $LIGO_REGISTRY_PASSWORD
|
- docker login -u $LIGO_REGISTRY_USER -p $LIGO_REGISTRY_PASSWORD
|
||||||
- docker push $LIGO_REGISTRY_IMAGE:next
|
- docker push ${LIGO_REGISTRY_IMAGE_BUILD:-ligolang/ligo}:next
|
||||||
only:
|
only:
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
@ -140,7 +142,7 @@ build-and-package-debian-9:
|
|||||||
<<: *docker
|
<<: *docker
|
||||||
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
||||||
stage: test
|
stage: test
|
||||||
variables:
|
variables:
|
||||||
target_os_family: "debian"
|
target_os_family: "debian"
|
||||||
target_os: "debian"
|
target_os: "debian"
|
||||||
target_os_version: "9"
|
target_os_version: "9"
|
||||||
@ -152,7 +154,7 @@ build-and-package-debian-10:
|
|||||||
<<: *docker
|
<<: *docker
|
||||||
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
||||||
stage: test
|
stage: test
|
||||||
variables:
|
variables:
|
||||||
target_os_family: "debian"
|
target_os_family: "debian"
|
||||||
target_os: "debian"
|
target_os: "debian"
|
||||||
target_os_version: "10"
|
target_os_version: "10"
|
||||||
@ -168,7 +170,7 @@ build-and-package-ubuntu-18-04:
|
|||||||
<<: *docker
|
<<: *docker
|
||||||
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
||||||
stage: test
|
stage: test
|
||||||
variables:
|
variables:
|
||||||
target_os_family: "debian"
|
target_os_family: "debian"
|
||||||
target_os: "ubuntu"
|
target_os: "ubuntu"
|
||||||
target_os_version: "18.04"
|
target_os_version: "18.04"
|
||||||
@ -180,7 +182,7 @@ build-and-package-ubuntu-19-04:
|
|||||||
<<: *docker
|
<<: *docker
|
||||||
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
||||||
stage: test
|
stage: test
|
||||||
variables:
|
variables:
|
||||||
target_os_family: "debian"
|
target_os_family: "debian"
|
||||||
target_os: "ubuntu"
|
target_os: "ubuntu"
|
||||||
target_os_version: "19.04"
|
target_os_version: "19.04"
|
||||||
@ -188,6 +190,13 @@ build-and-package-ubuntu-19-04:
|
|||||||
only:
|
only:
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
|
|
||||||
|
trigger-webide:
|
||||||
|
stage: ide
|
||||||
|
trigger:
|
||||||
|
include: tools/webide/webide-ci.yml
|
||||||
|
|
||||||
|
|
||||||
# Pages are deployed from dev, be careful not to override 'next'
|
# Pages are deployed from dev, be careful not to override 'next'
|
||||||
# in case something gets merged into 'dev' while releasing.
|
# in case something gets merged into 'dev' while releasing.
|
||||||
pages:
|
pages:
|
||||||
|
79
gitlab-pages/docs/reference/string.md
Normal file
79
gitlab-pages/docs/reference/string.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
id: string-reference
|
||||||
|
title: String
|
||||||
|
---
|
||||||
|
|
||||||
|
## String.size(s: string) : nat
|
||||||
|
|
||||||
|
Get the size of a string. [Michelson only supports ASCII strings](http://tezos.gitlab.io/whitedoc/michelson.html#constants)
|
||||||
|
so for now you can assume that each character takes one byte of storage.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function string_size (const s: string) : nat is size(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let size_op (s: string) : nat = String.size s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let size_op = (s: string): nat => String.size(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## String.length(s: string) : nat
|
||||||
|
|
||||||
|
Alias for `String.size`.
|
||||||
|
|
||||||
|
## String.slice(pos1: nat, pos2: nat, s: string) : string
|
||||||
|
|
||||||
|
Get the substring of `s` between `pos1` inclusive and `pos2` inclusive. For example
|
||||||
|
the string "tata" given to the function below would return "at".
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function slice_op (const s : string) : string is string_slice(1n , 2n , s)
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let slice_op (s: string) : string = String.slice 1n 2n s
|
||||||
|
```
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let slice_op = (s: string): string => String.slice(1n, 2n, s);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## String.sub(pos1: nat, pos2: nat, s: string) : string
|
||||||
|
|
||||||
|
Alias for `String.slice`.
|
||||||
|
|
||||||
|
## String.concat(s1: string, s2: string) : string
|
||||||
|
|
||||||
|
Concatenate two strings and return the result.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function concat_op (const s : string) : string is s ^ "toto"
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let concat_syntax (s: string) = s ^ "test_literal"
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let concat_syntax = (s: string) => s ++ "test_literal";
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
@ -7,13 +7,13 @@ let bad_contract basename =
|
|||||||
|
|
||||||
let%expect_test _ =
|
let%expect_test _ =
|
||||||
run_ligo_good [ "measure-contract" ; contract "coase.ligo" ; "main" ] ;
|
run_ligo_good [ "measure-contract" ; contract "coase.ligo" ; "main" ] ;
|
||||||
[%expect {| 2066 bytes |}] ;
|
[%expect {| 2062 bytes |}] ;
|
||||||
|
|
||||||
run_ligo_good [ "measure-contract" ; contract "multisig.ligo" ; "main" ] ;
|
run_ligo_good [ "measure-contract" ; contract "multisig.ligo" ; "main" ] ;
|
||||||
[%expect {| 1093 bytes |}] ;
|
[%expect {| 1093 bytes |}] ;
|
||||||
|
|
||||||
run_ligo_good [ "measure-contract" ; contract "multisig-v2.ligo" ; "main" ] ;
|
run_ligo_good [ "measure-contract" ; contract "multisig-v2.ligo" ; "main" ] ;
|
||||||
[%expect {| 2717 bytes |}] ;
|
[%expect {| 2713 bytes |}] ;
|
||||||
|
|
||||||
run_ligo_good [ "measure-contract" ; contract "vote.mligo" ; "main" ] ;
|
run_ligo_good [ "measure-contract" ; contract "vote.mligo" ; "main" ] ;
|
||||||
[%expect {| 642 bytes |}] ;
|
[%expect {| 642 bytes |}] ;
|
||||||
@ -86,7 +86,7 @@ let%expect_test _ =
|
|||||||
SWAP ;
|
SWAP ;
|
||||||
DIP { DUP ; CAR ; CAR } ;
|
DIP { DUP ; CAR ; CAR } ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
DUP ;
|
DUP ;
|
||||||
CAR ;
|
CAR ;
|
||||||
DIP { DUP ; CDR ; PUSH nat 1 ; ADD } ;
|
DIP { DUP ; CDR ; PUSH nat 1 ; ADD } ;
|
||||||
@ -168,7 +168,7 @@ let%expect_test _ =
|
|||||||
SWAP ;
|
SWAP ;
|
||||||
DIP { DUP ; CAR ; CDR } ;
|
DIP { DUP ; CAR ; CDR } ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
DUP ;
|
DUP ;
|
||||||
CAR ;
|
CAR ;
|
||||||
SOURCE ;
|
SOURCE ;
|
||||||
@ -182,7 +182,7 @@ let%expect_test _ =
|
|||||||
CDR ;
|
CDR ;
|
||||||
DIP { DIP { DUP } ; SWAP ; CAR ; CAR } ;
|
DIP { DIP { DUP } ; SWAP ; CAR ; CAR } ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
DUP ;
|
DUP ;
|
||||||
CDR ;
|
CDR ;
|
||||||
PUSH nat 1 ;
|
PUSH nat 1 ;
|
||||||
@ -262,7 +262,7 @@ let%expect_test _ =
|
|||||||
CAR ;
|
CAR ;
|
||||||
DIP { DUP } ;
|
DIP { DUP } ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
DUP ;
|
DUP ;
|
||||||
CAR ;
|
CAR ;
|
||||||
SOURCE ;
|
SOURCE ;
|
||||||
@ -488,7 +488,7 @@ let%expect_test _ =
|
|||||||
CAR ;
|
CAR ;
|
||||||
SENDER ;
|
SENDER ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
PUSH nat 1 ;
|
PUSH nat 1 ;
|
||||||
ADD ;
|
ADD ;
|
||||||
SOME ;
|
SOME ;
|
||||||
@ -529,7 +529,7 @@ let%expect_test _ =
|
|||||||
CAR ;
|
CAR ;
|
||||||
SENDER ;
|
SENDER ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
PUSH nat 1 ;
|
PUSH nat 1 ;
|
||||||
ADD ;
|
ADD ;
|
||||||
SOME ;
|
SOME ;
|
||||||
@ -572,7 +572,7 @@ let%expect_test _ =
|
|||||||
CAR ;
|
CAR ;
|
||||||
SENDER ;
|
SENDER ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
DUP ;
|
DUP ;
|
||||||
DIP { DIP 4 { DUP } ; DIG 4 ; CAR ; CDR ; CAR } ;
|
DIP { DIP 4 { DUP } ; DIG 4 ; CAR ; CDR ; CAR } ;
|
||||||
COMPARE ;
|
COMPARE ;
|
||||||
@ -768,7 +768,7 @@ let%expect_test _ =
|
|||||||
CAR ;
|
CAR ;
|
||||||
SENDER ;
|
SENDER ;
|
||||||
GET ;
|
GET ;
|
||||||
IF_NONE { PUSH string "GET_FORCE" ; FAILWITH } {} ;
|
IF_NONE { PUSH string "MAP FIND" ; FAILWITH } {} ;
|
||||||
PUSH nat 1 ;
|
PUSH nat 1 ;
|
||||||
SWAP ;
|
SWAP ;
|
||||||
SUB ;
|
SUB ;
|
||||||
|
36
src/bin/expect_tests/failwith_tests.ml
Normal file
36
src/bin/expect_tests/failwith_tests.ml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
open Cli_expect
|
||||||
|
|
||||||
|
let contract basename =
|
||||||
|
"../../test/contracts/" ^ basename
|
||||||
|
let bad_contract basename =
|
||||||
|
"../../test/contracts/negative/" ^ basename
|
||||||
|
|
||||||
|
let%expect_test _ =
|
||||||
|
run_ligo_good [ "run-function" ; contract "failwith.ligo" ; "failer" ; "1" ] ;
|
||||||
|
[%expect {|
|
||||||
|
failwith("some_string") |}];
|
||||||
|
|
||||||
|
run_ligo_good [ "run-function" ; contract "failwith.ligo" ; "failer" ; "1" ; "--format=json" ] ;
|
||||||
|
[%expect {|
|
||||||
|
{"status":"ok","content":"failwith(\"some_string\")"} |}];
|
||||||
|
|
||||||
|
|
||||||
|
run_ligo_good [ "dry-run" ; contract "subtle_nontail_fail.mligo" ; "main" ; "()" ; "()" ] ;
|
||||||
|
[%expect {|
|
||||||
|
failwith("This contract always fails") |}];
|
||||||
|
|
||||||
|
run_ligo_good [ "interpret" ; "assert(1=1)" ; "--syntax=pascaligo" ] ;
|
||||||
|
[%expect {|
|
||||||
|
Unit |}];
|
||||||
|
|
||||||
|
run_ligo_good [ "interpret" ; "assert(1=2)" ; "--syntax=pascaligo" ] ;
|
||||||
|
[%expect {|
|
||||||
|
failwith("failed assertion") |}];
|
||||||
|
|
||||||
|
run_ligo_good [ "interpret" ; "assert(1=1)" ; "--syntax=cameligo" ] ;
|
||||||
|
[%expect {|
|
||||||
|
Unit |}];
|
||||||
|
|
||||||
|
run_ligo_good [ "interpret" ; "assert(1=2)" ; "--syntax=cameligo" ] ;
|
||||||
|
[%expect {|
|
||||||
|
failwith("failed assertion") |}];
|
@ -346,6 +346,16 @@ module Wrap = struct
|
|||||||
P_variable unification_body]))
|
P_variable unification_body]))
|
||||||
] @ arg' @ body' , whole_expr
|
] @ arg' @ body' , whole_expr
|
||||||
|
|
||||||
|
(* This is pretty much a wrapper for an n-ary function. *)
|
||||||
|
let constant : O.type_value -> T.type_value list -> (constraints * T.type_variable) =
|
||||||
|
fun f args ->
|
||||||
|
let whole_expr = Core.fresh_type_variable () in
|
||||||
|
let args' = List.map type_expression_to_type_value args in
|
||||||
|
let args_tuple = O.P_constant (C_tuple , args') in
|
||||||
|
O.[
|
||||||
|
C_equation (f , P_constant (C_arrow , [args_tuple ; P_variable whole_expr]))
|
||||||
|
] , whole_expr
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
(* begin unionfind *)
|
(* begin unionfind *)
|
||||||
@ -727,47 +737,7 @@ let selector_break_ctor : (type_constraint_simpl, output_break_ctor) selector =
|
|||||||
| SC_Poly _ -> WasNotSelected (* TODO: ??? (beware: symmetry) *)
|
| SC_Poly _ -> WasNotSelected (* TODO: ??? (beware: symmetry) *)
|
||||||
| SC_Typeclass _ -> WasNotSelected
|
| SC_Typeclass _ -> WasNotSelected
|
||||||
|
|
||||||
let propagator_break_ctor : output_break_ctor propagator =
|
(* TODO: move this to a more appropriate place and/or auto-generate it. *)
|
||||||
fun selected dbs ->
|
|
||||||
let () = ignore (dbs) in (* this propagator doesn't need to use the dbs *)
|
|
||||||
let a = selected.a_k_var in
|
|
||||||
let b = selected.a_k'_var' in
|
|
||||||
(* produce constraints: *)
|
|
||||||
|
|
||||||
(* a.tv = b.tv *)
|
|
||||||
let eq1 = C_equation (P_variable a.tv, P_variable b.tv) in
|
|
||||||
(* a.c_tag = b.c_tag *)
|
|
||||||
if a.c_tag <> b.c_tag then
|
|
||||||
failwith "type error: incompatible types, not same ctor"
|
|
||||||
else
|
|
||||||
(* a.tv_list = b.tv_list *)
|
|
||||||
if List.length a.tv_list <> List.length b.tv_list then
|
|
||||||
failwith "type error: incompatible types, not same length"
|
|
||||||
else
|
|
||||||
let eqs3 = List.map2 (fun aa bb -> C_equation (P_variable aa, P_variable bb)) a.tv_list b.tv_list in
|
|
||||||
let eqs = eq1 :: eqs3 in
|
|
||||||
(eqs , []) (* no new assignments *)
|
|
||||||
|
|
||||||
(* TODO : with our selectors, the selection depends on the order in which the constraints are added :-( :-( :-( :-(
|
|
||||||
We need to return a lazy stream of constraints. *)
|
|
||||||
|
|
||||||
type output_specialize1 = { poly : c_poly_simpl ; a_k_var : c_constructor_simpl }
|
|
||||||
|
|
||||||
|
|
||||||
let (<?) ca cb =
|
|
||||||
if ca = 0 then cb () else ca
|
|
||||||
let rec compare_list f = function
|
|
||||||
| hd1::tl1 -> (function
|
|
||||||
[] -> 1
|
|
||||||
| hd2::tl2 ->
|
|
||||||
f hd1 hd2 <? fun () ->
|
|
||||||
compare_list f tl1 tl2)
|
|
||||||
| [] -> (function [] -> 0 | _::_ -> -1) (* This follows the behaviour of Pervasives.compare for lists of different length *)
|
|
||||||
let compare_type_variable a b =
|
|
||||||
Var.compare a b
|
|
||||||
let compare_label = function
|
|
||||||
| L_int a -> (function L_int b -> Int.compare a b | L_string _ -> -1)
|
|
||||||
| L_string a -> (function L_int _ -> 1 | L_string b -> String.compare a b)
|
|
||||||
let compare_simple_c_constant = function
|
let compare_simple_c_constant = function
|
||||||
| C_arrow -> (function
|
| C_arrow -> (function
|
||||||
(* N/A -> 1 *)
|
(* N/A -> 1 *)
|
||||||
@ -866,6 +836,83 @@ let compare_simple_c_constant = function
|
|||||||
| C_chain_id -> 0
|
| C_chain_id -> 0
|
||||||
(* N/A -> -1 *)
|
(* N/A -> -1 *)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(* Using a pretty-printer from the PP.ml module creates a dependency
|
||||||
|
loop, so the one that we need temporarily for debugging purposes
|
||||||
|
has been copied here. *)
|
||||||
|
let debug_pp_constant : _ -> constant_tag -> unit = fun ppf c_tag ->
|
||||||
|
let ct = match c_tag with
|
||||||
|
| Core.C_arrow -> "arrow"
|
||||||
|
| Core.C_option -> "option"
|
||||||
|
| Core.C_tuple -> "tuple"
|
||||||
|
| Core.C_record -> failwith "record"
|
||||||
|
| Core.C_variant -> failwith "variant"
|
||||||
|
| Core.C_map -> "map"
|
||||||
|
| Core.C_big_map -> "big_map"
|
||||||
|
| Core.C_list -> "list"
|
||||||
|
| Core.C_set -> "set"
|
||||||
|
| Core.C_unit -> "unit"
|
||||||
|
| Core.C_bool -> "bool"
|
||||||
|
| Core.C_string -> "string"
|
||||||
|
| Core.C_nat -> "nat"
|
||||||
|
| Core.C_mutez -> "mutez"
|
||||||
|
| Core.C_timestamp -> "timestamp"
|
||||||
|
| Core.C_int -> "int"
|
||||||
|
| Core.C_address -> "address"
|
||||||
|
| Core.C_bytes -> "bytes"
|
||||||
|
| Core.C_key_hash -> "key_hash"
|
||||||
|
| Core.C_key -> "key"
|
||||||
|
| Core.C_signature -> "signature"
|
||||||
|
| Core.C_operation -> "operation"
|
||||||
|
| Core.C_contract -> "contract"
|
||||||
|
| Core.C_chain_id -> "chain_id"
|
||||||
|
in
|
||||||
|
Format.fprintf ppf "%s" ct
|
||||||
|
|
||||||
|
let debug_pp_c_constructor_simpl ppf { tv; c_tag; tv_list } =
|
||||||
|
Format.fprintf ppf "CTOR %a %a(%a)" Var.pp tv debug_pp_constant c_tag PP_helpers.(list_sep Var.pp (const " , ")) tv_list
|
||||||
|
|
||||||
|
let propagator_break_ctor : output_break_ctor propagator =
|
||||||
|
fun selected dbs ->
|
||||||
|
let () = ignore (dbs) in (* this propagator doesn't need to use the dbs *)
|
||||||
|
let a = selected.a_k_var in
|
||||||
|
let b = selected.a_k'_var' in
|
||||||
|
(* produce constraints: *)
|
||||||
|
|
||||||
|
(* a.tv = b.tv *)
|
||||||
|
let eq1 = C_equation (P_variable a.tv, P_variable b.tv) in
|
||||||
|
(* a.c_tag = b.c_tag *)
|
||||||
|
if (compare_simple_c_constant a.c_tag b.c_tag) <> 0 then
|
||||||
|
failwith (Format.asprintf "type error: incompatible types, not same ctor %a vs. %a (compare returns %d)" debug_pp_c_constructor_simpl a debug_pp_c_constructor_simpl b (compare_simple_c_constant a.c_tag b.c_tag))
|
||||||
|
else
|
||||||
|
(* a.tv_list = b.tv_list *)
|
||||||
|
if List.length a.tv_list <> List.length b.tv_list then
|
||||||
|
failwith "type error: incompatible types, not same length"
|
||||||
|
else
|
||||||
|
let eqs3 = List.map2 (fun aa bb -> C_equation (P_variable aa, P_variable bb)) a.tv_list b.tv_list in
|
||||||
|
let eqs = eq1 :: eqs3 in
|
||||||
|
(eqs , []) (* no new assignments *)
|
||||||
|
|
||||||
|
(* TODO : with our selectors, the selection depends on the order in which the constraints are added :-( :-( :-( :-(
|
||||||
|
We need to return a lazy stream of constraints. *)
|
||||||
|
|
||||||
|
type output_specialize1 = { poly : c_poly_simpl ; a_k_var : c_constructor_simpl }
|
||||||
|
|
||||||
|
|
||||||
|
let (<?) ca cb =
|
||||||
|
if ca = 0 then cb () else ca
|
||||||
|
let rec compare_list f = function
|
||||||
|
| hd1::tl1 -> (function
|
||||||
|
[] -> 1
|
||||||
|
| hd2::tl2 ->
|
||||||
|
f hd1 hd2 <? fun () ->
|
||||||
|
compare_list f tl1 tl2)
|
||||||
|
| [] -> (function [] -> 0 | _::_ -> -1) (* This follows the behaviour of Pervasives.compare for lists of different length *)
|
||||||
|
let compare_type_variable a b =
|
||||||
|
Var.compare a b
|
||||||
|
let compare_label = function
|
||||||
|
| L_int a -> (function L_int b -> Int.compare a b | L_string _ -> -1)
|
||||||
|
| L_string a -> (function L_int _ -> 1 | L_string b -> String.compare a b)
|
||||||
let rec compare_typeclass a b = compare_list (compare_list compare_type_value) a b
|
let rec compare_typeclass a b = compare_list (compare_list compare_type_value) a b
|
||||||
and compare_type_value = function
|
and compare_type_value = function
|
||||||
| P_forall { binder=a1; constraints=a2; body=a3 } -> (function
|
| P_forall { binder=a1; constraints=a2; body=a3 } -> (function
|
||||||
|
@ -889,6 +889,7 @@ and type_expression : environment -> Solver.state -> ?tv_opt:O.type_value -> I.e
|
|||||||
let e' = Environment.add_ez_binder (fst binder) fresh e in
|
let e' = Environment.add_ez_binder (fst binder) fresh e in
|
||||||
|
|
||||||
let%bind (result , state') = type_expression e' state result in
|
let%bind (result , state') = type_expression e' state result in
|
||||||
|
let () = Printf.printf "this does not make use of the typed body, this code sounds buggy." in
|
||||||
let wrapped = Wrap.lambda fresh input_type' output_type' in
|
let wrapped = Wrap.lambda fresh input_type' output_type' in
|
||||||
return_wrapped
|
return_wrapped
|
||||||
(E_lambda {binder = fst binder; body=result}) (* TODO: is the type of the entire lambda enough to access the input_type=fresh; ? *)
|
(E_lambda {binder = fst binder; body=result}) (* TODO: is the type of the entire lambda enough to access the input_type=fresh; ? *)
|
||||||
@ -897,8 +898,17 @@ and type_expression : environment -> Solver.state -> ?tv_opt:O.type_value -> I.e
|
|||||||
|
|
||||||
| E_constant (name, lst) ->
|
| E_constant (name, lst) ->
|
||||||
let () = ignore (name , lst) in
|
let () = ignore (name , lst) in
|
||||||
let _t = Operators.Typer.Operators_types.constant_type name in
|
let%bind t = Operators.Typer.Operators_types.constant_type name in
|
||||||
Pervasives.failwith (Format.asprintf "TODO: E_constant (%a(%a))" Stage_common.PP.constant name (Format.pp_print_list Ast_simplified.PP.expression) lst)
|
let aux acc expr =
|
||||||
|
let (lst , state) = acc in
|
||||||
|
let%bind (expr, state') = type_expression e state expr in
|
||||||
|
ok (expr::lst , state') in
|
||||||
|
let%bind (lst , state') = bind_fold_list aux ([], state) lst in
|
||||||
|
let lst_annot = List.map (fun (x : O.value) -> x.type_annotation) lst in
|
||||||
|
let wrapped = Wrap.constant t lst_annot in
|
||||||
|
return_wrapped
|
||||||
|
(E_constant (name, lst))
|
||||||
|
state' wrapped
|
||||||
(*
|
(*
|
||||||
let%bind lst' = bind_list @@ List.map (type_expression e) lst in
|
let%bind lst' = bind_list @@ List.map (type_expression e) lst in
|
||||||
let tv_lst = List.map get_type_annotation lst' in
|
let tv_lst = List.map get_type_annotation lst' in
|
||||||
|
@ -490,7 +490,7 @@ and transpile_annotated_expression (ae:AST.annotated_expression) : expression re
|
|||||||
)
|
)
|
||||||
| E_look_up dsi -> (
|
| E_look_up dsi -> (
|
||||||
let%bind (ds', i') = bind_map_pair f dsi in
|
let%bind (ds', i') = bind_map_pair f dsi in
|
||||||
return @@ E_constant (C_MAP_GET, [i' ; ds'])
|
return @@ E_constant (C_MAP_FIND_OPT, [i' ; ds'])
|
||||||
)
|
)
|
||||||
| E_sequence (a , b) -> (
|
| E_sequence (a , b) -> (
|
||||||
let%bind a' = transpile_annotated_expression a in
|
let%bind a' = transpile_annotated_expression a in
|
||||||
|
@ -23,7 +23,7 @@ let is_pure_constant : constant -> bool =
|
|||||||
| C_NEG | C_OR | C_AND | C_XOR | C_NOT
|
| C_NEG | C_OR | C_AND | C_XOR | C_NOT
|
||||||
| C_EQ | C_NEQ | C_LT | C_LE | C_GT | C_GE
|
| C_EQ | C_NEQ | C_LT | C_LE | C_GT | C_GE
|
||||||
| C_SOME
|
| C_SOME
|
||||||
| C_UPDATE | C_MAP_GET | C_MAP_FIND_OPT | C_MAP_ADD | C_MAP_UPDATE
|
| C_UPDATE | C_MAP_FIND_OPT | C_MAP_ADD | C_MAP_UPDATE
|
||||||
| C_INT | C_ABS | C_IS_NAT
|
| C_INT | C_ABS | C_IS_NAT
|
||||||
| C_BALANCE | C_AMOUNT | C_ADDRESS | C_NOW | C_SOURCE | C_SENDER | C_CHAIN_ID
|
| C_BALANCE | C_AMOUNT | C_ADDRESS | C_NOW | C_SOURCE | C_SENDER | C_CHAIN_ID
|
||||||
| C_SET_MEM | C_SET_ADD | C_SET_REMOVE | C_SLICE
|
| C_SET_MEM | C_SET_ADD | C_SET_REMOVE | C_SLICE
|
||||||
@ -31,10 +31,10 @@ let is_pure_constant : constant -> bool =
|
|||||||
| C_HASH_KEY | C_BYTES_PACK | C_CONCAT
|
| C_HASH_KEY | C_BYTES_PACK | C_CONCAT
|
||||||
-> true
|
-> true
|
||||||
(* unfortunately impure: *)
|
(* unfortunately impure: *)
|
||||||
| C_ADD | C_SUB |C_MUL|C_DIV|C_MOD
|
| C_ADD | C_SUB |C_MUL|C_DIV|C_MOD | C_LSL | C_LSR
|
||||||
(* impure: *)
|
(* impure: *)
|
||||||
| C_ASSERTION | C_ASSERT_INFERRED
|
| C_ASSERTION | C_ASSERT_INFERRED
|
||||||
| C_MAP_GET_FORCE | C_MAP_FIND
|
| C_MAP_FIND
|
||||||
| C_FOLD_WHILE
|
| C_FOLD_WHILE
|
||||||
| C_CALL
|
| C_CALL
|
||||||
(* TODO... *)
|
(* TODO... *)
|
||||||
|
@ -66,7 +66,7 @@ module Simplify = struct
|
|||||||
module Pascaligo = struct
|
module Pascaligo = struct
|
||||||
|
|
||||||
let constants = function
|
let constants = function
|
||||||
| "get_force" -> ok C_MAP_GET_FORCE
|
| "assert" -> ok C_ASSERTION
|
||||||
| "get_chain_id" -> ok C_CHAIN_ID
|
| "get_chain_id" -> ok C_CHAIN_ID
|
||||||
| "transaction" -> ok C_CALL
|
| "transaction" -> ok C_CALL
|
||||||
| "get_contract" -> ok C_CONTRACT
|
| "get_contract" -> ok C_CONTRACT
|
||||||
@ -87,6 +87,8 @@ module Simplify = struct
|
|||||||
| "bitwise_or" -> ok C_OR
|
| "bitwise_or" -> ok C_OR
|
||||||
| "bitwise_and" -> ok C_AND
|
| "bitwise_and" -> ok C_AND
|
||||||
| "bitwise_xor" -> ok C_XOR
|
| "bitwise_xor" -> ok C_XOR
|
||||||
|
| "bitwise_lsl" -> ok C_LSL
|
||||||
|
| "bitwise_lsr" -> ok C_LSR
|
||||||
| "string_concat" -> ok C_CONCAT
|
| "string_concat" -> ok C_CONCAT
|
||||||
| "string_slice" -> ok C_SLICE
|
| "string_slice" -> ok C_SLICE
|
||||||
| "crypto_check" -> ok C_CHECK_SIGNATURE
|
| "crypto_check" -> ok C_CHECK_SIGNATURE
|
||||||
@ -104,12 +106,13 @@ module Simplify = struct
|
|||||||
| "list_iter" -> ok C_LIST_ITER
|
| "list_iter" -> ok C_LIST_ITER
|
||||||
| "list_fold" -> ok C_LIST_FOLD
|
| "list_fold" -> ok C_LIST_FOLD
|
||||||
| "list_map" -> ok C_LIST_MAP
|
| "list_map" -> ok C_LIST_MAP
|
||||||
|
| "get_force" -> ok C_MAP_FIND
|
||||||
| "map_iter" -> ok C_MAP_ITER
|
| "map_iter" -> ok C_MAP_ITER
|
||||||
| "map_map" -> ok C_MAP_MAP
|
| "map_map" -> ok C_MAP_MAP
|
||||||
| "map_fold" -> ok C_MAP_FOLD
|
| "map_fold" -> ok C_MAP_FOLD
|
||||||
| "map_remove" -> ok C_MAP_REMOVE
|
| "map_remove" -> ok C_MAP_REMOVE
|
||||||
| "map_update" -> ok C_MAP_UPDATE
|
| "map_update" -> ok C_MAP_UPDATE
|
||||||
| "map_get" -> ok C_MAP_GET
|
| "map_get" -> ok C_MAP_FIND_OPT
|
||||||
| "map_mem" -> ok C_MAP_MEM
|
| "map_mem" -> ok C_MAP_MEM
|
||||||
| "sha_256" -> ok C_SHA256
|
| "sha_256" -> ok C_SHA256
|
||||||
| "sha_512" -> ok C_SHA512
|
| "sha_512" -> ok C_SHA512
|
||||||
@ -163,7 +166,6 @@ module Simplify = struct
|
|||||||
| "Current.failwith" -> ok C_FAILWITH
|
| "Current.failwith" -> ok C_FAILWITH
|
||||||
| "failwith" -> ok C_FAILWITH
|
| "failwith" -> ok C_FAILWITH
|
||||||
|
|
||||||
| "Crypto.hash" -> ok C_HASH
|
|
||||||
| "Crypto.blake2b" -> ok C_BLAKE2b
|
| "Crypto.blake2b" -> ok C_BLAKE2b
|
||||||
| "Crypto.sha256" -> ok C_SHA256
|
| "Crypto.sha256" -> ok C_SHA256
|
||||||
| "Crypto.sha512" -> ok C_SHA512
|
| "Crypto.sha512" -> ok C_SHA512
|
||||||
@ -210,6 +212,8 @@ module Simplify = struct
|
|||||||
| "Bitwise.lor" -> ok C_OR
|
| "Bitwise.lor" -> ok C_OR
|
||||||
| "Bitwise.land" -> ok C_AND
|
| "Bitwise.land" -> ok C_AND
|
||||||
| "Bitwise.lxor" -> ok C_XOR
|
| "Bitwise.lxor" -> ok C_XOR
|
||||||
|
| "Bitwise.shift_left" -> ok C_LSL
|
||||||
|
| "Bitwise.shift_right" -> ok C_LSR
|
||||||
|
|
||||||
| "String.length" -> ok C_SIZE
|
| "String.length" -> ok C_SIZE
|
||||||
| "String.size" -> ok C_SIZE
|
| "String.size" -> ok C_SIZE
|
||||||
@ -324,51 +328,52 @@ module Typer = struct
|
|||||||
let tc_addargs a b c = tc [a;b;c] [ (*TODO…*) ]
|
let tc_addargs a b c = tc [a;b;c] [ (*TODO…*) ]
|
||||||
|
|
||||||
let t_none = forall "a" @@ fun a -> option a
|
let t_none = forall "a" @@ fun a -> option a
|
||||||
let t_sub = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_subarg a b c] => a --> b --> c (* TYPECLASS *)
|
let t_sub = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_subarg a b c] => tuple2 a b --> c (* TYPECLASS *)
|
||||||
let t_some = forall "a" @@ fun a -> a --> option a
|
let t_some = forall "a" @@ fun a -> a --> option a
|
||||||
let t_map_remove = forall2 "src" "dst" @@ fun src dst -> src --> map src dst --> map src dst
|
let t_map_remove = forall2 "src" "dst" @@ fun src dst -> tuple2 src (map src dst) --> map src dst
|
||||||
let t_map_add = forall2 "src" "dst" @@ fun src dst -> src --> dst --> map src dst --> map src dst
|
let t_map_add = forall2 "src" "dst" @@ fun src dst -> tuple3 src dst (map src dst) --> map src dst
|
||||||
let t_map_update = forall2 "src" "dst" @@ fun src dst -> src --> option dst --> map src dst --> map src dst
|
let t_map_update = forall2 "src" "dst" @@ fun src dst -> tuple3 src (option dst) (map src dst) --> map src dst
|
||||||
let t_map_mem = forall2 "src" "dst" @@ fun src dst -> src --> map src dst --> bool
|
let t_map_mem = forall2 "src" "dst" @@ fun src dst -> tuple2 src (map src dst) --> bool
|
||||||
let t_map_find = forall2 "src" "dst" @@ fun src dst -> src --> map src dst --> dst
|
let t_map_find = forall2 "src" "dst" @@ fun src dst -> tuple2 src (map src dst) --> dst
|
||||||
let t_map_find_opt = forall2 "src" "dst" @@ fun src dst -> src --> map src dst --> option dst
|
let t_map_find_opt = forall2 "src" "dst" @@ fun src dst -> tuple2 src (map src dst) --> option dst
|
||||||
let t_map_fold = forall3 "src" "dst" "acc" @@ fun src dst acc -> ( ( (src * dst) * acc ) --> acc ) --> map src dst --> acc --> acc
|
let t_map_fold = forall3 "src" "dst" "acc" @@ fun src dst acc -> tuple3 ( ( (src * dst) * acc ) --> acc ) (map src dst) acc --> acc
|
||||||
let t_map_map = forall3 "k" "v" "result" @@ fun k v result -> ((k * v) --> result) --> map k v --> map k result
|
let t_map_map = forall3 "k" "v" "result" @@ fun k v result -> tuple2 ((k * v) --> result) (map k v) --> map k result
|
||||||
|
|
||||||
(* TODO: the type of map_map_fold might be wrong, check it. *)
|
(* TODO: the type of map_map_fold might be wrong, check it. *)
|
||||||
let t_map_map_fold = forall4 "k" "v" "acc" "dst" @@ fun k v acc dst -> ( ((k * v) * acc) --> acc * dst ) --> map k v --> (k * v) --> (map k dst * acc)
|
let t_map_map_fold = forall4 "k" "v" "acc" "dst" @@ fun k v acc dst -> tuple3 ( ((k * v) * acc) --> acc * dst ) (map k v) (k * v) --> (map k dst * acc)
|
||||||
let t_map_iter = forall2 "k" "v" @@ fun k v -> ( (k * v) --> unit ) --> map k v --> unit
|
let t_map_iter = forall2 "k" "v" @@ fun k v -> tuple2 ( (k * v) --> unit ) (map k v) --> unit
|
||||||
let t_size = forall_tc "c" @@ fun c -> [tc_sizearg c] => c --> nat (* TYPECLASS *)
|
let t_size = forall_tc "c" @@ fun c -> [tc_sizearg c] => tuple1 c --> nat (* TYPECLASS *)
|
||||||
let t_slice = nat --> nat --> string --> string
|
let t_slice = tuple3 nat nat string --> string
|
||||||
let t_failwith = string --> unit
|
let t_failwith = tuple1 string --> unit
|
||||||
let t_get_force = forall2 "src" "dst" @@ fun src dst -> src --> map src dst --> dst
|
let t_get_force = forall2 "src" "dst" @@ fun src dst -> tuple2 src (map src dst) --> dst
|
||||||
let t_int = nat --> int
|
let t_int = tuple1 nat --> int
|
||||||
let t_bytes_pack = forall_tc "a" @@ fun a -> [tc_packable a] => a --> bytes (* TYPECLASS *)
|
let t_bytes_pack = forall_tc "a" @@ fun a -> [tc_packable a] => tuple1 a --> bytes (* TYPECLASS *)
|
||||||
let t_bytes_unpack = forall_tc "a" @@ fun a -> [tc_packable a] => bytes --> a (* TYPECLASS *)
|
let t_bytes_unpack = forall_tc "a" @@ fun a -> [tc_packable a] => tuple1 bytes --> a (* TYPECLASS *)
|
||||||
let t_hash256 = bytes --> bytes
|
let t_hash256 = tuple1 bytes --> bytes
|
||||||
let t_hash512 = bytes --> bytes
|
let t_hash512 = tuple1 bytes --> bytes
|
||||||
let t_blake2b = bytes --> bytes
|
let t_blake2b = tuple1 bytes --> bytes
|
||||||
let t_hash_key = key --> key_hash
|
let t_hash_key = tuple1 key --> key_hash
|
||||||
let t_check_signature = key --> signature --> bytes --> bool
|
let t_check_signature = tuple3 key signature bytes --> bool
|
||||||
let t_sender = address
|
let t_chain_id = tuple0 --> chain_id
|
||||||
let t_source = address
|
let t_sender = tuple0 --> address
|
||||||
let t_unit = unit
|
let t_source = tuple0 --> address
|
||||||
let t_amount = mutez
|
let t_unit = tuple0 --> unit
|
||||||
let t_address = address
|
let t_amount = tuple0 --> mutez
|
||||||
let t_now = timestamp
|
let t_address = tuple0 --> address
|
||||||
let t_transaction = forall "a" @@ fun a -> a --> mutez --> contract a --> operation
|
let t_now = tuple0 --> timestamp
|
||||||
let t_get_contract = forall "a" @@ fun a -> contract a
|
let t_transaction = forall "a" @@ fun a -> tuple3 a mutez (contract a) --> operation
|
||||||
let t_abs = int --> nat
|
let t_get_contract = forall "a" @@ fun a -> tuple0 --> contract a
|
||||||
let t_cons = forall "a" @@ fun a -> a --> list a --> list a
|
let t_abs = tuple1 int --> nat
|
||||||
let t_assertion = bool --> unit
|
let t_cons = forall "a" @@ fun a -> a --> tuple1 (list a) --> list a
|
||||||
let t_times = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_timargs a b c] => a --> b --> c (* TYPECLASS *)
|
let t_assertion = tuple1 bool --> unit
|
||||||
let t_div = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_divargs a b c] => a --> b --> c (* TYPECLASS *)
|
let t_times = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_timargs a b c] => tuple2 a b --> c (* TYPECLASS *)
|
||||||
let t_mod = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_modargs a b c] => a --> b --> c (* TYPECLASS *)
|
let t_div = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_divargs a b c] => tuple2 a b --> c (* TYPECLASS *)
|
||||||
let t_add = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_addargs a b c] => a --> b --> c (* TYPECLASS *)
|
let t_mod = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_modargs a b c] => tuple2 a b --> c (* TYPECLASS *)
|
||||||
let t_set_mem = forall "a" @@ fun a -> a --> set a --> bool
|
let t_add = forall3_tc "a" "b" "c" @@ fun a b c -> [tc_addargs a b c] => tuple2 a b --> c (* TYPECLASS *)
|
||||||
let t_set_add = forall "a" @@ fun a -> a --> set a --> set a
|
let t_set_mem = forall "a" @@ fun a -> tuple2 a (set a) --> bool
|
||||||
let t_set_remove = forall "a" @@ fun a -> a --> set a --> set a
|
let t_set_add = forall "a" @@ fun a -> tuple2 a (set a) --> set a
|
||||||
let t_not = bool --> bool
|
let t_set_remove = forall "a" @@ fun a -> tuple2 a (set a) --> set a
|
||||||
|
let t_not = tuple1 bool --> bool
|
||||||
|
|
||||||
let constant_type : constant -> Typesystem.Core.type_value result = function
|
let constant_type : constant -> Typesystem.Core.type_value result = function
|
||||||
| C_INT -> ok @@ t_int ;
|
| C_INT -> ok @@ t_int ;
|
||||||
@ -396,6 +401,8 @@ module Typer = struct
|
|||||||
| C_AND -> ok @@ failwith "t_and" ;
|
| C_AND -> ok @@ failwith "t_and" ;
|
||||||
| C_OR -> ok @@ failwith "t_or" ;
|
| C_OR -> ok @@ failwith "t_or" ;
|
||||||
| C_XOR -> ok @@ failwith "t_xor" ;
|
| C_XOR -> ok @@ failwith "t_xor" ;
|
||||||
|
| C_LSL -> ok @@ failwith "t_lsl" ;
|
||||||
|
| C_LSR -> ok @@ failwith "t_lsr" ;
|
||||||
(* COMPARATOR *)
|
(* COMPARATOR *)
|
||||||
| C_EQ -> ok @@ failwith "t_comparator EQ" ;
|
| C_EQ -> ok @@ failwith "t_comparator EQ" ;
|
||||||
| C_NEQ -> ok @@ failwith "t_comparator NEQ" ;
|
| C_NEQ -> ok @@ failwith "t_comparator NEQ" ;
|
||||||
@ -424,8 +431,6 @@ module Typer = struct
|
|||||||
| C_LIST_FOLD -> ok @@ failwith "t_list_fold" ;
|
| C_LIST_FOLD -> ok @@ failwith "t_list_fold" ;
|
||||||
| C_LIST_CONS -> ok @@ failwith "t_list_cons" ;
|
| C_LIST_CONS -> ok @@ failwith "t_list_cons" ;
|
||||||
(* MAP *)
|
(* MAP *)
|
||||||
| C_MAP_GET -> ok @@ failwith "t_map_get" ;
|
|
||||||
| C_MAP_GET_FORCE -> ok @@ failwith "t_map_get_force" ;
|
|
||||||
| C_MAP_ADD -> ok @@ t_map_add ;
|
| C_MAP_ADD -> ok @@ t_map_add ;
|
||||||
| C_MAP_REMOVE -> ok @@ t_map_remove ;
|
| C_MAP_REMOVE -> ok @@ t_map_remove ;
|
||||||
| C_MAP_UPDATE -> ok @@ t_map_update ;
|
| C_MAP_UPDATE -> ok @@ t_map_update ;
|
||||||
@ -442,7 +447,7 @@ module Typer = struct
|
|||||||
| C_BLAKE2b -> ok @@ t_blake2b ;
|
| C_BLAKE2b -> ok @@ t_blake2b ;
|
||||||
| C_HASH_KEY -> ok @@ t_hash_key ;
|
| C_HASH_KEY -> ok @@ t_hash_key ;
|
||||||
| C_CHECK_SIGNATURE -> ok @@ t_check_signature ;
|
| C_CHECK_SIGNATURE -> ok @@ t_check_signature ;
|
||||||
| C_CHAIN_ID -> ok @@ failwith "t_chain_id" ;
|
| C_CHAIN_ID -> ok @@ t_chain_id ;
|
||||||
(*BLOCKCHAIN *)
|
(*BLOCKCHAIN *)
|
||||||
| C_CONTRACT -> ok @@ t_get_contract ;
|
| C_CONTRACT -> ok @@ t_get_contract ;
|
||||||
| C_CONTRACT_ENTRYPOINT -> ok @@ failwith "t_get_entrypoint" ;
|
| C_CONTRACT_ENTRYPOINT -> ok @@ failwith "t_get_entrypoint" ;
|
||||||
@ -562,16 +567,6 @@ module Typer = struct
|
|||||||
let default = t_unit () in
|
let default = t_unit () in
|
||||||
ok @@ Simple_utils.Option.unopt ~default opt
|
ok @@ Simple_utils.Option.unopt ~default opt
|
||||||
|
|
||||||
let map_get_force = typer_2 "MAP_GET_FORCE" @@ fun i m ->
|
|
||||||
let%bind (src, dst) = bind_map_or (get_t_map , get_t_big_map) m in
|
|
||||||
let%bind _ = assert_type_value_eq (src, i) in
|
|
||||||
ok dst
|
|
||||||
|
|
||||||
let map_get = typer_2 "MAP_GET" @@ fun i m ->
|
|
||||||
let%bind (src, dst) = bind_map_or (get_t_map , get_t_big_map) m in
|
|
||||||
let%bind _ = assert_type_value_eq (src, i) in
|
|
||||||
ok @@ t_option dst ()
|
|
||||||
|
|
||||||
let int : typer = typer_1 "INT" @@ fun t ->
|
let int : typer = typer_1 "INT" @@ fun t ->
|
||||||
let%bind () = assert_t_nat t in
|
let%bind () = assert_t_nat t in
|
||||||
ok @@ t_int ()
|
ok @@ t_int ()
|
||||||
@ -1006,6 +1001,8 @@ module Typer = struct
|
|||||||
| C_AND -> ok @@ and_ ;
|
| C_AND -> ok @@ and_ ;
|
||||||
| C_OR -> ok @@ or_ ;
|
| C_OR -> ok @@ or_ ;
|
||||||
| C_XOR -> ok @@ xor ;
|
| C_XOR -> ok @@ xor ;
|
||||||
|
| C_LSL -> ok @@ lsl_;
|
||||||
|
| C_LSR -> ok @@ lsr_;
|
||||||
(* COMPARATOR *)
|
(* COMPARATOR *)
|
||||||
| C_EQ -> ok @@ comparator "EQ" ;
|
| C_EQ -> ok @@ comparator "EQ" ;
|
||||||
| C_NEQ -> ok @@ comparator "NEQ" ;
|
| C_NEQ -> ok @@ comparator "NEQ" ;
|
||||||
@ -1034,8 +1031,6 @@ module Typer = struct
|
|||||||
| C_LIST_FOLD -> ok @@ list_fold ;
|
| C_LIST_FOLD -> ok @@ list_fold ;
|
||||||
| C_LIST_CONS -> ok @@ list_cons ;
|
| C_LIST_CONS -> ok @@ list_cons ;
|
||||||
(* MAP *)
|
(* MAP *)
|
||||||
| C_MAP_GET -> ok @@ map_get ;
|
|
||||||
| C_MAP_GET_FORCE -> ok @@ map_get_force ;
|
|
||||||
| C_MAP_ADD -> ok @@ map_add ;
|
| C_MAP_ADD -> ok @@ map_add ;
|
||||||
| C_MAP_REMOVE -> ok @@ map_remove ;
|
| C_MAP_REMOVE -> ok @@ map_remove ;
|
||||||
| C_MAP_UPDATE -> ok @@ map_update ;
|
| C_MAP_UPDATE -> ok @@ map_update ;
|
||||||
@ -1103,6 +1098,8 @@ module Compiler = struct
|
|||||||
| C_OR -> ok @@ simple_binary @@ prim I_OR
|
| C_OR -> ok @@ simple_binary @@ prim I_OR
|
||||||
| C_AND -> ok @@ simple_binary @@ prim I_AND
|
| C_AND -> ok @@ simple_binary @@ prim I_AND
|
||||||
| C_XOR -> ok @@ simple_binary @@ prim I_XOR
|
| C_XOR -> ok @@ simple_binary @@ prim I_XOR
|
||||||
|
| C_LSL -> ok @@ simple_binary @@ prim I_LSL
|
||||||
|
| C_LSR -> ok @@ simple_binary @@ prim I_LSR
|
||||||
| C_NOT -> ok @@ simple_unary @@ prim I_NOT
|
| C_NOT -> ok @@ simple_unary @@ prim I_NOT
|
||||||
| C_PAIR -> ok @@ simple_binary @@ prim I_PAIR
|
| C_PAIR -> ok @@ simple_binary @@ prim I_PAIR
|
||||||
| C_CAR -> ok @@ simple_unary @@ prim I_CAR
|
| C_CAR -> ok @@ simple_unary @@ prim I_CAR
|
||||||
@ -1115,9 +1112,7 @@ module Compiler = struct
|
|||||||
| C_GE -> ok @@ simple_binary @@ seq [prim I_COMPARE ; prim I_GE]
|
| C_GE -> ok @@ simple_binary @@ seq [prim I_COMPARE ; prim I_GE]
|
||||||
| C_UPDATE -> ok @@ simple_ternary @@ prim I_UPDATE
|
| C_UPDATE -> ok @@ simple_ternary @@ prim I_UPDATE
|
||||||
| C_SOME -> ok @@ simple_unary @@ prim I_SOME
|
| C_SOME -> ok @@ simple_unary @@ prim I_SOME
|
||||||
| C_MAP_GET_FORCE -> ok @@ simple_binary @@ seq [prim I_GET ; i_assert_some_msg (i_push_string "GET_FORCE")]
|
|
||||||
| C_MAP_FIND -> ok @@ simple_binary @@ seq [prim I_GET ; i_assert_some_msg (i_push_string "MAP FIND")]
|
| C_MAP_FIND -> ok @@ simple_binary @@ seq [prim I_GET ; i_assert_some_msg (i_push_string "MAP FIND")]
|
||||||
| C_MAP_GET -> ok @@ simple_binary @@ prim I_GET
|
|
||||||
| C_MAP_MEM -> ok @@ simple_binary @@ prim I_MEM
|
| C_MAP_MEM -> ok @@ simple_binary @@ prim I_MEM
|
||||||
| C_MAP_FIND_OPT -> ok @@ simple_binary @@ prim I_GET
|
| C_MAP_FIND_OPT -> ok @@ simple_binary @@ prim I_GET
|
||||||
| C_MAP_ADD -> ok @@ simple_ternary @@ seq [dip (i_some) ; prim I_UPDATE]
|
| C_MAP_ADD -> ok @@ simple_ternary @@ seq [dip (i_some) ; prim I_UPDATE]
|
||||||
@ -1128,7 +1123,7 @@ module Compiler = struct
|
|||||||
| C_SIZE -> ok @@ simple_unary @@ prim I_SIZE
|
| C_SIZE -> ok @@ simple_unary @@ prim I_SIZE
|
||||||
| C_FAILWITH -> ok @@ simple_unary @@ prim I_FAILWITH
|
| C_FAILWITH -> ok @@ simple_unary @@ prim I_FAILWITH
|
||||||
| C_ASSERT_INFERRED -> ok @@ simple_binary @@ i_if (seq [i_failwith]) (seq [i_drop ; i_push_unit])
|
| C_ASSERT_INFERRED -> ok @@ simple_binary @@ i_if (seq [i_failwith]) (seq [i_drop ; i_push_unit])
|
||||||
| C_ASSERTION -> ok @@ simple_unary @@ i_if (seq [i_push_unit]) (seq [i_push_unit ; i_failwith])
|
| C_ASSERTION -> ok @@ simple_unary @@ i_if (seq [i_push_unit]) (seq [i_push_string "failed assertion" ; i_failwith])
|
||||||
| C_INT -> ok @@ simple_unary @@ prim I_INT
|
| C_INT -> ok @@ simple_unary @@ prim I_INT
|
||||||
| C_ABS -> ok @@ simple_unary @@ prim I_ABS
|
| C_ABS -> ok @@ simple_unary @@ prim I_ABS
|
||||||
| C_IS_NAT -> ok @@ simple_unary @@ prim I_ISNAT
|
| C_IS_NAT -> ok @@ simple_unary @@ prim I_ISNAT
|
||||||
|
@ -45,6 +45,8 @@ let constant ppf : constant -> unit = function
|
|||||||
| C_AND -> fprintf ppf "AND"
|
| C_AND -> fprintf ppf "AND"
|
||||||
| C_OR -> fprintf ppf "OR"
|
| C_OR -> fprintf ppf "OR"
|
||||||
| C_XOR -> fprintf ppf "XOR"
|
| C_XOR -> fprintf ppf "XOR"
|
||||||
|
| C_LSL -> fprintf ppf "LSL"
|
||||||
|
| C_LSR -> fprintf ppf "LSR"
|
||||||
(* COMPARATOR *)
|
(* COMPARATOR *)
|
||||||
| C_EQ -> fprintf ppf "EQ"
|
| C_EQ -> fprintf ppf "EQ"
|
||||||
| C_NEQ -> fprintf ppf "NEQ"
|
| C_NEQ -> fprintf ppf "NEQ"
|
||||||
@ -82,8 +84,6 @@ let constant ppf : constant -> unit = function
|
|||||||
| C_MAP -> fprintf ppf "MAP"
|
| C_MAP -> fprintf ppf "MAP"
|
||||||
| C_MAP_EMPTY -> fprintf ppf "MAP_EMPTY"
|
| C_MAP_EMPTY -> fprintf ppf "MAP_EMPTY"
|
||||||
| C_MAP_LITERAL -> fprintf ppf "MAP_LITERAL"
|
| C_MAP_LITERAL -> fprintf ppf "MAP_LITERAL"
|
||||||
| C_MAP_GET -> fprintf ppf "MAP_GET"
|
|
||||||
| C_MAP_GET_FORCE -> fprintf ppf "MAP_GET_FORCE"
|
|
||||||
| C_MAP_ADD -> fprintf ppf "MAP_ADD"
|
| C_MAP_ADD -> fprintf ppf "MAP_ADD"
|
||||||
| C_MAP_REMOVE -> fprintf ppf "MAP_REMOVE"
|
| C_MAP_REMOVE -> fprintf ppf "MAP_REMOVE"
|
||||||
| C_MAP_UPDATE -> fprintf ppf "MAP_UPDATE"
|
| C_MAP_UPDATE -> fprintf ppf "MAP_UPDATE"
|
||||||
@ -101,7 +101,6 @@ let constant ppf : constant -> unit = function
|
|||||||
| C_SHA256 -> fprintf ppf "SHA256"
|
| C_SHA256 -> fprintf ppf "SHA256"
|
||||||
| C_SHA512 -> fprintf ppf "SHA512"
|
| C_SHA512 -> fprintf ppf "SHA512"
|
||||||
| C_BLAKE2b -> fprintf ppf "BLAKE2b"
|
| C_BLAKE2b -> fprintf ppf "BLAKE2b"
|
||||||
| C_HASH -> fprintf ppf "HASH"
|
|
||||||
| C_HASH_KEY -> fprintf ppf "HASH_KEY"
|
| C_HASH_KEY -> fprintf ppf "HASH_KEY"
|
||||||
| C_CHECK_SIGNATURE -> fprintf ppf "CHECK_SIGNATURE"
|
| C_CHECK_SIGNATURE -> fprintf ppf "CHECK_SIGNATURE"
|
||||||
| C_CHAIN_ID -> fprintf ppf "CHAIN_ID"
|
| C_CHAIN_ID -> fprintf ppf "CHAIN_ID"
|
||||||
|
@ -162,6 +162,8 @@ type constant =
|
|||||||
| C_AND
|
| C_AND
|
||||||
| C_OR
|
| C_OR
|
||||||
| C_XOR
|
| C_XOR
|
||||||
|
| C_LSL
|
||||||
|
| C_LSR
|
||||||
(* COMPARATOR *)
|
(* COMPARATOR *)
|
||||||
| C_EQ
|
| C_EQ
|
||||||
| C_NEQ
|
| C_NEQ
|
||||||
@ -199,8 +201,6 @@ type constant =
|
|||||||
| C_MAP
|
| C_MAP
|
||||||
| C_MAP_EMPTY
|
| C_MAP_EMPTY
|
||||||
| C_MAP_LITERAL
|
| C_MAP_LITERAL
|
||||||
| C_MAP_GET
|
|
||||||
| C_MAP_GET_FORCE
|
|
||||||
| C_MAP_ADD
|
| C_MAP_ADD
|
||||||
| C_MAP_REMOVE
|
| C_MAP_REMOVE
|
||||||
| C_MAP_UPDATE
|
| C_MAP_UPDATE
|
||||||
@ -218,7 +218,6 @@ type constant =
|
|||||||
| C_SHA256
|
| C_SHA256
|
||||||
| C_SHA512
|
| C_SHA512
|
||||||
| C_BLAKE2b
|
| C_BLAKE2b
|
||||||
| C_HASH
|
|
||||||
| C_HASH_KEY
|
| C_HASH_KEY
|
||||||
| C_CHECK_SIGNATURE
|
| C_CHECK_SIGNATURE
|
||||||
| C_CHAIN_ID
|
| C_CHAIN_ID
|
||||||
|
@ -54,6 +54,7 @@ let mutez = P_constant (C_mutez , [])
|
|||||||
let timestamp = P_constant (C_timestamp , [])
|
let timestamp = P_constant (C_timestamp , [])
|
||||||
let int = P_constant (C_int , [])
|
let int = P_constant (C_int , [])
|
||||||
let address = P_constant (C_address , [])
|
let address = P_constant (C_address , [])
|
||||||
|
let chain_id = P_constant (C_chain_id , [])
|
||||||
let bytes = P_constant (C_bytes , [])
|
let bytes = P_constant (C_bytes , [])
|
||||||
let key = P_constant (C_key , [])
|
let key = P_constant (C_key , [])
|
||||||
let key_hash = P_constant (C_key_hash , [])
|
let key_hash = P_constant (C_key_hash , [])
|
||||||
@ -61,3 +62,9 @@ let signature = P_constant (C_signature , [])
|
|||||||
let operation = P_constant (C_operation , [])
|
let operation = P_constant (C_operation , [])
|
||||||
let contract t = P_constant (C_contract , [t])
|
let contract t = P_constant (C_contract , [t])
|
||||||
let ( * ) a b = pair a b
|
let ( * ) a b = pair a b
|
||||||
|
|
||||||
|
(* These are used temporarily to de-curry functions that correspond to Michelson operators *)
|
||||||
|
let tuple0 = P_constant (C_tuple , [])
|
||||||
|
let tuple1 a = P_constant (C_tuple , [a])
|
||||||
|
let tuple2 a b = P_constant (C_tuple , [a; b])
|
||||||
|
let tuple3 a b c = P_constant (C_tuple , [a; b; c])
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
// Test PascaLIGO bitwise operators
|
// Test PascaLIGO bitwise operators
|
||||||
|
|
||||||
function or_op (const n : nat) : nat is
|
function or_op (const n : nat) : nat is
|
||||||
begin skip end with bitwise_or(n , 4n)
|
bitwise_or(n , 4n)
|
||||||
|
|
||||||
function and_op (const n : nat) : nat is
|
function and_op (const n : nat) : nat is
|
||||||
begin skip end with bitwise_and(n , 7n)
|
bitwise_and(n , 7n)
|
||||||
|
|
||||||
function xor_op (const n : nat) : nat is
|
function xor_op (const n : nat) : nat is
|
||||||
begin skip end with bitwise_xor(n , 7n)
|
bitwise_xor(n , 7n)
|
||||||
|
|
||||||
|
function lsl_op (const n : nat) : nat is
|
||||||
|
bitwise_lsl(n , 7n)
|
||||||
|
|
||||||
|
function lsr_op (const n : nat) : nat is
|
||||||
|
bitwise_lsr(n , 7n)
|
@ -3,3 +3,5 @@
|
|||||||
let or_op (n: nat) : nat = Bitwise.lor n 4n
|
let or_op (n: nat) : nat = Bitwise.lor n 4n
|
||||||
let and_op (n: nat) : nat = Bitwise.land n 7n
|
let and_op (n: nat) : nat = Bitwise.land n 7n
|
||||||
let xor_op (n: nat) : nat = Bitwise.lxor n 7n
|
let xor_op (n: nat) : nat = Bitwise.lxor n 7n
|
||||||
|
let lsl_op (n: nat) : nat = Bitwise.shift_left n 7n
|
||||||
|
let lsr_op (n: nat) : nat = Bitwise.shift_right n 7n
|
||||||
|
@ -3,3 +3,5 @@
|
|||||||
let or_op = (n: nat): nat => Bitwise.lor(n, 4n);
|
let or_op = (n: nat): nat => Bitwise.lor(n, 4n);
|
||||||
let and_op = (n: nat): nat => Bitwise.land(n, 7n);
|
let and_op = (n: nat): nat => Bitwise.land(n, 7n);
|
||||||
let xor_op = (n: nat): nat => Bitwise.lxor(n, 7n);
|
let xor_op = (n: nat): nat => Bitwise.lxor(n, 7n);
|
||||||
|
let lsl_op = (n: nat) : nat => Bitwise.shift_left(n, 7n);
|
||||||
|
let lsr_op = (n: nat) : nat => Bitwise.shift_right(n, 7n);
|
@ -1,19 +0,0 @@
|
|||||||
type storage = {
|
|
||||||
challenge : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type param = {
|
|
||||||
new_challenge : string;
|
|
||||||
attempt : bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
let attempt (p: param) storage =
|
|
||||||
if Crypto.hash (Bytes.pack p.attempt) <> Bytes.pack storage.challenge
|
|
||||||
then failwith "Failed challenge"
|
|
||||||
else
|
|
||||||
let contract : unit contract =
|
|
||||||
Operation.get_contract sender in
|
|
||||||
let transfer : operation =
|
|
||||||
Operation.transaction (unit, contract, 10tz) in
|
|
||||||
let storage : storage = {challenge = p.new_challenge}
|
|
||||||
in ([] : operation list), storage
|
|
@ -348,6 +348,8 @@ let bitwise_arithmetic () : unit result =
|
|||||||
let%bind () = expect_eq program "and_op" (e_nat 10) (e_nat 2) in
|
let%bind () = expect_eq program "and_op" (e_nat 10) (e_nat 2) in
|
||||||
let%bind () = expect_eq program "xor_op" (e_nat 0) (e_nat 7) in
|
let%bind () = expect_eq program "xor_op" (e_nat 0) (e_nat 7) in
|
||||||
let%bind () = expect_eq program "xor_op" (e_nat 7) (e_nat 0) in
|
let%bind () = expect_eq program "xor_op" (e_nat 7) (e_nat 0) in
|
||||||
|
let%bind () = expect_eq program "lsl_op" (e_nat 1000) (e_nat 128000) in
|
||||||
|
let%bind () = expect_eq program "lsr_op" (e_nat 128000) (e_nat 1000) in
|
||||||
ok ()
|
ok ()
|
||||||
|
|
||||||
let bitwise_arithmetic_mligo () : unit result =
|
let bitwise_arithmetic_mligo () : unit result =
|
||||||
@ -364,6 +366,8 @@ let bitwise_arithmetic_mligo () : unit result =
|
|||||||
let%bind () = expect_eq program "and_op" (e_nat 10) (e_nat 2) in
|
let%bind () = expect_eq program "and_op" (e_nat 10) (e_nat 2) in
|
||||||
let%bind () = expect_eq program "xor_op" (e_nat 0) (e_nat 7) in
|
let%bind () = expect_eq program "xor_op" (e_nat 0) (e_nat 7) in
|
||||||
let%bind () = expect_eq program "xor_op" (e_nat 7) (e_nat 0) in
|
let%bind () = expect_eq program "xor_op" (e_nat 7) (e_nat 0) in
|
||||||
|
let%bind () = expect_eq program "lsl_op" (e_nat 1000) (e_nat 128000) in
|
||||||
|
let%bind () = expect_eq program "lsr_op" (e_nat 128000) (e_nat 1000) in
|
||||||
ok ()
|
ok ()
|
||||||
|
|
||||||
let bitwise_arithmetic_religo () : unit result =
|
let bitwise_arithmetic_religo () : unit result =
|
||||||
@ -380,6 +384,8 @@ let bitwise_arithmetic_religo () : unit result =
|
|||||||
let%bind () = expect_eq program "and_op" (e_nat 10) (e_nat 2) in
|
let%bind () = expect_eq program "and_op" (e_nat 10) (e_nat 2) in
|
||||||
let%bind () = expect_eq program "xor_op" (e_nat 0) (e_nat 7) in
|
let%bind () = expect_eq program "xor_op" (e_nat 0) (e_nat 7) in
|
||||||
let%bind () = expect_eq program "xor_op" (e_nat 7) (e_nat 0) in
|
let%bind () = expect_eq program "xor_op" (e_nat 7) (e_nat 0) in
|
||||||
|
let%bind () = expect_eq program "lsl_op" (e_nat 1000) (e_nat 128000) in
|
||||||
|
let%bind () = expect_eq program "lsr_op" (e_nat 128000) (e_nat 1000) in
|
||||||
ok ()
|
ok ()
|
||||||
|
|
||||||
let string_arithmetic () : unit result =
|
let string_arithmetic () : unit result =
|
||||||
@ -1458,12 +1464,6 @@ let assert_religo () : unit result =
|
|||||||
let%bind _ = expect_eq program "main" (make_input true) make_expected in
|
let%bind _ = expect_eq program "main" (make_input true) make_expected in
|
||||||
ok ()
|
ok ()
|
||||||
|
|
||||||
let guess_the_hash_mligo () : unit result =
|
|
||||||
let%bind program = mtype_file "./contracts/new-syntax.mligo" in
|
|
||||||
let make_input = fun n-> e_pair (e_int n) (e_int 42) in
|
|
||||||
let make_expected = fun n -> e_pair (e_typed_list [] t_operation) (e_int (42 + n)) in
|
|
||||||
expect_eq_n program "main" make_input make_expected
|
|
||||||
|
|
||||||
let guess_string_mligo () : unit result =
|
let guess_string_mligo () : unit result =
|
||||||
let%bind program = type_file "./contracts/guess_string.mligo" in
|
let%bind program = type_file "./contracts/guess_string.mligo" in
|
||||||
let make_input = fun n -> e_pair (e_int n) (e_int 42) in
|
let make_input = fun n -> e_pair (e_int n) (e_int 42) in
|
||||||
@ -2314,7 +2314,6 @@ let main = test_suite "Integration (End to End)" [
|
|||||||
(* test "list matching (mligo)" mligo_list ; *)
|
(* test "list matching (mligo)" mligo_list ; *)
|
||||||
test "list matching (mligo)" mligo_list ;
|
test "list matching (mligo)" mligo_list ;
|
||||||
test "list matching (religo)" religo_list ;
|
test "list matching (religo)" religo_list ;
|
||||||
(* test "guess the hash mligo" guess_the_hash_mligo ; WIP? *)
|
|
||||||
test "failwith ligo" failwith_ligo ;
|
test "failwith ligo" failwith_ligo ;
|
||||||
test "failwith mligo" failwith_mligo ;
|
test "failwith mligo" failwith_mligo ;
|
||||||
test "assert mligo" assert_mligo ;
|
test "assert mligo" assert_mligo ;
|
||||||
|
@ -122,6 +122,7 @@ let md_files = [
|
|||||||
"/gitlab-pages/docs/advanced/timestamps-addresses.md";
|
"/gitlab-pages/docs/advanced/timestamps-addresses.md";
|
||||||
"/gitlab-pages/docs/api/cli-commands.md";
|
"/gitlab-pages/docs/api/cli-commands.md";
|
||||||
"/gitlab-pages/docs/api/cheat-sheet.md";
|
"/gitlab-pages/docs/api/cheat-sheet.md";
|
||||||
|
"/gitlab-pages/docs/reference/string.md";
|
||||||
]
|
]
|
||||||
|
|
||||||
let md_root = "../../gitlab-pages/docs/language-basics/"
|
let md_root = "../../gitlab-pages/docs/language-basics/"
|
||||||
|
10
tools/webide/.editorconfig
Normal file
10
tools/webide/.editorconfig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
[*.{js,ts,tsx,json,css}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
quote_type = single
|
4
tools/webide/.gitignore
vendored
Normal file
4
tools/webide/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
tmp/
|
||||||
|
dist
|
||||||
|
*.log
|
32
tools/webide/Dockerfile
Normal file
32
tools/webide/Dockerfile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
FROM node:12-alpine as builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package.json
|
||||||
|
COPY yarn.lock yarn.lock
|
||||||
|
COPY packages/client packages/client
|
||||||
|
COPY packages/server packages/server
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
COPY tsconfig.json tsconfig.json
|
||||||
|
|
||||||
|
RUN yarn workspaces run build
|
||||||
|
|
||||||
|
FROM node:12-buster
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get -y install libev-dev perl pkg-config libgmp-dev libhidapi-dev m4 libcap-dev bubblewrap rsync
|
||||||
|
|
||||||
|
COPY ligo_deb10.deb /tmp/ligo_deb10.deb
|
||||||
|
RUN dpkg -i /tmp/ligo_deb10.deb && rm /tmp/ligo_deb10.deb
|
||||||
|
|
||||||
|
COPY --from=builder /app/packages/client/build /app/client/build
|
||||||
|
COPY --from=builder /app/node_modules /app/node_modules
|
||||||
|
COPY --from=builder /app/packages/server/dist/src /app/server/dist
|
||||||
|
|
||||||
|
ENV STATIC_ASSETS /app/client
|
||||||
|
ENV LIGO_CMD /bin/ligo
|
||||||
|
|
||||||
|
ENTRYPOINT [ "node", "server/dist/index.js" ]
|
12
tools/webide/README.md
Normal file
12
tools/webide/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Quick Start
|
||||||
|
|
||||||
|
Install `yarn`.
|
||||||
|
Run `yarn` to install dependencies.
|
||||||
|
|
||||||
|
## Server
|
||||||
|
|
||||||
|
See the README under the `packages/server/` for information about how to get started on the server development.
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
See the README under the `packages/client/` for information about how to get started on the client development.
|
17
tools/webide/package.json
Normal file
17
tools/webide/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "ligo-editor",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"workspaces": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"description": "",
|
||||||
|
"scripts": {
|
||||||
|
"prestart": "cd packages/client && npm run build",
|
||||||
|
"start": ""
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+ssh://git@gitlab.com:ligolang/ligo-web-ide.git"
|
||||||
|
}
|
||||||
|
}
|
23
tools/webide/packages/client/.gitignore
vendored
Normal file
23
tools/webide/packages/client/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
44
tools/webide/packages/client/README.md
Normal file
44
tools/webide/packages/client/README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `yarn start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.<br />
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.<br />
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `yarn test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.<br />
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `yarn build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.<br />
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.<br />
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `yarn eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
@ -0,0 +1,37 @@
|
|||||||
|
(*_*
|
||||||
|
name: Cameligo Contract
|
||||||
|
language: cameligo
|
||||||
|
compile:
|
||||||
|
entrypoint: main
|
||||||
|
dryRun:
|
||||||
|
entrypoint: main
|
||||||
|
parameters: Increment 1
|
||||||
|
storage: 0
|
||||||
|
deploy:
|
||||||
|
entrypoint: main
|
||||||
|
storage: 0
|
||||||
|
evaluateValue:
|
||||||
|
entrypoint: ""
|
||||||
|
evaluateFunction:
|
||||||
|
entrypoint: add
|
||||||
|
parameters: 5, 6
|
||||||
|
*_*)
|
||||||
|
type storage = int
|
||||||
|
|
||||||
|
(* variant defining pseudo multi-entrypoint actions *)
|
||||||
|
|
||||||
|
type action =
|
||||||
|
| Increment of int
|
||||||
|
| Decrement of int
|
||||||
|
|
||||||
|
let add (a,b: int * int) : int = a + b
|
||||||
|
let sub (a,b: int * int) : int = a - b
|
||||||
|
|
||||||
|
(* real entrypoint that re-routes the flow based on the action provided *)
|
||||||
|
|
||||||
|
let main (p,s: action * storage) =
|
||||||
|
let storage =
|
||||||
|
match p with
|
||||||
|
| Increment n -> add (s, n)
|
||||||
|
| Decrement n -> sub (s, n)
|
||||||
|
in ([] : operation list), storage
|
@ -0,0 +1,38 @@
|
|||||||
|
(*_*
|
||||||
|
name: Pascaligo Contract
|
||||||
|
language: pascaligo
|
||||||
|
compile:
|
||||||
|
entrypoint: main
|
||||||
|
dryRun:
|
||||||
|
entrypoint: main
|
||||||
|
parameters: Increment (1)
|
||||||
|
storage: 0
|
||||||
|
deploy:
|
||||||
|
entrypoint: main
|
||||||
|
storage: 0
|
||||||
|
evaluateValue:
|
||||||
|
entrypoint: ""
|
||||||
|
evaluateFunction:
|
||||||
|
entrypoint: add
|
||||||
|
parameters: (5, 6)
|
||||||
|
*_*)
|
||||||
|
// variant defining pseudo multi-entrypoint actions
|
||||||
|
type action is
|
||||||
|
| Increment of int
|
||||||
|
| Decrement of int
|
||||||
|
|
||||||
|
function add (const a : int ; const b : int) : int is
|
||||||
|
block { skip } with a + b
|
||||||
|
|
||||||
|
function subtract (const a : int ; const b : int) : int is
|
||||||
|
block { skip } with a - b
|
||||||
|
|
||||||
|
// real entrypoint that re-routes the flow based
|
||||||
|
// on the action provided
|
||||||
|
function main (const p : action ; const s : int) :
|
||||||
|
(list(operation) * int) is
|
||||||
|
block { skip } with ((nil : list(operation)),
|
||||||
|
case p of
|
||||||
|
| Increment(n) -> add(s, n)
|
||||||
|
| Decrement(n) -> subtract(s, n)
|
||||||
|
end)
|
@ -0,0 +1,39 @@
|
|||||||
|
(*_*
|
||||||
|
name: Reasonligo Contract
|
||||||
|
language: reasonligo
|
||||||
|
compile:
|
||||||
|
entrypoint: main
|
||||||
|
dryRun:
|
||||||
|
entrypoint: main
|
||||||
|
parameters: Increment (1)
|
||||||
|
storage: 0
|
||||||
|
deploy:
|
||||||
|
entrypoint: main
|
||||||
|
storage: 0
|
||||||
|
evaluateValue:
|
||||||
|
entrypoint: ""
|
||||||
|
evaluateFunction:
|
||||||
|
entrypoint: add
|
||||||
|
parameters: (5, 6)
|
||||||
|
*_*)
|
||||||
|
type storage = int;
|
||||||
|
|
||||||
|
/* variant defining pseudo multi-entrypoint actions */
|
||||||
|
|
||||||
|
type action =
|
||||||
|
| Increment(int)
|
||||||
|
| Decrement(int);
|
||||||
|
|
||||||
|
let add = ((a,b): (int, int)): int => a + b;
|
||||||
|
let sub = ((a,b): (int, int)): int => a - b;
|
||||||
|
|
||||||
|
/* real entrypoint that re-routes the flow based on the action provided */
|
||||||
|
|
||||||
|
let main = ((p,storage): (action, storage)) => {
|
||||||
|
let storage =
|
||||||
|
switch (p) {
|
||||||
|
| Increment(n) => add((storage, n))
|
||||||
|
| Decrement(n) => sub((storage, n))
|
||||||
|
};
|
||||||
|
([]: list(operation), storage);
|
||||||
|
};
|
84
tools/webide/packages/client/ligo_run.svg
Normal file
84
tools/webide/packages/client/ligo_run.svg
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
sodipodi:docname="ligo_run.svg"
|
||||||
|
id="svg4550"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 193.35434 193.35434"
|
||||||
|
height="193.35434"
|
||||||
|
width="193.35434"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
|
style="fill:none">
|
||||||
|
<metadata
|
||||||
|
id="metadata4554">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview4552"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.9624573"
|
||||||
|
inkscape:cx="24.54412"
|
||||||
|
inkscape:cy="104.17717"
|
||||||
|
inkscape:window-x="-12"
|
||||||
|
inkscape:window-y="-12"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4550"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0" />
|
||||||
|
<circle
|
||||||
|
cx="96.67717"
|
||||||
|
cy="96.67717"
|
||||||
|
r="74"
|
||||||
|
id="circle4541"
|
||||||
|
style="stroke:url(#paint0_linear);stroke-width:45.35433197;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<defs
|
||||||
|
id="defs4548">
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear"
|
||||||
|
x1="100"
|
||||||
|
y1="54"
|
||||||
|
x2="100"
|
||||||
|
y2="254"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(-3.322834,-57.322834)">
|
||||||
|
<stop
|
||||||
|
stop-color="#3AA0FF"
|
||||||
|
id="stop4543" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#0072DC"
|
||||||
|
id="stop4545" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
d="M 137.61977,80.627036 58.899178,68.296666 88.24752,141.00143 137.6208,80.624736 Z"
|
||||||
|
id="path4537"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#fc683a;stroke-width:2.53620434" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
118
tools/webide/packages/client/package-examples.js
Normal file
118
tools/webide/packages/client/package-examples.js
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
const createHash = require('crypto').createHash;
|
||||||
|
const glob = require('glob');
|
||||||
|
const join = require('path').join;
|
||||||
|
const fs = require('fs');
|
||||||
|
const YAML = require('yamljs');
|
||||||
|
|
||||||
|
function urlFriendlyHash(content) {
|
||||||
|
const hash = createHash('md5');
|
||||||
|
hash.update(content);
|
||||||
|
|
||||||
|
return hash
|
||||||
|
.digest('base64')
|
||||||
|
.replace(/\+/g, '-')
|
||||||
|
.replace(/\//g, '_')
|
||||||
|
.replace(/=/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToJson(content, path) {
|
||||||
|
const METADATA_REGEX = /\(\*_\*([^]*?)\*_\*\)\s*/;
|
||||||
|
const match = content.match(METADATA_REGEX);
|
||||||
|
|
||||||
|
if (!match || !match[1]) {
|
||||||
|
throw new Error(`Unable to find compiler configuration in ${path}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const config = YAML.parse(match[1]);
|
||||||
|
config.editor = {
|
||||||
|
language: config.language,
|
||||||
|
code: content.replace(METADATA_REGEX, '')
|
||||||
|
};
|
||||||
|
delete config.language;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
} catch (ex) {
|
||||||
|
throw new Error(`${path} doesn't contain valid metadata. ${ex}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findFiles(pattern, dir) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
glob(pattern, { cwd: dir }, (error, files) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(files);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFile(path) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.readFile(path, 'utf8', (error, content) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeFile(path, config) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.writeFile(path, JSON.stringify(config), error => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processExample(srcDir, file, destDir) {
|
||||||
|
const path = join(srcDir, file);
|
||||||
|
|
||||||
|
console.log(`Processing ${path}`);
|
||||||
|
|
||||||
|
const content = await readFile(path);
|
||||||
|
const config = convertToJson(content, path);
|
||||||
|
const id = urlFriendlyHash(file);
|
||||||
|
|
||||||
|
config.id = id;
|
||||||
|
|
||||||
|
await writeFile(join(destDir, id), config);
|
||||||
|
|
||||||
|
return { id: id, name: config.name };
|
||||||
|
}
|
||||||
|
|
||||||
|
function processExamples(srcDir, files, destDir) {
|
||||||
|
return Promise.all(files.map(file => processExample(srcDir, file, destDir)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
process.on('unhandledRejection', error => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
|
const EXAMPLES_DEST_DIR = join(process.cwd(), 'build', 'static', 'examples');
|
||||||
|
const EXAMPLES_DIR = join(process.cwd(), 'examples');
|
||||||
|
const EXAMPLES_GLOB = '**/*.ligo';
|
||||||
|
const EXAMPLES_LIST_FILE = 'list';
|
||||||
|
|
||||||
|
fs.mkdirSync(EXAMPLES_DEST_DIR, { recursive: true });
|
||||||
|
|
||||||
|
const files = await findFiles(EXAMPLES_GLOB, EXAMPLES_DIR);
|
||||||
|
const examples = await processExamples(
|
||||||
|
EXAMPLES_DIR,
|
||||||
|
files,
|
||||||
|
EXAMPLES_DEST_DIR
|
||||||
|
);
|
||||||
|
|
||||||
|
await writeFile(join(EXAMPLES_DEST_DIR, EXAMPLES_LIST_FILE), examples);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
16597
tools/webide/packages/client/package-lock.json
generated
Normal file
16597
tools/webide/packages/client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
61
tools/webide/packages/client/package.json
Normal file
61
tools/webide/packages/client/package.json
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"name": "client",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^5.11.2",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.1.6",
|
||||||
|
"@taquito/taquito": "^5.1.0-beta.1",
|
||||||
|
"@taquito/tezbridge-signer": "^5.1.0-beta.1",
|
||||||
|
"@types/jest": "24.0.18",
|
||||||
|
"@types/node": "12.7.12",
|
||||||
|
"@types/react": "16.9.5",
|
||||||
|
"@types/react-dom": "16.9.1",
|
||||||
|
"@types/react-outside-click-handler": "^1.2.0",
|
||||||
|
"axios": "^0.19.0",
|
||||||
|
"http-proxy-middleware": "^0.20.0",
|
||||||
|
"monaco-editor": "npm:@ligolang/monaco-editor@0.18.1",
|
||||||
|
"react": "^16.10.2",
|
||||||
|
"react-dom": "^16.10.2",
|
||||||
|
"react-outside-click-handler": "^1.3.0",
|
||||||
|
"react-redux": "^7.1.1",
|
||||||
|
"react-scripts": "3.2.0",
|
||||||
|
"react-spinners-kit": "^1.9.0",
|
||||||
|
"redux": "^4.0.4",
|
||||||
|
"redux-devtools": "^3.5.0",
|
||||||
|
"redux-thunk": "^2.3.0",
|
||||||
|
"styled-components": "^4.4.0",
|
||||||
|
"typescript": "3.6.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"postbuild": "node package-examples.js",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": "react-app"
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/glob": "^7.1.1",
|
||||||
|
"@types/react-redux": "^7.1.4",
|
||||||
|
"@types/styled-components": "^4.1.19",
|
||||||
|
"glob": "^7.1.6",
|
||||||
|
"node-sass": "^4.12.0",
|
||||||
|
"yamljs": "^0.3.0"
|
||||||
|
}
|
||||||
|
}
|
55
tools/webide/packages/client/public/index.html
Normal file
55
tools/webide/packages/client/public/index.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="The LIGO Playground for learning LIGO" />
|
||||||
|
<link rel="apple-touch-icon" href="logo192.png" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script
|
||||||
|
async
|
||||||
|
src="https://www.googletagmanager.com/gtag/js?id=UA-153751765-1"
|
||||||
|
></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() {
|
||||||
|
dataLayer.push(arguments);
|
||||||
|
}
|
||||||
|
gtag("js", new Date());
|
||||||
|
|
||||||
|
gtag("config", "UA-153751765-1");
|
||||||
|
</script>
|
||||||
|
<script src="https://www.tezbridge.com/plugin.js"></script>
|
||||||
|
<title>LIGO Playground</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
1
tools/webide/packages/client/public/logo.svg
Normal file
1
tools/webide/packages/client/public/logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 16 KiB |
25
tools/webide/packages/client/public/manifest.json
Normal file
25
tools/webide/packages/client/public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "LIGO Playground",
|
||||||
|
"name": "The LIGO Playground for learning LIGO",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "logo.svg",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/svg+xml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
2
tools/webide/packages/client/public/robots.txt
Normal file
2
tools/webide/packages/client/public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
9
tools/webide/packages/client/src/App.test.tsx
Normal file
9
tools/webide/packages/client/src/App.test.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
ReactDOM.render(<App />, div);
|
||||||
|
ReactDOM.unmountComponentAtNode(div);
|
||||||
|
});
|
55
tools/webide/packages/client/src/App.tsx
Normal file
55
tools/webide/packages/client/src/App.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { EditorComponent } from './components/editor';
|
||||||
|
import { Examples } from './components/examples';
|
||||||
|
import { FloatButtonComponent } from './components/float-button';
|
||||||
|
import { HeaderComponent } from './components/header';
|
||||||
|
import { TabsPanelComponent } from './components/tabs-panel';
|
||||||
|
import configureStore from './configure-store';
|
||||||
|
|
||||||
|
const store = configureStore();
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FeedbackContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<HeaderComponent></HeaderComponent>
|
||||||
|
<Container>
|
||||||
|
<Examples></Examples>
|
||||||
|
<EditorComponent></EditorComponent>
|
||||||
|
<TabsPanelComponent></TabsPanelComponent>
|
||||||
|
</Container>
|
||||||
|
<FeedbackContainer>
|
||||||
|
<FloatButtonComponent
|
||||||
|
tooltip="Report an issue"
|
||||||
|
text="!"
|
||||||
|
href="https://gitlab.com/ligolang/ligo-web-ide/issues"
|
||||||
|
></FloatButtonComponent>
|
||||||
|
<FloatButtonComponent
|
||||||
|
tooltip="Ask a question"
|
||||||
|
text="?"
|
||||||
|
href="https://discord.gg/9rhYaEt"
|
||||||
|
></FloatButtonComponent>
|
||||||
|
</FeedbackContainer>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
58
tools/webide/packages/client/src/components/checkbox.tsx
Normal file
58
tools/webide/packages/client/src/components/checkbox.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
const Container = styled.div<{ checked: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
height: 2.5em;
|
||||||
|
width: 2.5em;
|
||||||
|
background: var(--blue_trans2);
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CheckIcon = ({ visible, ...props }: { visible: boolean }) => (
|
||||||
|
<FontAwesomeIcon {...props} size="2x" icon={faCheck}></FontAwesomeIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Check = styled(CheckIcon)`
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
transition: transform 0.2s ease-in;
|
||||||
|
color: var(--orange);
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
!props.visible &&
|
||||||
|
css`
|
||||||
|
transition: scale(1);
|
||||||
|
opacity: 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CheckboxComponent = (props: {
|
||||||
|
checked: boolean;
|
||||||
|
onChanged: (value: boolean) => void;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const [isChecked, setChecked] = useState(props.checked);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={props.className}
|
||||||
|
checked={isChecked}
|
||||||
|
onClick={() => {
|
||||||
|
const newState = !isChecked;
|
||||||
|
|
||||||
|
setChecked(newState);
|
||||||
|
props.onChanged(newState);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check visible={isChecked}></Check>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
146
tools/webide/packages/client/src/components/command-select.tsx
Normal file
146
tools/webide/packages/client/src/components/command-select.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import OutsideClickHandler from 'react-outside-click-handler';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import { Command } from '../redux/types';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
flex: 2;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
min-width: 8em;
|
||||||
|
z-index: 2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-height: 2em;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
|
||||||
|
border: 1px solid var(--blue_trans1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ArrowIcon = ({ rotate, ...props }: { rotate: boolean }) => (
|
||||||
|
<FontAwesomeIcon {...props} icon={faCaretDown} size="lg"></FontAwesomeIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Arrow = styled(ArrowIcon)`
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--blue_trans1);
|
||||||
|
transition: transform 0.15s ease-in;
|
||||||
|
|
||||||
|
${(props: { rotate: boolean }) =>
|
||||||
|
props.rotate &&
|
||||||
|
css`
|
||||||
|
transform: rotate(180deg);
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const List = styled.ul`
|
||||||
|
position: absolute;
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: white;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: 1px 3px 10px 0px rgba(153, 153, 153, 0.4);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease-in;
|
||||||
|
|
||||||
|
${(props: { visible: boolean }) =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Option = styled.li`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 2em;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 0 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--blue_trans2);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CommandSelectComponent = (props: {
|
||||||
|
selected: Command;
|
||||||
|
onChange?: (value: Command) => void;
|
||||||
|
}) => {
|
||||||
|
const OPTIONS = {
|
||||||
|
[Command.Compile]: 'Compile',
|
||||||
|
[Command.Deploy]: 'Deploy',
|
||||||
|
[Command.DryRun]: 'Dry Run',
|
||||||
|
[Command.EvaluateFunction]: 'Evaluate Function',
|
||||||
|
[Command.EvaluateValue]: 'Evaluate Value'
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveOptionToTop = (option: Command) => {
|
||||||
|
return Object.keys(OPTIONS).reduce((list, entry) => {
|
||||||
|
if (entry === option) {
|
||||||
|
list.unshift(entry);
|
||||||
|
} else {
|
||||||
|
list.push(entry as Command);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}, [] as Command[]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [opened, open] = useState(false);
|
||||||
|
|
||||||
|
const selectOption = (option: Command) => {
|
||||||
|
if (props.selected !== option && props.onChange) {
|
||||||
|
props.onChange(option);
|
||||||
|
}
|
||||||
|
open(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<OutsideClickHandler onOutsideClick={() => open(false)}>
|
||||||
|
<List visible={opened}>
|
||||||
|
{moveOptionToTop(props.selected).map(option => (
|
||||||
|
<Option
|
||||||
|
id={option}
|
||||||
|
key={option}
|
||||||
|
onClick={() => selectOption(option)}
|
||||||
|
>
|
||||||
|
<span>{OPTIONS[option]}</span>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</OutsideClickHandler>
|
||||||
|
<Header id="command-select" onClick={() => open(true)}>
|
||||||
|
<span>{OPTIONS[props.selected]}</span>
|
||||||
|
<Arrow rotate={opened}></Arrow>
|
||||||
|
</Header>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
31
tools/webide/packages/client/src/components/compile-pane.tsx
Normal file
31
tools/webide/packages/client/src/components/compile-pane.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeEntrypointAction, CompileState } from '../redux/compile';
|
||||||
|
import { Group, Input, Label } from './inputs';
|
||||||
|
|
||||||
|
const Container = styled.div``;
|
||||||
|
|
||||||
|
export const CompilePaneComponent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const entrypoint = useSelector<AppState, CompileState['entrypoint']>(
|
||||||
|
state => state.compile.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="entrypoint">Entrypoint</Label>
|
||||||
|
<Input
|
||||||
|
id="entrypoint"
|
||||||
|
value={entrypoint}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeEntrypointAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
142
tools/webide/packages/client/src/components/configure-tab.tsx
Normal file
142
tools/webide/packages/client/src/components/configure-tab.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import { CompileAction } from '../redux/actions/compile';
|
||||||
|
import { DeployAction } from '../redux/actions/deploy';
|
||||||
|
import { DryRunAction } from '../redux/actions/dry-run';
|
||||||
|
import { EvaluateFunctionAction } from '../redux/actions/evaluate-function';
|
||||||
|
import { EvaluateValueAction } from '../redux/actions/evaluate-value';
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeDispatchedAction, ChangeSelectedAction, CommandState } from '../redux/command';
|
||||||
|
import { Command } from '../redux/types';
|
||||||
|
import { CommandSelectComponent } from './command-select';
|
||||||
|
import { CompilePaneComponent } from './compile-pane';
|
||||||
|
import { DeployPaneComponent } from './deploy-pane';
|
||||||
|
import { DryRunPaneComponent } from './dry-run-pane';
|
||||||
|
import { EvaluateFunctionPaneComponent } from './evaluate-function-pane';
|
||||||
|
import { EvaluateValuePaneComponent } from './evaluate-value-pane';
|
||||||
|
|
||||||
|
const Container = styled.div<{ visible?: boolean }>`
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1em 1em 0 1em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
transform: translateX(-100%);
|
||||||
|
transition: transform 0.2s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
transform: translateX(0px);
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CommonActionsGroup = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RunButton = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 2em;
|
||||||
|
min-width: 3em;
|
||||||
|
margin-left: 1em;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
background-color: var(--orange);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CommandPaneContainer = styled.div`
|
||||||
|
padding-top: 1em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function createAction(command: Command) {
|
||||||
|
switch (command) {
|
||||||
|
case Command.Compile:
|
||||||
|
return new CompileAction();
|
||||||
|
case Command.DryRun:
|
||||||
|
return new DryRunAction();
|
||||||
|
case Command.Deploy:
|
||||||
|
return new DeployAction();
|
||||||
|
case Command.EvaluateValue:
|
||||||
|
return new EvaluateValueAction();
|
||||||
|
case Command.EvaluateFunction:
|
||||||
|
return new EvaluateFunctionAction();
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported command');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConfigureTabComponent = (props: {
|
||||||
|
selected?: boolean;
|
||||||
|
onRun?: () => void;
|
||||||
|
}) => {
|
||||||
|
const dispatchedAction = useSelector<
|
||||||
|
AppState,
|
||||||
|
CommandState['dispatchedAction']
|
||||||
|
>(state => state.command.dispatchedAction);
|
||||||
|
|
||||||
|
const command = useSelector<AppState, CommandState['selected']>(
|
||||||
|
state => state.command.selected
|
||||||
|
);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container visible={props.selected}>
|
||||||
|
<CommonActionsGroup>
|
||||||
|
<CommandSelectComponent
|
||||||
|
selected={command}
|
||||||
|
onChange={command => {
|
||||||
|
dispatch({ ...new ChangeSelectedAction(command) });
|
||||||
|
}}
|
||||||
|
></CommandSelectComponent>
|
||||||
|
<RunButton
|
||||||
|
id="run"
|
||||||
|
onClick={() => {
|
||||||
|
if (dispatchedAction) {
|
||||||
|
dispatchedAction.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAction = createAction(command);
|
||||||
|
dispatch(newAction.getAction());
|
||||||
|
dispatch({ ...new ChangeDispatchedAction(newAction) });
|
||||||
|
|
||||||
|
props.onRun!();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Run
|
||||||
|
</RunButton>
|
||||||
|
</CommonActionsGroup>
|
||||||
|
<CommandPaneContainer>
|
||||||
|
{(command === Command.Compile && (
|
||||||
|
<CompilePaneComponent></CompilePaneComponent>
|
||||||
|
)) ||
|
||||||
|
(command === Command.DryRun && (
|
||||||
|
<DryRunPaneComponent></DryRunPaneComponent>
|
||||||
|
)) ||
|
||||||
|
(command === Command.Deploy && (
|
||||||
|
<DeployPaneComponent></DeployPaneComponent>
|
||||||
|
)) ||
|
||||||
|
(command === Command.EvaluateFunction && (
|
||||||
|
<EvaluateFunctionPaneComponent></EvaluateFunctionPaneComponent>
|
||||||
|
)) ||
|
||||||
|
(command === Command.EvaluateValue && (
|
||||||
|
<EvaluateValuePaneComponent></EvaluateValuePaneComponent>
|
||||||
|
))}
|
||||||
|
</CommandPaneContainer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
69
tools/webide/packages/client/src/components/deploy-pane.tsx
Normal file
69
tools/webide/packages/client/src/components/deploy-pane.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeEntrypointAction, ChangeStorageAction, DeployState, UseTezBridgeAction } from '../redux/deploy';
|
||||||
|
import { CheckboxComponent } from './checkbox';
|
||||||
|
import { Group, HGroup, Input, Label, Textarea } from './inputs';
|
||||||
|
|
||||||
|
const Container = styled.div``;
|
||||||
|
|
||||||
|
const Checkbox = styled(CheckboxComponent)`
|
||||||
|
margin-right: 0.3em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Hint = styled.span`
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.8em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DeployPaneComponent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const entrypoint = useSelector<AppState, DeployState['entrypoint']>(
|
||||||
|
state => state.deploy.entrypoint
|
||||||
|
);
|
||||||
|
const storage = useSelector<AppState, DeployState['storage']>(
|
||||||
|
state => state.deploy.storage
|
||||||
|
);
|
||||||
|
const useTezBridge = useSelector<AppState, DeployState['useTezBridge']>(
|
||||||
|
state => state.deploy.useTezBridge
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="entrypoint">Entrypoint</Label>
|
||||||
|
<Input
|
||||||
|
id="entrypoint"
|
||||||
|
value={entrypoint}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeEntrypointAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="storage">Storage</Label>
|
||||||
|
<Textarea
|
||||||
|
id="storage"
|
||||||
|
rows={9}
|
||||||
|
value={storage}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeStorageAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Textarea>
|
||||||
|
</Group>
|
||||||
|
<HGroup>
|
||||||
|
<Checkbox
|
||||||
|
checked={!useTezBridge}
|
||||||
|
onChanged={value => dispatch({ ...new UseTezBridgeAction(!value) })}
|
||||||
|
></Checkbox>
|
||||||
|
<Label htmlFor="tezbridge">
|
||||||
|
We'll sign for you
|
||||||
|
<br />
|
||||||
|
<Hint>Got your own key? Deselect to sign with TezBridge</Hint>
|
||||||
|
</Label>
|
||||||
|
</HGroup>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
59
tools/webide/packages/client/src/components/dry-run-pane.tsx
Normal file
59
tools/webide/packages/client/src/components/dry-run-pane.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeEntrypointAction, ChangeParametersAction, ChangeStorageAction, DryRunState } from '../redux/dry-run';
|
||||||
|
import { Group, Input, Label, Textarea } from './inputs';
|
||||||
|
|
||||||
|
const Container = styled.div``;
|
||||||
|
|
||||||
|
export const DryRunPaneComponent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const entrypoint = useSelector<AppState, DryRunState['entrypoint']>(
|
||||||
|
state => state.dryRun.entrypoint
|
||||||
|
);
|
||||||
|
const parameters = useSelector<AppState, DryRunState['parameters']>(
|
||||||
|
state => state.dryRun.parameters
|
||||||
|
);
|
||||||
|
const storage = useSelector<AppState, DryRunState['storage']>(
|
||||||
|
state => state.dryRun.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="entrypoint">Entrypoint</Label>
|
||||||
|
<Input
|
||||||
|
id="entrypoint"
|
||||||
|
value={entrypoint}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeEntrypointAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="parameters">Parameters</Label>
|
||||||
|
<Textarea
|
||||||
|
id="parameters"
|
||||||
|
rows={9}
|
||||||
|
value={parameters}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeParametersAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Textarea>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="storage">Storage</Label>
|
||||||
|
<Textarea
|
||||||
|
id="storage"
|
||||||
|
rows={9}
|
||||||
|
value={storage}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeStorageAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Textarea>
|
||||||
|
</Group>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
32
tools/webide/packages/client/src/components/editor.tsx
Normal file
32
tools/webide/packages/client/src/components/editor.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { MonacoComponent } from './monaco';
|
||||||
|
import { ShareComponent } from './share';
|
||||||
|
import { SyntaxSelectComponent } from './syntax-select';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
flex: 2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
min-height: 2.5em;
|
||||||
|
border-bottom: 5px solid var(--blue_trans1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const EditorComponent = () => {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Header>
|
||||||
|
<SyntaxSelectComponent></SyntaxSelectComponent>
|
||||||
|
<ShareComponent></ShareComponent>
|
||||||
|
</Header>
|
||||||
|
<MonacoComponent></MonacoComponent>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeEntrypointAction, ChangeParametersAction, EvaluateFunctionState } from '../redux/evaluate-function';
|
||||||
|
import { Group, Input, Label, Textarea } from './inputs';
|
||||||
|
|
||||||
|
const Container = styled.div``;
|
||||||
|
|
||||||
|
export const EvaluateFunctionPaneComponent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const entrypoint = useSelector<AppState, EvaluateFunctionState['entrypoint']>(
|
||||||
|
state => state.evaluateFunction.entrypoint
|
||||||
|
);
|
||||||
|
const parameters = useSelector<AppState, EvaluateFunctionState['parameters']>(
|
||||||
|
state => state.evaluateFunction.parameters
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="entrypoint">Entrypoint</Label>
|
||||||
|
<Input
|
||||||
|
id="entrypoint"
|
||||||
|
value={entrypoint}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeEntrypointAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="parameters">Parameters</Label>
|
||||||
|
<Textarea
|
||||||
|
id="parameters"
|
||||||
|
rows={9}
|
||||||
|
value={parameters}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeParametersAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Textarea>
|
||||||
|
</Group>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeEntrypointAction, EvaluateValueState } from '../redux/evaluate-value';
|
||||||
|
import { Group, Input, Label } from './inputs';
|
||||||
|
|
||||||
|
const Container = styled.div``;
|
||||||
|
|
||||||
|
export const EvaluateValuePaneComponent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const entrypoint = useSelector<AppState, EvaluateValueState['entrypoint']>(
|
||||||
|
state => state.evaluateValue.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="entrypoint">Entrypoint</Label>
|
||||||
|
<Input
|
||||||
|
id="entrypoint"
|
||||||
|
value={entrypoint}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeEntrypointAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
107
tools/webide/packages/client/src/components/examples.tsx
Normal file
107
tools/webide/packages/client/src/components/examples.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeSelectedAction, ExamplesState } from '../redux/examples';
|
||||||
|
import { getExample } from '../services/api';
|
||||||
|
|
||||||
|
const bgColor = 'transparent';
|
||||||
|
const borderSize = '5px';
|
||||||
|
const verticalPadding = '0.8em';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
flex: 0.5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MenuItem = styled.div<{ selected: boolean }>`
|
||||||
|
padding: ${verticalPadding} 0 ${verticalPadding} 1em;
|
||||||
|
height: 1.5em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: ${props =>
|
||||||
|
props.selected ? 'var(--blue_trans1)' : bgColor};
|
||||||
|
border-left: ${`${borderSize} solid ${bgColor}`};
|
||||||
|
border-left-color: ${props => (props.selected ? 'var(--blue)' : bgColor)};
|
||||||
|
|
||||||
|
:first-child {
|
||||||
|
margin-top: ${props => (props.selected ? '0' : `-${borderSize}`)};
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
background-color: ${props =>
|
||||||
|
props.selected ? 'var(--blue_trans1)' : 'var(--blue_trans2)'};
|
||||||
|
border-left: ${`${borderSize} solid ${bgColor}`};
|
||||||
|
border-left-color: ${props =>
|
||||||
|
props.selected ? 'var(--blue)' : 'transparent'};
|
||||||
|
:first-child {
|
||||||
|
margin-top: ${props => (props.selected ? '0' : `-${borderSize}`)};
|
||||||
|
padding-top: ${props =>
|
||||||
|
props.selected
|
||||||
|
? `${verticalPadding}`
|
||||||
|
: `calc(${verticalPadding} - ${borderSize})`};
|
||||||
|
border-top: ${props =>
|
||||||
|
props.selected ? '' : `${borderSize} solid var(--blue_opaque1)`};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MenuContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: var(--content_height);
|
||||||
|
box-sizing: border-box;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = styled.div<{ firstChildSelected: boolean }>`
|
||||||
|
border-bottom: ${props =>
|
||||||
|
props.firstChildSelected ? '' : '5px solid var(--blue_trans1)'};
|
||||||
|
min-height: 2.5em;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Examples = () => {
|
||||||
|
const examples = useSelector<AppState, ExamplesState['list']>(
|
||||||
|
(state: AppState) => state.examples.list
|
||||||
|
);
|
||||||
|
const selectedExample = useSelector<AppState, ExamplesState['selected']>(
|
||||||
|
(state: AppState) => state.examples.selected
|
||||||
|
);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Header
|
||||||
|
firstChildSelected={
|
||||||
|
!!selectedExample && examples[0].id === selectedExample.id
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>Examples</span>
|
||||||
|
</Header>
|
||||||
|
<MenuContainer>
|
||||||
|
{examples.map(example => {
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
id={example.id}
|
||||||
|
key={example.id}
|
||||||
|
selected={!!selectedExample && example.id === selectedExample.id}
|
||||||
|
onClick={async () => {
|
||||||
|
const response = await getExample(example.id);
|
||||||
|
|
||||||
|
dispatch({ ...new ChangeSelectedAction(response) });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{example.name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MenuContainer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
83
tools/webide/packages/client/src/components/float-button.tsx
Normal file
83
tools/webide/packages/client/src/components/float-button.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Button = styled.a`
|
||||||
|
margin: 0.1em;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bolder;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
background-color: var(--button_float);
|
||||||
|
box-shadow: 1px 3px 15px 0px rgba(153, 153, 153, 0.4);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
transform-origin: center center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
background-color: var(--blue);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Tooltip = styled.div<{ visible?: boolean }>`
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 3;
|
||||||
|
white-space: nowrap;
|
||||||
|
transform: translateX(-6.5em);
|
||||||
|
|
||||||
|
font-size: var(--font_sub_size);
|
||||||
|
color: var(--tooltip_foreground);
|
||||||
|
background-color: var(--tooltip_background);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease 0.2s;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
opacity: 1;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FloatButtonComponent = (props: {
|
||||||
|
tooltip: string;
|
||||||
|
text: string;
|
||||||
|
href: string;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const [isTooltipShowing, setShowTooltip] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={props.className}>
|
||||||
|
<Tooltip visible={isTooltipShowing}>{props.tooltip}</Tooltip>
|
||||||
|
<Button
|
||||||
|
onMouseOver={() => setShowTooltip(true)}
|
||||||
|
onMouseOut={() => setShowTooltip(false)}
|
||||||
|
href={props.href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</Button>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
68
tools/webide/packages/client/src/components/header.tsx
Normal file
68
tools/webide/packages/client/src/components/header.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
font-family: 'DM Sans', 'Open Sans', sans-serif;
|
||||||
|
box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.3);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Group = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Logo = styled.div`
|
||||||
|
font-size: 1.25em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Link = styled.a`
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #3aa0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
${(props: { versionStyle?: boolean }) =>
|
||||||
|
props.versionStyle &&
|
||||||
|
css`
|
||||||
|
background-color: #efefef;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 3em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HeaderComponent = () => {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Link href="https://ligolang.org">
|
||||||
|
<Logo>LIGO</Logo>
|
||||||
|
</Link>
|
||||||
|
<Link versionStyle href="https://ligolang.org/versions">
|
||||||
|
next
|
||||||
|
</Link>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Link href="https://ligolang.org/docs/intro/installation">Docs</Link>
|
||||||
|
<Link href="https://ligolang.org/docs/tutorials/get-started/tezos-taco-shop-smart-contract">
|
||||||
|
Tutorials
|
||||||
|
</Link>
|
||||||
|
<Link href="https://ligolang.org/blog">Blog</Link>
|
||||||
|
<Link href="https://ligolang.org/docs/contributors/origin">
|
||||||
|
Contribute
|
||||||
|
</Link>
|
||||||
|
</Group>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
47
tools/webide/packages/client/src/components/inputs.tsx
Normal file
47
tools/webide/packages/client/src/components/inputs.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Group = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HGroup = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Label = styled.label`
|
||||||
|
font-size: 1em;
|
||||||
|
color: rgba(153, 153, 153, 1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Input = styled.input`
|
||||||
|
margin: 0.3em 0 0.7em 0;
|
||||||
|
background-color: #eff7ff;
|
||||||
|
border-style: none;
|
||||||
|
border-bottom: 5px solid #e1f1ff;
|
||||||
|
padding: 0.5em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: Menlo, Monaco, 'Courier New', monospace;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: #e1f1ff;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Textarea = styled.textarea`
|
||||||
|
resize: vertical;
|
||||||
|
margin: 0.3em 0 0.7em 0;
|
||||||
|
background-color: #eff7ff;
|
||||||
|
border-style: none;
|
||||||
|
border-bottom: 5px solid #e1f1ff;
|
||||||
|
padding: 0.5em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: Menlo, Monaco, 'Courier New', monospace;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: #e1f1ff;
|
||||||
|
}
|
||||||
|
`;
|
86
tools/webide/packages/client/src/components/monaco.tsx
Normal file
86
tools/webide/packages/client/src/components/monaco.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { useDispatch, useStore } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeCodeAction } from '../redux/editor';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
height: var(--content_height);
|
||||||
|
|
||||||
|
/* This font size is used to calcuate code font size */
|
||||||
|
font-size: 0.8em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MonacoComponent = () => {
|
||||||
|
let containerRef = useRef(null);
|
||||||
|
const store = useStore();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cleanupFunc: Array<() => void> = [];
|
||||||
|
const { editor: editorState } = store.getState();
|
||||||
|
const model = monaco.editor.createModel(
|
||||||
|
editorState.code,
|
||||||
|
editorState.language
|
||||||
|
);
|
||||||
|
|
||||||
|
monaco.editor.defineTheme('ligoTheme', {
|
||||||
|
base: 'vs',
|
||||||
|
inherit: true,
|
||||||
|
rules: [],
|
||||||
|
colors: {
|
||||||
|
'editor.background': '#eff7ff',
|
||||||
|
'editor.lineHighlightBackground': '#cee3ff',
|
||||||
|
'editorLineNumber.foreground': '#888'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
monaco.editor.setTheme('ligoTheme');
|
||||||
|
|
||||||
|
const htmlElement = (containerRef.current as unknown) as HTMLElement;
|
||||||
|
const fontSize = window
|
||||||
|
.getComputedStyle(htmlElement, null)
|
||||||
|
.getPropertyValue('font-size');
|
||||||
|
|
||||||
|
const editor = monaco.editor.create(htmlElement, {
|
||||||
|
fontSize: parseFloat(fontSize),
|
||||||
|
model: model,
|
||||||
|
automaticLayout: true,
|
||||||
|
minimap: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { dispose } = editor.onDidChangeModelContent(() => {
|
||||||
|
dispatch({ ...new ChangeCodeAction(editor.getValue()) });
|
||||||
|
});
|
||||||
|
|
||||||
|
cleanupFunc.push(dispose);
|
||||||
|
|
||||||
|
cleanupFunc.push(
|
||||||
|
store.subscribe(() => {
|
||||||
|
const { editor: editorState }: AppState = store.getState();
|
||||||
|
|
||||||
|
if (editorState.code !== editor.getValue()) {
|
||||||
|
editor.setValue(editorState.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editorState.language !== model.getModeId()) {
|
||||||
|
if (editorState.language === 'reasonligo') {
|
||||||
|
monaco.editor.setModelLanguage(model, 'javascript');
|
||||||
|
} else {
|
||||||
|
monaco.editor.setModelLanguage(model, editorState.language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return function cleanUp() {
|
||||||
|
cleanupFunc.forEach(f => f());
|
||||||
|
};
|
||||||
|
}, [store, dispatch]);
|
||||||
|
|
||||||
|
return <Container id="editor" ref={containerRef}></Container>;
|
||||||
|
};
|
149
tools/webide/packages/client/src/components/output-tab.tsx
Normal file
149
tools/webide/packages/client/src/components/output-tab.tsx
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { PushSpinner } from 'react-spinners-kit';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { CommandState } from '../redux/command';
|
||||||
|
import { DoneLoadingAction, LoadingState } from '../redux/loading';
|
||||||
|
import { ResultState } from '../redux/result';
|
||||||
|
|
||||||
|
const Container = styled.div<{ visible?: boolean }>`
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
font-family: Menlo, Monaco, 'Courier New', monospace;
|
||||||
|
overflow: scroll;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.2s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
transform: translateX(0px);
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CancelButton = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
background-color: #fc683a;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
margin: 1em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Output = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.8em;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
/* This font size is used to calcuate spinner size */
|
||||||
|
font-size: 1em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LoadingContainer = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LoadingMessage = styled.div`
|
||||||
|
padding: 1em 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Pre = styled.pre`
|
||||||
|
margin: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const OutputTabComponent = (props: {
|
||||||
|
selected?: boolean;
|
||||||
|
onCancel?: () => void;
|
||||||
|
}) => {
|
||||||
|
const output = useSelector<AppState, ResultState['output']>(
|
||||||
|
state => state.result.output
|
||||||
|
);
|
||||||
|
const contract = useSelector<AppState, ResultState['contract']>(
|
||||||
|
state => state.result.contract
|
||||||
|
);
|
||||||
|
|
||||||
|
const loading = useSelector<AppState, LoadingState>(state => state.loading);
|
||||||
|
|
||||||
|
const dispatchedAction = useSelector<
|
||||||
|
AppState,
|
||||||
|
CommandState['dispatchedAction']
|
||||||
|
>(state => state.command.dispatchedAction);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const outputRef = useRef(null);
|
||||||
|
const [spinnerSize, setSpinnerSize] = useState(50);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const htmlElement = (outputRef.current as unknown) as HTMLElement;
|
||||||
|
const fontSize = window
|
||||||
|
.getComputedStyle(htmlElement, null)
|
||||||
|
.getPropertyValue('font-size');
|
||||||
|
|
||||||
|
setSpinnerSize(parseFloat(fontSize) * 3);
|
||||||
|
}, [setSpinnerSize]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container visible={props.selected}>
|
||||||
|
<Output id="output" ref={outputRef}>
|
||||||
|
{loading.loading && (
|
||||||
|
<LoadingContainer>
|
||||||
|
<PushSpinner size={spinnerSize} color="#fedace" />
|
||||||
|
<LoadingMessage>{loading.message}</LoadingMessage>
|
||||||
|
<CancelButton
|
||||||
|
onClick={() => {
|
||||||
|
if (dispatchedAction) {
|
||||||
|
dispatchedAction.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
|
||||||
|
if (props.onCancel) {
|
||||||
|
props.onCancel();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</CancelButton>
|
||||||
|
</LoadingContainer>
|
||||||
|
)}
|
||||||
|
{!loading.loading &&
|
||||||
|
((output.length !== 0 && <Pre>{output}</Pre>) ||
|
||||||
|
(contract.length !== 0 && (
|
||||||
|
<span>
|
||||||
|
The contract was successfully deployed to the babylonnet test
|
||||||
|
network.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The address of your new contract is: <i>{contract}</i>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
View your new contract using{' '}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={`https://better-call.dev/babylon/${contract}`}
|
||||||
|
>
|
||||||
|
Better Call Dev
|
||||||
|
</a>
|
||||||
|
!
|
||||||
|
</span>
|
||||||
|
)))}
|
||||||
|
</Output>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
200
tools/webide/packages/client/src/components/share.tsx
Normal file
200
tools/webide/packages/client/src/components/share.tsx
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import { faCopy } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeShareLinkAction, ShareState } from '../redux/share';
|
||||||
|
import { share } from '../services/api';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Button = styled.div<{ clicked?: boolean }>`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
height: 2em;
|
||||||
|
width: 6em;
|
||||||
|
color: var(--blue);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 1em;
|
||||||
|
transition: width 0.3s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.clicked &&
|
||||||
|
css`
|
||||||
|
width: 2em;
|
||||||
|
background-color: white;
|
||||||
|
`}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--blue_opaque1);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Label = styled.span<{ visible?: boolean }>`
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.3s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
!props.visible &&
|
||||||
|
css`
|
||||||
|
opacity: 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CopyIcon = ({ visible, ...props }: { visible: boolean }) => (
|
||||||
|
<FontAwesomeIcon {...props} icon={faCopy}></FontAwesomeIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Copy = styled(CopyIcon)`
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.3s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
!props.visible &&
|
||||||
|
css`
|
||||||
|
opacity: 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Input = styled.input<{ visible?: boolean }>`
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--blue);
|
||||||
|
border-radius: 1em;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
height: 2em;
|
||||||
|
width: 2em;
|
||||||
|
transform: translateX(-0.3em);
|
||||||
|
border: none;
|
||||||
|
padding: 0 1em;
|
||||||
|
font-size: 1em;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
transition: width 0.3s ease-in;
|
||||||
|
outline: none;
|
||||||
|
${props =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
opacity: 1;
|
||||||
|
width: 25em;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Tooltip = styled.div<{ visible?: boolean }>`
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 3;
|
||||||
|
transform: translateY(2.5em);
|
||||||
|
font-size: var(--font_sub_size);
|
||||||
|
color: var(--tooltip_foreground);
|
||||||
|
background-color: var(--tooltip_background);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease 0.2s;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
opacity: 1;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const shareAction = () => {
|
||||||
|
return async function(dispatch: Dispatch, getState: () => AppState) {
|
||||||
|
try {
|
||||||
|
const { hash } = await share(getState());
|
||||||
|
dispatch({ ...new ChangeShareLinkAction(hash) });
|
||||||
|
} catch (ex) {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function copy(element: HTMLInputElement): boolean {
|
||||||
|
element.select();
|
||||||
|
element.setSelectionRange(0, 99999);
|
||||||
|
return document.execCommand('copy');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShareComponent = () => {
|
||||||
|
const inputEl = useRef<HTMLInputElement>(null);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const shareLink = useSelector<AppState, ShareState['link']>(
|
||||||
|
state => state.share.link
|
||||||
|
);
|
||||||
|
const [clicked, setClicked] = useState(false);
|
||||||
|
const [isTooltipShowing, setShowTooltip] = useState(false);
|
||||||
|
|
||||||
|
const SHARE_TOOLTIP = 'Share code';
|
||||||
|
const COPY_TOOLTIP = 'Copy link';
|
||||||
|
const COPIED_TOOLTIP = 'Copied!';
|
||||||
|
const [tooltipMessage, setTooltipMessage] = useState(SHARE_TOOLTIP);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shareLink) {
|
||||||
|
if (inputEl.current && copy(inputEl.current)) {
|
||||||
|
setTooltipMessage(COPIED_TOOLTIP);
|
||||||
|
setShowTooltip(true);
|
||||||
|
} else {
|
||||||
|
setClicked(true);
|
||||||
|
setTooltipMessage(COPY_TOOLTIP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setClicked(false);
|
||||||
|
setShowTooltip(false);
|
||||||
|
setTooltipMessage(SHARE_TOOLTIP);
|
||||||
|
}
|
||||||
|
}, [shareLink]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Input
|
||||||
|
id="share-link"
|
||||||
|
visible={!!shareLink}
|
||||||
|
readOnly
|
||||||
|
ref={inputEl}
|
||||||
|
value={shareLink ? `${window.location.origin}/p/${shareLink}` : ''}
|
||||||
|
></Input>
|
||||||
|
<Button
|
||||||
|
id="share"
|
||||||
|
clicked={clicked}
|
||||||
|
onMouseOver={() => {
|
||||||
|
if (tooltipMessage === COPIED_TOOLTIP) {
|
||||||
|
setTooltipMessage(COPY_TOOLTIP);
|
||||||
|
}
|
||||||
|
setShowTooltip(true);
|
||||||
|
}}
|
||||||
|
onMouseOut={() => setShowTooltip(false)}
|
||||||
|
onClick={() => {
|
||||||
|
if (!shareLink) {
|
||||||
|
dispatch(shareAction());
|
||||||
|
setClicked(true);
|
||||||
|
setTooltipMessage(COPY_TOOLTIP);
|
||||||
|
} else if (inputEl.current) {
|
||||||
|
copy(inputEl.current);
|
||||||
|
setTooltipMessage(COPIED_TOOLTIP);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Label visible={!clicked}>Share</Label>
|
||||||
|
<Copy visible={clicked}></Copy>
|
||||||
|
<Tooltip visible={isTooltipShowing}>{tooltipMessage}</Tooltip>
|
||||||
|
</Button>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
148
tools/webide/packages/client/src/components/syntax-select.tsx
Normal file
148
tools/webide/packages/client/src/components/syntax-select.tsx
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import OutsideClickHandler from 'react-outside-click-handler';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { ChangeLanguageAction, EditorState } from '../redux/editor';
|
||||||
|
import { Language } from '../redux/types';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
min-width: 10em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 2em;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
|
||||||
|
border: 1px solid var(--blue_trans1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ArrowIcon = ({ rotate, ...props }: { rotate: boolean }) => (
|
||||||
|
<FontAwesomeIcon {...props} icon={faCaretDown} size="lg"></FontAwesomeIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Arrow = styled(ArrowIcon)`
|
||||||
|
margin-left: 0.5em;
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--blue_trans1);
|
||||||
|
transition: transform 0.15s ease-in;
|
||||||
|
|
||||||
|
${(props: { rotate: boolean }) =>
|
||||||
|
props.rotate &&
|
||||||
|
css`
|
||||||
|
transform: rotate(180deg);
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const List = styled.ul`
|
||||||
|
position: absolute;
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: white;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: 1px 3px 10px 0px rgba(153, 153, 153, 0.4);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease-in;
|
||||||
|
|
||||||
|
${(props: { visible: boolean }) =>
|
||||||
|
props.visible &&
|
||||||
|
css`
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Option = styled.li`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 2em;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 0 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--blue_trans2);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SyntaxSelectComponent = () => {
|
||||||
|
const OPTIONS = {
|
||||||
|
[Language.PascaLigo]: 'PascaLIGO',
|
||||||
|
[Language.CameLigo]: 'CameLIGO',
|
||||||
|
[Language.ReasonLIGO]: 'ReasonLIGO'
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveOptionToTop = (option: Language) => {
|
||||||
|
return Object.keys(OPTIONS).reduce((list, entry) => {
|
||||||
|
if (entry === option) {
|
||||||
|
list.unshift(entry);
|
||||||
|
} else {
|
||||||
|
list.push(entry as Language);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}, [] as Language[]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const language = useSelector<AppState, EditorState['language']>(
|
||||||
|
state => state.editor.language
|
||||||
|
);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [opened, open] = useState(false);
|
||||||
|
|
||||||
|
const selectOption = (option: Language) => {
|
||||||
|
if (language !== option) {
|
||||||
|
dispatch({ ...new ChangeLanguageAction(option) });
|
||||||
|
}
|
||||||
|
open(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<OutsideClickHandler onOutsideClick={() => open(false)}>
|
||||||
|
<List visible={opened}>
|
||||||
|
{moveOptionToTop(language).map(option => (
|
||||||
|
<Option
|
||||||
|
id={option}
|
||||||
|
key={option}
|
||||||
|
onClick={() => selectOption(option)}
|
||||||
|
>
|
||||||
|
<span>{OPTIONS[option]}</span>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</OutsideClickHandler>
|
||||||
|
<Header id="syntax-select" onClick={() => open(true)}>
|
||||||
|
<span>{OPTIONS[language]}</span>
|
||||||
|
<Arrow rotate={opened}></Arrow>
|
||||||
|
</Header>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
94
tools/webide/packages/client/src/components/tabs-panel.tsx
Normal file
94
tools/webide/packages/client/src/components/tabs-panel.tsx
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import { ConfigureTabComponent } from './configure-tab';
|
||||||
|
import { OutputTabComponent } from './output-tab';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 5px solid var(--blue_trans1);
|
||||||
|
min-height: 2.5em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Label = styled.span`
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--orange);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Tab = styled.div<{ selected?: boolean }>`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Underline = styled.div<{ selectedTab: number }>`
|
||||||
|
position: relative;
|
||||||
|
top: -5px;
|
||||||
|
background-color: var(--orange);
|
||||||
|
height: 5px;
|
||||||
|
margin-bottom: -5px;
|
||||||
|
width: calc(100% / 2);
|
||||||
|
transition: transform 0.2s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
css`
|
||||||
|
transform: translateX(calc(${props.selectedTab} * 100%));
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TabsPanelComponent = () => {
|
||||||
|
const TABS = [
|
||||||
|
{ index: 0, label: 'Configure', id: 'configure-tab' },
|
||||||
|
{ index: 1, label: 'Output', id: 'output-tab' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const [selectedTab, selectTab] = useState(TABS[0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Header>
|
||||||
|
{TABS.map(tab => (
|
||||||
|
<Tab id={tab.id} selected={selectedTab.index === tab.index}>
|
||||||
|
<Label onClick={() => selectTab(tab)}>{tab.label}</Label>
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
|
</Header>
|
||||||
|
<Underline selectedTab={selectedTab.index}></Underline>
|
||||||
|
<Content>
|
||||||
|
<ConfigureTabComponent
|
||||||
|
selected={selectedTab.index === 0}
|
||||||
|
onRun={() => {
|
||||||
|
selectTab(TABS[1]);
|
||||||
|
}}
|
||||||
|
></ConfigureTabComponent>
|
||||||
|
<OutputTabComponent
|
||||||
|
selected={selectedTab.index === 1}
|
||||||
|
onCancel={() => {
|
||||||
|
selectTab(TABS[0]);
|
||||||
|
}}
|
||||||
|
></OutputTabComponent>
|
||||||
|
</Content>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
83
tools/webide/packages/client/src/components/toggle.tsx
Normal file
83
tools/webide/packages/client/src/components/toggle.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
const Container = styled.div<{ checked: boolean }>`
|
||||||
|
position: relative;
|
||||||
|
height: 2em;
|
||||||
|
width: 3.5em;
|
||||||
|
border-radius: 1em;
|
||||||
|
background-color: var(--blue_trans1);
|
||||||
|
border: 1px solid var(--blue);
|
||||||
|
transition: background-color 0.2s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.checked &&
|
||||||
|
css`
|
||||||
|
background-color: var(--blue);
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Button = styled.div<{ checked: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
height: 2em;
|
||||||
|
width: 2em;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
right: calc(1.5em);
|
||||||
|
transition: right 0.2s ease-in;
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
props.checked &&
|
||||||
|
css`
|
||||||
|
right: 0;
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CheckIcon = ({ visible, ...props }: { visible: boolean }) => (
|
||||||
|
<FontAwesomeIcon {...props} icon={faCheck}></FontAwesomeIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Check = styled(CheckIcon)`
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.2s ease-in;
|
||||||
|
color: var(--blue);
|
||||||
|
|
||||||
|
${props =>
|
||||||
|
!props.visible &&
|
||||||
|
css`
|
||||||
|
opacity: 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ToggleComponent = (props: {
|
||||||
|
checked: boolean;
|
||||||
|
onChanged: (value: boolean) => void;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const [isChecked, setChecked] = useState(props.checked);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={props.className} checked={isChecked}>
|
||||||
|
<Button
|
||||||
|
checked={isChecked}
|
||||||
|
onClick={() => {
|
||||||
|
const newState = !isChecked;
|
||||||
|
|
||||||
|
setChecked(newState);
|
||||||
|
props.onChanged(newState);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check visible={isChecked}></Check>
|
||||||
|
</Button>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
31
tools/webide/packages/client/src/configure-store.ts
Normal file
31
tools/webide/packages/client/src/configure-store.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { applyMiddleware, createStore, Middleware } from 'redux';
|
||||||
|
import ReduxThunk from 'redux-thunk';
|
||||||
|
|
||||||
|
import rootReducer, { AppState } from './redux/app';
|
||||||
|
|
||||||
|
declare var defaultServerState: AppState | undefined;
|
||||||
|
|
||||||
|
export default function configureStore() {
|
||||||
|
const store = createStore(
|
||||||
|
rootReducer,
|
||||||
|
{
|
||||||
|
...(typeof defaultServerState === 'undefined' ? {} : defaultServerState)
|
||||||
|
},
|
||||||
|
applyMiddleware(ReduxThunk, cleanRouteOnAction)
|
||||||
|
);
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanRouteOnAction: Middleware = store => next => action => {
|
||||||
|
const { share } = store.getState();
|
||||||
|
next(action);
|
||||||
|
const state = store.getState();
|
||||||
|
if (
|
||||||
|
share.link !== undefined &&
|
||||||
|
state.share.link === undefined &&
|
||||||
|
window.location.pathname !== '/'
|
||||||
|
) {
|
||||||
|
window.history.replaceState({}, document.title, '/');
|
||||||
|
}
|
||||||
|
};
|
79
tools/webide/packages/client/src/index.css
Normal file
79
tools/webide/packages/client/src/index.css
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
:root {
|
||||||
|
/* Note: the LIGO header should be ripped from the main ligolang.org homepage. Specs not included here :-) */
|
||||||
|
/* width of all colored bands: 4px */
|
||||||
|
--orange: #fc683a;
|
||||||
|
--orange_trans: #fedace;
|
||||||
|
|
||||||
|
--blue: rgba(14, 116, 255, 1);
|
||||||
|
--button_float: rgba(14, 116, 255, 0.85);
|
||||||
|
--blue_trans1: rgba(14, 116, 255, 0.15); /* #e1f1ff; */
|
||||||
|
--blue_opaque1: #dbeaff;
|
||||||
|
--blue_trans2: rgba(14, 116, 255, 0.08); /* #eff7ff; */
|
||||||
|
|
||||||
|
--grey: #888;
|
||||||
|
|
||||||
|
--box-shadow: 1px 3px 10px 0px rgba(153, 153, 153, 0.4); /* or #999999 */
|
||||||
|
--border_radius: 3px;
|
||||||
|
|
||||||
|
/* text, where 1rem = 16px */
|
||||||
|
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
--font_weight: 400;
|
||||||
|
|
||||||
|
--font_menu_hover: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
||||||
|
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
--font_menu_size: 1rem;
|
||||||
|
--font_menu_color: rgba(0, 0, 0, 1);
|
||||||
|
|
||||||
|
--font_sub_size: 0.8em;
|
||||||
|
--font_sub_color: rgba(51, 51, 51, 1); /* or #333333; */
|
||||||
|
|
||||||
|
--font_label: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
||||||
|
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
--font_label_size: 0.8rem;
|
||||||
|
--font_label_color: rgba(153, 153, 153, 1); /* or #999999 */
|
||||||
|
|
||||||
|
--font_code: Consolas, source-code-pro, Menlo, Monaco, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
--font_code_size: 0.8rem;
|
||||||
|
--font_code_color: rgba(51, 51, 51, 1); /* or #333333; */
|
||||||
|
|
||||||
|
/* filler text for empty panel */
|
||||||
|
--font_ghost: 2rem;
|
||||||
|
--font_ghost_weight: 700;
|
||||||
|
--font_ghost_color: rgba(153, 153, 153, 0.5); /* or #CFCFCF */
|
||||||
|
|
||||||
|
--content_height: 85vh;
|
||||||
|
|
||||||
|
--tooltip_foreground: white;
|
||||||
|
--tooltip_background: rgba(0, 0, 0, 0.75) /*#404040*/;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1vw;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-editor .current-line ~ .line-numbers {
|
||||||
|
color: var(--orange);
|
||||||
|
border-left: 4px solid var(--blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-editor .margin-view-overlays .current-line,
|
||||||
|
.monaco-editor .view-overlays .current-line {
|
||||||
|
background-color: var(--blue_trans1);
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
12
tools/webide/packages/client/src/index.tsx
Normal file
12
tools/webide/packages/client/src/index.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
import * as serviceWorker from './serviceWorker';
|
||||||
|
|
||||||
|
ReactDOM.render(<App />, document.getElementById('root'));
|
||||||
|
|
||||||
|
// If you want your app to work offline and load faster, you can change
|
||||||
|
// unregister() to register() below. Note this comes with some pitfalls.
|
||||||
|
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||||
|
serviceWorker.unregister();
|
1
tools/webide/packages/client/src/react-app-env.d.ts
vendored
Normal file
1
tools/webide/packages/client/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
@ -0,0 +1,13 @@
|
|||||||
|
export abstract class CancellableAction {
|
||||||
|
private cancelled = false;
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCancelled() {
|
||||||
|
return this.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getAction(): any;
|
||||||
|
}
|
39
tools/webide/packages/client/src/redux/actions/compile.ts
Normal file
39
tools/webide/packages/client/src/redux/actions/compile.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { compileContract, getErrorMessage } from '../../services/api';
|
||||||
|
import { AppState } from '../app';
|
||||||
|
import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
|
||||||
|
import { ChangeOutputAction } from '../result';
|
||||||
|
import { CancellableAction } from './cancellable';
|
||||||
|
|
||||||
|
export class CompileAction extends CancellableAction {
|
||||||
|
getAction() {
|
||||||
|
return async (dispatch: Dispatch, getState: () => AppState) => {
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Compiling contract...') });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { editor, compile: compileState } = getState();
|
||||||
|
const michelsonCode = await compileContract(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
compileState.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new ChangeOutputAction(michelsonCode.result) });
|
||||||
|
} catch (ex) {
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
99
tools/webide/packages/client/src/redux/actions/deploy.ts
Normal file
99
tools/webide/packages/client/src/redux/actions/deploy.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { Tezos } from '@taquito/taquito';
|
||||||
|
import { TezBridgeSigner } from '@taquito/tezbridge-signer';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { compileContract, compileExpression, deploy, getErrorMessage } from '../../services/api';
|
||||||
|
import { AppState } from '../app';
|
||||||
|
import { MichelsonFormat } from '../compile';
|
||||||
|
import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
|
||||||
|
import { ChangeContractAction, ChangeOutputAction } from '../result';
|
||||||
|
import { CancellableAction } from './cancellable';
|
||||||
|
|
||||||
|
Tezos.setProvider({
|
||||||
|
rpc: 'https://api.tez.ie/rpc/babylonnet',
|
||||||
|
signer: new TezBridgeSigner()
|
||||||
|
});
|
||||||
|
|
||||||
|
export class DeployAction extends CancellableAction {
|
||||||
|
async deployWithTezBridge(dispatch: Dispatch, getState: () => AppState) {
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Compiling contract...') });
|
||||||
|
|
||||||
|
const { editor: editorState, deploy: deployState } = getState();
|
||||||
|
|
||||||
|
const michelsonCode = await compileContract(
|
||||||
|
editorState.language,
|
||||||
|
editorState.code,
|
||||||
|
deployState.entrypoint,
|
||||||
|
MichelsonFormat.Json
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Compiling storage...') });
|
||||||
|
const michelsonStorage = await compileExpression(
|
||||||
|
editorState.language,
|
||||||
|
deployState.storage,
|
||||||
|
MichelsonFormat.Json
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Waiting for TezBridge signer...') });
|
||||||
|
|
||||||
|
const op = await Tezos.contract.originate({
|
||||||
|
code: JSON.parse(michelsonCode.result),
|
||||||
|
init: JSON.parse(michelsonStorage.result)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Deploying to babylon network...') });
|
||||||
|
return await op.contract();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deployOnServerSide(dispatch: Dispatch, getState: () => AppState) {
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Deploying to babylon network...') });
|
||||||
|
|
||||||
|
const { editor: editorState, deploy: deployState } = getState();
|
||||||
|
|
||||||
|
return await deploy(
|
||||||
|
editorState.language,
|
||||||
|
editorState.code,
|
||||||
|
deployState.entrypoint,
|
||||||
|
deployState.storage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAction() {
|
||||||
|
return async (dispatch: Dispatch, getState: () => AppState) => {
|
||||||
|
const { deploy } = getState();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const contract = deploy.useTezBridge
|
||||||
|
? await this.deployWithTezBridge(dispatch, getState)
|
||||||
|
: await this.deployOnServerSide(dispatch, getState);
|
||||||
|
|
||||||
|
if (!contract || this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new ChangeContractAction(contract.address) });
|
||||||
|
} catch (ex) {
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
41
tools/webide/packages/client/src/redux/actions/dry-run.ts
Normal file
41
tools/webide/packages/client/src/redux/actions/dry-run.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { dryRun, getErrorMessage } from '../../services/api';
|
||||||
|
import { AppState } from '../app';
|
||||||
|
import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
|
||||||
|
import { ChangeOutputAction } from '../result';
|
||||||
|
import { CancellableAction } from './cancellable';
|
||||||
|
|
||||||
|
export class DryRunAction extends CancellableAction {
|
||||||
|
getAction() {
|
||||||
|
return async (dispatch: Dispatch, getState: () => AppState) => {
|
||||||
|
dispatch({
|
||||||
|
...new UpdateLoadingAction('Waiting for dry run results...')
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { editor, dryRun: dryRunState } = getState();
|
||||||
|
const result = await dryRun(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
dryRunState.entrypoint,
|
||||||
|
dryRunState.parameters,
|
||||||
|
dryRunState.storage
|
||||||
|
);
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({ ...new ChangeOutputAction(result.output) });
|
||||||
|
} catch (ex) {
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { getErrorMessage, runFunction } from '../../services/api';
|
||||||
|
import { AppState } from '../app';
|
||||||
|
import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
|
||||||
|
import { ChangeOutputAction } from '../result';
|
||||||
|
import { CancellableAction } from './cancellable';
|
||||||
|
|
||||||
|
export class EvaluateFunctionAction extends CancellableAction {
|
||||||
|
getAction() {
|
||||||
|
return async (dispatch: Dispatch, getState: () => AppState) => {
|
||||||
|
const { editor, evaluateFunction: evaluateFunctionState } = getState();
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
...new UpdateLoadingAction(
|
||||||
|
`Evaluating ${evaluateFunctionState.entrypoint} ${evaluateFunctionState.parameters}...`
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await runFunction(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
evaluateFunctionState.entrypoint,
|
||||||
|
evaluateFunctionState.parameters
|
||||||
|
);
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({ ...new ChangeOutputAction(result.output) });
|
||||||
|
} catch (ex) {
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { evaluateValue, getErrorMessage } from '../../services/api';
|
||||||
|
import { AppState } from '../app';
|
||||||
|
import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
|
||||||
|
import { ChangeOutputAction } from '../result';
|
||||||
|
import { CancellableAction } from './cancellable';
|
||||||
|
|
||||||
|
export class EvaluateValueAction extends CancellableAction {
|
||||||
|
getAction() {
|
||||||
|
return async (dispatch: Dispatch, getState: () => AppState) => {
|
||||||
|
const { editor, evaluateValue: evaluateValueState } = getState();
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
...new UpdateLoadingAction(
|
||||||
|
`Evaluating "${evaluateValueState.entrypoint}" entrypoint...`
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await evaluateValue(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
evaluateValueState.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new ChangeOutputAction(result.code) });
|
||||||
|
} catch (ex) {
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
41
tools/webide/packages/client/src/redux/app.ts
Normal file
41
tools/webide/packages/client/src/redux/app.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { combineReducers } from 'redux';
|
||||||
|
|
||||||
|
import command, { CommandState } from './command';
|
||||||
|
import compile, { CompileState } from './compile';
|
||||||
|
import deploy, { DeployState } from './deploy';
|
||||||
|
import dryRun, { DryRunState } from './dry-run';
|
||||||
|
import editor, { EditorState } from './editor';
|
||||||
|
import evaluateFunction, { EvaluateFunctionState } from './evaluate-function';
|
||||||
|
import evaluateValue, { EvaluateValueState } from './evaluate-value';
|
||||||
|
import examples, { ExamplesState } from './examples';
|
||||||
|
import loading, { LoadingState } from './loading';
|
||||||
|
import result, { ResultState } from './result';
|
||||||
|
import share, { ShareState } from './share';
|
||||||
|
|
||||||
|
export interface AppState {
|
||||||
|
editor: EditorState;
|
||||||
|
share: ShareState;
|
||||||
|
compile: CompileState;
|
||||||
|
dryRun: DryRunState;
|
||||||
|
deploy: DeployState;
|
||||||
|
evaluateFunction: EvaluateFunctionState;
|
||||||
|
evaluateValue: EvaluateValueState;
|
||||||
|
result: ResultState;
|
||||||
|
command: CommandState;
|
||||||
|
examples: ExamplesState;
|
||||||
|
loading: LoadingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
editor,
|
||||||
|
share,
|
||||||
|
compile,
|
||||||
|
dryRun,
|
||||||
|
deploy,
|
||||||
|
evaluateFunction,
|
||||||
|
evaluateValue,
|
||||||
|
result,
|
||||||
|
command,
|
||||||
|
examples,
|
||||||
|
loading
|
||||||
|
});
|
45
tools/webide/packages/client/src/redux/command.ts
Normal file
45
tools/webide/packages/client/src/redux/command.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { CancellableAction } from './actions/cancellable';
|
||||||
|
import { Command } from './types';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeSelected = 'command-change-selected',
|
||||||
|
ChangeDispatchedAction = 'command-change-dispatched-action'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandState {
|
||||||
|
selected: Command;
|
||||||
|
dispatchedAction: CancellableAction | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeSelectedAction {
|
||||||
|
public readonly type = ActionType.ChangeSelected;
|
||||||
|
constructor(public payload: CommandState['selected']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeDispatchedAction {
|
||||||
|
public readonly type = ActionType.ChangeDispatchedAction;
|
||||||
|
constructor(public payload: CommandState['dispatchedAction']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = ChangeSelectedAction | ChangeDispatchedAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: CommandState = {
|
||||||
|
selected: Command.Compile,
|
||||||
|
dispatchedAction: null
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): CommandState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selected: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeDispatchedAction:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
dispatchedAction: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
41
tools/webide/packages/client/src/redux/compile.ts
Normal file
41
tools/webide/packages/client/src/redux/compile.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { ActionType as ExamplesActionType, ChangeSelectedAction as ChangeSelectedExampleAction } from './examples';
|
||||||
|
|
||||||
|
export enum MichelsonFormat {
|
||||||
|
Text = 'text',
|
||||||
|
Json = 'json'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeEntrypoint = 'compile-change-entrypoint'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompileState {
|
||||||
|
entrypoint: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeEntrypointAction {
|
||||||
|
public readonly type = ActionType.ChangeEntrypoint;
|
||||||
|
constructor(public payload: CompileState['entrypoint']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = ChangeEntrypointAction | ChangeSelectedExampleAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: CompileState = {
|
||||||
|
entrypoint: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): CompileState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ExamplesActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(!action.payload ? DEFAULT_STATE : action.payload.compile)
|
||||||
|
};
|
||||||
|
case ActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entrypoint: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
66
tools/webide/packages/client/src/redux/deploy.ts
Normal file
66
tools/webide/packages/client/src/redux/deploy.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { ActionType as ExamplesActionType, ChangeSelectedAction as ChangeSelectedExampleAction } from './examples';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeEntrypoint = 'deploy-change-entrypoint',
|
||||||
|
ChangeStorage = 'deploy-change-storage',
|
||||||
|
UseTezBridge = 'deploy-use-tezbridge'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeployState {
|
||||||
|
entrypoint: string;
|
||||||
|
storage: string;
|
||||||
|
useTezBridge: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeEntrypointAction {
|
||||||
|
public readonly type = ActionType.ChangeEntrypoint;
|
||||||
|
constructor(public payload: DeployState['entrypoint']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeStorageAction {
|
||||||
|
public readonly type = ActionType.ChangeStorage;
|
||||||
|
constructor(public payload: DeployState['storage']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UseTezBridgeAction {
|
||||||
|
public readonly type = ActionType.UseTezBridge;
|
||||||
|
constructor(public payload: DeployState['useTezBridge']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| ChangeEntrypointAction
|
||||||
|
| ChangeStorageAction
|
||||||
|
| UseTezBridgeAction
|
||||||
|
| ChangeSelectedExampleAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: DeployState = {
|
||||||
|
entrypoint: '',
|
||||||
|
storage: '',
|
||||||
|
useTezBridge: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): DeployState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ExamplesActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(!action.payload ? DEFAULT_STATE : action.payload.deploy)
|
||||||
|
};
|
||||||
|
case ActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entrypoint: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeStorage:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
storage: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.UseTezBridge:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
useTezBridge: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
66
tools/webide/packages/client/src/redux/dry-run.ts
Normal file
66
tools/webide/packages/client/src/redux/dry-run.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { ActionType as ExamplesActionType, ChangeSelectedAction as ChangeSelectedExampleAction } from './examples';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeEntrypoint = 'dry-run-change-entrypoint',
|
||||||
|
ChangeParameters = 'dry-run-change-parameters',
|
||||||
|
ChangeStorage = 'dry-run-change-storage'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DryRunState {
|
||||||
|
entrypoint: string;
|
||||||
|
parameters: string;
|
||||||
|
storage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeEntrypointAction {
|
||||||
|
public readonly type = ActionType.ChangeEntrypoint;
|
||||||
|
constructor(public payload: DryRunState['entrypoint']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeParametersAction {
|
||||||
|
public readonly type = ActionType.ChangeParameters;
|
||||||
|
constructor(public payload: DryRunState['parameters']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeStorageAction {
|
||||||
|
public readonly type = ActionType.ChangeStorage;
|
||||||
|
constructor(public payload: DryRunState['storage']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| ChangeEntrypointAction
|
||||||
|
| ChangeParametersAction
|
||||||
|
| ChangeStorageAction
|
||||||
|
| ChangeSelectedExampleAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: DryRunState = {
|
||||||
|
entrypoint: '',
|
||||||
|
parameters: '',
|
||||||
|
storage: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): DryRunState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ExamplesActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(!action.payload ? DEFAULT_STATE : action.payload.dryRun)
|
||||||
|
};
|
||||||
|
case ActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entrypoint: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeParameters:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
parameters: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeStorage:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
storage: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
53
tools/webide/packages/client/src/redux/editor.ts
Normal file
53
tools/webide/packages/client/src/redux/editor.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { ActionType as ExamplesActionType, ChangeSelectedAction as ChangeSelectedExampleAction } from './examples';
|
||||||
|
import { Language } from './types';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeLanguage = 'editor-change-language',
|
||||||
|
ChangeCode = 'editor-change-code'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditorState {
|
||||||
|
language: Language;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeLanguageAction {
|
||||||
|
public readonly type = ActionType.ChangeLanguage;
|
||||||
|
constructor(public payload: EditorState['language']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeCodeAction {
|
||||||
|
public readonly type = ActionType.ChangeCode;
|
||||||
|
constructor(public payload: EditorState['code']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| ChangeCodeAction
|
||||||
|
| ChangeLanguageAction
|
||||||
|
| ChangeSelectedExampleAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: EditorState = {
|
||||||
|
language: Language.CameLigo,
|
||||||
|
code: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): EditorState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ExamplesActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(!action.payload ? DEFAULT_STATE : action.payload.editor)
|
||||||
|
};
|
||||||
|
case ActionType.ChangeLanguage:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
language: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeCode:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
code: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
55
tools/webide/packages/client/src/redux/evaluate-function.ts
Normal file
55
tools/webide/packages/client/src/redux/evaluate-function.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { ActionType as ExamplesActionType, ChangeSelectedAction as ChangeSelectedExampleAction } from './examples';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeEntrypoint = 'evaluate-function-change-entrypoint',
|
||||||
|
ChangeParameters = 'evaluate-function-change-parameters'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EvaluateFunctionState {
|
||||||
|
entrypoint: string;
|
||||||
|
parameters: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeEntrypointAction {
|
||||||
|
public readonly type = ActionType.ChangeEntrypoint;
|
||||||
|
constructor(public payload: EvaluateFunctionState['entrypoint']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeParametersAction {
|
||||||
|
public readonly type = ActionType.ChangeParameters;
|
||||||
|
constructor(public payload: EvaluateFunctionState['parameters']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| ChangeEntrypointAction
|
||||||
|
| ChangeParametersAction
|
||||||
|
| ChangeSelectedExampleAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: EvaluateFunctionState = {
|
||||||
|
entrypoint: '',
|
||||||
|
parameters: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (
|
||||||
|
state = DEFAULT_STATE,
|
||||||
|
action: Action
|
||||||
|
): EvaluateFunctionState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ExamplesActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(!action.payload ? DEFAULT_STATE : action.payload.evaluateFunction)
|
||||||
|
};
|
||||||
|
case ActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entrypoint: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeParameters:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
parameters: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
36
tools/webide/packages/client/src/redux/evaluate-value.ts
Normal file
36
tools/webide/packages/client/src/redux/evaluate-value.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { ActionType as ExamplesActionType, ChangeSelectedAction as ChangeSelectedExampleAction } from './examples';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeEntrypoint = 'evaluate-value-change-entrypoint'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EvaluateValueState {
|
||||||
|
entrypoint: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeEntrypointAction {
|
||||||
|
public readonly type = ActionType.ChangeEntrypoint;
|
||||||
|
constructor(public payload: EvaluateValueState['entrypoint']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = ChangeEntrypointAction | ChangeSelectedExampleAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: EvaluateValueState = {
|
||||||
|
entrypoint: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): EvaluateValueState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ExamplesActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(!action.payload ? DEFAULT_STATE : action.payload.evaluateValue)
|
||||||
|
};
|
||||||
|
case ActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entrypoint: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
17
tools/webide/packages/client/src/redux/example.ts
Normal file
17
tools/webide/packages/client/src/redux/example.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { CompileState } from './compile';
|
||||||
|
import { DeployState } from './deploy';
|
||||||
|
import { DryRunState } from './dry-run';
|
||||||
|
import { EditorState } from './editor';
|
||||||
|
import { EvaluateFunctionState } from './evaluate-function';
|
||||||
|
import { EvaluateValueState } from './evaluate-value';
|
||||||
|
|
||||||
|
export interface ExampleState {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
editor: EditorState;
|
||||||
|
compile: CompileState;
|
||||||
|
dryRun: DryRunState;
|
||||||
|
deploy: DeployState;
|
||||||
|
evaluateFunction: EvaluateFunctionState;
|
||||||
|
evaluateValue: EvaluateValueState;
|
||||||
|
}
|
38
tools/webide/packages/client/src/redux/examples.ts
Normal file
38
tools/webide/packages/client/src/redux/examples.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { ExampleState } from './example';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeSelected = 'examples-change-selected'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExampleItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExamplesState {
|
||||||
|
selected: ExampleState | null;
|
||||||
|
list: ExampleItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeSelectedAction {
|
||||||
|
public readonly type = ActionType.ChangeSelected;
|
||||||
|
constructor(public payload: ExamplesState['selected']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = ChangeSelectedAction;
|
||||||
|
|
||||||
|
export const DEFAULT_STATE: ExamplesState = {
|
||||||
|
selected: null,
|
||||||
|
list: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): ExamplesState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.ChangeSelected:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selected: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
42
tools/webide/packages/client/src/redux/loading.ts
Normal file
42
tools/webide/packages/client/src/redux/loading.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export enum ActionType {
|
||||||
|
UpdateLoading = 'loading-update-loading',
|
||||||
|
DoneLoading = 'loading-done-loading'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadingState {
|
||||||
|
loading: boolean;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateLoadingAction {
|
||||||
|
public readonly type = ActionType.UpdateLoading;
|
||||||
|
constructor(public payload: LoadingState['message']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DoneLoadingAction {
|
||||||
|
public readonly type = ActionType.DoneLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = UpdateLoadingAction | DoneLoadingAction;
|
||||||
|
|
||||||
|
export const DEFAULT_STATE: LoadingState = {
|
||||||
|
loading: false,
|
||||||
|
message: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): LoadingState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.UpdateLoading:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
message: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.DoneLoading:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...DEFAULT_STATE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
43
tools/webide/packages/client/src/redux/result.ts
Normal file
43
tools/webide/packages/client/src/redux/result.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
export enum ActionType {
|
||||||
|
ChangeOutput = 'result-change-output',
|
||||||
|
ChangeContract = 'result-change-contract'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResultState {
|
||||||
|
output: string;
|
||||||
|
contract: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeOutputAction {
|
||||||
|
public readonly type = ActionType.ChangeOutput;
|
||||||
|
constructor(public payload: ResultState['output']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeContractAction {
|
||||||
|
public readonly type = ActionType.ChangeContract;
|
||||||
|
constructor(public payload: ResultState['contract']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action = ChangeOutputAction | ChangeContractAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: ResultState = {
|
||||||
|
output: '',
|
||||||
|
contract: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): ResultState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.ChangeOutput:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
output: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeContract:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
output: DEFAULT_STATE.output,
|
||||||
|
contract: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
82
tools/webide/packages/client/src/redux/share.ts
Normal file
82
tools/webide/packages/client/src/redux/share.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { ActionType as CompileActionType, ChangeEntrypointAction as ChangeCompileEntrypointAction } from './compile';
|
||||||
|
import {
|
||||||
|
ActionType as DeployActionType,
|
||||||
|
ChangeEntrypointAction as ChangeDeployEntrypointAction,
|
||||||
|
ChangeStorageAction as ChangeDeployStorageAction,
|
||||||
|
UseTezBridgeAction,
|
||||||
|
} from './deploy';
|
||||||
|
import {
|
||||||
|
ActionType as DryRunActionType,
|
||||||
|
ChangeEntrypointAction as ChangeDryRunEntrypointAction,
|
||||||
|
ChangeParametersAction as ChangeDryRunParametersAction,
|
||||||
|
ChangeStorageAction as ChangeDryRunStorageAction,
|
||||||
|
} from './dry-run';
|
||||||
|
import { ActionType as EditorActionType, ChangeCodeAction, ChangeLanguageAction } from './editor';
|
||||||
|
import {
|
||||||
|
ActionType as EvaluateFunctionActionType,
|
||||||
|
ChangeEntrypointAction as ChangeEvaluateFunctionEntrypointAction,
|
||||||
|
ChangeParametersAction as ChangeEvaluateFunctionParametersAction,
|
||||||
|
} from './evaluate-function';
|
||||||
|
import {
|
||||||
|
ActionType as EvaluateValueActionType,
|
||||||
|
ChangeEntrypointAction as ChangeEvaluateValueEntrypointAction,
|
||||||
|
} from './evaluate-value';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeShareLink = 'share-change-link'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShareState {
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeShareLinkAction {
|
||||||
|
public readonly type = ActionType.ChangeShareLink;
|
||||||
|
constructor(public payload: ShareState['link']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| ChangeShareLinkAction
|
||||||
|
| ChangeCodeAction
|
||||||
|
| ChangeLanguageAction
|
||||||
|
| ChangeCompileEntrypointAction
|
||||||
|
| ChangeDeployEntrypointAction
|
||||||
|
| ChangeDeployStorageAction
|
||||||
|
| UseTezBridgeAction
|
||||||
|
| ChangeDryRunEntrypointAction
|
||||||
|
| ChangeDryRunParametersAction
|
||||||
|
| ChangeDryRunStorageAction
|
||||||
|
| ChangeEvaluateFunctionEntrypointAction
|
||||||
|
| ChangeEvaluateFunctionParametersAction
|
||||||
|
| ChangeEvaluateValueEntrypointAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: ShareState = {
|
||||||
|
link: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = DEFAULT_STATE, action: Action): ShareState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case EditorActionType.ChangeCode:
|
||||||
|
case EditorActionType.ChangeLanguage:
|
||||||
|
case CompileActionType.ChangeEntrypoint:
|
||||||
|
case DeployActionType.ChangeEntrypoint:
|
||||||
|
case DeployActionType.ChangeStorage:
|
||||||
|
case DeployActionType.UseTezBridge:
|
||||||
|
case DryRunActionType.ChangeEntrypoint:
|
||||||
|
case DryRunActionType.ChangeParameters:
|
||||||
|
case DryRunActionType.ChangeStorage:
|
||||||
|
case EvaluateFunctionActionType.ChangeEntrypoint:
|
||||||
|
case EvaluateFunctionActionType.ChangeParameters:
|
||||||
|
case EvaluateValueActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...DEFAULT_STATE
|
||||||
|
};
|
||||||
|
case ActionType.ChangeShareLink:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
link: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
13
tools/webide/packages/client/src/redux/types.ts
Normal file
13
tools/webide/packages/client/src/redux/types.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export enum Language {
|
||||||
|
PascaLigo = 'pascaligo',
|
||||||
|
CameLigo = 'cameligo',
|
||||||
|
ReasonLIGO = 'reasonligo'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Command {
|
||||||
|
Compile = 'compile',
|
||||||
|
DryRun = 'dry-run',
|
||||||
|
EvaluateValue = 'evaluate-value',
|
||||||
|
EvaluateFunction = 'evaluate-function',
|
||||||
|
Deploy = 'deploy'
|
||||||
|
}
|
143
tools/webide/packages/client/src/serviceWorker.ts
Normal file
143
tools/webide/packages/client/src/serviceWorker.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// This optional code is used to register a service worker.
|
||||||
|
// register() is not called by default.
|
||||||
|
|
||||||
|
// This lets the app load faster on subsequent visits in production, and gives
|
||||||
|
// it offline capabilities. However, it also means that developers (and users)
|
||||||
|
// will only see deployed updates on subsequent visits to a page, after all the
|
||||||
|
// existing tabs open on the page have been closed, since previously cached
|
||||||
|
// resources are updated in the background.
|
||||||
|
|
||||||
|
// To learn more about the benefits of this model and instructions on how to
|
||||||
|
// opt-in, read https://bit.ly/CRA-PWA
|
||||||
|
|
||||||
|
const isLocalhost = Boolean(
|
||||||
|
window.location.hostname === 'localhost' ||
|
||||||
|
// [::1] is the IPv6 localhost address.
|
||||||
|
window.location.hostname === '[::1]' ||
|
||||||
|
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||||
|
window.location.hostname.match(
|
||||||
|
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
type Config = {
|
||||||
|
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||||
|
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function register(config?: Config) {
|
||||||
|
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||||
|
// The URL constructor is available in all browsers that support SW.
|
||||||
|
const publicUrl = new URL(
|
||||||
|
(process as { env: { [key: string]: string } }).env.PUBLIC_URL,
|
||||||
|
window.location.href
|
||||||
|
);
|
||||||
|
if (publicUrl.origin !== window.location.origin) {
|
||||||
|
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||||
|
// from what our page is served on. This might happen if a CDN is used to
|
||||||
|
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||||
|
|
||||||
|
if (isLocalhost) {
|
||||||
|
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||||
|
checkValidServiceWorker(swUrl, config);
|
||||||
|
|
||||||
|
// Add some additional logging to localhost, pointing developers to the
|
||||||
|
// service worker/PWA documentation.
|
||||||
|
navigator.serviceWorker.ready.then(() => {
|
||||||
|
console.log(
|
||||||
|
'This web app is being served cache-first by a service ' +
|
||||||
|
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Is not localhost. Just register service worker
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerValidSW(swUrl: string, config?: Config) {
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register(swUrl)
|
||||||
|
.then(registration => {
|
||||||
|
registration.onupdatefound = () => {
|
||||||
|
const installingWorker = registration.installing;
|
||||||
|
if (installingWorker == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
installingWorker.onstatechange = () => {
|
||||||
|
if (installingWorker.state === 'installed') {
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
// At this point, the updated precached content has been fetched,
|
||||||
|
// but the previous service worker will still serve the older
|
||||||
|
// content until all client tabs are closed.
|
||||||
|
console.log(
|
||||||
|
'New content is available and will be used when all ' +
|
||||||
|
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute callback
|
||||||
|
if (config && config.onUpdate) {
|
||||||
|
config.onUpdate(registration);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// At this point, everything has been precached.
|
||||||
|
// It's the perfect time to display a
|
||||||
|
// "Content is cached for offline use." message.
|
||||||
|
console.log('Content is cached for offline use.');
|
||||||
|
|
||||||
|
// Execute callback
|
||||||
|
if (config && config.onSuccess) {
|
||||||
|
config.onSuccess(registration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error during service worker registration:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||||
|
// Check if the service worker can be found. If it can't reload the page.
|
||||||
|
fetch(swUrl)
|
||||||
|
.then(response => {
|
||||||
|
// Ensure service worker exists, and that we really are getting a JS file.
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
if (
|
||||||
|
response.status === 404 ||
|
||||||
|
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||||
|
) {
|
||||||
|
// No service worker found. Probably a different app. Reload the page.
|
||||||
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
|
registration.unregister().then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Service worker found. Proceed as normal.
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log(
|
||||||
|
'No internet connection found. App is running in offline mode.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unregister() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
|
registration.unregister();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
132
tools/webide/packages/client/src/services/api.ts
Normal file
132
tools/webide/packages/client/src/services/api.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { AppState } from '../redux/app';
|
||||||
|
import { Language } from '../redux/types';
|
||||||
|
|
||||||
|
export async function getExample(id: string) {
|
||||||
|
const response = await axios.get(`/static/examples/${id}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileContract(
|
||||||
|
syntax: Language,
|
||||||
|
code: string,
|
||||||
|
entrypoint: string,
|
||||||
|
format?: string
|
||||||
|
) {
|
||||||
|
const response = await axios.post('/api/compile-contract', {
|
||||||
|
syntax,
|
||||||
|
code,
|
||||||
|
entrypoint,
|
||||||
|
format
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileExpression(
|
||||||
|
syntax: Language,
|
||||||
|
expression: string,
|
||||||
|
format?: string
|
||||||
|
) {
|
||||||
|
const response = await axios.post('/api/compile-expression', {
|
||||||
|
syntax,
|
||||||
|
expression,
|
||||||
|
format
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function dryRun(
|
||||||
|
syntax: Language,
|
||||||
|
code: string,
|
||||||
|
entrypoint: string,
|
||||||
|
parameters: string,
|
||||||
|
storage: string
|
||||||
|
) {
|
||||||
|
// For whatever reason, storage set by examples is not treated as a string. So we convert it here.
|
||||||
|
storage = `${storage}`;
|
||||||
|
|
||||||
|
const response = await axios.post('/api/dry-run', {
|
||||||
|
syntax,
|
||||||
|
code,
|
||||||
|
entrypoint,
|
||||||
|
parameters,
|
||||||
|
storage
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function share({
|
||||||
|
editor,
|
||||||
|
compile,
|
||||||
|
dryRun,
|
||||||
|
deploy,
|
||||||
|
evaluateValue,
|
||||||
|
evaluateFunction
|
||||||
|
}: Partial<AppState>) {
|
||||||
|
const response = await axios.post('/api/share', {
|
||||||
|
editor,
|
||||||
|
compile,
|
||||||
|
dryRun,
|
||||||
|
deploy,
|
||||||
|
evaluateValue,
|
||||||
|
evaluateFunction
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deploy(
|
||||||
|
syntax: Language,
|
||||||
|
code: string,
|
||||||
|
entrypoint: string,
|
||||||
|
storage: string
|
||||||
|
) {
|
||||||
|
// For whatever reason, storage set by examples is not treated as a string. So we convert it here.
|
||||||
|
storage = `${storage}`;
|
||||||
|
|
||||||
|
const response = await axios.post('/api/deploy', {
|
||||||
|
syntax,
|
||||||
|
code,
|
||||||
|
entrypoint,
|
||||||
|
storage
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function evaluateValue(
|
||||||
|
syntax: Language,
|
||||||
|
code: string,
|
||||||
|
entrypoint: string
|
||||||
|
) {
|
||||||
|
const response = await axios.post('/api/evaluate-value', {
|
||||||
|
syntax,
|
||||||
|
code,
|
||||||
|
entrypoint
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runFunction(
|
||||||
|
syntax: Language,
|
||||||
|
code: string,
|
||||||
|
entrypoint: string,
|
||||||
|
parameters: string
|
||||||
|
) {
|
||||||
|
const response = await axios.post('/api/run-function', {
|
||||||
|
syntax,
|
||||||
|
code,
|
||||||
|
entrypoint,
|
||||||
|
parameters
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getErrorMessage(ex: any): string {
|
||||||
|
if (ex.response && ex.response.data) {
|
||||||
|
return ex.response.data.error;
|
||||||
|
} else if (ex instanceof Error) {
|
||||||
|
return ex.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.stringify(ex);
|
||||||
|
}
|
11
tools/webide/packages/client/src/setupProxy.js
Normal file
11
tools/webide/packages/client/src/setupProxy.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const proxy = require('http-proxy-middleware');
|
||||||
|
|
||||||
|
module.exports = function(app) {
|
||||||
|
app.use(
|
||||||
|
'/api',
|
||||||
|
proxy({
|
||||||
|
target: 'http://localhost:8080',
|
||||||
|
changeOrigin: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
19
tools/webide/packages/client/tsconfig.json
Normal file
19
tools/webide/packages/client/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
12
tools/webide/packages/e2e/Dockerfile
Normal file
12
tools/webide/packages/e2e/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
FROM alekzonder/puppeteer:latest as puppeteer
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package.json
|
||||||
|
COPY package-lock.json package-lock.json
|
||||||
|
COPY jest-puppeteer.config.js jest-puppeteer.config.js
|
||||||
|
COPY test test
|
||||||
|
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
ENTRYPOINT [ "npm", "run", "test" ]
|
18
tools/webide/packages/e2e/docker-compose.yml
Normal file
18
tools/webide/packages/e2e/docker-compose.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
webide:
|
||||||
|
image: "${WEBIDE_IMAGE}"
|
||||||
|
environment:
|
||||||
|
- DATA_DIR=/tmp
|
||||||
|
volumes:
|
||||||
|
- /tmp:/tmp
|
||||||
|
logging:
|
||||||
|
driver: none
|
||||||
|
e2e:
|
||||||
|
build: .
|
||||||
|
environment:
|
||||||
|
- API_HOST=http://webide:8080
|
||||||
|
volumes:
|
||||||
|
- /tmp:/tmp
|
||||||
|
depends_on:
|
||||||
|
- webide
|
13
tools/webide/packages/e2e/jest-puppeteer.config.js
Normal file
13
tools/webide/packages/e2e/jest-puppeteer.config.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
launch: {
|
||||||
|
args: [
|
||||||
|
'--no-sandbox',
|
||||||
|
'--disable-setuid-sandbox'
|
||||||
|
],
|
||||||
|
defaultViewport: {
|
||||||
|
width: 1920,
|
||||||
|
height: 1080
|
||||||
|
},
|
||||||
|
headless: true
|
||||||
|
}
|
||||||
|
};
|
4820
tools/webide/packages/e2e/package-lock.json
generated
Normal file
4820
tools/webide/packages/e2e/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
tools/webide/packages/e2e/package.json
Normal file
22
tools/webide/packages/e2e/package.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "e2e",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "jest --runInBand"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "jest-puppeteer"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"jest": "^24.9.0",
|
||||||
|
"jest-puppeteer": "^4.3.0",
|
||||||
|
"puppeteer": "^1.20.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"node-fetch": "^2.6.0"
|
||||||
|
}
|
||||||
|
}
|
112
tools/webide/packages/e2e/test/common-utils.js
Normal file
112
tools/webide/packages/e2e/test/common-utils.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generic utils
|
||||||
|
//
|
||||||
|
exports.sleep = (time) => {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, time));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.clearText = async keyboard => {
|
||||||
|
await keyboard.down('Shift');
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
await keyboard.press('ArrowUp');
|
||||||
|
}
|
||||||
|
await keyboard.up('Shift');
|
||||||
|
await keyboard.press('Backspace');
|
||||||
|
await keyboard.down('Shift');
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
await keyboard.press('ArrowDown');
|
||||||
|
}
|
||||||
|
await keyboard.up('Shift');
|
||||||
|
await keyboard.press('Backspace');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.createResponseCallback = (page, url) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
page.on('response', function callback(response) {
|
||||||
|
if (response.url() === url) {
|
||||||
|
resolve(response);
|
||||||
|
page.removeListener('response', callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getInnerText = id => {
|
||||||
|
let element = document.getElementById(id);
|
||||||
|
return element && element.textContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getInputValue = id => {
|
||||||
|
let element = document.getElementById(id);
|
||||||
|
return element && element.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Application specific utils
|
||||||
|
//
|
||||||
|
exports.API_HOST = process.env['API_HOST'] || 'http://127.0.0.1:8080';
|
||||||
|
exports.API_ROOT = `${exports.API_HOST}/api`;
|
||||||
|
|
||||||
|
exports.fetchExamples = async () => (await fetch(`${exports.API_HOST}/static/examples/list`)).json();
|
||||||
|
|
||||||
|
exports.runCommandAndGetOutputFor = async (command, endpoint) => {
|
||||||
|
await page.click('#configure-tab');
|
||||||
|
await exports.sleep(1000);
|
||||||
|
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click(`#${command}`);
|
||||||
|
|
||||||
|
// Gotta create response callback before clicking run because some responses are too fast
|
||||||
|
const responseCallback = exports.createResponseCallback(page, `${exports.API_ROOT}/${endpoint}`);
|
||||||
|
|
||||||
|
await page.click('#run');
|
||||||
|
await responseCallback;
|
||||||
|
|
||||||
|
return page.evaluate(exports.getInnerText, 'output');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.verifyAllExamples = async (action, done) => {
|
||||||
|
const examples = await exports.fetchExamples();
|
||||||
|
|
||||||
|
for (example of examples) {
|
||||||
|
await page.click(`#${example.id}`);
|
||||||
|
|
||||||
|
expect(await action()).not.toContain('Error: ');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.verifyWithBlankParameter = async (command, parameter, action, done) => {
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click(`#${command}`);
|
||||||
|
|
||||||
|
await page.click(`#${parameter}`);
|
||||||
|
await exports.clearText(page.keyboard);
|
||||||
|
|
||||||
|
expect(await action()).toEqual(`Error: "${parameter}" is not allowed to be empty`);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.verifyEntrypointBlank = async (command, action, done) => {
|
||||||
|
exports.verifyWithBlankParameter(command, 'entrypoint', action, done);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.verifyParametersBlank = async (command, action, done) => {
|
||||||
|
exports.verifyWithBlankParameter(command, 'parameters', action, done);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.verifyStorageBlank = async (command, action, done) => {
|
||||||
|
exports.verifyWithBlankParameter(command, 'storage', action, done);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.verifyWithCompilationError = async (action, done) => {
|
||||||
|
await page.click('#editor');
|
||||||
|
await page.keyboard.type('asdf');
|
||||||
|
|
||||||
|
expect(await action()).toContain('Error: ');
|
||||||
|
done();
|
||||||
|
};
|
34
tools/webide/packages/e2e/test/compile-contract.spec.js
Normal file
34
tools/webide/packages/e2e/test/compile-contract.spec.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const commonUtils = require('./common-utils');
|
||||||
|
|
||||||
|
const API_HOST = commonUtils.API_HOST;
|
||||||
|
|
||||||
|
const runCommandAndGetOutputFor = commonUtils.runCommandAndGetOutputFor;
|
||||||
|
|
||||||
|
const verifyEntrypointBlank = commonUtils.verifyEntrypointBlank;
|
||||||
|
const verifyAllExamples = commonUtils.verifyAllExamples;
|
||||||
|
const verifyWithCompilationError = commonUtils.verifyWithCompilationError;
|
||||||
|
|
||||||
|
const COMMAND = 'compile';
|
||||||
|
const COMMAND_ENDPOINT = 'compile-contract';
|
||||||
|
|
||||||
|
async function action() {
|
||||||
|
return await runCommandAndGetOutputFor(COMMAND, COMMAND_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Compile contract', () => {
|
||||||
|
beforeAll(() => jest.setTimeout(60000));
|
||||||
|
|
||||||
|
beforeEach(async () => await page.goto(API_HOST));
|
||||||
|
|
||||||
|
it('should compile for each examples', async done => {
|
||||||
|
verifyAllExamples(action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when entrypoint is blank', async done => {
|
||||||
|
verifyEntrypointBlank(COMMAND, action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when code has compilation error', async done => {
|
||||||
|
verifyWithCompilationError(action, done);
|
||||||
|
});
|
||||||
|
});
|
44
tools/webide/packages/e2e/test/dry-run.spec.js
Normal file
44
tools/webide/packages/e2e/test/dry-run.spec.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const commonUtils = require('./common-utils');
|
||||||
|
|
||||||
|
const API_HOST = commonUtils.API_HOST;
|
||||||
|
|
||||||
|
const runCommandAndGetOutputFor = commonUtils.runCommandAndGetOutputFor;
|
||||||
|
|
||||||
|
const verifyAllExamples = commonUtils.verifyAllExamples;
|
||||||
|
const verifyEntrypointBlank = commonUtils.verifyEntrypointBlank;
|
||||||
|
const verifyParametersBlank = commonUtils.verifyParametersBlank;
|
||||||
|
const verifyStorageBlank = commonUtils.verifyStorageBlank;
|
||||||
|
const verifyWithCompilationError = commonUtils.verifyWithCompilationError;
|
||||||
|
|
||||||
|
const COMMAND = 'dry-run';
|
||||||
|
const COMMAND_ENDPOINT = 'dry-run';
|
||||||
|
|
||||||
|
async function action() {
|
||||||
|
return await runCommandAndGetOutputFor(COMMAND, COMMAND_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Dry run contract', () => {
|
||||||
|
beforeAll(() => jest.setTimeout(60000));
|
||||||
|
|
||||||
|
beforeEach(async () => await page.goto(API_HOST));
|
||||||
|
|
||||||
|
it('should dry run for examples', async done => {
|
||||||
|
verifyAllExamples(action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when entrypoint is blank', async done => {
|
||||||
|
verifyEntrypointBlank(COMMAND, action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when parameters is blank', async done => {
|
||||||
|
verifyParametersBlank(COMMAND, action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when storage is blank', async done => {
|
||||||
|
verifyStorageBlank(COMMAND, action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when code has compilation error', async done => {
|
||||||
|
verifyWithCompilationError(action, done);
|
||||||
|
});
|
||||||
|
});
|
39
tools/webide/packages/e2e/test/evaluate-function.spec.js
Normal file
39
tools/webide/packages/e2e/test/evaluate-function.spec.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
const commonUtils = require('./common-utils');
|
||||||
|
|
||||||
|
const API_HOST = commonUtils.API_HOST;
|
||||||
|
|
||||||
|
const runCommandAndGetOutputFor = commonUtils.runCommandAndGetOutputFor;
|
||||||
|
|
||||||
|
const verifyAllExamples = commonUtils.verifyAllExamples;
|
||||||
|
const verifyEntrypointBlank = commonUtils.verifyEntrypointBlank;
|
||||||
|
const verifyParametersBlank = commonUtils.verifyParametersBlank;
|
||||||
|
const verifyWithCompilationError = commonUtils.verifyWithCompilationError;
|
||||||
|
|
||||||
|
const COMMAND = 'evaluate-function';
|
||||||
|
const COMMAND_ENDPOINT = 'run-function';
|
||||||
|
|
||||||
|
async function action() {
|
||||||
|
return await runCommandAndGetOutputFor(COMMAND, COMMAND_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Evaluate function', () => {
|
||||||
|
beforeAll(() => jest.setTimeout(60000));
|
||||||
|
|
||||||
|
beforeEach(async () => await page.goto(API_HOST));
|
||||||
|
|
||||||
|
it('should evaluate function for each examples', async done => {
|
||||||
|
verifyAllExamples(action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when entrypoint is blank', async done => {
|
||||||
|
verifyEntrypointBlank(COMMAND, action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when parameters is blank', async done => {
|
||||||
|
verifyParametersBlank(COMMAND, action, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when code has compilation error', async done => {
|
||||||
|
verifyWithCompilationError(action, done);
|
||||||
|
});
|
||||||
|
});
|
210
tools/webide/packages/e2e/test/share.spec.js
Normal file
210
tools/webide/packages/e2e/test/share.spec.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
const commonUtils = require('./common-utils');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const API_HOST = commonUtils.API_HOST;
|
||||||
|
const API_ROOT = commonUtils.API_ROOT;
|
||||||
|
|
||||||
|
const getInnerText = commonUtils.getInnerText;
|
||||||
|
const getInputValue = commonUtils.getInputValue;
|
||||||
|
const createResponseCallback = commonUtils.createResponseCallback;
|
||||||
|
const clearText = commonUtils.clearText;
|
||||||
|
|
||||||
|
describe('Share', () => {
|
||||||
|
beforeAll(() => jest.setTimeout(60000));
|
||||||
|
|
||||||
|
it('should generate a link', async done => {
|
||||||
|
await page.goto(API_HOST);
|
||||||
|
|
||||||
|
await page.click('#editor');
|
||||||
|
await clearText(page.keyboard);
|
||||||
|
await page.keyboard.type('asdf');
|
||||||
|
|
||||||
|
const responseCallback = createResponseCallback(page, `${API_ROOT}/share`);
|
||||||
|
await page.click('#share');
|
||||||
|
await responseCallback;
|
||||||
|
|
||||||
|
const actualShareLink = await page.evaluate(getInputValue, 'share-link');
|
||||||
|
const expectedShareLink = `${API_HOST}/p/sIpy-2D9ExpCojwuBNw_-g`;
|
||||||
|
|
||||||
|
expect(actualShareLink).toEqual(expectedShareLink);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with v0 schema', async done => {
|
||||||
|
const id = 'v0-schema';
|
||||||
|
const expectedShareLink = `${API_HOST}/p/${id}`;
|
||||||
|
const v0State = {
|
||||||
|
language: 'cameligo',
|
||||||
|
code: 'somecode',
|
||||||
|
entrypoint: 'main',
|
||||||
|
parameters: '1',
|
||||||
|
storage: '2'
|
||||||
|
};
|
||||||
|
fs.writeFileSync(`/tmp/${id}.txt`, JSON.stringify(v0State));
|
||||||
|
|
||||||
|
await page.goto(expectedShareLink);
|
||||||
|
|
||||||
|
// Check share link is correct
|
||||||
|
const actualShareLink = await page.evaluate(getInputValue, 'share-link');
|
||||||
|
expect(actualShareLink).toEqual(expectedShareLink);
|
||||||
|
|
||||||
|
// Check the code is correct. Note, because we are getting inner text we will get
|
||||||
|
// a line number as well. Therefore the expected value has a '1' prefix
|
||||||
|
const actualCode = await page.evaluate(getInnerText, 'editor');
|
||||||
|
expect(actualCode).toContain(`1${v0State.code}`);
|
||||||
|
|
||||||
|
// Check compile configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#compile');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v0State.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check dry run configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#dry-run');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v0State.entrypoint
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'parameters')).toEqual(
|
||||||
|
v0State.parameters
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'storage')).toEqual(
|
||||||
|
v0State.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check deploy configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#deploy');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v0State.entrypoint
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'storage')).toEqual(
|
||||||
|
v0State.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check evaluate function configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#evaluate-function');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v0State.entrypoint
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'parameters')).toEqual(
|
||||||
|
v0State.parameters
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check evaluate value configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#evaluate-value');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v0State.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with v1 schema', async done => {
|
||||||
|
const id = 'v1-schema';
|
||||||
|
const expectedShareLink = `${API_HOST}/p/${id}`;
|
||||||
|
const v1State = {
|
||||||
|
version: 'v1',
|
||||||
|
state: {
|
||||||
|
editor: {
|
||||||
|
language: 'cameligo',
|
||||||
|
code: 'somecode'
|
||||||
|
},
|
||||||
|
compile: {
|
||||||
|
entrypoint: 'main'
|
||||||
|
},
|
||||||
|
dryRun: {
|
||||||
|
entrypoint: 'main',
|
||||||
|
parameters: '1',
|
||||||
|
storage: '2'
|
||||||
|
},
|
||||||
|
deploy: {
|
||||||
|
entrypoint: 'main',
|
||||||
|
storage: '3',
|
||||||
|
useTezBridge: false
|
||||||
|
},
|
||||||
|
evaluateFunction: {
|
||||||
|
entrypoint: 'add',
|
||||||
|
parameters: '(1, 2)'
|
||||||
|
},
|
||||||
|
evaluateValue: {
|
||||||
|
entrypoint: 'a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fs.writeFileSync(`/tmp/${id}.txt`, JSON.stringify(v1State));
|
||||||
|
|
||||||
|
await page.goto(expectedShareLink);
|
||||||
|
|
||||||
|
// Check share link is correct
|
||||||
|
const actualShareLink = await page.evaluate(getInputValue, 'share-link');
|
||||||
|
expect(actualShareLink).toEqual(expectedShareLink);
|
||||||
|
|
||||||
|
// Check the code is correct. Note, because we are getting inner text we will get
|
||||||
|
// a line number as well. Therefore the expected value has a '1' prefix
|
||||||
|
const actualCode = await page.evaluate(getInnerText, 'editor');
|
||||||
|
expect(actualCode).toContain(`1${v1State.state.editor.code}`);
|
||||||
|
|
||||||
|
// Check compile configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#compile');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v1State.state.compile.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check dry run configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#dry-run');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v1State.state.dryRun.entrypoint
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'parameters')).toEqual(
|
||||||
|
v1State.state.dryRun.parameters
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'storage')).toEqual(
|
||||||
|
v1State.state.dryRun.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check deploy configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#deploy');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v1State.state.deploy.entrypoint
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'storage')).toEqual(
|
||||||
|
v1State.state.deploy.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check evaluate function configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#evaluate-function');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v1State.state.evaluateFunction.entrypoint
|
||||||
|
);
|
||||||
|
expect(await page.evaluate(getInputValue, 'parameters')).toEqual(
|
||||||
|
v1State.state.evaluateFunction.parameters
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check evaluate value configuration
|
||||||
|
await page.click('#command-select');
|
||||||
|
await page.click('#evaluate-value');
|
||||||
|
|
||||||
|
expect(await page.evaluate(getInputValue, 'entrypoint')).toEqual(
|
||||||
|
v1State.state.evaluateValue.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
22
tools/webide/packages/server/README.md
Normal file
22
tools/webide/packages/server/README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Quick Start
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn start
|
||||||
|
open http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
# Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
## `yarn start`
|
||||||
|
|
||||||
|
Runs the server in development mode. This will also start the client.
|
||||||
|
|
||||||
|
## `yarn test`
|
||||||
|
|
||||||
|
Runs tests.
|
||||||
|
|
||||||
|
## `yarn build`
|
||||||
|
|
||||||
|
Builds the application for production to the `dist` folder.
|
7
tools/webide/packages/server/jest.config.js
Normal file
7
tools/webide/packages/server/jest.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
roots: ['test'],
|
||||||
|
testMatch: ['**/?(*.)+(spec|test).+(ts|tsx|js)'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(ts|tsx)?$': 'ts-jest'
|
||||||
|
}
|
||||||
|
};
|
7506
tools/webide/packages/server/package-lock.json
generated
Normal file
7506
tools/webide/packages/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
45
tools/webide/packages/server/package.json
Normal file
45
tools/webide/packages/server/package.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"prestart": "cd ../client && npm run build",
|
||||||
|
"start": "nodemon -r @ts-tools/node/r -r tsconfig-paths/register ./src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ts-tools/node": "^1.0.0",
|
||||||
|
"@types/express": "^4.17.1",
|
||||||
|
"@types/express-winston": "^3.0.4",
|
||||||
|
"@types/hapi__joi": "^16.0.1",
|
||||||
|
"@types/jest": "^24.0.23",
|
||||||
|
"@types/joi": "^14.3.3",
|
||||||
|
"@types/node": "10",
|
||||||
|
"@types/tmp": "^0.1.0",
|
||||||
|
"@types/winston": "^2.4.4",
|
||||||
|
"jest": "^24.9.0",
|
||||||
|
"nodemon": "^1.19.3",
|
||||||
|
"ts-jest": "^24.1.0",
|
||||||
|
"ts-node": "^8.4.1",
|
||||||
|
"tsconfig-paths": "^3.9.0",
|
||||||
|
"typescript": "~3.6.3"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@google-cloud/storage": "^4.0.0",
|
||||||
|
"@hapi/joi": "^16.1.7",
|
||||||
|
"@taquito/taquito": "^5.1.0-beta.1",
|
||||||
|
"@types/node-fetch": "^2.5.4",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-winston": "^4.0.1",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"sanitize-html": "^1.20.1",
|
||||||
|
"tmp": "^0.1.0",
|
||||||
|
"winston": "^3.2.1"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
import joi from '@hapi/joi';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
import { CompilerError, LigoCompiler } from '../ligo-compiler';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
|
||||||
|
interface CompileBody {
|
||||||
|
syntax: string;
|
||||||
|
code: string;
|
||||||
|
entrypoint: string;
|
||||||
|
format?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateRequest = (body: any): { value: CompileBody; error: any } => {
|
||||||
|
return joi
|
||||||
|
.object({
|
||||||
|
syntax: joi.string().required(),
|
||||||
|
code: joi.string().required(),
|
||||||
|
entrypoint: joi.string().required(),
|
||||||
|
format: joi.string().optional()
|
||||||
|
})
|
||||||
|
.validate(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function compileContractHandler(req: Request, res: Response) {
|
||||||
|
const { error, value: body } = validateRequest(req.body);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const michelsonCode = await new LigoCompiler().compileContract(
|
||||||
|
body.syntax,
|
||||||
|
body.code,
|
||||||
|
body.entrypoint,
|
||||||
|
body.format || 'text'
|
||||||
|
);
|
||||||
|
|
||||||
|
res.send({ result: michelsonCode });
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex instanceof CompilerError) {
|
||||||
|
res.status(400).json({ error: ex.message });
|
||||||
|
} else {
|
||||||
|
logger.error(ex);
|
||||||
|
res.sendStatus(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user