module Ext_array : sig 
#1 "ext_array.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val reverse_range : 'a array -> int -> int -> unit
(** Some utilities for {!Array} operations *)

val reverse_in_place : 'a array -> unit

val reverse : 'a array -> 'a array

val reverse_of_list : 'a list -> 'a array

val filter : 'a array -> ('a -> bool) -> 'a array

val filter_map : 'a array -> ('a -> 'b option) -> 'b array

val range : int -> int -> int array

val map2i : (int -> 'a -> 'b -> 'c) -> 'a array -> 'b array -> 'c array

val to_list_f : 'a array -> ('a -> 'b) -> 'b list

val to_list_map : 'a array -> ('a -> 'b option) -> 'b list

val to_list_map_acc : 'a array -> 'b list -> ('a -> 'b option) -> 'b list

val of_list_map : 'a list -> ('a -> 'b) -> 'b array

val rfind_with_index : 'a array -> ('a -> 'b -> bool) -> 'b -> int

type 'a split = No_split | Split of 'a array * 'a array

val find_and_split : 'a array -> ('a -> 'b -> bool) -> 'b -> 'a split

val exists : 'a array -> ('a -> bool) -> bool

val is_empty : 'a array -> bool

val for_all2_no_exn : 'a array -> 'b array -> ('a -> 'b -> bool) -> bool

val for_alli : 'a array -> (int -> 'a -> bool) -> bool

val map : 'a array -> ('a -> 'b) -> 'b array

val iter : 'a array -> ('a -> unit) -> unit

val fold_left : 'b array -> 'a -> ('a -> 'b -> 'a) -> 'a

val get_or : 'a array -> int -> (unit -> 'a) -> 'a

end = struct
#1 "ext_array.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

external ( .!() ) : 'a array -> int -> 'a = "%array_unsafe_get"

external ( .!()<- ) : 'a array -> int -> 'a -> unit = "%array_unsafe_set"

let reverse_range a i len =
  if len = 0 then ()
  else
    for k = 0 to (len - 1) / 2 do
      let t = a.!(i + k) in
      a.!(i + k) <- a.!(i + len - 1 - k);
      a.!(i + len - 1 - k) <- t
    done

let reverse_in_place a = reverse_range a 0 (Array.length a)

let reverse a =
  let b_len = Array.length a in
  if b_len = 0 then [||]
  else
    let b = Array.copy a in
    for i = 0 to b_len - 1 do
      Array.unsafe_set b i (Array.unsafe_get a (b_len - 1 - i))
    done;
    b

let reverse_of_list = function
  | [] -> [||]
  | hd :: tl ->
      let len = List.length tl in
      let a = Array.make (len + 1) hd in
      let rec fill i = function
        | [] -> a
        | hd :: tl ->
            Array.unsafe_set a i hd;
            fill (i - 1) tl
      in
      fill (len - 1) tl

let filter a f =
  let arr_len = Array.length a in
  let rec aux acc i =
    if i = arr_len then reverse_of_list acc
    else
      let v = Array.unsafe_get a i in
      if f v then aux (v :: acc) (i + 1) else aux acc (i + 1)
  in
  aux [] 0

let filter_map a (f : _ -> _ option) =
  let arr_len = Array.length a in
  let rec aux acc i =
    if i = arr_len then reverse_of_list acc
    else
      let v = Array.unsafe_get a i in
      match f v with Some v -> aux (v :: acc) (i + 1) | None -> aux acc (i + 1)
  in
  aux [] 0

let range from to_ =
  if from > to_ then invalid_arg "Ext_array.range"
  else Array.init (to_ - from + 1) (fun i -> i + from)

let map2i f a b =
  let len = Array.length a in
  if len <> Array.length b then invalid_arg "Ext_array.map2i"
  else Array.mapi (fun i a -> f i a (Array.unsafe_get b i)) a

let rec tolist_f_aux a f i res =
  if i < 0 then res
  else
    let v = Array.unsafe_get a i in
    tolist_f_aux a f (i - 1) (f v :: res)

let to_list_f a f = tolist_f_aux a f (Array.length a - 1) []

let rec tolist_aux a f i res =
  if i < 0 then res
  else
    tolist_aux a f (i - 1)
      (match f a.!(i) with Some v -> v :: res | None -> res)

let to_list_map a f = tolist_aux a f (Array.length a - 1) []

let to_list_map_acc a acc f = tolist_aux a f (Array.length a - 1) acc

let of_list_map a f =
  match a with
  | [] -> [||]
  | [ a0 ] ->
      let b0 = f a0 in
      [| b0 |]
  | [ a0; a1 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      [| b0; b1 |]
  | [ a0; a1; a2 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      [| b0; b1; b2 |]
  | [ a0; a1; a2; a3 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      let b3 = f a3 in
      [| b0; b1; b2; b3 |]
  | [ a0; a1; a2; a3; a4 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      let b3 = f a3 in
      let b4 = f a4 in
      [| b0; b1; b2; b3; b4 |]
  | a0 :: a1 :: a2 :: a3 :: a4 :: tl ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      let b3 = f a3 in
      let b4 = f a4 in
      let len = List.length tl + 5 in
      let arr = Array.make len b0 in
      Array.unsafe_set arr 1 b1;
      Array.unsafe_set arr 2 b2;
      Array.unsafe_set arr 3 b3;
      Array.unsafe_set arr 4 b4;
      let rec fill i = function
        | [] -> arr
        | hd :: tl ->
            Array.unsafe_set arr i (f hd);
            fill (i + 1) tl
      in
      fill 5 tl

(**
   {[
     # rfind_with_index [|1;2;3|] (=) 2;;
     - : int = 1
               # rfind_with_index [|1;2;3|] (=) 1;;
     - : int = 0
               # rfind_with_index [|1;2;3|] (=) 3;;
     - : int = 2
               # rfind_with_index [|1;2;3|] (=) 4;;
     - : int = -1
   ]}
*)
let rfind_with_index arr cmp v =
  let len = Array.length arr in
  let rec aux i =
    if i < 0 then i
    else if cmp (Array.unsafe_get arr i) v then i
    else aux (i - 1)
  in
  aux (len - 1)

type 'a split = No_split | Split of 'a array * 'a array

let find_with_index arr cmp v =
  let len = Array.length arr in
  let rec aux i len =
    if i >= len then -1
    else if cmp (Array.unsafe_get arr i) v then i
    else aux (i + 1) len
  in
  aux 0 len

let find_and_split arr cmp v : _ split =
  let i = find_with_index arr cmp v in
  if i < 0 then No_split
  else
    Split (Array.sub arr 0 i, Array.sub arr (i + 1) (Array.length arr - i - 1))

(** TODO: available since 4.03, use {!Array.exists} *)

let exists a p =
  let n = Array.length a in
  let rec loop i =
    if i = n then false
    else if p (Array.unsafe_get a i) then true
    else loop (succ i)
  in
  loop 0

let is_empty arr = Array.length arr = 0

let rec unsafe_loop index len p xs ys =
  if index >= len then true
  else
    p (Array.unsafe_get xs index) (Array.unsafe_get ys index)
    && unsafe_loop (succ index) len p xs ys

let for_alli a p =
  let n = Array.length a in
  let rec loop i =
    if i = n then true
    else if p i (Array.unsafe_get a i) then loop (succ i)
    else false
  in
  loop 0

let for_all2_no_exn xs ys p =
  let len_xs = Array.length xs in
  let len_ys = Array.length ys in
  len_xs = len_ys && unsafe_loop 0 len_xs p xs ys

let map a f =
  let open Array in
  let l = length a in
  if l = 0 then [||]
  else
    let r = make l (f (unsafe_get a 0)) in
    for i = 1 to l - 1 do
      unsafe_set r i (f (unsafe_get a i))
    done;
    r

let iter a f =
  let open Array in
  for i = 0 to length a - 1 do
    f (unsafe_get a i)
  done

let fold_left a x f =
  let open Array in
  let r = ref x in
  for i = 0 to length a - 1 do
    r := f !r (unsafe_get a i)
  done;
  !r

let get_or arr i cb =
  if i >= 0 && i < Array.length arr then Array.unsafe_get arr i else cb ()

end
module Ext_bytes : sig 
#1 "ext_bytes.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

external unsafe_blit_string : string -> int -> bytes -> int -> int -> unit
  = "caml_blit_string"
  [@@noalloc]

end = struct
#1 "ext_bytes.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

external unsafe_blit_string : string -> int -> bytes -> int -> int -> unit
  = "caml_blit_string"
  [@@noalloc]

end
module Ext_buffer : sig 
#1 "ext_buffer.mli"
(***********************************************************************)
(*                                                                     *)
(*                                OCaml                                *)
(*                                                                     *)
(*  Pierre Weis and Xavier Leroy, projet Cristal, INRIA Rocquencourt   *)
(*                                                                     *)
(*  Copyright 1999 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the GNU Library General Public License, with    *)
(*  the special exception on linking described in file ../LICENSE.     *)
(*                                                                     *)
(***********************************************************************)

(** Extensible buffers.

    This module implements buffers that automatically expand
    as necessary.  It provides accumulative concatenation of strings
    in quasi-linear time (instead of quadratic time when strings are
    concatenated pairwise).
*)

(* ReScript customization: customized for efficient digest *)

type t
(** The abstract type of buffers. *)

val create : int -> t
(** [create n] returns a fresh buffer, initially empty.
    The [n] parameter is the initial size of the internal byte sequence
    that holds the buffer contents. That byte sequence is automatically
    reallocated when more than [n] characters are stored in the buffer,
    but shrinks back to [n] characters when [reset] is called.
    For best performance, [n] should be of the same order of magnitude
    as the number of characters that are expected to be stored in
    the buffer (for instance, 80 for a buffer that holds one output
    line).  Nothing bad will happen if the buffer grows beyond that
    limit, however. In doubt, take [n = 16] for instance.
    If [n] is not between 1 and {!Sys.max_string_length}, it will
    be clipped to that interval. *)

val contents : t -> string
(** Return a copy of the current contents of the buffer.
    The buffer itself is unchanged. *)

val length : t -> int
(** Return the number of characters currently contained in the buffer. *)

val is_empty : t -> bool

val clear : t -> unit
(** Empty the buffer. *)

val add_char : t -> char -> unit
  [@@inline]
(** [add_char b c] appends the character [c] at the end of the buffer [b]. *)

val add_string : t -> string -> unit
(** [add_string b s] appends the string [s] at the end of the buffer [b]. *)

(* val add_bytes : t -> bytes -> unit *)
(** [add_string b s] appends the string [s] at the end of the buffer [b].
    @since 4.02 *)

(* val add_substring : t -> string -> int -> int -> unit *)
(** [add_substring b s ofs len] takes [len] characters from offset
    [ofs] in string [s] and appends them at the end of the buffer [b]. *)

(* val add_subbytes : t -> bytes -> int -> int -> unit *)
(** [add_substring b s ofs len] takes [len] characters from offset
    [ofs] in byte sequence [s] and appends them at the end of the buffer [b].
    @since 4.02 *)

(* val add_buffer : t -> t -> unit *)
(** [add_buffer b1 b2] appends the current contents of buffer [b2]
    at the end of buffer [b1].  [b2] is not modified. *)

(* val add_channel : t -> in_channel -> int -> unit *)
(** [add_channel b ic n] reads exactly [n] character from the
    input channel [ic] and stores them at the end of buffer [b].
    Raise [End_of_file] if the channel contains fewer than [n]
    characters. *)

val output_buffer : out_channel -> t -> unit
(** [output_buffer oc b] writes the current contents of buffer [b]
    on the output channel [oc]. *)

val digest : t -> Digest.t

val not_equal : t -> string -> bool

val add_int_1 : t -> int -> unit

val add_int_2 : t -> int -> unit

val add_int_3 : t -> int -> unit

val add_int_4 : t -> int -> unit

val add_string_char : t -> string -> char -> unit

val add_ninja_prefix_var : t -> string -> unit

val add_char_string : t -> char -> string -> unit

end = struct
#1 "ext_buffer.ml"
(**************************************************************************)
(*                                                                        *)
(*                                 OCaml                                  *)
(*                                                                        *)
(*    Pierre Weis and Xavier Leroy, projet Cristal, INRIA Rocquencourt    *)
(*                                                                        *)
(*   Copyright 1999 Institut National de Recherche en Informatique et     *)
(*     en Automatique.                                                    *)
(*                                                                        *)
(*   All rights reserved.  This file is distributed under the terms of    *)
(*   the GNU Lesser General Public License version 2.1, with the          *)
(*   special exception on linking described in the file LICENSE.          *)
(*                                                                        *)
(**************************************************************************)

(* Extensible buffers *)

type t = {
  mutable buffer : bytes;
  mutable position : int;
  mutable length : int;
}

let create n =
  let n = if n < 1 then 1 else n in
  let s = Bytes.create n in
  { buffer = s; position = 0; length = n }

let contents b = Bytes.sub_string b.buffer 0 b.position
(* let to_bytes b = Bytes.sub b.buffer 0 b.position  *)

(* let sub b ofs len =
   if ofs < 0 || len < 0 || ofs > b.position - len
   then invalid_arg "Ext_buffer.sub"
   else Bytes.sub_string b.buffer ofs len *)

(* let blit src srcoff dst dstoff len =
   if len < 0 || srcoff < 0 || srcoff > src.position - len
             || dstoff < 0 || dstoff > (Bytes.length dst) - len
   then invalid_arg "Ext_buffer.blit"
   else
    Bytes.unsafe_blit src.buffer srcoff dst dstoff len *)

let length b = b.position

let is_empty b = b.position = 0

let clear b = b.position <- 0

(* let reset b =
   b.position <- 0; b.buffer <- b.initial_buffer;
   b.length <- Bytes.length b.buffer *)

let resize b more =
  let len = b.length in
  let new_len = ref len in
  while b.position + more > !new_len do
    new_len := 2 * !new_len
  done;
  let new_buffer = Bytes.create !new_len in
  (* PR#6148: let's keep using [blit] rather than [unsafe_blit] in
     this tricky function that is slow anyway. *)
  Bytes.blit b.buffer 0 new_buffer 0 b.position;
  b.buffer <- new_buffer;
  b.length <- !new_len;
  assert (b.position + more <= b.length)

let[@inline] add_char b c =
  let pos = b.position in
  if pos >= b.length then resize b 1;
  Bytes.unsafe_set b.buffer pos c;
  b.position <- pos + 1

(* let add_substring b s offset len =
   if offset < 0 || len < 0 || offset > String.length s - len
   then invalid_arg "Ext_buffer.add_substring/add_subbytes";
   let new_position = b.position + len in
   if new_position > b.length then resize b len;
   Ext_bytes.unsafe_blit_string s offset b.buffer b.position len;
   b.position <- new_position *)

(* let add_subbytes b s offset len =
   add_substring b (Bytes.unsafe_to_string s) offset len *)

let add_string b s =
  let len = String.length s in
  let new_position = b.position + len in
  if new_position > b.length then resize b len;
  Ext_bytes.unsafe_blit_string s 0 b.buffer b.position len;
  b.position <- new_position

(* TODO: micro-optimzie *)
let add_string_char b s c =
  let s_len = String.length s in
  let len = s_len + 1 in
  let new_position = b.position + len in
  if new_position > b.length then resize b len;
  let b_buffer = b.buffer in
  Ext_bytes.unsafe_blit_string s 0 b_buffer b.position s_len;
  Bytes.unsafe_set b_buffer (new_position - 1) c;
  b.position <- new_position

let add_char_string b c s =
  let s_len = String.length s in
  let len = s_len + 1 in
  let new_position = b.position + len in
  if new_position > b.length then resize b len;
  let b_buffer = b.buffer in
  let b_position = b.position in
  Bytes.unsafe_set b_buffer b_position c;
  Ext_bytes.unsafe_blit_string s 0 b_buffer (b_position + 1) s_len;
  b.position <- new_position

(* equivalent to add_char " "; add_char "$"; add_string s  *)
let add_ninja_prefix_var b s =
  let s_len = String.length s in
  let len = s_len + 2 in
  let new_position = b.position + len in
  if new_position > b.length then resize b len;
  let b_buffer = b.buffer in
  let b_position = b.position in
  Bytes.unsafe_set b_buffer b_position ' ';
  Bytes.unsafe_set b_buffer (b_position + 1) '$';
  Ext_bytes.unsafe_blit_string s 0 b_buffer (b_position + 2) s_len;
  b.position <- new_position

(* let add_bytes b s = add_string b (Bytes.unsafe_to_string s)

   let add_buffer b bs =
   add_subbytes b bs.buffer 0 bs.position *)

(* let add_channel b ic len =
   if len < 0
    || len > Sys.max_string_length
    then   (* PR#5004 *)
    invalid_arg "Ext_buffer.add_channel";
   if b.position + len > b.length then resize b len;
   really_input ic b.buffer b.position len;
   b.position <- b.position + len *)

let output_buffer oc b = output oc b.buffer 0 b.position

external unsafe_string : bytes -> int -> int -> Digest.t = "caml_md5_string"

let digest b = unsafe_string b.buffer 0 b.position

let rec not_equal_aux (b : bytes) (s : string) i len =
  if i >= len then false
  else
    Bytes.unsafe_get b i <> String.unsafe_get s i
    || not_equal_aux b s (i + 1) len

(** avoid a large copy *)
let not_equal (b : t) (s : string) =
  let b_len = b.position in
  let s_len = String.length s in
  b_len <> s_len || not_equal_aux b.buffer s 0 s_len

(**
   It could be one byte, two bytes, three bytes and four bytes 
   TODO: inline for better performance
*)
let add_int_1 (b : t) (x : int) =
  let c = Char.unsafe_chr (x land 0xff) in
  let pos = b.position in
  if pos >= b.length then resize b 1;
  Bytes.unsafe_set b.buffer pos c;
  b.position <- pos + 1

let add_int_2 (b : t) (x : int) =
  let c1 = Char.unsafe_chr (x land 0xff) in
  let c2 = Char.unsafe_chr ((x lsr 8) land 0xff) in
  let pos = b.position in
  if pos + 1 >= b.length then resize b 2;
  let b_buffer = b.buffer in
  Bytes.unsafe_set b_buffer pos c1;
  Bytes.unsafe_set b_buffer (pos + 1) c2;
  b.position <- pos + 2

let add_int_3 (b : t) (x : int) =
  let c1 = Char.unsafe_chr (x land 0xff) in
  let c2 = Char.unsafe_chr ((x lsr 8) land 0xff) in
  let c3 = Char.unsafe_chr ((x lsr 16) land 0xff) in
  let pos = b.position in
  if pos + 2 >= b.length then resize b 3;
  let b_buffer = b.buffer in
  Bytes.unsafe_set b_buffer pos c1;
  Bytes.unsafe_set b_buffer (pos + 1) c2;
  Bytes.unsafe_set b_buffer (pos + 2) c3;
  b.position <- pos + 3

let add_int_4 (b : t) (x : int) =
  let c1 = Char.unsafe_chr (x land 0xff) in
  let c2 = Char.unsafe_chr ((x lsr 8) land 0xff) in
  let c3 = Char.unsafe_chr ((x lsr 16) land 0xff) in
  let c4 = Char.unsafe_chr ((x lsr 24) land 0xff) in
  let pos = b.position in
  if pos + 3 >= b.length then resize b 4;
  let b_buffer = b.buffer in
  Bytes.unsafe_set b_buffer pos c1;
  Bytes.unsafe_set b_buffer (pos + 1) c2;
  Bytes.unsafe_set b_buffer (pos + 2) c3;
  Bytes.unsafe_set b_buffer (pos + 3) c4;
  b.position <- pos + 4

end
module Ext_spec : sig 
#1 "ext_spec.mli"
type 'a t = (string * 'a * string) array

val assoc3 : 'a t -> string -> 'a option

end = struct
#1 "ext_spec.ml"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* A small module which is also used by {!Bsb_helper} *)
type 'a t = (string * 'a * string) array

let rec unsafe_loop i (l : 'a t) n x =
  if i = n then None
  else
    let y1, y2, _ = Array.unsafe_get l i in
    if y1 = x then Some y2 else unsafe_loop (i + 1) l n x

let assoc3 (l : 'a t) (x : string) : 'a option =
  let n = Array.length l in
  unsafe_loop 0 l n x

end
module Ext_string : sig 
#1 "ext_string.pp.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)








(** Extension to the standard library [String] module, fixed some bugs like
    avoiding locale sensitivity *) 

(** default is false *)    
val split_by : ?keep_empty:bool -> (char -> bool) -> string -> string list


(** remove whitespace letters ('\t', '\n', ' ') on both side*)
val trim : string -> string 


(** default is false *)
val split : ?keep_empty:bool -> string -> char -> string list

(** split by space chars for quick scripting *)
val quick_split_by_ws : string -> string list 



val starts_with : string -> string -> bool

(**
   return [-1] when not found, the returned index is useful 
   see [ends_with_then_chop]
*)
val ends_with_index : string -> string -> int

val ends_with : string -> string -> bool

(**
   [ends_with_then_chop name ext]
   @example:
   {[
     ends_with_then_chop "a.cmj" ".cmj"
       "a"
   ]}
   This is useful in controlled or file case sensitve system
*)
val ends_with_then_chop : string -> string -> string option




(**
   [for_all_from  s start p]
   if [start] is negative, it raises,
   if [start] is too large, it returns true
*)
val for_all_from:
  string -> 
  int -> 
  (char -> bool) -> 
  bool 

val for_all : 
  string -> 
  (char -> bool) -> 
  bool

val is_empty : string -> bool

val repeat : int -> string -> string 

val equal : string -> string -> bool

(**
   [extract_until s cursor sep]
   When [sep] not found, the cursor is updated to -1,
   otherwise cursor is increased to 1 + [sep_position]
   User can not determine whether it is found or not by
   telling the return string is empty since 
   "\n\n" would result in an empty string too.
*)
(* val extract_until:
   string -> 
   int ref -> (* cursor to be updated *)
   char -> 
   string *)

val index_count:  
  string -> 
  int ->
  char -> 
  int -> 
  int 

(* val index_next :
   string -> 
   int ->
   char -> 
   int  *)


(**
   [find ~start ~sub s]
   returns [-1] if not found
*)
val find : ?start:int -> sub:string -> string -> int

val contain_substring : string -> string -> bool 

val non_overlap_count : sub:string -> string -> int 

val rfind : sub:string -> string -> int

(** [tail_from s 1]
    return a substring from offset 1 (inclusive)
*)
val tail_from : string -> int -> string


(** returns negative number if not found *)
val rindex_neg : string -> char -> int 

val rindex_opt : string -> char -> int option


val no_char : string -> char -> int -> int -> bool 


val no_slash : string -> bool 

(** return negative means no slash, otherwise [i] means the place for first slash *)
val no_slash_idx : string -> int 

val no_slash_idx_from : string -> int -> int 

(** if no conversion happens, reference equality holds *)
val replace_slash_backward : string -> string 

(** if no conversion happens, reference equality holds *)
val replace_backward_slash : string -> string 

val empty : string 


external compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;  
  
val single_space : string

val concat3 : string -> string -> string -> string 
val concat4 : string -> string -> string -> string -> string 
val concat5 : string -> string -> string -> string -> string -> string  
val inter2 : string -> string -> string
val inter3 : string -> string -> string -> string 
val inter4 : string -> string -> string -> string -> string
val concat_array : string -> string array -> string 

val single_colon : string 

val parent_dir_lit : string
val current_dir_lit : string

val capitalize_ascii : string -> string

val capitalize_sub:
  string -> 
  int -> 
  string

val uncapitalize_ascii : string -> string

val lowercase_ascii : string -> string 

(** Play parity to {!Ext_buffer.add_int_1} *)
(* val get_int_1 : string -> int -> int 
   val get_int_2 : string -> int -> int 
   val get_int_3 : string -> int -> int 
   val get_int_4 : string -> int -> int  *)

val get_1_2_3_4 : 
  string -> 
  off:int ->  
  int -> 
  int 

val unsafe_sub :   
  string -> 
  int -> 
  int -> 
  string

val is_valid_hash_number:
  string -> 
  bool

val hash_number_as_i32_exn:
  string ->
  int32

val first_marshal_char:  
  string -> 
  bool
end = struct
#1 "ext_string.pp.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)







(*
   {[ split " test_unsafe_obj_ffi_ppx.cmi" ~keep_empty:false ' ']}
*)
let split_by ?(keep_empty=false) is_delim str =
  let len = String.length str in
  let rec loop acc last_pos pos =
    if pos = -1 then
      if last_pos = 0 && not keep_empty then

        acc
      else 
        String.sub str 0 last_pos :: acc
    else
    if is_delim str.[pos] then
      let new_len = (last_pos - pos - 1) in
      if new_len <> 0 || keep_empty then 
        let v = String.sub str (pos + 1) new_len in
        loop ( v :: acc)
          pos (pos - 1)
      else loop acc pos (pos - 1)
    else loop acc last_pos (pos - 1)
  in
  loop [] len (len - 1)

let trim s = 
  let i = ref 0  in
  let j = String.length s in 
  while !i < j &&  
        let u = String.unsafe_get s !i in 
        u = '\t' || u = '\n' || u = ' ' 
  do 
    incr i;
  done;
  let k = ref (j - 1)  in 
  while !k >= !i && 
        let u = String.unsafe_get s !k in 
        u = '\t' || u = '\n' || u = ' ' do 
    decr k ;
  done;
  String.sub s !i (!k - !i + 1)

let split ?keep_empty  str on = 
  if str = "" then [] else 
    split_by ?keep_empty (fun x -> (x : char) = on) str  ;;

let quick_split_by_ws str : string list = 
  split_by ~keep_empty:false (fun x -> x = '\t' || x = '\n' || x = ' ') str

let starts_with s beg = 
  let beg_len = String.length beg in
  let s_len = String.length s in
  beg_len <=  s_len &&
  (let i = ref 0 in
   while !i <  beg_len 
         && String.unsafe_get s !i =
            String.unsafe_get beg !i do 
     incr i 
   done;
   !i = beg_len
  )

let rec ends_aux s end_ j k = 
  if k < 0 then (j + 1)
  else if String.unsafe_get s j = String.unsafe_get end_ k then 
    ends_aux s end_ (j - 1) (k - 1)
  else  -1   

(** return an index which is minus when [s] does not 
    end with [beg]
*)
let ends_with_index s end_ : int = 
  let s_finish = String.length s - 1 in
  let s_beg = String.length end_ - 1 in
  if s_beg > s_finish then -1
  else
    ends_aux s end_ s_finish s_beg

let ends_with s end_ = ends_with_index s end_ >= 0 

let ends_with_then_chop s beg = 
  let i =  ends_with_index s beg in 
  if i >= 0 then Some (String.sub s 0 i) 
  else None

(* let check_suffix_case = ends_with  *)
(* let check_suffix_case_then_chop = ends_with_then_chop *)

(* let check_any_suffix_case s suffixes = 
   Ext_list.exists suffixes (fun x -> check_suffix_case s x)  *)

(* let check_any_suffix_case_then_chop s suffixes = 
   let rec aux suffixes = 
    match suffixes with 
    | [] -> None 
    | x::xs -> 
      let id = ends_with_index s x in 
      if id >= 0 then Some (String.sub s 0 id)
      else aux xs in 
   aux suffixes     *)




(* it is unsafe to expose such API as unsafe since 
   user can provide bad input range 

*)
let rec unsafe_for_all_range s ~start ~finish p =     
  start > finish ||
  p (String.unsafe_get s start) && 
  unsafe_for_all_range s ~start:(start + 1) ~finish p

let for_all_from s start  p = 
  let len = String.length s in 
  if start < 0  then invalid_arg "Ext_string.for_all_from"
  else unsafe_for_all_range s ~start ~finish:(len - 1) p 


let for_all s (p : char -> bool)  =   
  unsafe_for_all_range s ~start:0  ~finish:(String.length s - 1) p 

let is_empty s = String.length s = 0


let repeat n s  =
  let len = String.length s in
  let res = Bytes.create(n * len) in
  for i = 0 to pred n do
    String.blit s 0 res (i * len) len
  done;
  Bytes.to_string res




let unsafe_is_sub ~sub i s j ~len =
  let rec check k =
    if k = len
    then true
    else 
      String.unsafe_get sub (i+k) = 
      String.unsafe_get s (j+k) && check (k+1)
  in
  j+len <= String.length s && check 0



let find ?(start=0) ~sub s =
  let exception Local_exit in
  let n = String.length sub in
  let s_len = String.length s in 
  let i = ref start in  
  try
    while !i + n <= s_len do
      if unsafe_is_sub ~sub 0 s !i ~len:n then
        raise_notrace Local_exit;
      incr i
    done;
    -1
  with Local_exit ->
    !i

let contain_substring s sub = 
  find s ~sub >= 0 

(** TODO: optimize 
    avoid nonterminating when string is empty 
*)
let non_overlap_count ~sub s = 
  let sub_len = String.length sub in 
  let rec aux  acc off = 
    let i = find ~start:off ~sub s  in 
    if i < 0 then acc 
    else aux (acc + 1) (i + sub_len) in
  if String.length sub = 0 then invalid_arg "Ext_string.non_overlap_count"
  else aux 0 0  


let rfind ~sub s =
  let exception Local_exit in   
  let n = String.length sub in
  let i = ref (String.length s - n) in
  try
    while !i >= 0 do
      if unsafe_is_sub ~sub 0 s !i ~len:n then 
        raise_notrace Local_exit;
      decr i
    done;
    -1
  with Local_exit ->
    !i

let tail_from s x = 
  let len = String.length s  in 
  if  x > len then invalid_arg ("Ext_string.tail_from " ^s ^ " : "^ string_of_int x )
  else String.sub s x (len - x)

let equal (x : string) y  = x = y

(* let rec index_rec s lim i c =
   if i >= lim then -1 else
   if String.unsafe_get s i = c then i 
   else index_rec s lim (i + 1) c *)



let rec index_rec_count s lim i c count =
  if i >= lim then -1 else
  if String.unsafe_get s i = c then 
    if count = 1 then i 
    else index_rec_count s lim (i + 1) c (count - 1)
  else index_rec_count s lim (i + 1) c count

let index_count s i c count =     
  let lim = String.length s in 
  if i < 0 || i >= lim || count < 1 then 
    invalid_arg ("index_count: ( " ^string_of_int i ^ "," ^string_of_int count ^ ")" );
  index_rec_count s lim i c count 

(* let index_next s i c =   
   index_count s i c 1  *)

(* let extract_until s cursor c =       
   let len = String.length s in   
   let start = !cursor in 
   if start < 0 || start >= len then (
    cursor := -1;
    ""
    )
   else 
    let i = index_rec s len start c in   
    let finish = 
      if i < 0 then (      
        cursor := -1 ;
        len 
      )
      else (
        cursor := i + 1;
        i 
      ) in 
    String.sub s start (finish - start) *)

let rec rindex_rec s i c =
  if i < 0 then i else
  if String.unsafe_get s i = c then i else rindex_rec s (i - 1) c;;

let rec rindex_rec_opt s i c =
  if i < 0 then None else
  if String.unsafe_get s i = c then Some i else rindex_rec_opt s (i - 1) c;;

let rindex_neg s c = 
  rindex_rec s (String.length s - 1) c;;

let rindex_opt s c = 
  rindex_rec_opt s (String.length s - 1) c;;


(** TODO: can be improved to return a positive integer instead *)
let rec unsafe_no_char x ch i  last_idx = 
  i > last_idx  || 
  (String.unsafe_get x i <> ch && unsafe_no_char x ch (i + 1)  last_idx)

let rec unsafe_no_char_idx x ch i last_idx = 
  if i > last_idx  then -1 
  else 
  if String.unsafe_get x i <> ch then 
    unsafe_no_char_idx x ch (i + 1)  last_idx
  else i

let no_char x ch i len  : bool =
  let str_len = String.length x in 
  if i < 0 || i >= str_len || len >= str_len then invalid_arg "Ext_string.no_char"   
  else unsafe_no_char x ch i len 


let no_slash x = 
  unsafe_no_char x '/' 0 (String.length x - 1)

let no_slash_idx x = 
  unsafe_no_char_idx x '/' 0 (String.length x - 1)

let no_slash_idx_from x from = 
  let last_idx = String.length x - 1  in 
  assert (from >= 0); 
  unsafe_no_char_idx x '/' from last_idx

let replace_slash_backward (x : string ) = 
  let len = String.length x in 
  if unsafe_no_char x '/' 0  (len - 1) then x 
  else 
    String.map (function 
        | '/' -> '\\'
        | x -> x ) x 

let replace_backward_slash (x : string)=
  let len = String.length x in
  if unsafe_no_char x '\\' 0  (len -1) then x 
  else  
    String.map (function 
        |'\\'-> '/'
        | x -> x) x

let empty = ""


external compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;    

let single_space = " "
let single_colon = ":"

let concat_array sep (s : string array) =   
  let s_len = Array.length s in 
  match s_len with 
  | 0 -> empty 
  | 1 -> Array.unsafe_get s 0
  | _ ->     
    let sep_len = String.length sep in 
    let len = ref 0 in 
    for i = 0 to  s_len - 1 do 
      len := !len + String.length (Array.unsafe_get s i)
    done;
    let target = 
      Bytes.create 
        (!len + (s_len - 1) * sep_len ) in    
    let hd = (Array.unsafe_get s 0) in     
    let hd_len = String.length hd in 
    String.unsafe_blit hd  0  target 0 hd_len;   
    let current_offset = ref hd_len in     
    for i = 1 to s_len - 1 do 
      String.unsafe_blit sep 0 target  !current_offset sep_len;
      let cur = Array.unsafe_get s i in 
      let cur_len = String.length cur in     
      let new_off_set = (!current_offset + sep_len ) in
      String.unsafe_blit cur 0 target new_off_set cur_len; 
      current_offset := 
        new_off_set + cur_len ; 
    done;
    Bytes.unsafe_to_string target   

let concat3 a b c = 
  let a_len = String.length a in 
  let b_len = String.length b in 
  let c_len = String.length c in 
  let len = a_len + b_len + c_len in 
  let target = Bytes.create len in 
  String.unsafe_blit a 0 target 0 a_len ; 
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  Bytes.unsafe_to_string target

let concat4 a b c d =
  let a_len = String.length a in 
  let b_len = String.length b in 
  let c_len = String.length c in 
  let d_len = String.length d in 
  let len = a_len + b_len + c_len + d_len in 

  let target = Bytes.create len in 
  String.unsafe_blit a 0 target 0 a_len ; 
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
  Bytes.unsafe_to_string target


let concat5 a b c d e =
  let a_len = String.length a in 
  let b_len = String.length b in 
  let c_len = String.length c in 
  let d_len = String.length d in 
  let e_len = String.length e in 
  let len = a_len + b_len + c_len + d_len + e_len in 

  let target = Bytes.create len in 
  String.unsafe_blit a 0 target 0 a_len ; 
  String.unsafe_blit b 0 target a_len b_len;
  String.unsafe_blit c 0 target (a_len + b_len) c_len;
  String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
  String.unsafe_blit e 0 target (a_len + b_len + c_len + d_len) e_len;
  Bytes.unsafe_to_string target



let inter2 a b = 
  concat3 a single_space b 


let inter3 a b c = 
  concat5 a  single_space  b  single_space  c 





let inter4 a b c d =
  concat_array single_space [| a; b ; c; d|]


let parent_dir_lit = ".."    
let current_dir_lit = "."


(* reference {!Bytes.unppercase} *)
let capitalize_ascii (s : string) : string = 
  if String.length s = 0 then s 
  else 
    begin
      let c = String.unsafe_get s 0 in 
      if (c >= 'a' && c <= 'z')
      || (c >= '\224' && c <= '\246')
      || (c >= '\248' && c <= '\254') then 
        let uc = Char.unsafe_chr (Char.code c - 32) in 
        let bytes = Bytes.of_string s in
        Bytes.unsafe_set bytes 0 uc;
        Bytes.unsafe_to_string bytes 
      else s 
    end

let capitalize_sub (s : string) len : string = 
  let slen = String.length s in 
  if  len < 0 || len > slen then invalid_arg "Ext_string.capitalize_sub"
  else 
  if len = 0 then ""
  else 
    let bytes = Bytes.create len in 
    let uc = 
      let c = String.unsafe_get s 0 in 
      if (c >= 'a' && c <= 'z')
      || (c >= '\224' && c <= '\246')
      || (c >= '\248' && c <= '\254') then 
        Char.unsafe_chr (Char.code c - 32) else c in 
    Bytes.unsafe_set bytes 0 uc;
    for i = 1 to len - 1 do 
      Bytes.unsafe_set bytes i (String.unsafe_get s i)
    done ;
    Bytes.unsafe_to_string bytes 



let uncapitalize_ascii =
  String.uncapitalize_ascii

let lowercase_ascii = String.lowercase_ascii

external (.![]) : string -> int -> int = "%string_unsafe_get"

let get_int_1_unsafe (x : string) off : int = 
  x.![off]

let get_int_2_unsafe (x : string) off : int =   
  x.![off] lor   
  x.![off+1] lsl 8

let get_int_3_unsafe (x : string) off : int = 
  x.![off] lor   
  x.![off+1] lsl 8  lor 
  x.![off+2] lsl 16


let get_int_4_unsafe (x : string) off : int =     
  x.![off] lor   
  x.![off+1] lsl 8  lor 
  x.![off+2] lsl 16 lor
  x.![off+3] lsl 24 

let get_1_2_3_4 (x : string) ~off len : int =  
  if len = 1 then get_int_1_unsafe x off 
  else if len = 2 then get_int_2_unsafe x off 
  else if len = 3 then get_int_3_unsafe x off 
  else if len = 4 then get_int_4_unsafe x off 
  else assert false

let unsafe_sub  x offs len =
  let b = Bytes.create len in 
  Ext_bytes.unsafe_blit_string x offs b 0 len;
  (Bytes.unsafe_to_string b)

let is_valid_hash_number (x:string) = 
  let len = String.length x in 
  len > 0 && (
    let a = x.![0] in 
    a <= 57 &&
    (if len > 1 then 
       a > 48 && 
       for_all_from x 1 (function '0' .. '9' -> true | _ -> false)
     else
       a >= 48 )
  ) 


let hash_number_as_i32_exn 
    ( x : string) : int32 = 
  Int32.of_string x    


let first_marshal_char (x : string) = 
    x <> ""   &&
    ( String.unsafe_get x  0 = '\132')
  
end
module Bsb_arg : sig 
#1 "bsb_arg.mli"
(* Copyright (C) 2020 - Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type string_action =
  | String_call of (string -> unit)
  | String_set of string ref

type unit_action = Unit_call of (unit -> unit) | Unit_set of bool ref

type spec = Unit of unit_action | String of string_action

type anon_fun = rev_args:string list -> unit

exception Bad of string

val parse_exn :
  usage:string ->
  argv:string array ->
  ?start:int ->
  ?finish:int ->
  (string * spec * string) array ->
  anon_fun ->
  unit

val bad_arg : string -> 'a

end = struct
#1 "bsb_arg.ml"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type anon_fun = rev_args:string list -> unit

type string_action =
  | String_call of (string -> unit)
  | String_set of string ref

type unit_action = Unit_call of (unit -> unit) | Unit_set of bool ref

type spec = Unit of unit_action | String of string_action

type error = Unknown of string | Missing of string

type t = spec Ext_spec.t

exception Bad of string

let bad_arg s = raise_notrace (Bad s)

let ( +> ) = Ext_buffer.add_string

let usage_b (buf : Ext_buffer.t) ~usage (speclist : t) =
  buf +> usage;
  if Ext_array.is_empty speclist then ()
  else (
    buf +> "\nOptions:\n";
    let max_col = ref 0 in
    Ext_array.iter speclist (fun (key, _, _) ->
        if String.length key > !max_col then max_col := String.length key);
    Ext_array.iter speclist (fun (key, _, doc) ->
        if not (Ext_string.starts_with doc "*internal*") then (
          buf +> "  ";
          buf +> key;
          buf +> String.make (!max_col - String.length key + 2) ' ';
          let cur = ref 0 in
          let doc_length = String.length doc in
          while !cur < doc_length do
            if !cur <> 0 then (
              buf +> "\n";
              buf +> String.make (!max_col + 4) ' ');
            match String.index_from_opt doc !cur '\n' with
            | None ->
                buf +> String.sub doc !cur (String.length doc - !cur);
                cur := doc_length
            | Some new_line_pos ->
                buf +> String.sub doc !cur (new_line_pos - !cur);
                cur := new_line_pos + 1
          done;
          buf +> "\n")))

let stop_raise ~usage ~(error : error) (speclist : t) =
  let b = Ext_buffer.create 200 in
  (match error with
  | Unknown ("-help" | "--help" | "-h") ->
      usage_b b ~usage speclist;
      Ext_buffer.output_buffer stdout b;
      exit 0
  | Unknown s ->
      b +> "unknown option: '";
      b +> s;
      b +> "'.\n"
  | Missing s ->
      b +> "option '";
      b +> s;
      b +> "' needs an argument.\n");
  usage_b b ~usage speclist;
  bad_arg (Ext_buffer.contents b)

let parse_exn ~usage ~argv ?(start = 1) ?(finish = Array.length argv)
    (speclist : t) anonfun =
  let current = ref start in
  let rev_list = ref [] in
  while !current < finish do
    let s = argv.(!current) in
    incr current;
    if s <> "" && s.[0] = '-' then
      match Ext_spec.assoc3 speclist s with
      | Some action -> (
          match action with
          | Unit r -> (
              match r with
              | Unit_set r -> r.contents <- true
              | Unit_call f -> f ())
          | String f -> (
              if !current >= finish then
                stop_raise ~usage ~error:(Missing s) speclist
              else
                let arg = argv.(!current) in
                incr current;
                match f with
                | String_call f -> f arg
                | String_set u -> u.contents <- arg))
      | None -> stop_raise ~usage ~error:(Unknown s) speclist
    else rev_list := s :: !rev_list
  done;
  anonfun ~rev_args:!rev_list

end
module Bsb_build_schemas
= struct
#1 "bsb_build_schemas.ml"
(* Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* let files = "files" *)
let version = "version"
let name = "name"

(* let ocaml_config = "ocaml-config" *)
let bsdep = "bsdep"
let ppx_flags = "ppx-flags"
let pp_flags = "pp-flags"
let bsc = "bsc"
let refmt = "refmt"
let bs_external_includes = "bs-external-includes"
let bs_lib_dir = "bs-lib-dir"
let bs_dependencies = "bs-dependencies"
let pinned_dependencies = "pinned-dependencies"
let bs_dev_dependencies = "bs-dev-dependencies"
let sources = "sources"
let dir = "dir"
let files = "files"
let subdirs = "subdirs"
let bsc_flags = "bsc-flags"
let excludes = "excludes"
let slow_re = "slow-re"
let resources = "resources"
let public = "public"
let js_post_build = "js-post-build"
let cmd = "cmd"
let ninja = "ninja"
let package_specs = "package-specs"
let generate_merlin = "generate-merlin"
let type_ = "type"
let dev = "dev"
let export_all = "all"
let export_none = "none"
let use_stdlib = "use-stdlib"
let external_stdlib = "external-stdlib"
let reason = "reason"
let react_jsx = "react-jsx"
let jsx = "jsx"
let jsx_version = "version"
let jsx_module = "module"
let jsx_mode = "mode"
let jsx_v3_dependencies = "v3-dependencies"
let entries = "entries"
let backend = "backend"
let main_module = "main-module"
let cut_generators = "cut-generators"
let generators = "generators"
let command = "command"
let edge = "edge"
let namespace = "namespace"
let in_source = "in-source"
let warnings = "warnings"
let number = "number"
let error = "error"
let suffix = "suffix"
let gentypeconfig = "gentypeconfig"
let language = "language"
let path = "path"
let ignored_dirs = "ignored-dirs"

end
module Ext_list : sig 
#1 "ext_list.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val map : 'a list -> ('a -> 'b) -> 'b list

val map_combine : 'a list -> 'b list -> ('a -> 'c) -> ('c * 'b) list

val combine_array : 'a array -> 'b list -> ('a -> 'c) -> ('c * 'b) list

val combine_array_append :
  'a array -> 'b list -> ('c * 'b) list -> ('a -> 'c) -> ('c * 'b) list

val has_string : string list -> string -> bool

val map_split_opt :
  'a list -> ('a -> 'b option * 'c option) -> 'b list * 'c list

val mapi : 'a list -> (int -> 'a -> 'b) -> 'b list

val mapi_append : 'a list -> (int -> 'a -> 'b) -> 'b list -> 'b list

val map_snd : ('a * 'b) list -> ('b -> 'c) -> ('a * 'c) list

val map_last : 'a list -> (bool -> 'a -> 'b) -> 'b list
(** [map_last f xs ]
    will pass [true] to [f] for the last element, 
    [false] otherwise. 
    For empty list, it returns empty
*)

val last : 'a list -> 'a
(** [last l]
    return the last element
    raise if the list is empty
*)

val append : 'a list -> 'a list -> 'a list

val append_one : 'a list -> 'a -> 'a list

val map_append : 'b list -> 'a list -> ('b -> 'a) -> 'a list

val fold_right : 'a list -> 'b -> ('a -> 'b -> 'b) -> 'b

val fold_right2 : 'a list -> 'b list -> 'c -> ('a -> 'b -> 'c -> 'c) -> 'c

val fold_right3 :
  'a list -> 'b list -> 'c list -> 'd -> ('a -> 'b -> 'c -> 'd -> 'd) -> 'd

val map2 : 'a list -> 'b list -> ('a -> 'b -> 'c) -> 'c list

val map2i : 'a list -> 'b list -> (int -> 'a -> 'b -> 'c) -> 'c list

val fold_left_with_offset :
  'a list -> 'acc -> int -> ('a -> 'acc -> int -> 'acc) -> 'acc

val filter_map : 'a list -> ('a -> 'b option) -> 'b list
(** @unused *)

val exclude : 'a list -> ('a -> bool) -> 'a list
(** [exclude p l] is the opposite of [filter p l] *)

val exclude_with_val : 'a list -> ('a -> bool) -> 'a list option
(** [excludes p l]
    return a tuple [excluded,newl]
    where [exluded] is true indicates that at least one  
    element is removed,[newl] is the new list where all [p x] for [x] is false

*)

val same_length : 'a list -> 'b list -> bool

val init : int -> (int -> 'a) -> 'a list

val split_at : 'a list -> int -> 'a list * 'a list
(** [split_at n l]
    will split [l] into two lists [a,b], [a] will be of length [n], 
    otherwise, it will raise
*)

val split_at_last : 'a list -> 'a list * 'a
(** [split_at_last l]
    It is equivalent to [split_at (List.length l - 1) l ]
*)

val filter_mapi : 'a list -> ('a -> int -> 'b option) -> 'b list

val filter_map2 : 'a list -> 'b list -> ('a -> 'b -> 'c option) -> 'c list

val length_compare : 'a list -> int -> [ `Gt | `Eq | `Lt ]

val length_ge : 'a list -> int -> bool

(**

   {[length xs = length ys + n ]}
   input n should be positive 
   TODO: input checking
*)

val length_larger_than_n : 'a list -> 'a list -> int -> bool

val rev_map_append : 'a list -> 'b list -> ('a -> 'b) -> 'b list
(**
   [rev_map_append f l1 l2]
   [map f l1] and reverse it to append [l2]
   This weird semantics is due to it is the most efficient operation
   we can do
*)

val flat_map : 'a list -> ('a -> 'b list) -> 'b list

val flat_map_append : 'a list -> 'b list -> ('a -> 'b list) -> 'b list

val stable_group : 'a list -> ('a -> 'a -> bool) -> 'a list list
(**
    [stable_group eq lst]
    Example:
    Input:
   {[
     stable_group (=) [1;2;3;4;3]
   ]}
    Output:
   {[
     [[1];[2];[4];[3;3]]
   ]}
    TODO: this is O(n^2) behavior 
    which could be improved later
*)

val drop : 'a list -> int -> 'a list
(** [drop n list]
    raise when [n] is negative
    raise when list's length is less than [n]
*)

val find_first : 'a list -> ('a -> bool) -> 'a option

val find_first_not : 'a list -> ('a -> bool) -> 'a option
(** [find_first_not p lst ]
    if all elements in [lst] pass, return [None] 
    otherwise return the first element [e] as [Some e] which
    fails the predicate
*)

(** [find_opt f l] returns [None] if all return [None],  
    otherwise returns the first one. 
*)

val find_opt : 'a list -> ('a -> 'b option) -> 'b option

val find_def : 'a list -> ('a -> 'b option) -> 'b -> 'b

val rev_iter : 'a list -> ('a -> unit) -> unit

val iter : 'a list -> ('a -> unit) -> unit

val for_all : 'a list -> ('a -> bool) -> bool

val for_all_snd : ('a * 'b) list -> ('b -> bool) -> bool

val for_all2_no_exn : 'a list -> 'b list -> ('a -> 'b -> bool) -> bool
(** [for_all2_no_exn p xs ys]
    return [true] if all satisfied,
    [false] otherwise or length not equal
*)

val split_map : 'a list -> ('a -> 'b * 'c) -> 'b list * 'c list
(** [f] is applied follow the list order *)

val reduce_from_left : 'a list -> ('a -> 'a -> 'a) -> 'a
(** [fn] is applied from left to right *)

val sort_via_array : 'a list -> ('a -> 'a -> int) -> 'a list

val sort_via_arrayf : 'a list -> ('a -> 'a -> int) -> ('a -> 'b) -> 'b list

val assoc_by_string : (string * 'a) list -> string -> 'a option -> 'a
(** [assoc_by_string default key lst]
    if  [key] is found in the list  return that val,
    other unbox the [default], 
    otherwise [assert false ]
*)

val assoc_by_int : (int * 'a) list -> int -> 'a option -> 'a

val nth_opt : 'a list -> int -> 'a option

val iter_snd : ('a * 'b) list -> ('b -> unit) -> unit

val iter_fst : ('a * 'b) list -> ('a -> unit) -> unit

val exists : 'a list -> ('a -> bool) -> bool

val exists_fst : ('a * 'b) list -> ('a -> bool) -> bool

val exists_snd : ('a * 'b) list -> ('b -> bool) -> bool

val concat_append : 'a list list -> 'a list -> 'a list

val fold_left2 : 'a list -> 'b list -> 'c -> ('a -> 'b -> 'c -> 'c) -> 'c

val fold_left : 'a list -> 'b -> ('b -> 'a -> 'b) -> 'b

val singleton_exn : 'a list -> 'a

val mem_string : string list -> string -> bool

val filter : 'a list -> ('a -> bool) -> 'a list

val array_list_filter_map :
  'a array -> 'b list -> ('a -> 'b -> 'c option) -> 'c list

end = struct
#1 "ext_list.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

external ( .!() ) : 'a array -> int -> 'a = "%array_unsafe_get"

let rec map l f =
  match l with
  | [] -> []
  | [ x1 ] ->
      let y1 = f x1 in
      [ y1 ]
  | [ x1; x2 ] ->
      let y1 = f x1 in
      let y2 = f x2 in
      [ y1; y2 ]
  | [ x1; x2; x3 ] ->
      let y1 = f x1 in
      let y2 = f x2 in
      let y3 = f x3 in
      [ y1; y2; y3 ]
  | [ x1; x2; x3; x4 ] ->
      let y1 = f x1 in
      let y2 = f x2 in
      let y3 = f x3 in
      let y4 = f x4 in
      [ y1; y2; y3; y4 ]
  | x1 :: x2 :: x3 :: x4 :: x5 :: tail ->
      let y1 = f x1 in
      let y2 = f x2 in
      let y3 = f x3 in
      let y4 = f x4 in
      let y5 = f x5 in
      y1 :: y2 :: y3 :: y4 :: y5 :: map tail f

let rec has_string l f =
  match l with
  | [] -> false
  | [ x1 ] -> x1 = f
  | [ x1; x2 ] -> x1 = f || x2 = f
  | [ x1; x2; x3 ] -> x1 = f || x2 = f || x3 = f
  | x1 :: x2 :: x3 :: x4 -> x1 = f || x2 = f || x3 = f || has_string x4 f

let rec map_combine l1 l2 f =
  match (l1, l2) with
  | [], [] -> []
  | a1 :: l1, a2 :: l2 -> (f a1, a2) :: map_combine l1 l2 f
  | _, _ -> invalid_arg "Ext_list.map_combine"

let rec arr_list_combine_unsafe arr l i j acc f =
  if i = j then acc
  else
    match l with
    | [] -> invalid_arg "Ext_list.combine"
    | h :: tl ->
        (f arr.!(i), h) :: arr_list_combine_unsafe arr tl (i + 1) j acc f

let combine_array_append arr l acc f =
  let len = Array.length arr in
  arr_list_combine_unsafe arr l 0 len acc f

let combine_array arr l f =
  let len = Array.length arr in
  arr_list_combine_unsafe arr l 0 len [] f

let rec arr_list_filter_map_unasfe arr l i j acc f =
  if i = j then acc
  else
    match l with
    | [] -> invalid_arg "Ext_list.arr_list_filter_map_unsafe"
    | h :: tl -> (
        match f arr.!(i) h with
        | None -> arr_list_filter_map_unasfe arr tl (i + 1) j acc f
        | Some v -> v :: arr_list_filter_map_unasfe arr tl (i + 1) j acc f)

let array_list_filter_map arr l f =
  let len = Array.length arr in
  arr_list_filter_map_unasfe arr l 0 len [] f

let rec map_split_opt (xs : 'a list) (f : 'a -> 'b option * 'c option) :
    'b list * 'c list =
  match xs with
  | [] -> ([], [])
  | x :: xs -> (
      let c, d = f x in
      let cs, ds = map_split_opt xs f in
      ( (match c with Some c -> c :: cs | None -> cs),
        match d with Some d -> d :: ds | None -> ds ))

let rec map_snd l f =
  match l with
  | [] -> []
  | [ (v1, x1) ] ->
      let y1 = f x1 in
      [ (v1, y1) ]
  | [ (v1, x1); (v2, x2) ] ->
      let y1 = f x1 in
      let y2 = f x2 in
      [ (v1, y1); (v2, y2) ]
  | [ (v1, x1); (v2, x2); (v3, x3) ] ->
      let y1 = f x1 in
      let y2 = f x2 in
      let y3 = f x3 in
      [ (v1, y1); (v2, y2); (v3, y3) ]
  | [ (v1, x1); (v2, x2); (v3, x3); (v4, x4) ] ->
      let y1 = f x1 in
      let y2 = f x2 in
      let y3 = f x3 in
      let y4 = f x4 in
      [ (v1, y1); (v2, y2); (v3, y3); (v4, y4) ]
  | (v1, x1) :: (v2, x2) :: (v3, x3) :: (v4, x4) :: (v5, x5) :: tail ->
      let y1 = f x1 in
      let y2 = f x2 in
      let y3 = f x3 in
      let y4 = f x4 in
      let y5 = f x5 in
      (v1, y1) :: (v2, y2) :: (v3, y3) :: (v4, y4) :: (v5, y5) :: map_snd tail f

let rec map_last l f =
  match l with
  | [] -> []
  | [ x1 ] ->
      let y1 = f true x1 in
      [ y1 ]
  | [ x1; x2 ] ->
      let y1 = f false x1 in
      let y2 = f true x2 in
      [ y1; y2 ]
  | [ x1; x2; x3 ] ->
      let y1 = f false x1 in
      let y2 = f false x2 in
      let y3 = f true x3 in
      [ y1; y2; y3 ]
  | [ x1; x2; x3; x4 ] ->
      let y1 = f false x1 in
      let y2 = f false x2 in
      let y3 = f false x3 in
      let y4 = f true x4 in
      [ y1; y2; y3; y4 ]
  | x1 :: x2 :: x3 :: x4 :: tail ->
      (* make sure that tail is not empty *)
      let y1 = f false x1 in
      let y2 = f false x2 in
      let y3 = f false x3 in
      let y4 = f false x4 in
      y1 :: y2 :: y3 :: y4 :: map_last tail f

let rec mapi_aux lst i f tail =
  match lst with
  | [] -> tail
  | a :: l ->
      let r = f i a in
      r :: mapi_aux l (i + 1) f tail

let mapi lst f = mapi_aux lst 0 f []

let mapi_append lst f tail = mapi_aux lst 0 f tail

let rec last xs =
  match xs with
  | [ x ] -> x
  | _ :: tl -> last tl
  | [] -> invalid_arg "Ext_list.last"

let rec append_aux l1 l2 =
  match l1 with
  | [] -> l2
  | [ a0 ] -> a0 :: l2
  | [ a0; a1 ] -> a0 :: a1 :: l2
  | [ a0; a1; a2 ] -> a0 :: a1 :: a2 :: l2
  | [ a0; a1; a2; a3 ] -> a0 :: a1 :: a2 :: a3 :: l2
  | [ a0; a1; a2; a3; a4 ] -> a0 :: a1 :: a2 :: a3 :: a4 :: l2
  | a0 :: a1 :: a2 :: a3 :: a4 :: rest ->
      a0 :: a1 :: a2 :: a3 :: a4 :: append_aux rest l2

let append l1 l2 = match l2 with [] -> l1 | _ -> append_aux l1 l2

let append_one l1 x = append_aux l1 [ x ]

let rec map_append l1 l2 f =
  match l1 with
  | [] -> l2
  | [ a0 ] -> f a0 :: l2
  | [ a0; a1 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      b0 :: b1 :: l2
  | [ a0; a1; a2 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      b0 :: b1 :: b2 :: l2
  | [ a0; a1; a2; a3 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      let b3 = f a3 in
      b0 :: b1 :: b2 :: b3 :: l2
  | [ a0; a1; a2; a3; a4 ] ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      let b3 = f a3 in
      let b4 = f a4 in
      b0 :: b1 :: b2 :: b3 :: b4 :: l2
  | a0 :: a1 :: a2 :: a3 :: a4 :: rest ->
      let b0 = f a0 in
      let b1 = f a1 in
      let b2 = f a2 in
      let b3 = f a3 in
      let b4 = f a4 in
      b0 :: b1 :: b2 :: b3 :: b4 :: map_append rest l2 f

let rec fold_right l acc f =
  match l with
  | [] -> acc
  | [ a0 ] -> f a0 acc
  | [ a0; a1 ] -> f a0 (f a1 acc)
  | [ a0; a1; a2 ] -> f a0 (f a1 (f a2 acc))
  | [ a0; a1; a2; a3 ] -> f a0 (f a1 (f a2 (f a3 acc)))
  | [ a0; a1; a2; a3; a4 ] -> f a0 (f a1 (f a2 (f a3 (f a4 acc))))
  | a0 :: a1 :: a2 :: a3 :: a4 :: rest ->
      f a0 (f a1 (f a2 (f a3 (f a4 (fold_right rest acc f)))))

let rec fold_right2 l r acc f =
  match (l, r) with
  | [], [] -> acc
  | [ a0 ], [ b0 ] -> f a0 b0 acc
  | [ a0; a1 ], [ b0; b1 ] -> f a0 b0 (f a1 b1 acc)
  | [ a0; a1; a2 ], [ b0; b1; b2 ] -> f a0 b0 (f a1 b1 (f a2 b2 acc))
  | [ a0; a1; a2; a3 ], [ b0; b1; b2; b3 ] ->
      f a0 b0 (f a1 b1 (f a2 b2 (f a3 b3 acc)))
  | [ a0; a1; a2; a3; a4 ], [ b0; b1; b2; b3; b4 ] ->
      f a0 b0 (f a1 b1 (f a2 b2 (f a3 b3 (f a4 b4 acc))))
  | a0 :: a1 :: a2 :: a3 :: a4 :: arest, b0 :: b1 :: b2 :: b3 :: b4 :: brest ->
      f a0 b0
        (f a1 b1 (f a2 b2 (f a3 b3 (f a4 b4 (fold_right2 arest brest acc f)))))
  | _, _ -> invalid_arg "Ext_list.fold_right2"

let rec fold_right3 l r last acc f =
  match (l, r, last) with
  | [], [], [] -> acc
  | [ a0 ], [ b0 ], [ c0 ] -> f a0 b0 c0 acc
  | [ a0; a1 ], [ b0; b1 ], [ c0; c1 ] -> f a0 b0 c0 (f a1 b1 c1 acc)
  | [ a0; a1; a2 ], [ b0; b1; b2 ], [ c0; c1; c2 ] ->
      f a0 b0 c0 (f a1 b1 c1 (f a2 b2 c2 acc))
  | [ a0; a1; a2; a3 ], [ b0; b1; b2; b3 ], [ c0; c1; c2; c3 ] ->
      f a0 b0 c0 (f a1 b1 c1 (f a2 b2 c2 (f a3 b3 c3 acc)))
  | [ a0; a1; a2; a3; a4 ], [ b0; b1; b2; b3; b4 ], [ c0; c1; c2; c3; c4 ] ->
      f a0 b0 c0 (f a1 b1 c1 (f a2 b2 c2 (f a3 b3 c3 (f a4 b4 c4 acc))))
  | ( a0 :: a1 :: a2 :: a3 :: a4 :: arest,
      b0 :: b1 :: b2 :: b3 :: b4 :: brest,
      c0 :: c1 :: c2 :: c3 :: c4 :: crest ) ->
      f a0 b0 c0
        (f a1 b1 c1
           (f a2 b2 c2
              (f a3 b3 c3 (f a4 b4 c4 (fold_right3 arest brest crest acc f)))))
  | _, _, _ -> invalid_arg "Ext_list.fold_right2"

let rec map2i l r f =
  match (l, r) with
  | [], [] -> []
  | [ a0 ], [ b0 ] -> [ f 0 a0 b0 ]
  | [ a0; a1 ], [ b0; b1 ] ->
      let c0 = f 0 a0 b0 in
      let c1 = f 1 a1 b1 in
      [ c0; c1 ]
  | [ a0; a1; a2 ], [ b0; b1; b2 ] ->
      let c0 = f 0 a0 b0 in
      let c1 = f 1 a1 b1 in
      let c2 = f 2 a2 b2 in
      [ c0; c1; c2 ]
  | [ a0; a1; a2; a3 ], [ b0; b1; b2; b3 ] ->
      let c0 = f 0 a0 b0 in
      let c1 = f 1 a1 b1 in
      let c2 = f 2 a2 b2 in
      let c3 = f 3 a3 b3 in
      [ c0; c1; c2; c3 ]
  | [ a0; a1; a2; a3; a4 ], [ b0; b1; b2; b3; b4 ] ->
      let c0 = f 0 a0 b0 in
      let c1 = f 1 a1 b1 in
      let c2 = f 2 a2 b2 in
      let c3 = f 3 a3 b3 in
      let c4 = f 4 a4 b4 in
      [ c0; c1; c2; c3; c4 ]
  | a0 :: a1 :: a2 :: a3 :: a4 :: arest, b0 :: b1 :: b2 :: b3 :: b4 :: brest ->
      let c0 = f 0 a0 b0 in
      let c1 = f 1 a1 b1 in
      let c2 = f 2 a2 b2 in
      let c3 = f 3 a3 b3 in
      let c4 = f 4 a4 b4 in
      c0 :: c1 :: c2 :: c3 :: c4 :: map2i arest brest f
  | _, _ -> invalid_arg "Ext_list.map2"

let rec map2 l r f =
  match (l, r) with
  | [], [] -> []
  | [ a0 ], [ b0 ] -> [ f a0 b0 ]
  | [ a0; a1 ], [ b0; b1 ] ->
      let c0 = f a0 b0 in
      let c1 = f a1 b1 in
      [ c0; c1 ]
  | [ a0; a1; a2 ], [ b0; b1; b2 ] ->
      let c0 = f a0 b0 in
      let c1 = f a1 b1 in
      let c2 = f a2 b2 in
      [ c0; c1; c2 ]
  | [ a0; a1; a2; a3 ], [ b0; b1; b2; b3 ] ->
      let c0 = f a0 b0 in
      let c1 = f a1 b1 in
      let c2 = f a2 b2 in
      let c3 = f a3 b3 in
      [ c0; c1; c2; c3 ]
  | [ a0; a1; a2; a3; a4 ], [ b0; b1; b2; b3; b4 ] ->
      let c0 = f a0 b0 in
      let c1 = f a1 b1 in
      let c2 = f a2 b2 in
      let c3 = f a3 b3 in
      let c4 = f a4 b4 in
      [ c0; c1; c2; c3; c4 ]
  | a0 :: a1 :: a2 :: a3 :: a4 :: arest, b0 :: b1 :: b2 :: b3 :: b4 :: brest ->
      let c0 = f a0 b0 in
      let c1 = f a1 b1 in
      let c2 = f a2 b2 in
      let c3 = f a3 b3 in
      let c4 = f a4 b4 in
      c0 :: c1 :: c2 :: c3 :: c4 :: map2 arest brest f
  | _, _ -> invalid_arg "Ext_list.map2"

let rec fold_left_with_offset l accu i f =
  match l with
  | [] -> accu
  | a :: l -> fold_left_with_offset l (f a accu i) (i + 1) f

let rec filter_map xs (f : 'a -> 'b option) =
  match xs with
  | [] -> []
  | y :: ys -> (
      match f y with None -> filter_map ys f | Some z -> z :: filter_map ys f)

let rec exclude (xs : 'a list) (p : 'a -> bool) : 'a list =
  match xs with
  | [] -> []
  | x :: xs -> if p x then exclude xs p else x :: exclude xs p

let rec exclude_with_val l p =
  match l with
  | [] -> None
  | a0 :: xs -> (
      if p a0 then Some (exclude xs p)
      else
        match xs with
        | [] -> None
        | a1 :: rest -> (
            if p a1 then Some (a0 :: exclude rest p)
            else
              match exclude_with_val rest p with
              | None -> None
              | Some rest -> Some (a0 :: a1 :: rest)))

let rec same_length xs ys =
  match (xs, ys) with
  | [], [] -> true
  | _ :: xs, _ :: ys -> same_length xs ys
  | _, _ -> false

let init n f =
  match n with
  | 0 -> []
  | 1 ->
      let a0 = f 0 in
      [ a0 ]
  | 2 ->
      let a0 = f 0 in
      let a1 = f 1 in
      [ a0; a1 ]
  | 3 ->
      let a0 = f 0 in
      let a1 = f 1 in
      let a2 = f 2 in
      [ a0; a1; a2 ]
  | 4 ->
      let a0 = f 0 in
      let a1 = f 1 in
      let a2 = f 2 in
      let a3 = f 3 in
      [ a0; a1; a2; a3 ]
  | 5 ->
      let a0 = f 0 in
      let a1 = f 1 in
      let a2 = f 2 in
      let a3 = f 3 in
      let a4 = f 4 in
      [ a0; a1; a2; a3; a4 ]
  | _ -> Array.to_list (Array.init n f)

let rec rev_append l1 l2 =
  match l1 with
  | [] -> l2
  | [ a0 ] -> a0 :: l2 (* single element is common *)
  | [ a0; a1 ] -> a1 :: a0 :: l2
  | a0 :: a1 :: a2 :: rest -> rev_append rest (a2 :: a1 :: a0 :: l2)

let rev l = rev_append l []

let rec small_split_at n acc l =
  if n <= 0 then (rev acc, l)
  else
    match l with
    | x :: xs -> small_split_at (n - 1) (x :: acc) xs
    | _ -> invalid_arg "Ext_list.split_at"

let split_at l n = small_split_at n [] l

let rec split_at_last_aux acc x =
  match x with
  | [] -> invalid_arg "Ext_list.split_at_last"
  | [ x ] -> (rev acc, x)
  | y0 :: ys -> split_at_last_aux (y0 :: acc) ys

let split_at_last (x : 'a list) =
  match x with
  | [] -> invalid_arg "Ext_list.split_at_last"
  | [ a0 ] -> ([], a0)
  | [ a0; a1 ] -> ([ a0 ], a1)
  | [ a0; a1; a2 ] -> ([ a0; a1 ], a2)
  | [ a0; a1; a2; a3 ] -> ([ a0; a1; a2 ], a3)
  | [ a0; a1; a2; a3; a4 ] -> ([ a0; a1; a2; a3 ], a4)
  | a0 :: a1 :: a2 :: a3 :: a4 :: rest ->
      let rev, last = split_at_last_aux [] rest in
      (a0 :: a1 :: a2 :: a3 :: a4 :: rev, last)

(**
   can not do loop unroll due to state combination
*)
let filter_mapi xs f =
  let rec aux i xs =
    match xs with
    | [] -> []
    | y :: ys -> (
        match f y i with
        | None -> aux (i + 1) ys
        | Some z -> z :: aux (i + 1) ys)
  in
  aux 0 xs

let rec filter_map2 xs ys (f : 'a -> 'b -> 'c option) =
  match (xs, ys) with
  | [], [] -> []
  | u :: us, v :: vs -> (
      match f u v with
      | None -> filter_map2 us vs f (* idea: rec f us vs instead? *)
      | Some z -> z :: filter_map2 us vs f)
  | _ -> invalid_arg "Ext_list.filter_map2"

let rec rev_map_append l1 l2 f =
  match l1 with [] -> l2 | a :: l -> rev_map_append l (f a :: l2) f

(** It is not worth loop unrolling, 
    it is already tail-call, and we need to be careful 
    about evaluation order when unroll
*)
let rec flat_map_aux f acc append lx =
  match lx with
  | [] -> rev_append acc append
  | a0 :: rest ->
      let new_acc =
        match f a0 with
        | [] -> acc
        | [ a0 ] -> a0 :: acc
        | [ a0; a1 ] -> a1 :: a0 :: acc
        | a0 :: a1 :: a2 :: rest -> rev_append rest (a2 :: a1 :: a0 :: acc)
      in
      flat_map_aux f new_acc append rest

let flat_map lx f = flat_map_aux f [] [] lx

let flat_map_append lx append f = flat_map_aux f [] append lx

let rec length_compare l n =
  if n < 0 then `Gt
  else
    match l with
    | _ :: xs -> length_compare xs (n - 1)
    | [] -> if n = 0 then `Eq else `Lt

let rec length_ge l n =
  if n > 0 then match l with _ :: tl -> length_ge tl (n - 1) | [] -> false
  else true

(**
   {[length xs = length ys + n ]}
*)
let rec length_larger_than_n xs ys n =
  match (xs, ys) with
  | _, [] -> length_compare xs n = `Eq
  | _ :: xs, _ :: ys -> length_larger_than_n xs ys n
  | [], _ -> false

let rec group (eq : 'a -> 'a -> bool) lst =
  match lst with [] -> [] | x :: xs -> aux eq x (group eq xs)

and aux eq (x : 'a) (xss : 'a list list) : 'a list list =
  match xss with
  | [] -> [ [ x ] ]
  | (y0 :: _ as y) :: ys ->
      (* cannot be empty *)
      if eq x y0 then (x :: y) :: ys else y :: aux eq x ys
  | _ :: _ -> assert false

let stable_group lst eq = group eq lst |> rev

let rec drop h n =
  if n < 0 then invalid_arg "Ext_list.drop"
  else if n = 0 then h
  else
    match h with [] -> invalid_arg "Ext_list.drop" | _ :: tl -> drop tl (n - 1)

let rec find_first x p =
  match x with [] -> None | x :: l -> if p x then Some x else find_first l p

let rec find_first_not xs p =
  match xs with
  | [] -> None
  | a :: l -> if p a then find_first_not l p else Some a

let rec rev_iter l f =
  match l with
  | [] -> ()
  | [ x1 ] -> f x1
  | [ x1; x2 ] ->
      f x2;
      f x1
  | [ x1; x2; x3 ] ->
      f x3;
      f x2;
      f x1
  | [ x1; x2; x3; x4 ] ->
      f x4;
      f x3;
      f x2;
      f x1
  | x1 :: x2 :: x3 :: x4 :: x5 :: tail ->
      rev_iter tail f;
      f x5;
      f x4;
      f x3;
      f x2;
      f x1

let rec iter l f =
  match l with
  | [] -> ()
  | [ x1 ] -> f x1
  | [ x1; x2 ] ->
      f x1;
      f x2
  | [ x1; x2; x3 ] ->
      f x1;
      f x2;
      f x3
  | [ x1; x2; x3; x4 ] ->
      f x1;
      f x2;
      f x3;
      f x4
  | x1 :: x2 :: x3 :: x4 :: x5 :: tail ->
      f x1;
      f x2;
      f x3;
      f x4;
      f x5;
      iter tail f

let rec for_all lst p =
  match lst with [] -> true | a :: l -> p a && for_all l p

let rec for_all_snd lst p =
  match lst with [] -> true | (_, a) :: l -> p a && for_all_snd l p

let rec for_all2_no_exn l1 l2 p =
  match (l1, l2) with
  | [], [] -> true
  | a1 :: l1, a2 :: l2 -> p a1 a2 && for_all2_no_exn l1 l2 p
  | _, _ -> false

let rec find_opt xs p =
  match xs with
  | [] -> None
  | x :: l -> ( match p x with Some _ as v -> v | None -> find_opt l p)

let rec find_def xs p def =
  match xs with
  | [] -> def
  | x :: l -> ( match p x with Some v -> v | None -> find_def l p def)

let rec split_map l f =
  match l with
  | [] -> ([], [])
  | [ x1 ] ->
      let a0, b0 = f x1 in
      ([ a0 ], [ b0 ])
  | [ x1; x2 ] ->
      let a1, b1 = f x1 in
      let a2, b2 = f x2 in
      ([ a1; a2 ], [ b1; b2 ])
  | [ x1; x2; x3 ] ->
      let a1, b1 = f x1 in
      let a2, b2 = f x2 in
      let a3, b3 = f x3 in
      ([ a1; a2; a3 ], [ b1; b2; b3 ])
  | [ x1; x2; x3; x4 ] ->
      let a1, b1 = f x1 in
      let a2, b2 = f x2 in
      let a3, b3 = f x3 in
      let a4, b4 = f x4 in
      ([ a1; a2; a3; a4 ], [ b1; b2; b3; b4 ])
  | x1 :: x2 :: x3 :: x4 :: x5 :: tail ->
      let a1, b1 = f x1 in
      let a2, b2 = f x2 in
      let a3, b3 = f x3 in
      let a4, b4 = f x4 in
      let a5, b5 = f x5 in
      let ass, bss = split_map tail f in
      (a1 :: a2 :: a3 :: a4 :: a5 :: ass, b1 :: b2 :: b3 :: b4 :: b5 :: bss)

let sort_via_array lst cmp =
  let arr = Array.of_list lst in
  Array.sort cmp arr;
  Array.to_list arr

let sort_via_arrayf lst cmp f =
  let arr = Array.of_list lst in
  Array.sort cmp arr;
  Ext_array.to_list_f arr f

let rec assoc_by_string lst (k : string) def =
  match lst with
  | [] -> ( match def with None -> assert false | Some x -> x)
  | (k1, v1) :: rest -> if k1 = k then v1 else assoc_by_string rest k def

let rec assoc_by_int lst (k : int) def =
  match lst with
  | [] -> ( match def with None -> assert false | Some x -> x)
  | (k1, v1) :: rest -> if k1 = k then v1 else assoc_by_int rest k def

let rec nth_aux l n =
  match l with
  | [] -> None
  | a :: l -> if n = 0 then Some a else nth_aux l (n - 1)

let nth_opt l n = if n < 0 then None else nth_aux l n

let rec iter_snd lst f =
  match lst with
  | [] -> ()
  | (_, x) :: xs ->
      f x;
      iter_snd xs f

let rec iter_fst lst f =
  match lst with
  | [] -> ()
  | (x, _) :: xs ->
      f x;
      iter_fst xs f

let rec exists l p = match l with [] -> false | x :: xs -> p x || exists xs p

let rec exists_fst l p =
  match l with [] -> false | (a, _) :: l -> p a || exists_fst l p

let rec exists_snd l p =
  match l with [] -> false | (_, a) :: l -> p a || exists_snd l p

let rec concat_append (xss : 'a list list) (xs : 'a list) : 'a list =
  match xss with [] -> xs | l :: r -> append l (concat_append r xs)

let rec fold_left l accu f =
  match l with [] -> accu | a :: l -> fold_left l (f accu a) f

let reduce_from_left lst fn =
  match lst with
  | first :: rest -> fold_left rest first fn
  | _ -> invalid_arg "Ext_list.reduce_from_left"

let rec fold_left2 l1 l2 accu f =
  match (l1, l2) with
  | [], [] -> accu
  | a1 :: l1, a2 :: l2 -> fold_left2 l1 l2 (f a1 a2 accu) f
  | _, _ -> invalid_arg "Ext_list.fold_left2"

let singleton_exn xs = match xs with [ x ] -> x | _ -> assert false

let rec mem_string (xs : string list) (x : string) =
  match xs with [] -> false | a :: l -> a = x || mem_string l x

let filter lst p =
  let rec find ~p accu lst =
    match lst with
    | [] -> rev accu
    | x :: l -> if p x then find (x :: accu) l ~p else find accu l ~p
  in
  find [] lst ~p

end
module Map_gen : sig 
#1 "map_gen.mli"
type ('key, +'a) t = private
  | Empty
  | Leaf of { k : 'key; v : 'a }
  | Node of { l : ('key, 'a) t; k : 'key; v : 'a; r : ('key, 'a) t; h : int }

val cardinal : ('a, 'b) t -> int

val bindings : ('a, 'b) t -> ('a * 'b) list

val fill_array_with_f : ('a, 'b) t -> int -> 'c array -> ('a -> 'b -> 'c) -> int

val fill_array_aux : ('a, 'b) t -> int -> ('a * 'b) array -> int

val to_sorted_array : ('key, 'a) t -> ('key * 'a) array

val to_sorted_array_with_f : ('a, 'b) t -> ('a -> 'b -> 'c) -> 'c array

val keys : ('a, 'b) t -> 'a list

val height : ('a, 'b) t -> int

val singleton : 'a -> 'b -> ('a, 'b) t

val unsafe_node : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t -> int -> ('a, 'b) t
  [@@inline]

val unsafe_two_elements : 'a -> 'b -> 'a -> 'b -> ('a, 'b) t
  [@@inline]
(** smaller comes first *)

val bal : ('a, 'b) t -> 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t

val empty : ('a, 'b) t

val is_empty : ('a, 'b) t -> bool

val merge : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t

val iter : ('a, 'b) t -> ('a -> 'b -> unit) -> unit

val map : ('a, 'b) t -> ('b -> 'c) -> ('a, 'c) t

val mapi : ('a, 'b) t -> ('a -> 'b -> 'c) -> ('a, 'c) t

val fold : ('a, 'b) t -> 'c -> ('a -> 'b -> 'c -> 'c) -> 'c

val for_all : ('a, 'b) t -> ('a -> 'b -> bool) -> bool

val exists : ('a, 'b) t -> ('a -> 'b -> bool) -> bool

val join : ('a, 'b) t -> 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t

val concat : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t

val concat_or_join : ('a, 'b) t -> 'a -> 'b option -> ('a, 'b) t -> ('a, 'b) t

module type S = sig
  type key

  type +'a t

  val empty : 'a t

  val compare_key : key -> key -> int

  val is_empty : 'a t -> bool

  val mem : 'a t -> key -> bool

  val to_sorted_array : 'a t -> (key * 'a) array

  val to_sorted_array_with_f : 'a t -> (key -> 'a -> 'b) -> 'b array

  val add : 'a t -> key -> 'a -> 'a t

  val adjust : 'a t -> key -> ('a option -> 'a) -> 'a t

  val singleton : key -> 'a -> 'a t

  val remove : 'a t -> key -> 'a t

  (* val merge :
     'a t -> 'b t -> (key -> 'a option -> 'b option -> 'c option) -> 'c t *)
  val disjoint_merge_exn : 'a t -> 'a t -> (key -> 'a -> 'a -> exn) -> 'a t

  val iter : 'a t -> (key -> 'a -> unit) -> unit

  val fold : 'a t -> 'b -> (key -> 'a -> 'b -> 'b) -> 'b

  val for_all : 'a t -> (key -> 'a -> bool) -> bool

  val exists : 'a t -> (key -> 'a -> bool) -> bool

  (* val filter : 'a t -> (key -> 'a -> bool) -> 'a t *)
  (* val partition : 'a t -> (key -> 'a -> bool) -> 'a t * 'a t *)
  val cardinal : 'a t -> int

  val bindings : 'a t -> (key * 'a) list

  val keys : 'a t -> key list
  (* val choose : 'a t -> key * 'a *)

  val find_exn : 'a t -> key -> 'a

  val find_opt : 'a t -> key -> 'a option

  val find_default : 'a t -> key -> 'a -> 'a

  val map : 'a t -> ('a -> 'b) -> 'b t

  val mapi : 'a t -> (key -> 'a -> 'b) -> 'b t

  val of_list : (key * 'a) list -> 'a t

  val of_array : (key * 'a) array -> 'a t

  val add_list : (key * 'b) list -> 'b t -> 'b t
end

end = struct
#1 "map_gen.ml"
(***********************************************************************)
(*                                                                     *)
(*                                OCaml                                *)
(*                                                                     *)
(*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         *)
(*                                                                     *)
(*  Copyright 1996 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the GNU Library General Public License, with    *)
(*  the special exception on linking described in file ../LICENSE.     *)
(*                                                                     *)
(***********************************************************************)

[@@@warnerror "+55"]
(* adapted from stdlib *)

type ('key, 'a) t0 =
  | Empty
  | Leaf of { k : 'key; v : 'a }
  | Node of { l : ('key, 'a) t0; k : 'key; v : 'a; r : ('key, 'a) t0; h : int }

type ('key, 'a) parital_node = {
  l : ('key, 'a) t0;
  k : 'key;
  v : 'a;
  r : ('key, 'a) t0;
  h : int;
}

external ( ~! ) : ('key, 'a) t0 -> ('key, 'a) parital_node = "%identity"

let empty = Empty

let rec map x f =
  match x with
  | Empty -> Empty
  | Leaf { k; v } -> Leaf { k; v = f v }
  | Node ({ l; v; r } as x) ->
      let l' = map l f in
      let d' = f v in
      let r' = map r f in
      Node { x with l = l'; v = d'; r = r' }

let rec mapi x f =
  match x with
  | Empty -> Empty
  | Leaf { k; v } -> Leaf { k; v = f k v }
  | Node ({ l; k; v; r } as x) ->
      let l' = mapi l f in
      let v' = f k v in
      let r' = mapi r f in
      Node { x with l = l'; v = v'; r = r' }

let[@inline] calc_height a b = (if a >= b then a else b) + 1

let[@inline] singleton k v = Leaf { k; v }

let[@inline] height = function Empty -> 0 | Leaf _ -> 1 | Node { h } -> h

let[@inline] unsafe_node k v l r h = Node { l; k; v; r; h }

let[@inline] unsafe_two_elements k1 v1 k2 v2 =
  unsafe_node k2 v2 (singleton k1 v1) empty 2

let[@inline] unsafe_node_maybe_leaf k v l r h =
  if h = 1 then Leaf { k; v } else Node { l; k; v; r; h }

type ('key, +'a) t = ('key, 'a) t0 = private
  | Empty
  | Leaf of { k : 'key; v : 'a }
  | Node of { l : ('key, 'a) t; k : 'key; v : 'a; r : ('key, 'a) t; h : int }

let rec cardinal_aux acc = function
  | Empty -> acc
  | Leaf _ -> acc + 1
  | Node { l; r } -> cardinal_aux (cardinal_aux (acc + 1) r) l

let cardinal s = cardinal_aux 0 s

let rec bindings_aux accu = function
  | Empty -> accu
  | Leaf { k; v } -> (k, v) :: accu
  | Node { l; k; v; r } -> bindings_aux ((k, v) :: bindings_aux accu r) l

let bindings s = bindings_aux [] s

let rec fill_array_with_f (s : _ t) i arr f : int =
  match s with
  | Empty -> i
  | Leaf { k; v } ->
      Array.unsafe_set arr i (f k v);
      i + 1
  | Node { l; k; v; r } ->
      let inext = fill_array_with_f l i arr f in
      Array.unsafe_set arr inext (f k v);
      fill_array_with_f r (inext + 1) arr f

let rec fill_array_aux (s : _ t) i arr : int =
  match s with
  | Empty -> i
  | Leaf { k; v } ->
      Array.unsafe_set arr i (k, v);
      i + 1
  | Node { l; k; v; r } ->
      let inext = fill_array_aux l i arr in
      Array.unsafe_set arr inext (k, v);
      fill_array_aux r (inext + 1) arr

let to_sorted_array (s : ('key, 'a) t) : ('key * 'a) array =
  match s with
  | Empty -> [||]
  | Leaf { k; v } -> [| (k, v) |]
  | Node { l; k; v; r } ->
      let len = cardinal_aux (cardinal_aux 1 r) l in
      let arr = Array.make len (k, v) in
      ignore (fill_array_aux s 0 arr : int);
      arr

let to_sorted_array_with_f (type key a b) (s : (key, a) t) (f : key -> a -> b) :
    b array =
  match s with
  | Empty -> [||]
  | Leaf { k; v } -> [| f k v |]
  | Node { l; k; v; r } ->
      let len = cardinal_aux (cardinal_aux 1 r) l in
      let arr = Array.make len (f k v) in
      ignore (fill_array_with_f s 0 arr f : int);
      arr

let rec keys_aux accu = function
  | Empty -> accu
  | Leaf { k } -> k :: accu
  | Node { l; k; r } -> keys_aux (k :: keys_aux accu r) l

let keys s = keys_aux [] s

let bal l x d r =
  let hl = height l in
  let hr = height r in
  if hl > hr + 2 then
    let { l = ll; r = lr; v = lv; k = lk; h = _ } = ~!l in
    let hll = height ll in
    let hlr = height lr in
    if hll >= hlr then
      let hnode = calc_height hlr hr in
      unsafe_node lk lv ll
        (unsafe_node_maybe_leaf x d lr r hnode)
        (calc_height hll hnode)
    else
      let { l = lrl; r = lrr; k = lrk; v = lrv } = ~!lr in
      let hlrl = height lrl in
      let hlrr = height lrr in
      let hlnode = calc_height hll hlrl in
      let hrnode = calc_height hlrr hr in
      unsafe_node lrk lrv
        (unsafe_node_maybe_leaf lk lv ll lrl hlnode)
        (unsafe_node_maybe_leaf x d lrr r hrnode)
        (calc_height hlnode hrnode)
  else if hr > hl + 2 then
    let { l = rl; r = rr; k = rk; v = rv } = ~!r in
    let hrr = height rr in
    let hrl = height rl in
    if hrr >= hrl then
      let hnode = calc_height hl hrl in
      unsafe_node rk rv
        (unsafe_node_maybe_leaf x d l rl hnode)
        rr (calc_height hnode hrr)
    else
      let { l = rll; r = rlr; k = rlk; v = rlv } = ~!rl in
      let hrll = height rll in
      let hrlr = height rlr in
      let hlnode = calc_height hl hrll in
      let hrnode = calc_height hrlr hrr in
      unsafe_node rlk rlv
        (unsafe_node_maybe_leaf x d l rll hlnode)
        (unsafe_node_maybe_leaf rk rv rlr rr hrnode)
        (calc_height hlnode hrnode)
  else unsafe_node_maybe_leaf x d l r (calc_height hl hr)

let[@inline] is_empty = function Empty -> true | _ -> false

let rec min_binding_exn = function
  | Empty -> raise Not_found
  | Leaf { k; v } -> (k, v)
  | Node { l; k; v } -> (
      match l with Empty -> (k, v) | Leaf _ | Node _ -> min_binding_exn l)

let rec remove_min_binding = function
  | Empty -> invalid_arg "Map.remove_min_elt"
  | Leaf _ -> empty
  | Node { l = Empty; r } -> r
  | Node { l; k; v; r } -> bal (remove_min_binding l) k v r

let merge t1 t2 =
  match (t1, t2) with
  | Empty, t -> t
  | t, Empty -> t
  | _, _ ->
      let x, d = min_binding_exn t2 in
      bal t1 x d (remove_min_binding t2)

let rec iter x f =
  match x with
  | Empty -> ()
  | Leaf { k; v } -> (f k v : unit)
  | Node { l; k; v; r } ->
      iter l f;
      f k v;
      iter r f

let rec fold m accu f =
  match m with
  | Empty -> accu
  | Leaf { k; v } -> f k v accu
  | Node { l; k; v; r } -> fold r (f k v (fold l accu f)) f

let rec for_all x p =
  match x with
  | Empty -> true
  | Leaf { k; v } -> p k v
  | Node { l; k; v; r } -> p k v && for_all l p && for_all r p

let rec exists x p =
  match x with
  | Empty -> false
  | Leaf { k; v } -> p k v
  | Node { l; k; v; r } -> p k v || exists l p || exists r p

(* Beware: those two functions assume that the added k is *strictly*
   smaller (or bigger) than all the present keys in the tree; it
   does not test for equality with the current min (or max) key.

   Indeed, they are only used during the "join" operation which
   respects this precondition.
*)

let rec add_min k v = function
  | Empty -> singleton k v
  | Leaf l -> unsafe_two_elements k v l.k l.v
  | Node tree -> bal (add_min k v tree.l) tree.k tree.v tree.r

let rec add_max k v = function
  | Empty -> singleton k v
  | Leaf l -> unsafe_two_elements l.k l.v k v
  | Node tree -> bal tree.l tree.k tree.v (add_max k v tree.r)

(* Same as create and bal, but no assumptions are made on the
   relative heights of l and r. *)

let rec join l v d r =
  match l with
  | Empty -> add_min v d r
  | Leaf leaf -> add_min leaf.k leaf.v (add_min v d r)
  | Node xl -> (
      match r with
      | Empty -> add_max v d l
      | Leaf leaf -> add_max leaf.k leaf.v (add_max v d l)
      | Node xr ->
          let lh = xl.h in
          let rh = xr.h in
          if lh > rh + 2 then bal xl.l xl.k xl.v (join xl.r v d r)
          else if rh > lh + 2 then bal (join l v d xr.l) xr.k xr.v xr.r
          else unsafe_node v d l r (calc_height lh rh))

(* Merge two trees l and r into one.
   All elements of l must precede the elements of r.
   No assumption on the heights of l and r. *)

let concat t1 t2 =
  match (t1, t2) with
  | Empty, t -> t
  | t, Empty -> t
  | _, _ ->
      let x, d = min_binding_exn t2 in
      join t1 x d (remove_min_binding t2)

let concat_or_join t1 v d t2 =
  match d with Some d -> join t1 v d t2 | None -> concat t1 t2

module type S = sig
  type key

  type +'a t

  val empty : 'a t

  val compare_key : key -> key -> int

  val is_empty : 'a t -> bool

  val mem : 'a t -> key -> bool

  val to_sorted_array : 'a t -> (key * 'a) array

  val to_sorted_array_with_f : 'a t -> (key -> 'a -> 'b) -> 'b array

  val add : 'a t -> key -> 'a -> 'a t
  (** [add x y m] 
      If [x] was already bound in [m], its previous binding disappears. *)

  val adjust : 'a t -> key -> ('a option -> 'a) -> 'a t
  (** [adjust acc k replace ] if not exist [add (replace None ], otherwise 
      [add k v (replace (Some old))]
  *)

  val singleton : key -> 'a -> 'a t

  val remove : 'a t -> key -> 'a t
  (** [remove x m] returns a map containing the same bindings as
      [m], except for [x] which is unbound in the returned map. *)

  (* val merge:
       'a t -> 'b t ->
       (key -> 'a option -> 'b option -> 'c option) ->  'c t *)
  (** [merge f m1 m2] computes a map whose keys is a subset of keys of [m1]
      and of [m2]. The presence of each such binding, and the corresponding
      value, is determined with the function [f].
      @since 3.12.0
  *)

  val disjoint_merge_exn : 'a t -> 'a t -> (key -> 'a -> 'a -> exn) -> 'a t
  (* merge two maps, will raise if they have the same key *)

  val iter : 'a t -> (key -> 'a -> unit) -> unit
  (** [iter f m] applies [f] to all bindings in map [m].
      The bindings are passed to [f] in increasing order. *)

  val fold : 'a t -> 'b -> (key -> 'a -> 'b -> 'b) -> 'b
  (** [fold f m a] computes [(f kN dN ... (f k1 d1 a)...)],
      where [k1 ... kN] are the keys of all bindings in [m]
      (in increasing order) *)

  val for_all : 'a t -> (key -> 'a -> bool) -> bool
  (** [for_all p m] checks if all the bindings of the map.
      order unspecified
  *)

  val exists : 'a t -> (key -> 'a -> bool) -> bool
  (** [exists p m] checks if at least one binding of the map
      satisfy the predicate [p]. 
      order unspecified
  *)

  (* val filter: 'a t -> (key -> 'a -> bool) -> 'a t *)
  (** [filter p m] returns the map with all the bindings in [m]
      that satisfy predicate [p].
      order unspecified
  *)

  (* val partition: 'a t -> (key -> 'a -> bool) ->  'a t * 'a t *)
  (** [partition p m] returns a pair of maps [(m1, m2)], where
      [m1] contains all the bindings of [s] that satisfy the
      predicate [p], and [m2] is the map with all the bindings of
      [s] that do not satisfy [p].
  *)

  val cardinal : 'a t -> int
  (** Return the number of bindings of a map. *)

  val bindings : 'a t -> (key * 'a) list
  (** Return the list of all bindings of the given map.
      The returned list is sorted in increasing order with respect
      to the ordering *)

  val keys : 'a t -> key list
  (* Increasing order *)

  (* val split: 'a t -> key -> 'a t * 'a option * 'a t *)
  (** [split x m] returns a triple [(l, data, r)], where
        [l] is the map with all the bindings of [m] whose key
      is strictly less than [x];
        [r] is the map with all the bindings of [m] whose key
      is strictly greater than [x];
        [data] is [None] if [m] contains no binding for [x],
        or [Some v] if [m] binds [v] to [x].
      @since 3.12.0
  *)

  val find_exn : 'a t -> key -> 'a
  (** [find x m] returns the current binding of [x] in [m],
      or raises [Not_found] if no such binding exists. *)

  val find_opt : 'a t -> key -> 'a option

  val find_default : 'a t -> key -> 'a -> 'a

  val map : 'a t -> ('a -> 'b) -> 'b t
  (** [map f m] returns a map with same domain as [m], where the
      associated value [a] of all bindings of [m] has been
      replaced by the result of the application of [f] to [a].
      The bindings are passed to [f] in increasing order
      with respect to the ordering over the type of the keys. *)

  val mapi : 'a t -> (key -> 'a -> 'b) -> 'b t
  (** Same as {!Map.S.map}, but the function receives as arguments both the
      key and the associated value for each binding of the map. *)

  val of_list : (key * 'a) list -> 'a t

  val of_array : (key * 'a) array -> 'a t

  val add_list : (key * 'b) list -> 'b t -> 'b t
end

end
module Map_string : sig 
#1 "map_string.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

include Map_gen.S with type key = string

end = struct
#1 "map_string.ml"

# 2 "ext/map.cppo.ml"
(* we don't create [map_poly], since some operations require raise an exception which carries [key] *)

# 5 "ext/map.cppo.ml"
type key = string 
let compare_key = Ext_string.compare
let [@inline] eq_key (x : key) y = x = y
    
# 19 "ext/map.cppo.ml"
    (* let [@inline] (=) (a : int) b = a = b *)
type + 'a t = (key,'a) Map_gen.t

let empty = Map_gen.empty 
let is_empty = Map_gen.is_empty
let iter = Map_gen.iter
let fold = Map_gen.fold
let for_all = Map_gen.for_all 
let exists = Map_gen.exists 
let singleton = Map_gen.singleton 
let cardinal = Map_gen.cardinal
let bindings = Map_gen.bindings
let to_sorted_array = Map_gen.to_sorted_array
let to_sorted_array_with_f = Map_gen.to_sorted_array_with_f
let keys = Map_gen.keys



let map = Map_gen.map 
let mapi = Map_gen.mapi
let bal = Map_gen.bal 
let height = Map_gen.height 


let rec add (tree : _ Map_gen.t as 'a) x data  : 'a = match tree with 
  | Empty ->
    singleton x data
  | Leaf {k;v} ->
    let c = compare_key x k in 
    if c = 0 then singleton x data else
    if c < 0 then 
      Map_gen.unsafe_two_elements x data k v 
    else 
      Map_gen.unsafe_two_elements k v x data  
  | Node {l; k ; v ; r; h} ->
    let c = compare_key x k in
    if c = 0 then
      Map_gen.unsafe_node x data l r h (* at least need update data *)
    else if c < 0 then
      bal (add l x data ) k v r
    else
      bal l k v (add r x data )


let rec adjust (tree : _ Map_gen.t as 'a) x replace  : 'a = 
  match tree with 
  | Empty ->
    singleton x (replace None)
  | Leaf {k ; v} -> 
    let c = compare_key x k in 
    if c = 0 then singleton x (replace (Some v)) else 
    if c < 0 then 
      Map_gen.unsafe_two_elements x (replace None) k v   
    else
      Map_gen.unsafe_two_elements k v x (replace None)   
  | Node ({l; k ; r} as tree) ->
    let c = compare_key x k in
    if c = 0 then
      Map_gen.unsafe_node x (replace  (Some tree.v)) l r tree.h
    else if c < 0 then
      bal (adjust l x  replace ) k tree.v r
    else
      bal l k tree.v (adjust r x  replace )


let rec find_exn (tree : _ Map_gen.t ) x = match tree with 
  | Empty ->
    raise Not_found
  | Leaf leaf -> 
    if eq_key x leaf.k then leaf.v else raise Not_found  
  | Node tree ->
    let c = compare_key x tree.k in
    if c = 0 then tree.v
    else find_exn (if c < 0 then tree.l else tree.r) x

let rec find_opt (tree : _ Map_gen.t ) x = match tree with 
  | Empty -> None 
  | Leaf leaf -> 
    if eq_key x leaf.k then Some leaf.v else None
  | Node tree ->
    let c = compare_key x tree.k in
    if c = 0 then Some tree.v
    else find_opt (if c < 0 then tree.l else tree.r) x

let rec find_default (tree : _ Map_gen.t ) x  default     = match tree with 
  | Empty -> default  
  | Leaf leaf -> 
    if eq_key x leaf.k then  leaf.v else default
  | Node tree ->
    let c = compare_key x tree.k in
    if c = 0 then tree.v
    else find_default (if c < 0 then tree.l else tree.r) x default

let rec mem (tree : _ Map_gen.t )  x= match tree with 
  | Empty ->
    false
  | Leaf leaf -> eq_key x leaf.k 
  | Node{l; k ;  r} ->
    let c = compare_key x k in
    c = 0 || mem (if c < 0 then l else r) x 

let rec remove (tree : _ Map_gen.t as 'a) x : 'a = match tree with 
  | Empty -> empty
  | Leaf leaf -> 
    if eq_key x leaf.k then empty 
    else tree
  | Node{l; k ; v; r} ->
    let c = compare_key x k in
    if c = 0 then
      Map_gen.merge l r
    else if c < 0 then
      bal (remove l x) k v r
    else
      bal l k v (remove r x )

type 'a split = 
  | Yes of {l : (key,'a) Map_gen.t; r : (key,'a)Map_gen.t ; v : 'a}
  | No of {l : (key,'a) Map_gen.t; r : (key,'a)Map_gen.t }


let rec split  (tree : (key,'a) Map_gen.t) x : 'a split  = 
  match tree with 
  | Empty ->
    No {l = empty; r = empty}
  | Leaf leaf -> 
    let c = compare_key x leaf.k in 
    if c = 0 then Yes {l = empty; v= leaf.v; r = empty} 
    else if c < 0 then No { l = empty; r = tree }
    else  No { l = tree; r = empty}
  | Node {l; k ; v ; r} ->
    let c = compare_key x k in
    if c = 0 then Yes {l; v; r}
    else if c < 0 then      
      match  split l x with 
      | Yes result -> Yes {result with r = Map_gen.join result.r k v r }
      | No result -> No {result with r = Map_gen.join result.r k v r } 
    else
      match split r x with 
      | Yes result -> 
        Yes {result with l = Map_gen.join l k v result.l}
      | No result -> 
        No {result with l = Map_gen.join l k v result.l}


let rec disjoint_merge_exn  
    (s1 : _ Map_gen.t) 
    (s2  : _ Map_gen.t) 
    fail : _ Map_gen.t =
  match s1 with
  | Empty -> s2  
  | Leaf ({k } as l1)  -> 
    begin match s2 with 
      | Empty -> s1 
      | Leaf l2 -> 
        let c = compare_key k l2.k in 
        if c = 0 then raise_notrace (fail k l1.v l2.v)
        else if c < 0 then Map_gen.unsafe_two_elements l1.k l1.v l2.k l2.v
        else Map_gen.unsafe_two_elements l2.k l2.v k l1.v
      | Node _ -> 
        adjust s2 k (fun data -> 
            match data with 
            |  None -> l1.v
            | Some s2v  -> raise_notrace (fail k l1.v s2v)
          )        
    end
  | Node ({k} as xs1) -> 
    if  xs1.h >= height s2 then
      begin match split s2 k with 
        | No {l; r} -> 
          Map_gen.join 
            (disjoint_merge_exn  xs1.l l fail)
            k 
            xs1.v 
            (disjoint_merge_exn xs1.r r fail)
        | Yes { v =  s2v} ->
          raise_notrace (fail k xs1.v s2v)
      end        
    else let [@warning "-8"] (Node ({k} as s2) : _ Map_gen.t)  = s2 in 
      begin match  split s1 k with 
        | No {l;  r} -> 
          Map_gen.join 
            (disjoint_merge_exn  l s2.l fail) k s2.v 
            (disjoint_merge_exn  r s2.r fail)
        | Yes { v = s1v} -> 
          raise_notrace (fail k s1v s2.v)
      end






let add_list (xs : _ list ) init = 
  Ext_list.fold_left xs init (fun  acc (k,v) -> add acc k v )

let of_list xs = add_list xs empty

let of_array xs = 
  Ext_array.fold_left xs empty (fun acc (k,v) -> add acc k v ) 

end
module Bsb_db : sig 
#1 "bsb_db.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** Store a file called [.bsbuild] that can be communicated 
    between [bsb.exe] and [bsb_helper.exe]. 
    [bsb.exe] stores such data which would be retrieved by 
    [bsb_helper.exe]. It is currently used to combine with 
    ocamldep to figure out which module->file it depends on
*)

type case = bool

type info =
  | Intf
  (* intemediate state *)
  | Impl
  | Impl_intf

type syntax_kind = Ml | Reason | Res

type module_info = {
  mutable info : info;
  dir : string;
  syntax_kind : syntax_kind;
  (* This is actually not stored in bsbuild meta info
     since creating .d file only emit .cmj/.cmi dependencies, so it does not
     need know which syntax it is written
  *)
  case : bool;
  name_sans_extension : string;
}

type map = module_info Map_string.t

type 'a cat = { mutable lib : 'a; mutable dev : 'a }

type t = map cat

(** store  the meta data indexed by {!Bsb_dir_index}
    {[
      0 --> lib group
        1 --> dev 1 group
                    .

    ]}
*)

end = struct
#1 "bsb_db.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type case = bool
(** true means upper case*)

type info =
  | Intf
  (* intemediate state *)
  | Impl
  | Impl_intf

type syntax_kind = Ml | Reason | Res

type module_info = {
  mutable info : info;
  dir : string;
  syntax_kind : syntax_kind;
  case : bool;
  name_sans_extension : string;
}

type map = module_info Map_string.t

type 'a cat = { mutable lib : 'a; mutable dev : 'a }

type t = map cat
(** indexed by the group *)

end
module Set_gen : sig 
#1 "set_gen.mli"
type 'a t = private
  | Empty
  | Leaf of 'a
  | Node of { l : 'a t; v : 'a; r : 'a t; h : int }

val empty : 'a t

val is_empty : 'a t -> bool [@@inline]

val unsafe_two_elements : 'a -> 'a -> 'a t

val cardinal : 'a t -> int

val elements : 'a t -> 'a list

val choose : 'a t -> 'a

val iter : 'a t -> ('a -> unit) -> unit

val fold : 'a t -> 'c -> ('a -> 'c -> 'c) -> 'c

val for_all : 'a t -> ('a -> bool) -> bool

val exists : 'a t -> ('a -> bool) -> bool

val check : 'a t -> unit

val bal : 'a t -> 'a -> 'a t -> 'a t

val remove_min_elt : 'a t -> 'a t

val singleton : 'a -> 'a t

val internal_merge : 'a t -> 'a t -> 'a t

val internal_join : 'a t -> 'a -> 'a t -> 'a t

val internal_concat : 'a t -> 'a t -> 'a t

val partition : 'a t -> ('a -> bool) -> 'a t * 'a t

val of_sorted_array : 'a array -> 'a t

val is_ordered : cmp:('a -> 'a -> int) -> 'a t -> bool

val invariant : cmp:('a -> 'a -> int) -> 'a t -> bool

module type S = sig
  type elt

  type t

  val empty : t

  val is_empty : t -> bool

  val iter : t -> (elt -> unit) -> unit

  val fold : t -> 'a -> (elt -> 'a -> 'a) -> 'a

  val for_all : t -> (elt -> bool) -> bool

  val exists : t -> (elt -> bool) -> bool

  val singleton : elt -> t

  val cardinal : t -> int

  val elements : t -> elt list

  val choose : t -> elt

  val mem : t -> elt -> bool

  val add : t -> elt -> t

  val remove : t -> elt -> t

  val union : t -> t -> t

  val inter : t -> t -> t

  val diff : t -> t -> t

  val of_list : elt list -> t

  val of_sorted_array : elt array -> t

  val invariant : t -> bool

  val print : Format.formatter -> t -> unit
end

end = struct
#1 "set_gen.ml"
(***********************************************************************)
(*                                                                     *)
(*                                OCaml                                *)
(*                                                                     *)
(*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         *)
(*                                                                     *)
(*  Copyright 1996 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the GNU Library General Public License, with    *)
(*  the special exception on linking described in file ../LICENSE.     *)
(*                                                                     *)
(***********************************************************************)
[@@@warnerror "+55"]

(* balanced tree based on stdlib distribution *)

type 'a t0 =
  | Empty
  | Leaf of 'a
  | Node of { l : 'a t0; v : 'a; r : 'a t0; h : int }

type 'a partial_node = { l : 'a t0; v : 'a; r : 'a t0; h : int }

external ( ~! ) : 'a t0 -> 'a partial_node = "%identity"

let empty = Empty

let[@inline] height = function Empty -> 0 | Leaf _ -> 1 | Node { h } -> h

let[@inline] calc_height a b = (if a >= b then a else b) + 1

(*
     Invariants:
     1. {[ l < v < r]}
     2. l and r balanced
     3. [height l] - [height r] <= 2
*)
let[@inline] unsafe_node v l r h = Node { l; v; r; h }

let[@inline] unsafe_node_maybe_leaf v l r h =
  if h = 1 then Leaf v else Node { l; v; r; h }

let[@inline] singleton x = Leaf x

let[@inline] unsafe_two_elements x v = unsafe_node v (singleton x) empty 2

type 'a t = 'a t0 = private
  | Empty
  | Leaf of 'a
  | Node of { l : 'a t0; v : 'a; r : 'a t0; h : int }

(* Smallest and greatest element of a set *)

let rec min_exn = function
  | Empty -> raise Not_found
  | Leaf v -> v
  | Node { l; v } -> ( match l with Empty -> v | Leaf _ | Node _ -> min_exn l)

let[@inline] is_empty = function Empty -> true | _ -> false

let rec cardinal_aux acc = function
  | Empty -> acc
  | Leaf _ -> acc + 1
  | Node { l; r } -> cardinal_aux (cardinal_aux (acc + 1) r) l

let cardinal s = cardinal_aux 0 s

let rec elements_aux accu = function
  | Empty -> accu
  | Leaf v -> v :: accu
  | Node { l; v; r } -> elements_aux (v :: elements_aux accu r) l

let elements s = elements_aux [] s

let choose = min_exn

let rec iter x f =
  match x with
  | Empty -> ()
  | Leaf v -> f v
  | Node { l; v; r } ->
      iter l f;
      f v;
      iter r f

let rec fold s accu f =
  match s with
  | Empty -> accu
  | Leaf v -> f v accu
  | Node { l; v; r } -> fold r (f v (fold l accu f)) f

let rec for_all x p =
  match x with
  | Empty -> true
  | Leaf v -> p v
  | Node { l; v; r } -> p v && for_all l p && for_all r p

let rec exists x p =
  match x with
  | Empty -> false
  | Leaf v -> p v
  | Node { l; v; r } -> p v || exists l p || exists r p

exception Height_invariant_broken

exception Height_diff_borken

let rec check_height_and_diff = function
  | Empty -> 0
  | Leaf _ -> 1
  | Node { l; r; h } ->
      let hl = check_height_and_diff l in
      let hr = check_height_and_diff r in
      if h <> calc_height hl hr then raise Height_invariant_broken
      else
        let diff = abs (hl - hr) in
        if diff > 2 then raise Height_diff_borken else h

let check tree = ignore (check_height_and_diff tree)

(* Same as create, but performs one step of rebalancing if necessary.
    Invariants:
    1. {[ l < v < r ]}
    2. l and r balanced
    3. | height l - height r | <= 3.

    Proof by indunction

    Lemma: the height of  [bal l v r] will bounded by [max l r] + 1
*)
let bal l v r : _ t =
  let hl = height l in
  let hr = height r in
  if hl > hr + 2 then
    let { l = ll; r = lr; v = lv; h = _ } = ~!l in
    let hll = height ll in
    let hlr = height lr in
    if hll >= hlr then
      let hnode = calc_height hlr hr in
      unsafe_node lv ll
        (unsafe_node_maybe_leaf v lr r hnode)
        (calc_height hll hnode)
    else
      let { l = lrl; r = lrr; v = lrv } = ~!lr in
      let hlrl = height lrl in
      let hlrr = height lrr in
      let hlnode = calc_height hll hlrl in
      let hrnode = calc_height hlrr hr in
      unsafe_node lrv
        (unsafe_node_maybe_leaf lv ll lrl hlnode)
        (unsafe_node_maybe_leaf v lrr r hrnode)
        (calc_height hlnode hrnode)
  else if hr > hl + 2 then
    let { l = rl; r = rr; v = rv } = ~!r in
    let hrr = height rr in
    let hrl = height rl in
    if hrr >= hrl then
      let hnode = calc_height hl hrl in
      unsafe_node rv
        (unsafe_node_maybe_leaf v l rl hnode)
        rr (calc_height hnode hrr)
    else
      let { l = rll; r = rlr; v = rlv } = ~!rl in
      let hrll = height rll in
      let hrlr = height rlr in
      let hlnode = calc_height hl hrll in
      let hrnode = calc_height hrlr hrr in
      unsafe_node rlv
        (unsafe_node_maybe_leaf v l rll hlnode)
        (unsafe_node_maybe_leaf rv rlr rr hrnode)
        (calc_height hlnode hrnode)
  else unsafe_node_maybe_leaf v l r (calc_height hl hr)

let rec remove_min_elt = function
  | Empty -> invalid_arg "Set.remove_min_elt"
  | Leaf _ -> empty
  | Node { l = Empty; r } -> r
  | Node { l; v; r } -> bal (remove_min_elt l) v r

(*
    All elements of l must precede the elements of r.
        Assume | height l - height r | <= 2.
    weak form of [concat]
*)

let internal_merge l r =
  match (l, r) with
  | Empty, t -> t
  | t, Empty -> t
  | _, _ -> bal l (min_exn r) (remove_min_elt r)

(* Beware: those two functions assume that the added v is *strictly*
    smaller (or bigger) than all the present elements in the tree; it
    does not test for equality with the current min (or max) element.
    Indeed, they are only used during the "join" operation which
    respects this precondition.
*)

let rec add_min v = function
  | Empty -> singleton v
  | Leaf x -> unsafe_two_elements v x
  | Node n -> bal (add_min v n.l) n.v n.r

let rec add_max v = function
  | Empty -> singleton v
  | Leaf x -> unsafe_two_elements x v
  | Node n -> bal n.l n.v (add_max v n.r)

(** 
    Invariants:
    1. l < v < r 
    2. l and r are balanced 

    Proof by induction
    The height of output will be ~~ (max (height l) (height r) + 2)
    Also use the lemma from [bal]
*)
let rec internal_join l v r =
  match (l, r) with
  | Empty, _ -> add_min v r
  | _, Empty -> add_max v l
  | Leaf lv, Node { h = rh } ->
      if rh > 3 then add_min lv (add_min v r) (* FIXME: could inlined *)
      else unsafe_node v l r (rh + 1)
  | Leaf _, Leaf _ -> unsafe_node v l r 2
  | Node { h = lh }, Leaf rv ->
      if lh > 3 then add_max rv (add_max v l) else unsafe_node v l r (lh + 1)
  | ( Node { l = ll; v = lv; r = lr; h = lh },
      Node { l = rl; v = rv; r = rr; h = rh } ) ->
      if lh > rh + 2 then
        (* proof by induction:
           now [height of ll] is [lh - 1]
        *)
        bal ll lv (internal_join lr v r)
      else if rh > lh + 2 then bal (internal_join l v rl) rv rr
      else unsafe_node v l r (calc_height lh rh)

(*
    Required Invariants: 
    [t1] < [t2]  
*)
let internal_concat t1 t2 =
  match (t1, t2) with
  | Empty, t -> t
  | t, Empty -> t
  | _, _ -> internal_join t1 (min_exn t2) (remove_min_elt t2)

let rec partition x p =
  match x with
  | Empty -> (empty, empty)
  | Leaf v ->
      let pv = p v in
      if pv then (x, empty) else (empty, x)
  | Node { l; v; r } ->
      (* call [p] in the expected left-to-right order *)
      let lt, lf = partition l p in
      let pv = p v in
      let rt, rf = partition r p in
      if pv then (internal_join lt v rt, internal_concat lf rf)
      else (internal_concat lt rt, internal_join lf v rf)

let of_sorted_array l =
  let rec sub start n l =
    if n = 0 then empty
    else if n = 1 then
      let x0 = Array.unsafe_get l start in
      singleton x0
    else if n = 2 then
      let x0 = Array.unsafe_get l start in
      let x1 = Array.unsafe_get l (start + 1) in
      unsafe_node x1 (singleton x0) empty 2
    else if n = 3 then
      let x0 = Array.unsafe_get l start in
      let x1 = Array.unsafe_get l (start + 1) in
      let x2 = Array.unsafe_get l (start + 2) in
      unsafe_node x1 (singleton x0) (singleton x2) 2
    else
      let nl = n / 2 in
      let left = sub start nl l in
      let mid = start + nl in
      let v = Array.unsafe_get l mid in
      let right = sub (mid + 1) (n - nl - 1) l in
      unsafe_node v left right (calc_height (height left) (height right))
  in
  sub 0 (Array.length l) l

let is_ordered ~cmp tree =
  let rec is_ordered_min_max tree =
    match tree with
    | Empty -> `Empty
    | Leaf v -> `V (v, v)
    | Node { l; v; r } -> (
        match is_ordered_min_max l with
        | `No -> `No
        | `Empty -> (
            match is_ordered_min_max r with
            | `No -> `No
            | `Empty -> `V (v, v)
            | `V (l, r) -> if cmp v l < 0 then `V (v, r) else `No)
        | `V (min_v, max_v) -> (
            match is_ordered_min_max r with
            | `No -> `No
            | `Empty -> if cmp max_v v < 0 then `V (min_v, v) else `No
            | `V (min_v_r, max_v_r) ->
                if cmp max_v min_v_r < 0 then `V (min_v, max_v_r) else `No))
  in
  is_ordered_min_max tree <> `No

let invariant ~cmp t =
  check t;
  is_ordered ~cmp t

module type S = sig
  type elt

  type t

  val empty : t

  val is_empty : t -> bool

  val iter : t -> (elt -> unit) -> unit

  val fold : t -> 'a -> (elt -> 'a -> 'a) -> 'a

  val for_all : t -> (elt -> bool) -> bool

  val exists : t -> (elt -> bool) -> bool

  val singleton : elt -> t

  val cardinal : t -> int

  val elements : t -> elt list

  val choose : t -> elt

  val mem : t -> elt -> bool

  val add : t -> elt -> t

  val remove : t -> elt -> t

  val union : t -> t -> t

  val inter : t -> t -> t

  val diff : t -> t -> t

  val of_list : elt list -> t

  val of_sorted_array : elt array -> t

  val invariant : t -> bool

  val print : Format.formatter -> t -> unit
end

end
module Set_string : sig 
#1 "set_string.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

include Set_gen.S with type elt = string

end = struct
#1 "set_string.ml"
# 1 "ext/set.cppo.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)


# 27 "ext/set.cppo.ml"
type elt = string
let compare_elt = Ext_string.compare 
let [@inline] eq_elt (x : elt) y = x = y
let print_elt = Format.pp_print_string


# 52 "ext/set.cppo.ml"
(* let (=) (a:int) b = a = b *)

type ('a ) t0 = 'a Set_gen.t 

type  t = elt t0

let empty = Set_gen.empty 
let is_empty = Set_gen.is_empty
let iter = Set_gen.iter
let fold = Set_gen.fold
let for_all = Set_gen.for_all 
let exists = Set_gen.exists 
let singleton = Set_gen.singleton 
let cardinal = Set_gen.cardinal
let elements = Set_gen.elements
let choose = Set_gen.choose 

let of_sorted_array = Set_gen.of_sorted_array

let rec mem (tree : t) (x : elt) =  match tree with 
  | Empty -> false
  | Leaf v -> eq_elt x  v 
  | Node{l; v; r} ->
    let c = compare_elt x v in
    c = 0 || mem (if c < 0 then l else r) x

type split = 
  | Yes of  {l : t ;  r :  t }
  | No of { l : t; r : t}  

let [@inline] split_l (x : split) = 
  match x with 
  | Yes {l} | No {l} -> l 

let [@inline] split_r (x : split) = 
  match x with 
  | Yes {r} | No {r} -> r       

let [@inline] split_pres (x : split) = match x with | Yes _ -> true | No _ -> false   

let rec split (tree : t) x : split =  match tree with 
  | Empty ->
     No {l = empty;  r = empty}
  | Leaf v ->   
    let c = compare_elt x v in
    if c = 0 then Yes {l = empty; r = empty}
    else if c < 0 then
      No {l = empty;  r = tree}
    else
      No {l = tree;  r = empty}
  | Node {l; v; r} ->
    let c = compare_elt x v in
    if c = 0 then Yes {l; r}
    else if c < 0 then
      match split l x with 
      | Yes result -> 
        Yes { result with r = Set_gen.internal_join result.r v r }
      | No result ->
        No { result with r= Set_gen.internal_join result.r v r }
    else
      match split r x with
      | Yes result -> 
        Yes {result with l = Set_gen.internal_join l v result.l}
      | No result ->   
        No {result with l = Set_gen.internal_join l v result.l}

let rec add (tree : t) x : t =  match tree with 
  | Empty -> singleton x
  | Leaf v -> 
    let c = compare_elt x v in
    if c = 0 then tree else     
    if c < 0 then 
      Set_gen.unsafe_two_elements x v
    else 
      Set_gen.unsafe_two_elements v x 
  | Node {l; v; r} as t ->
    let c = compare_elt x v in
    if c = 0 then t else
    if c < 0 then Set_gen.bal (add l x ) v r else Set_gen.bal l v (add r x )

let rec union (s1 : t) (s2 : t) : t  =
  match (s1, s2) with
  | (Empty, t) 
  | (t, Empty) -> t
  | Node _, Leaf v2 ->
    add s1 v2 
  | Leaf v1, Node _ -> 
    add s2 v1 
  | Leaf x, Leaf v -> 
    let c = compare_elt x v in
    if c = 0 then s1 else     
    if c < 0 then 
      Set_gen.unsafe_two_elements x v
    else 
      Set_gen.unsafe_two_elements v x
  | Node{l=l1; v=v1; r=r1; h=h1}, Node{l=l2; v=v2; r=r2; h=h2} ->
    if h1 >= h2 then    
      let split_result =  split s2 v1 in
      Set_gen.internal_join 
        (union l1 (split_l split_result)) v1 
        (union r1 (split_r split_result))  
    else    
      let split_result =  split s1 v2 in
      Set_gen.internal_join 
        (union (split_l split_result) l2) v2 
        (union (split_r split_result) r2)


let rec inter (s1 : t)  (s2 : t) : t  =
  match (s1, s2) with
  | (Empty, _) 
  | (_, Empty) -> empty  
  | Leaf v, _ -> 
    if mem s2 v then s1 else empty
  | Node ({ v } as s1), _ ->
    let result = split s2 v in 
    if split_pres result then 
      Set_gen.internal_join 
        (inter s1.l (split_l result)) 
        v 
        (inter s1.r (split_r result))
    else
      Set_gen.internal_concat 
        (inter s1.l (split_l result)) 
        (inter s1.r (split_r result))


let rec diff (s1 : t) (s2 : t) : t  =
  match (s1, s2) with
  | (Empty, _) -> empty
  | (t1, Empty) -> t1
  | Leaf v, _-> 
    if mem s2 v then empty else s1 
  | (Node({ v} as s1), _) ->
    let result =  split s2 v in
    if split_pres result then 
      Set_gen.internal_concat 
        (diff s1.l (split_l result)) 
        (diff s1.r (split_r result))    
    else
      Set_gen.internal_join 
        (diff s1.l (split_l result))
        v 
        (diff s1.r (split_r result))







let rec remove (tree : t)  (x : elt) : t = match tree with 
  | Empty -> empty (* This case actually would be never reached *)
  | Leaf v ->     
    if eq_elt x  v then empty else tree    
  | Node{l; v; r} ->
    let c = compare_elt x v in
    if c = 0 then Set_gen.internal_merge l r else
    if c < 0 then Set_gen.bal (remove l x) v r else Set_gen.bal l v (remove r x )

(* let compare s1 s2 = Set_gen.compare ~cmp:compare_elt s1 s2  *)



let of_list l =
  match l with
  | [] -> empty
  | [x0] -> singleton x0
  | [x0; x1] -> add (singleton x0) x1 
  | [x0; x1; x2] -> add (add (singleton x0)  x1) x2 
  | [x0; x1; x2; x3] -> add (add (add (singleton x0) x1 ) x2 ) x3 
  | [x0; x1; x2; x3; x4] -> add (add (add (add (singleton x0) x1) x2 ) x3 ) x4 
  | _ -> 
    let arrs = Array.of_list l in 
    Array.sort compare_elt arrs ; 
    of_sorted_array arrs



(* also check order *)
let invariant t =
  Set_gen.check t ;
  Set_gen.is_ordered ~cmp:compare_elt t          

let print fmt s = 
  Format.fprintf 
   fmt   "@[<v>{%a}@]@."
    (fun fmt s   -> 
       iter s
         (fun e -> Format.fprintf fmt "@[<v>%a@],@ " 
         print_elt e) 
    )
    s     






end
module Bsb_file_groups : sig 
#1 "bsb_file_groups.mli"
(* Copyright (C) 2018 - Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type public = Export_none | Export_all | Export_set of Set_string.t

type build_generator = {
  input : string list;
  output : string list;
  command : string;
}

type file_group = {
  dir : string;
  sources : Bsb_db.map;
  resources : string list;
  public : public;
  is_dev : bool;
  (* false means not in dev mode *)
  generators : build_generator list;
      (* output of [generators] should be added to [sources],
         if it is [.ml,.mli,.re,.rei]
      *)
}

type file_groups = file_group list

type t = private { files : file_groups; globbed_dirs : string list }

val empty : t

val merge : t -> t -> t

val cons : file_group:file_group -> ?globbed_dir:string -> t -> t

val is_empty : file_group -> bool

end = struct
#1 "bsb_file_groups.ml"
(* Copyright (C) 2018 - Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type public = Export_none | Export_all | Export_set of Set_string.t

type build_generator = {
  input : string list;
  output : string list;
  command : string;
}

type file_group = {
  dir : string;
  sources : Bsb_db.map;
  resources : string list;
  public : public;
  is_dev : bool;
  generators : build_generator list;
      (* output of [generators] should be added to [sources],
         if it is [.ml,.mli,.re,.rei]
      *)
}

type file_groups = file_group list

type t = { files : file_groups; globbed_dirs : string list }

let empty : t = { files = []; globbed_dirs = [] }

let merge (u : t) (v : t) =
  if u == empty then v
  else if v == empty then u
  else
    {
      files = Ext_list.append u.files v.files;
      globbed_dirs = Ext_list.append u.globbed_dirs v.globbed_dirs;
    }

let cons ~file_group ?globbed_dir (v : t) : t =
  {
    files = file_group :: v.files;
    globbed_dirs =
      (match globbed_dir with
      | None -> v.globbed_dirs
      | Some f -> f :: v.globbed_dirs);
  }

(** when [is_empty file_group]
    we don't need issue [-I] [-S] in [.merlin] file
*)
let is_empty (x : file_group) =
  Map_string.is_empty x.sources && x.resources = [] && x.generators = []

end
module Bs_version : sig 
#1 "bs_version.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val version : string

val header : string

val package_name : string ref

end = struct
#1 "bs_version.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
let version = "10.1.4"
let header = "// Generated by ReScript, PLEASE EDIT WITH CARE"
let package_name = ref "rescript"

end
module Bsb_pkg_types : sig 
#1 "bsb_pkg_types.mli"
(* Copyright (C) 2019- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t = Global of string | Scope of string * scope

and scope = string

val to_string : t -> string

val print : Format.formatter -> t -> unit

val equal : t -> t -> bool

(* The second element could be empty or dropped 
*)
val extract_pkg_name_and_file : string -> t * string

val string_as_package : string -> t

end = struct
#1 "bsb_pkg_types.ml"
(* Copyright (C) 2018- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Filename.concat

type t = Global of string | Scope of string * scope

and scope = string

let to_string (x : t) =
  match x with Global s -> s | Scope (s, scope) -> scope // s

let print fmt (x : t) =
  match x with
  | Global s -> Format.pp_print_string fmt s
  | Scope (name, scope) -> Format.fprintf fmt "%s/%s" scope name

let equal (x : t) y =
  match (x, y) with
  | Scope (a0, a1), Scope (b0, b1) -> a0 = b0 && a1 = b1
  | Global a0, Global b0 -> a0 = b0
  | Scope _, Global _ | Global _, Scope _ -> false

(**
   input: {[
     @hello/yy/xx
        hello/yy
   ]}
   FIXME: fix invalid input
   {[
     hello//xh//helo
   ]}
*)
let extract_pkg_name_and_file (s : string) =
  let len = String.length s in
  assert (len > 0);
  let v = String.unsafe_get s 0 in
  if v = '@' then (
    let scope_id = Ext_string.no_slash_idx s in
    assert (scope_id > 0);
    let pkg_id = Ext_string.no_slash_idx_from s (scope_id + 1) in
    let scope = String.sub s 0 scope_id in

    if pkg_id < 0 then
      (Scope (String.sub s (scope_id + 1) (len - scope_id - 1), scope), "")
    else
      ( Scope (String.sub s (scope_id + 1) (pkg_id - scope_id - 1), scope),
        String.sub s (pkg_id + 1) (len - pkg_id - 1) ))
  else
    let pkg_id = Ext_string.no_slash_idx s in
    if pkg_id < 0 then (Global s, "")
    else
      ( Global (String.sub s 0 pkg_id),
        String.sub s (pkg_id + 1) (len - pkg_id - 1) )

let string_as_package (s : string) : t =
  let len = String.length s in
  assert (len > 0);
  let v = String.unsafe_get s 0 in
  if v = '@' then (
    let scope_id = Ext_string.no_slash_idx s in
    assert (scope_id > 0);
    (* better-eror message for invalid scope package:
       @rescript/std
    *)
    Scope
      (String.sub s (scope_id + 1) (len - scope_id - 1), String.sub s 0 scope_id))
  else Global s

end
module Ext_json_types
= struct
#1 "ext_json_types.ml"
(* Copyright (C) 2015-2017 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type loc = Lexing.position

type json_str = { str : string; loc : loc }

type json_flo = { flo : string; loc : loc }

type json_array = { content : t array; loc_start : loc; loc_end : loc }

and json_map = { map : t Map_string.t; loc : loc }

and t =
  | True of loc
  | False of loc
  | Null of loc
  | Flo of json_flo
  | Str of json_str
  | Arr of json_array
  | Obj of json_map

end
module Ext_position : sig 
#1 "ext_position.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t = Lexing.position = {
  pos_fname : string;
  pos_lnum : int;
  pos_bol : int;
  pos_cnum : int;
}

val offset : t -> t -> t
(** [offset pos newpos]
    return a new position
    here [newpos] is zero based, the use case is that
    at position [pos], we get a string and Lexing from that string,
    therefore, we get a [newpos] and we need rebase it on top of 
    [pos]
*)

val lexbuf_from_channel_with_fname : in_channel -> string -> Lexing.lexbuf

val print : Format.formatter -> t -> unit

end = struct
#1 "ext_position.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t = Lexing.position = {
  pos_fname : string;
  pos_lnum : int;
  pos_bol : int;
  pos_cnum : int;
}

let offset (x : t) (y : t) =
  {
    x with
    pos_lnum = x.pos_lnum + y.pos_lnum - 1;
    pos_cnum = x.pos_cnum + y.pos_cnum;
    pos_bol = (if y.pos_lnum = 1 then x.pos_bol else x.pos_cnum + y.pos_bol);
  }

let print fmt (pos : t) =
  Format.fprintf fmt "(line %d, column %d)" pos.pos_lnum
    (pos.pos_cnum - pos.pos_bol)

let lexbuf_from_channel_with_fname ic fname =
  let x = Lexing.from_function (fun buf n -> input ic buf 0 n) in
  let pos : t =
    {
      pos_fname = fname;
      pos_lnum = 1;
      pos_bol = 0;
      pos_cnum = 0 (* copied from zero_pos*);
    }
  in
  x.lex_start_p <- pos;
  x.lex_curr_p <- pos;
  x

end
module Ext_json : sig 
#1 "ext_json.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type path = string list

type status = No_path | Found of Ext_json_types.t | Wrong_type of path

type callback =
  [ `Str of string -> unit
  | `Str_loc of string -> Lexing.position -> unit
  | `Flo of string -> unit
  | `Flo_loc of string -> Lexing.position -> unit
  | `Bool of bool -> unit
  | `Obj of Ext_json_types.t Map_string.t -> unit
  | `Arr of Ext_json_types.t array -> unit
  | `Arr_loc of
    Ext_json_types.t array -> Lexing.position -> Lexing.position -> unit
  | `Null of unit -> unit
  | `Not_found of unit -> unit
  | `Id of Ext_json_types.t -> unit ]

val test :
  ?fail:(unit -> unit) ->
  string ->
  callback ->
  Ext_json_types.t Map_string.t ->
  Ext_json_types.t Map_string.t

val loc_of : Ext_json_types.t -> Ext_position.t

end = struct
#1 "ext_json.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type callback =
  [ `Str of string -> unit
  | `Str_loc of string -> Lexing.position -> unit
  | `Flo of string -> unit
  | `Flo_loc of string -> Lexing.position -> unit
  | `Bool of bool -> unit
  | `Obj of Ext_json_types.t Map_string.t -> unit
  | `Arr of Ext_json_types.t array -> unit
  | `Arr_loc of
    Ext_json_types.t array -> Lexing.position -> Lexing.position -> unit
  | `Null of unit -> unit
  | `Not_found of unit -> unit
  | `Id of Ext_json_types.t -> unit ]

type path = string list

type status = No_path | Found of Ext_json_types.t | Wrong_type of path

let test ?(fail = fun () -> ()) key (cb : callback)
    (m : Ext_json_types.t Map_string.t) =
  (match (Map_string.find_exn m key, cb) with
  | exception Not_found -> (
      match cb with `Not_found f -> f () | _ -> fail ())
  | True _, `Bool cb -> cb true
  | False _, `Bool cb -> cb false
  | Flo { flo = s }, `Flo cb -> cb s
  | Flo { flo = s; loc }, `Flo_loc cb -> cb s loc
  | Obj { map = b }, `Obj cb -> cb b
  | Arr { content }, `Arr cb -> cb content
  | Arr { content; loc_start; loc_end }, `Arr_loc cb ->
      cb content loc_start loc_end
  | Null _, `Null cb -> cb ()
  | Str { str = s }, `Str cb -> cb s
  | Str { str = s; loc }, `Str_loc cb -> cb s loc
  | any, `Id cb -> cb any
  | _, _ -> fail ());
  m

let loc_of (x : Ext_json_types.t) =
  match x with
  | True p | False p | Null p -> p
  | Str p -> p.loc
  | Arr p -> p.loc_start
  | Obj p -> p.loc
  | Flo p -> p.loc

end
module Bsb_exception : sig 
#1 "bsb_exception.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type error
(**
    This module is used for fatal errros
*)

exception Error of error

val print : Format.formatter -> error -> unit

val package_not_found : pkg:Bsb_pkg_types.t -> json:string option -> 'a

val conflict_module : string -> string -> string -> exn

val errorf : loc:Ext_position.t -> ('a, unit, string, 'b) format4 -> 'a

val config_error : Ext_json_types.t -> string -> 'a

val invalid_spec : string -> 'a

val invalid_json : string -> 'a

val no_implementation : string -> 'a

val not_consistent : string -> 'a

end = struct
#1 "bsb_exception.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type error =
  | Package_not_found of Bsb_pkg_types.t * string option (* json file *)
  | Json_config of Ext_position.t * string
  | Invalid_json of string
  | Invalid_spec of string
  | Conflict_module of string * string * string
  | No_implementation of string
  | Not_consistent of string

exception Error of error

let error err = raise (Error err)

let package_not_found ~pkg ~json = error (Package_not_found (pkg, json))

let print (fmt : Format.formatter) (x : error) =
  match x with
  | Conflict_module (modname, dir1, dir2) ->
      Format.fprintf fmt
        "@{<error>Error:@} %s found in two directories: (%s, %s)\n\
         File names must be unique per project" modname dir1 dir2
  | Not_consistent modname ->
      Format.fprintf fmt
        "@{<error>Error:@} %s has implementation/interface in non-consistent \
         syntax(reason/ocaml)"
        modname
  | No_implementation modname ->
      Format.fprintf fmt
        "@{<error>Error:@} %s does not have implementation file" modname
  | Package_not_found (name, json_opt) ->
      let in_json =
        match json_opt with None -> Ext_string.empty | Some x -> " in " ^ x
      in
      let name = Bsb_pkg_types.to_string name in
      if Ext_string.equal name !Bs_version.package_name then
        Format.fprintf fmt
          "File \"bsconfig.json\", line 1\n\
           @{<error>Error:@} package @{<error>%s@} is not found %s\n\
           It's the basic, required package. If you have it installed globally,\n\
           Please run `npm link rescript` to make it available" name in_json
      else
        Format.fprintf fmt
          "File \"bsconfig.json\", line 1\n\
           @{<error>Error:@} package @{<error>%s@} not found or built %s\n\
           - Did you install it?\n\
           - If you did, did you run `rescript build -with-deps`?" name in_json
  | Json_config (pos, s) ->
      Format.fprintf fmt
        "File %S, line %d:\n\
         @{<error>Error:@} %s \n\
         For more details, please checkout the schema \
         https://rescript-lang.org/docs/manual/latest/build-configuration-schema"
        pos.pos_fname pos.pos_lnum s
  | Invalid_spec s ->
      Format.fprintf fmt "@{<error>Error: Invalid bsconfig.json %s@}" s
  | Invalid_json s ->
      Format.fprintf fmt
        "File %S, line 1\n@{<error>Error: Invalid json format@}" s

let conflict_module modname dir1 dir2 =
  Error (Conflict_module (modname, dir1, dir2))

let no_implementation modname = error (No_implementation modname)

let not_consistent modname = error (Not_consistent modname)

let errorf ~loc fmt =
  Format.ksprintf (fun s -> error (Json_config (loc, s))) fmt

let config_error config fmt =
  let loc = Ext_json.loc_of config in

  error (Json_config (loc, fmt))

let invalid_spec s = error (Invalid_spec s)

let invalid_json s = error (Invalid_json s)

let () =
  Printexc.register_printer (fun x ->
      match x with Error x -> Some (Format.asprintf "%a" print x) | _ -> None)

end
module Bsb_jsx
= struct
#1 "bsb_jsx.ml"
type version = Jsx_v3 | Jsx_v4
type module_ = React
type mode = Classic | Automatic
type dependencies = string list

type t = {
  version : version option;
  module_ : module_ option;
  mode : mode option;
  v3_dependencies : dependencies;
}

let encode_no_nl jsx =
  (match jsx.version with
  | None -> ""
  | Some Jsx_v3 -> "3"
  | Some Jsx_v4 -> "4")
  ^ (match jsx.module_ with None -> "" | Some React -> "React")
  ^
  match jsx.mode with
  | None -> ""
  | Some Classic -> "Classic"
  | Some Automatic -> "Automatic"

let ( .?() ) = Map_string.find_opt
let ( |? ) m (key, cb) = m |> Ext_json.test key cb

let get_list_string_acc (s : Ext_json_types.t array) acc =
  Ext_array.to_list_map_acc s acc (fun x ->
      match x with Str x -> Some x.str | _ -> None)

let get_list_string s = get_list_string_acc s []

let from_map map =
  let version : version option ref = ref None in
  let module_ : module_ option ref = ref None in
  let mode : mode option ref = ref None in
  let v3_dependencies : dependencies ref = ref [] in
  map
  |? ( Bsb_build_schemas.jsx,
       `Obj
         (fun m ->
           match m.?(Bsb_build_schemas.jsx_version) with
           | Some (Flo { loc; flo }) -> (
               match flo with
               | "3" -> version := Some Jsx_v3
               | "4" -> version := Some Jsx_v4
               | _ -> Bsb_exception.errorf ~loc "Unsupported jsx version %s" flo
               )
           | Some x ->
               Bsb_exception.config_error x
                 "Unexpected input (expect a version number) for jsx version"
           | None -> ()) )
  |? ( Bsb_build_schemas.jsx,
       `Obj
         (fun m ->
           match m.?(Bsb_build_schemas.jsx_module) with
           | Some (Str { loc; str }) -> (
               match str with
               | "react" -> module_ := Some React
               | _ -> Bsb_exception.errorf ~loc "Unsupported jsx module %s" str)
           | Some x ->
               Bsb_exception.config_error x
                 "Unexpected input (jsx module name) for jsx module"
           | None -> ()) )
  |? ( Bsb_build_schemas.jsx,
       `Obj
         (fun m ->
           match m.?(Bsb_build_schemas.jsx_mode) with
           | Some (Str { loc; str }) -> (
               match str with
               | "classic" -> mode := Some Classic
               | "automatic" -> mode := Some Automatic
               | _ -> Bsb_exception.errorf ~loc "Unsupported jsx mode %s" str)
           | Some x ->
               Bsb_exception.config_error x
                 "Unexpected input (expect classic or automatic) for jsx mode"
           | None -> ()) )
  |? ( Bsb_build_schemas.jsx,
       `Obj
         (fun m ->
           match m.?(Bsb_build_schemas.jsx_v3_dependencies) with
           | Some (Arr { content }) ->
            v3_dependencies := get_list_string content
           | Some x ->
               Bsb_exception.config_error x
                 "Unexpected input for jsx v3-dependencies"
           | None -> ()) )
  |> ignore;
  {
    version = !version;
    module_ = !module_;
    mode = !mode;
    v3_dependencies = !v3_dependencies;
  }

end
module Ext_module_system
= struct
#1 "ext_module_system.ml"
type t = NodeJS | Es6 | Es6_global

end
module Ext_pervasives : sig 
#1 "ext_pervasives.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** Extension to standard library [Pervavives] module, safe to open 
*)

external reraise : exn -> 'a = "%reraise"

val finally : 'a -> clean:('a -> unit) -> ('a -> 'b) -> 'b

(* val try_it : (unit -> 'a) ->  unit  *)

val with_file_as_chan : string -> (out_channel -> 'a) -> 'a

val max_int : int -> int -> int

val min_int : int -> int -> int

val max_int_option : int option -> int option -> int option

(* external id : 'a -> 'a = "%identity" *)

(** Copied from {!Btype.hash_variant}:
    need sync up and add test case
*)
(* val hash_variant : string -> int *)

(* val todo : string -> 'a *)

val nat_of_string_exn : string -> int

val parse_nat_of_string : string -> int ref -> int

end = struct
#1 "ext_pervasives.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

external reraise : exn -> 'a = "%reraise"

let finally v ~clean:action f =
  match f v with
  | exception e ->
      action v;
      reraise e
  | e ->
      action v;
      e

(* let try_it f  =
   try ignore (f ()) with _ -> () *)

let with_file_as_chan filename f =
  finally (open_out_bin filename) ~clean:close_out f

let max_int (x : int) y = if x >= y then x else y

let min_int (x : int) y = if x < y then x else y

let max_int_option (x : int option) y =
  match (x, y) with
  | None, _ -> y
  | Some _, None -> x
  | Some x0, Some y0 -> if x0 >= y0 then x else y

(* external id : 'a -> 'a = "%identity" *)

(*
   let hash_variant s =
     let accu = ref 0 in
     for i = 0 to String.length s - 1 do
       accu := 223 * !accu + Char.code s.[i]
     done;
     (* reduce to 31 bits *)
     accu := !accu land (1 lsl 31 - 1);
     (* make it signed for 64 bits architectures *)
     if !accu > 0x3FFFFFFF then !accu - (1 lsl 31) else !accu *)

(* let todo loc =
   failwith (loc ^ " Not supported yet")
*)

let rec int_of_string_aux s acc off len =
  if off >= len then acc
  else
    let d = Char.code (String.unsafe_get s off) - 48 in
    if d >= 0 && d <= 9 then int_of_string_aux s ((10 * acc) + d) (off + 1) len
    else -1
(* error *)

let nat_of_string_exn (s : string) =
  let acc = int_of_string_aux s 0 0 (String.length s) in
  if acc < 0 then invalid_arg s else acc

(** return index *)
let parse_nat_of_string (s : string) (cursor : int ref) =
  let current = !cursor in
  assert (current >= 0);
  let acc = ref 0 in
  let s_len = String.length s in
  let todo = ref true in
  let cur = ref current in
  while !todo && !cursor < s_len do
    let d = Char.code (String.unsafe_get s !cur) - 48 in
    if d >= 0 && d <= 9 then (
      acc := (10 * !acc) + d;
      incr cur)
    else todo := false
  done;
  cursor := !cur;
  !acc

end
module Ext_fmt
= struct
#1 "ext_fmt.ml"
let with_file_as_pp filename f =
  Ext_pervasives.finally (open_out_bin filename) ~clean:close_out (fun chan ->
      let fmt = Format.formatter_of_out_channel chan in
      let v = f fmt in
      Format.pp_print_flush fmt ();
      v)

let failwithf ~loc fmt = Format.ksprintf (fun s -> failwith (loc ^ s)) fmt

let invalid_argf fmt = Format.ksprintf invalid_arg fmt

end
module Ext_sys : sig 
#1 "ext_sys.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val is_directory_no_exn : string -> bool

val is_windows_or_cygwin : bool

end = struct
#1 "ext_sys.pp.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** TODO: not exported yet, wait for Windows Fix*)

external is_directory_no_exn : string -> bool = "caml_sys_is_directory_no_exn"



let is_windows_or_cygwin = Sys.win32 || Sys.cygwin



end
module Literals
= struct
#1 "literals.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let js_array_ctor = "Array"

let js_type_number = "number"

let js_type_string = "string"

let js_type_object = "object"

let js_type_boolean = "boolean"

let js_undefined = "undefined"

let js_prop_length = "length"

let prim = "prim"

let param = "param"

let partial_arg = "partial_arg"

let tmp = "tmp"

let create = "create" (* {!Caml_exceptions.create}*)

let runtime = "runtime" (* runtime directory *)

let stdlib = "stdlib"

let imul = "imul" (* signed int32 mul *)

let setter_suffix = "#="

let setter_suffix_len = String.length setter_suffix

let debugger = "debugger"

let fn_run = "fn_run"

let method_run = "method_run"

let fn_method = "fn_method"

let fn_mk = "fn_mk"
(*let js_fn_runmethod = "js_fn_runmethod"*)

(** nodejs *)
let node_modules = "node_modules"

let node_modules_length = String.length "node_modules"

let package_json = "package.json"

let bsconfig_json = "bsconfig.json"

let build_ninja = "build.ninja"

(* Name of the library file created for each external dependency. *)
let library_file = "lib"

let suffix_a = ".a"

let suffix_cmj = ".cmj"

let suffix_cmo = ".cmo"

let suffix_cma = ".cma"

let suffix_cmi = ".cmi"

let suffix_cmx = ".cmx"

let suffix_cmxa = ".cmxa"

let suffix_mll = ".mll"

let suffix_ml = ".ml"

let suffix_mli = ".mli"

let suffix_re = ".re"

let suffix_rei = ".rei"

let suffix_res = ".res"

let suffix_resi = ".resi"

let suffix_mlmap = ".mlmap"

let suffix_cmt = ".cmt"

let suffix_cmti = ".cmti"

let suffix_ast = ".ast"

let suffix_iast = ".iast"

let suffix_d = ".d"

let suffix_js = ".js"

let suffix_bs_js = ".bs.js"

let suffix_mjs = ".mjs"

let suffix_bs_mjs = ".bs.mjs"

let suffix_cjs = ".cjs"

let suffix_bs_cjs = ".bs.cjs"

let suffix_gen_js = ".gen.js"

let suffix_gen_tsx = ".gen.tsx"

let commonjs = "commonjs"

let es6 = "es6"

let es6_global = "es6-global"

let unused_attribute = "Unused attribute "

(** Used when produce node compatible paths *)
let node_sep = "/"

let node_parent = ".."

let node_current = "."

let gentype_import1 = "genType.import"
let gentype_import2 = "gentype.import"

let bsbuild_cache = ".bsbuild"

let sourcedirs_meta = ".sourcedirs.json"

(* Note the build system should check the validity of filenames
   espeically, it should not contain '-'
*)
let ns_sep_char = '-'

let ns_sep = "-"

let exception_id = "RE_EXN_ID"

let polyvar_hash = "NAME"

let polyvar_value = "VAL"

let cons = "::"

let hd = "hd"

let tl = "tl"

let lazy_done = "LAZY_DONE"

let lazy_val = "VAL"

let pure = "@__PURE__"

end
module Ext_path : sig 
#1 "ext_path.mli"
(* Copyright (C) 2017 Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t

val simple_convert_node_path_to_os_path : string -> string
(** Js_output is node style, which means 
    separator is only '/'

    if the path contains 'node_modules', 
    [node_relative_path] will discard its prefix and 
    just treat it as a library instead
*)

val combine : string -> string -> string
(**
   [combine path1 path2]
   1. add some simplifications when concatenating
   2. when [path2] is absolute, return [path2]
*)

(**
   {[
     get_extension "a.txt" = ".txt"
       get_extension "a" = ""
   ]}
*)

val node_rebase_file : from:string -> to_:string -> string -> string

val rel_normalized_absolute_path : from:string -> string -> string
(** 
   TODO: could be highly optimized
   if [from] and [to] resolve to the same path, a zero-length string is returned 
   Given that two paths are directory

   A typical use case is 
   {[
     Filename.concat 
       (rel_normalized_absolute_path cwd (Filename.dirname a))
       (Filename.basename a)
   ]}
*)

val normalize_absolute_path : string -> string

val absolute_cwd_path : string -> string

val concat : string -> string -> string
(** [concat dirname filename]
    The same as {!Filename.concat} except a tiny optimization 
    for current directory simplification
*)

val check_suffix_case : string -> string -> bool

(* It is lazy so that it will not hit errors when in script mode *)
val package_dir : string Lazy.t

end = struct
#1 "ext_path.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* [@@@warning "-37"] *)
type t = (* | File of string  *)
  | Dir of string [@@unboxed]

let simple_convert_node_path_to_os_path =
  if Sys.unix then fun x -> x
  else if Sys.win32 || Sys.cygwin then Ext_string.replace_slash_backward
  else failwith ("Unknown OS : " ^ Sys.os_type)

let cwd = lazy (Sys.getcwd ())

let split_by_sep_per_os : string -> string list =
  if Ext_sys.is_windows_or_cygwin then fun x ->
    (* on Windows, we can still accept -bs-package-output lib/js *)
    Ext_string.split_by
      (fun x -> match x with '/' | '\\' -> true | _ -> false)
      x
  else fun x -> Ext_string.split x '/'

(** example
    {[
      "/bb/mbigc/mbig2899/bgit/bucklescript/jscomp/stdlib/external/pervasives.cmj"
        "/bb/mbigc/mbig2899/bgit/bucklescript/jscomp/stdlib/ocaml_array.ml"
    ]}

    The other way
    {[

      "/bb/mbigc/mbig2899/bgit/bucklescript/jscomp/stdlib/ocaml_array.ml"
        "/bb/mbigc/mbig2899/bgit/bucklescript/jscomp/stdlib/external/pervasives.cmj"
    ]}
    {[
      "/bb/mbigc/mbig2899/bgit/bucklescript/jscomp/stdlib//ocaml_array.ml"
    ]}
    {[
      /a/b
      /c/d
    ]}
*)
let node_relative_path ~from:(file_or_dir_2 : t) (file_or_dir_1 : t) =
  let relevant_dir1 =
    match file_or_dir_1 with Dir x -> x
    (* | File file1 ->  Filename.dirname file1 *)
  in
  let relevant_dir2 =
    match file_or_dir_2 with Dir x -> x
    (* | File file2 -> Filename.dirname file2  *)
  in
  let dir1 = split_by_sep_per_os relevant_dir1 in
  let dir2 = split_by_sep_per_os relevant_dir2 in
  let rec go (dir1 : string list) (dir2 : string list) =
    match (dir1, dir2) with
    | "." :: xs, ys -> go xs ys
    | xs, "." :: ys -> go xs ys
    | x :: xs, y :: ys when x = y -> go xs ys
    | _, _ -> Ext_list.map_append dir2 dir1 (fun _ -> Literals.node_parent)
  in
  match go dir1 dir2 with
  | x :: _ as ys when x = Literals.node_parent ->
      String.concat Literals.node_sep ys
  | ys -> String.concat Literals.node_sep @@ (Literals.node_current :: ys)

let node_concat ~dir base = dir ^ Literals.node_sep ^ base

let node_rebase_file ~from ~to_ file =
  node_concat
    ~dir:
      (if from = to_ then Literals.node_current
      else node_relative_path ~from:(Dir from) (Dir to_))
    file

(***
   {[
     Filename.concat "." "";;
     "./"
   ]}
*)
let combine path1 path2 =
  if Filename.is_relative path2 then
    if Ext_string.is_empty path2 then path1
    else if path1 = Filename.current_dir_name then path2
    else if path2 = Filename.current_dir_name then path1
    else Filename.concat path1 path2
  else path2

let ( // ) x y =
  if x = Filename.current_dir_name then y
  else if y = Filename.current_dir_name then x
  else Filename.concat x y

(**
   {[
     split_aux "//ghosg//ghsogh/";;
     - : string * string list = ("/", ["ghosg"; "ghsogh"])
   ]}
   Note that 
   {[
     Filename.dirname "/a/" = "/"
       Filename.dirname "/a/b/" = Filename.dirname "/a/b" = "/a"
   ]}
   Special case:
   {[
     basename "//" = "/"
       basename "///"  = "/"
   ]}
   {[
     basename "" =  "."
       basename "" = "."
       dirname "" = "."
       dirname "" =  "."
   ]}  
*)
let split_aux p =
  let rec go p acc =
    let dir = Filename.dirname p in
    if dir = p then (dir, acc)
    else
      let new_path = Filename.basename p in
      if Ext_string.equal new_path Filename.dir_sep then go dir acc
        (* We could do more path simplification here
           leave to [rel_normalized_absolute_path]
        *)
      else go dir (new_path :: acc)
  in

  go p []

(** 
   TODO: optimization
   if [from] and [to] resolve to the same path, a zero-length string is returned 

   This function is useed in [es6-global] and 
   [amdjs-global] format and tailored for `rollup`
*)
let rel_normalized_absolute_path ~from to_ =
  let root1, paths1 = split_aux from in
  let root2, paths2 = split_aux to_ in
  if root1 <> root2 then root2
  else
    let rec go xss yss =
      match (xss, yss) with
      | x :: xs, y :: ys ->
          if Ext_string.equal x y then go xs ys
          else if x = Filename.current_dir_name then go xs yss
          else if y = Filename.current_dir_name then go xss ys
          else
            let start =
              Ext_list.fold_left xs Ext_string.parent_dir_lit (fun acc _ ->
                  acc // Ext_string.parent_dir_lit)
            in
            Ext_list.fold_left yss start (fun acc v -> acc // v)
      | [], [] -> Ext_string.empty
      | [], y :: ys -> Ext_list.fold_left ys y (fun acc x -> acc // x)
      | _ :: xs, [] ->
          Ext_list.fold_left xs Ext_string.parent_dir_lit (fun acc _ ->
              acc // Ext_string.parent_dir_lit)
    in
    let v = go paths1 paths2 in

    if Ext_string.is_empty v then Literals.node_current
    else if
      v = "." || v = ".."
      || Ext_string.starts_with v "./"
      || Ext_string.starts_with v "../"
    then v
    else "./" ^ v

(*TODO: could be hgighly optimized later
  {[
    normalize_absolute_path "/gsho/./..";;

    normalize_absolute_path "/a/b/../c../d/e/f";;

    normalize_absolute_path "/gsho/./..";;

    normalize_absolute_path "/gsho/./../..";;

    normalize_absolute_path "/a/b/c/d";;

    normalize_absolute_path "/a/b/c/d/";;

    normalize_absolute_path "/a/";;

    normalize_absolute_path "/a";;
  ]}
*)

(** See tests in {!Ounit_path_tests} *)
let normalize_absolute_path x =
  let drop_if_exist xs = match xs with [] -> [] | _ :: xs -> xs in
  let rec normalize_list acc paths =
    match paths with
    | [] -> acc
    | x :: xs ->
        if Ext_string.equal x Ext_string.current_dir_lit then
          normalize_list acc xs
        else if Ext_string.equal x Ext_string.parent_dir_lit then
          normalize_list (drop_if_exist acc) xs
        else normalize_list (x :: acc) xs
  in
  let root, paths = split_aux x in
  let rev_paths = normalize_list [] paths in
  let rec go acc rev_paths =
    match rev_paths with
    | [] -> Filename.concat root acc
    | last :: rest -> go (Filename.concat last acc) rest
  in
  match rev_paths with [] -> root | last :: rest -> go last rest

let absolute_path cwd s =
  let process s =
    let s = if Filename.is_relative s then Lazy.force cwd // s else s in
    (* Now simplify . and .. components *)
    let rec aux s =
      let base, dir = (Filename.basename s, Filename.dirname s) in
      if dir = s then dir
      else if base = Filename.current_dir_name then aux dir
      else if base = Filename.parent_dir_name then Filename.dirname (aux dir)
      else aux dir // base
    in
    aux s
  in
  process s

let absolute_cwd_path s = absolute_path cwd s

(* let absolute cwd s =
   match s with
   | File x -> File (absolute_path cwd x )
   | Dir x -> Dir (absolute_path cwd x) *)

let concat dirname filename =
  if filename = Filename.current_dir_name then dirname
  else if dirname = Filename.current_dir_name then filename
  else Filename.concat dirname filename

let check_suffix_case = Ext_string.ends_with

(* Input must be absolute directory *)
let rec find_root_filename ~cwd filename =
  if Sys.file_exists (Filename.concat cwd filename) then cwd
  else
    let cwd' = Filename.dirname cwd in
    if String.length cwd' < String.length cwd then
      find_root_filename ~cwd:cwd' filename
    else Ext_fmt.failwithf ~loc:__LOC__ "%s not found from %s" filename cwd

let find_package_json_dir cwd = find_root_filename ~cwd Literals.bsconfig_json

let package_dir = lazy (find_package_json_dir (Lazy.force cwd))

end
module Bsb_config : sig 
#1 "bsb_config.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val ocaml_bin_install_prefix : string -> string

val proj_rel : string -> string

val lib_lit : string

val lib_js : string

val lib_bs : string

val lib_es6 : string

val lib_es6_global : string

val lib_ocaml : string

val all_lib_artifacts : string list

(* we need generate path relative to [lib/bs] directory in the opposite direction *)
val rev_lib_bs_prefix : string -> string

val lib_bs_prefix_of_format : Ext_module_system.t -> string

val top_prefix_of_format : Ext_module_system.t -> string
(** default not install, only when -make-world, its dependencies will be installed  *)

end = struct
#1 "bsb_config.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
let ( // ) = Ext_path.combine

let lib_lit = "lib"

let lib_js = lib_lit // "js"

let lib_ocaml = lib_lit // "ocaml"

let lib_bs = lib_lit // "bs"

let lib_es6 = lib_lit // "es6"

let lib_es6_global = lib_lit // "es6_global"

let all_lib_artifacts = [ lib_js; lib_ocaml; lib_bs; lib_es6; lib_es6_global ]

let rev_lib_bs = ".." // ".."

(* access the js directory from "lib/bs",
   it would be '../js'
*)
let lib_bs_prefix_of_format (x : Ext_module_system.t) =
  ".."
  // match x with NodeJS -> "js" | Es6 -> "es6" | Es6_global -> "es6_global"

(* lib/js, lib/es6, lib/es6_global *)
let top_prefix_of_format (x : Ext_module_system.t) =
  match x with
  | NodeJS -> lib_js
  | Es6 -> lib_es6
  | Es6_global -> lib_es6_global

let rev_lib_bs_prefix p = rev_lib_bs // p

let ocaml_bin_install_prefix p = lib_ocaml // p

let proj_rel path = rev_lib_bs // path

(** it may not be a bad idea to hard code the binary path 
    of bsb in configuration time
*)

(* let cmd_package_specs = ref None  *)

end
module Ext_color : sig 
#1 "ext_color.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White

type style = FG of color | BG of color | Bold | Dim

val ansi_of_tag : string -> string
(** Input is the tag for example `@{<warning>@}` return escape code *)

val reset_lit : string

end = struct
#1 "ext_color.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White

type style = FG of color | BG of color | Bold | Dim

(* let ansi_of_color = function
   | Black -> "0"
   | Red -> "1"
   | Green -> "2"
   | Yellow -> "3"
   | Blue -> "4"
   | Magenta -> "5"
   | Cyan -> "6"
   | White -> "7" *)

let code_of_style = function
  | FG Black -> "30"
  | FG Red -> "31"
  | FG Green -> "32"
  | FG Yellow -> "33"
  | FG Blue -> "34"
  | FG Magenta -> "35"
  | FG Cyan -> "36"
  | FG White -> "37"
  | BG Black -> "40"
  | BG Red -> "41"
  | BG Green -> "42"
  | BG Yellow -> "43"
  | BG Blue -> "44"
  | BG Magenta -> "45"
  | BG Cyan -> "46"
  | BG White -> "47"
  | Bold -> "1"
  | Dim -> "2"

(** TODO: add more styles later *)
let style_of_tag s =
  match s with
  | "error" -> [ Bold; FG Red ]
  | "warning" -> [ Bold; FG Magenta ]
  | "info" -> [ Bold; FG Yellow ]
  | "dim" -> [ Dim ]
  | "filename" -> [ FG Cyan ]
  | _ -> []

let ansi_of_tag s =
  let l = style_of_tag s in
  let s = String.concat ";" (Ext_list.map l code_of_style) in
  "\x1b[" ^ s ^ "m"

let reset_lit = "\x1b[0m"

end
module Bsb_log : sig 
#1 "bsb_log.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val setup : unit -> unit

type level = Debug | Info | Warn | Error

val log_level : level ref

type 'a fmt = Format.formatter -> ('a, Format.formatter, unit) format -> 'a

type 'a log = ('a, Format.formatter, unit) format -> 'a

val verbose : unit -> unit

val debug : 'a log

val info : 'a log

val warn : 'a log

val error : 'a log

val info_args : string array -> unit

end = struct
#1 "bsb_log.ml"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ninja_ansi_forced =
  lazy (try Sys.getenv "NINJA_ANSI_FORCED" with Not_found -> "")

let color_enabled = lazy (Unix.isatty Unix.stdout)

(* same logic as [ninja.exe] *)
let get_color_enabled () =
  let colorful =
    match ninja_ansi_forced with
    | (lazy "1") -> true
    | (lazy ("0" | "false")) -> false
    | _ -> Lazy.force color_enabled
  in
  colorful

let color_functions : Format.formatter_tag_functions =
  {
    mark_open_tag =
      (fun s ->
        if get_color_enabled () then Ext_color.ansi_of_tag s
        else Ext_string.empty);
    mark_close_tag =
      (fun _ ->
        if get_color_enabled () then Ext_color.reset_lit else Ext_string.empty);
    print_open_tag = (fun _ -> ());
    print_close_tag = (fun _ -> ());
  }

(* let set_color ppf =
   Format.pp_set_formatter_tag_functions ppf color_functions *)

let setup () =
  Format.pp_set_mark_tags Format.std_formatter true;
  Format.pp_set_mark_tags Format.err_formatter true;
  Format.pp_set_formatter_tag_functions Format.std_formatter color_functions;
  Format.pp_set_formatter_tag_functions Format.err_formatter color_functions

type level = Debug | Info | Warn | Error

let int_of_level (x : level) =
  match x with Debug -> 0 | Info -> 1 | Warn -> 2 | Error -> 3

let log_level = ref Warn

let verbose () = log_level := Debug

let dfprintf level fmt =
  if int_of_level level >= int_of_level !log_level then Format.fprintf fmt
  else Format.ifprintf fmt

type 'a fmt = Format.formatter -> ('a, Format.formatter, unit) format -> 'a

type 'a log = ('a, Format.formatter, unit) format -> 'a

let debug fmt = dfprintf Debug Format.std_formatter fmt

let info fmt = dfprintf Info Format.std_formatter fmt

let warn fmt = dfprintf Warn Format.err_formatter fmt

let error fmt = dfprintf Error Format.err_formatter fmt

let info_args (args : string array) =
  if int_of_level Info >= int_of_level !log_level then (
    for i = 0 to Array.length args - 1 do
      Format.pp_print_string Format.std_formatter (Array.unsafe_get args i);
      Format.pp_print_string Format.std_formatter Ext_string.single_space
    done;
    Format.pp_print_newline Format.std_formatter ())
  else ()

end
module Bsb_real_path : sig 
#1 "bsb_real_path.mli"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val is_same_paths_via_io : string -> string -> bool

end = struct
#1 "bsb_real_path.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Filename.concat

let normalize_exn (s : string) : string =
  let old_cwd = Sys.getcwd () in
  Unix.chdir s;
  let normalized = Sys.getcwd () in
  Unix.chdir old_cwd;
  normalized

let real_path p =
  match Sys.is_directory p with
  | exception _ ->
      let rec resolve dir =
        if Sys.file_exists dir then normalize_exn dir
        else
          let parent = Filename.dirname dir in
          if dir = parent then dir else resolve parent // Filename.basename dir
      in
      let p = if Filename.is_relative p then Sys.getcwd () // p else p in
      resolve p
  | true -> normalize_exn p
  | false -> (
      let dir = normalize_exn (Filename.dirname p) in
      match Filename.basename p with "." -> dir | base -> dir // base)

let is_same_paths_via_io a b = if a = b then true else real_path a = real_path b

end
module Ext_utf8 : sig 
#1 "ext_utf8.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type byte = Single of int | Cont of int | Leading of int * int | Invalid

val classify : char -> byte

val follow : string -> int -> int -> int -> int * int

val next : string -> remaining:int -> int -> int
(** 
   return [-1] if failed 
*)

exception Invalid_utf8 of string

val decode_utf8_string : string -> int list

val encode_codepoint : int -> string

end = struct
#1 "ext_utf8.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type byte = Single of int | Cont of int | Leading of int * int | Invalid

(** [classify chr] returns the {!byte} corresponding to [chr] *)
let classify chr =
  let c = int_of_char chr in
  (* Classify byte according to leftmost 0 bit *)
  if c land 0b1000_0000 = 0 then Single c
  else if (* c 0b0____*)
          c land 0b0100_0000 = 0 then Cont (c land 0b0011_1111)
  else if (* c 0b10___*)
          c land 0b0010_0000 = 0 then Leading (1, c land 0b0001_1111)
  else if (* c 0b110__*)
          c land 0b0001_0000 = 0 then Leading (2, c land 0b0000_1111)
  else if (* c 0b1110_ *)
          c land 0b0000_1000 = 0 then Leading (3, c land 0b0000_0111)
  else if (* c 0b1111_0___*)
          c land 0b0000_0100 = 0 then Leading (4, c land 0b0000_0011)
  else if (* c 0b1111_10__*)
          c land 0b0000_0010 = 0 then Leading (5, c land 0b0000_0001)
    (* c 0b1111_110__ *)
  else Invalid

exception Invalid_utf8 of string

(* when the first char is [Leading],
   TODO: need more error checking
   when out of bond
*)
let rec follow s n (c : int) offset =
  if n = 0 then (c, offset)
  else
    match classify s.[offset + 1] with
    | Cont cc -> follow s (n - 1) ((c lsl 6) lor (cc land 0x3f)) (offset + 1)
    | _ -> raise (Invalid_utf8 "Continuation byte expected")

let rec next s ~remaining offset =
  if remaining = 0 then offset
  else
    match classify s.[offset + 1] with
    | Cont _cc -> next s ~remaining:(remaining - 1) (offset + 1)
    | _ -> -1
    | exception _ -> -1
(* it can happen when out of bound *)

let decode_utf8_string s =
  let lst = ref [] in
  let add elem = lst := elem :: !lst in
  let rec decode_utf8_cont s i s_len =
    if i = s_len then ()
    else
      match classify s.[i] with
      | Single c ->
          add c;
          decode_utf8_cont s (i + 1) s_len
      | Cont _ -> raise (Invalid_utf8 "Unexpected continuation byte")
      | Leading (n, c) ->
          let c', i' = follow s n c i in
          add c';
          decode_utf8_cont s (i' + 1) s_len
      | Invalid -> raise (Invalid_utf8 "Invalid byte")
  in
  decode_utf8_cont s 0 (String.length s);
  List.rev !lst

(** To decode {j||j} we need verify in the ast so that we have better error 
    location, then we do the decode later
*)

(* let verify s loc =
   assert false *)

let encode_codepoint c =
  (* reused from syntax/src/res_utf8.ml *)
  let h2 = 0b1100_0000 in
  let h3 = 0b1110_0000 in
  let h4 = 0b1111_0000 in
  let cont_mask = 0b0011_1111 in
  if c <= 127 then (
    let bytes = (Bytes.create [@doesNotRaise]) 1 in
    Bytes.unsafe_set bytes 0 (Char.unsafe_chr c);
    Bytes.unsafe_to_string bytes)
  else if c <= 2047 then (
    let bytes = (Bytes.create [@doesNotRaise]) 2 in
    Bytes.unsafe_set bytes 0 (Char.unsafe_chr (h2 lor (c lsr 6)));
    Bytes.unsafe_set bytes 1
      (Char.unsafe_chr (0b1000_0000 lor (c land cont_mask)));
    Bytes.unsafe_to_string bytes)
  else if c <= 65535 then (
    let bytes = (Bytes.create [@doesNotRaise]) 3 in
    Bytes.unsafe_set bytes 0 (Char.unsafe_chr (h3 lor (c lsr 12)));
    Bytes.unsafe_set bytes 1
      (Char.unsafe_chr (0b1000_0000 lor ((c lsr 6) land cont_mask)));
    Bytes.unsafe_set bytes 2
      (Char.unsafe_chr (0b1000_0000 lor (c land cont_mask)));
    Bytes.unsafe_to_string bytes)
  else
    (* if c <= max then *)
    let bytes = (Bytes.create [@doesNotRaise]) 4 in
    Bytes.unsafe_set bytes 0 (Char.unsafe_chr (h4 lor (c lsr 18)));
    Bytes.unsafe_set bytes 1
      (Char.unsafe_chr (0b1000_0000 lor ((c lsr 12) land cont_mask)));
    Bytes.unsafe_set bytes 2
      (Char.unsafe_chr (0b1000_0000 lor ((c lsr 6) land cont_mask)));
    Bytes.unsafe_set bytes 3
      (Char.unsafe_chr (0b1000_0000 lor (c land cont_mask)));
    Bytes.unsafe_to_string bytes


end
module Ext_util : sig 
#1 "ext_util.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val power_2_above : int -> int -> int

val stats_to_string : Hashtbl.statistics -> string

val string_of_int_as_char : int -> string


end = struct
#1 "ext_util.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(**
   {[
     (power_2_above 16 63 = 64)
       (power_2_above 16 76 = 128)
   ]}
*)
let rec power_2_above x n =
  if x >= n then x
  else if x * 2 > Sys.max_array_length then x
  else power_2_above (x * 2) n

let stats_to_string
    ({ num_bindings; num_buckets; max_bucket_length; bucket_histogram } :
      Hashtbl.statistics) =
  Printf.sprintf "bindings: %d,buckets: %d, longest: %d, hist:[%s]" num_bindings
    num_buckets max_bucket_length
    (String.concat ","
       (Array.to_list (Array.map string_of_int bucket_histogram)))

let string_of_int_as_char (i : int) : string =
  if i <= 255 && i >= 0 then Format.asprintf "%C" (Char.unsafe_chr i)
  else
    let str =
      match Char.unsafe_chr i with
      | '\'' -> "\\'"
      | '\\' -> "\\\\"
      | '\n' -> "\\n"
      | '\t' -> "\\t"
      | '\r' -> "\\r"
      | '\b' -> "\\b"
      | ' ' .. '~' as c ->
          let s = (Bytes.create [@doesNotRaise]) 1 in
          Bytes.unsafe_set s 0 c;
          Bytes.unsafe_to_string s
      | _ -> Ext_utf8.encode_codepoint i
    in
    Printf.sprintf "\'%s\'" str

end
module Hash_gen
= struct
#1 "hash_gen.ml"
(***********************************************************************)
(*                                                                     *)
(*                                OCaml                                *)
(*                                                                     *)
(*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         *)
(*                                                                     *)
(*  Copyright 1996 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the GNU Library General Public License, with    *)
(*  the special exception on linking described in file ../LICENSE.     *)
(*                                                                     *)
(***********************************************************************)

(* Hash tables *)

(* We do dynamic hashing, and resize the table and rehash the elements
   when buckets become too long. *)

type ('a, 'b) bucket =
  | Empty
  | Cons of {
      mutable key : 'a;
      mutable data : 'b;
      mutable next : ('a, 'b) bucket;
    }

type ('a, 'b) t = {
  mutable size : int;
  (* number of entries *)
  mutable data : ('a, 'b) bucket array;
  (* the buckets *)
  initial_size : int; (* initial array size *)
}

let create initial_size =
  let s = Ext_util.power_2_above 16 initial_size in
  { initial_size = s; size = 0; data = Array.make s Empty }

let clear h =
  h.size <- 0;
  let len = Array.length h.data in
  for i = 0 to len - 1 do
    Array.unsafe_set h.data i Empty
  done

let reset h =
  h.size <- 0;
  h.data <- Array.make h.initial_size Empty

let length h = h.size

let resize indexfun h =
  let odata = h.data in
  let osize = Array.length odata in
  let nsize = osize * 2 in
  if nsize < Sys.max_array_length then (
    let ndata = Array.make nsize Empty in
    let ndata_tail = Array.make nsize Empty in
    h.data <- ndata;
    (* so that indexfun sees the new bucket count *)
    let rec insert_bucket = function
      | Empty -> ()
      | Cons { key; next } as cell ->
          let nidx = indexfun h key in
          (match Array.unsafe_get ndata_tail nidx with
          | Empty -> Array.unsafe_set ndata nidx cell
          | Cons tail -> tail.next <- cell);
          Array.unsafe_set ndata_tail nidx cell;
          insert_bucket next
    in
    for i = 0 to osize - 1 do
      insert_bucket (Array.unsafe_get odata i)
    done;
    for i = 0 to nsize - 1 do
      match Array.unsafe_get ndata_tail i with
      | Empty -> ()
      | Cons tail -> tail.next <- Empty
    done)

let iter h f =
  let rec do_bucket = function
    | Empty -> ()
    | Cons l ->
        f l.key l.data;
        do_bucket l.next
  in
  let d = h.data in
  for i = 0 to Array.length d - 1 do
    do_bucket (Array.unsafe_get d i)
  done

let fold h init f =
  let rec do_bucket b accu =
    match b with
    | Empty -> accu
    | Cons l -> do_bucket l.next (f l.key l.data accu)
  in
  let d = h.data in
  let accu = ref init in
  for i = 0 to Array.length d - 1 do
    accu := do_bucket (Array.unsafe_get d i) !accu
  done;
  !accu

let to_list h f = fold h [] (fun k data acc -> f k data :: acc)

let rec small_bucket_mem (lst : _ bucket) eq key =
  match lst with
  | Empty -> false
  | Cons lst -> (
      eq key lst.key
      ||
      match lst.next with
      | Empty -> false
      | Cons lst -> (
          eq key lst.key
          ||
          match lst.next with
          | Empty -> false
          | Cons lst -> eq key lst.key || small_bucket_mem lst.next eq key))

let rec small_bucket_opt eq key (lst : _ bucket) : _ option =
  match lst with
  | Empty -> None
  | Cons lst -> (
      if eq key lst.key then Some lst.data
      else
        match lst.next with
        | Empty -> None
        | Cons lst -> (
            if eq key lst.key then Some lst.data
            else
              match lst.next with
              | Empty -> None
              | Cons lst ->
                  if eq key lst.key then Some lst.data
                  else small_bucket_opt eq key lst.next))

let rec small_bucket_key_opt eq key (lst : _ bucket) : _ option =
  match lst with
  | Empty -> None
  | Cons { key = k; next } -> (
      if eq key k then Some k
      else
        match next with
        | Empty -> None
        | Cons { key = k; next } -> (
            if eq key k then Some k
            else
              match next with
              | Empty -> None
              | Cons { key = k; next } ->
                  if eq key k then Some k else small_bucket_key_opt eq key next)
      )

let rec small_bucket_default eq key default (lst : _ bucket) =
  match lst with
  | Empty -> default
  | Cons lst -> (
      if eq key lst.key then lst.data
      else
        match lst.next with
        | Empty -> default
        | Cons lst -> (
            if eq key lst.key then lst.data
            else
              match lst.next with
              | Empty -> default
              | Cons lst ->
                  if eq key lst.key then lst.data
                  else small_bucket_default eq key default lst.next))

let rec remove_bucket h (i : int) key ~(prec : _ bucket) (buck : _ bucket)
    eq_key =
  match buck with
  | Empty -> ()
  | Cons { key = k; next } ->
      if eq_key k key then (
        h.size <- h.size - 1;
        match prec with
        | Empty -> Array.unsafe_set h.data i next
        | Cons c -> c.next <- next)
      else remove_bucket h i key ~prec:buck next eq_key

let rec replace_bucket key data (buck : _ bucket) eq_key =
  match buck with
  | Empty -> true
  | Cons slot ->
      if eq_key slot.key key then (
        slot.key <- key;
        slot.data <- data;
        false)
      else replace_bucket key data slot.next eq_key

module type S = sig
  type key

  type 'a t

  val create : int -> 'a t

  val clear : 'a t -> unit

  val reset : 'a t -> unit

  val add : 'a t -> key -> 'a -> unit

  val add_or_update : 'a t -> key -> update:('a -> 'a) -> 'a -> unit

  val remove : 'a t -> key -> unit

  val find_exn : 'a t -> key -> 'a

  val find_all : 'a t -> key -> 'a list

  val find_opt : 'a t -> key -> 'a option

  val find_key_opt : 'a t -> key -> key option
  (** return the key found in the hashtbl.
      Use case: when you find the key existed in hashtbl, 
      you want to use the one stored in the hashtbl. 
      (they are semantically equivlanent, but may have other information different) 
  *)

  val find_default : 'a t -> key -> 'a -> 'a

  val replace : 'a t -> key -> 'a -> unit

  val mem : 'a t -> key -> bool

  val iter : 'a t -> (key -> 'a -> unit) -> unit

  val fold : 'a t -> 'b -> (key -> 'a -> 'b -> 'b) -> 'b

  val length : 'a t -> int

  (* val stats: 'a t -> Hashtbl.statistics *)
  val to_list : 'a t -> (key -> 'a -> 'c) -> 'c list

  val of_list2 : key list -> 'a list -> 'a t
end

end
module Hash : sig 
#1 "hash.mli"
module Make (Key : Hashtbl.HashedType) : Hash_gen.S with type key = Key.t

end = struct
#1 "hash.ml"
# 22 "ext/hash.cppo.ml"
module Make (Key : Hashtbl.HashedType) = struct 
  type key = Key.t 
  type 'a t = (key, 'a)  Hash_gen.t 
  let key_index (h : _ t ) (key : key) =
    (Key.hash  key ) land (Array.length h.data - 1)
  let eq_key = Key.equal   


  
# 33 "ext/hash.cppo.ml"
  type ('a, 'b) bucket = ('a,'b) Hash_gen.bucket
  let create = Hash_gen.create
  let clear = Hash_gen.clear
  let reset = Hash_gen.reset
  let iter = Hash_gen.iter
  let to_list = Hash_gen.to_list
  let fold = Hash_gen.fold
  let length = Hash_gen.length
  (* let stats = Hash_gen.stats *)



  let add (h : _ t) key data =
    let i = key_index h key in
    let h_data = h.data in   
    Array.unsafe_set h_data i (Cons{key; data; next=Array.unsafe_get h_data i});
    h.size <- h.size + 1;
    if h.size > Array.length h_data lsl 1 then Hash_gen.resize key_index h

  (* after upgrade to 4.04 we should provide an efficient [replace_or_init] *)
  let add_or_update 
      (h : 'a t) 
      (key : key) 
      ~update:(modf : 'a -> 'a) 
      (default :  'a) : unit =
    let rec find_bucket (bucketlist : _ bucket) : bool =
      match bucketlist with
      | Cons rhs  ->
        if eq_key rhs.key key then begin rhs.data <- modf rhs.data; false end
        else find_bucket rhs.next
      | Empty -> true in
    let i = key_index h key in 
    let h_data = h.data in 
    if find_bucket (Array.unsafe_get h_data i) then
      begin 
        Array.unsafe_set h_data i  (Cons{key; data=default; next = Array.unsafe_get h_data i});
        h.size <- h.size + 1 ;
        if h.size > Array.length h_data lsl 1 then Hash_gen.resize key_index h 
      end

  let remove (h : _ t ) key =
    let i = key_index h key in
    let h_data = h.data in 
    Hash_gen.remove_bucket h i key ~prec:Empty (Array.unsafe_get h_data i) eq_key

  (* for short bucket list, [find_rec is not called ] *)
  let rec find_rec key (bucketlist : _ bucket) = match bucketlist with  
    | Empty ->
      raise Not_found
    | Cons rhs  ->
      if eq_key key rhs.key then rhs.data else find_rec key rhs.next

  let find_exn (h : _ t) key =
    match Array.unsafe_get h.data (key_index h key) with
    | Empty -> raise Not_found
    | Cons rhs  ->
      if eq_key key rhs.key then rhs.data else
        match rhs.next with
        | Empty -> raise Not_found
        | Cons rhs  ->
          if eq_key key rhs.key then rhs.data else
            match rhs.next with
            | Empty -> raise Not_found
            | Cons rhs ->
              if eq_key key rhs.key  then rhs.data else find_rec key rhs.next

  let find_opt (h : _ t) key =
    Hash_gen.small_bucket_opt eq_key key (Array.unsafe_get h.data (key_index h key))

  let find_key_opt (h : _ t) key =
    Hash_gen.small_bucket_key_opt eq_key key (Array.unsafe_get h.data (key_index h key))

  let find_default (h : _ t) key default = 
    Hash_gen.small_bucket_default eq_key key default (Array.unsafe_get h.data (key_index h key))

  let find_all (h : _ t) key =
    let rec find_in_bucket (bucketlist : _ bucket) = match bucketlist with 
      | Empty ->
        []
      | Cons rhs  ->
        if eq_key key rhs.key
        then rhs.data :: find_in_bucket rhs.next
        else find_in_bucket rhs.next in
    find_in_bucket (Array.unsafe_get h.data (key_index h key))


  let replace h key data =
    let i = key_index h key in
    let h_data = h.data in 
    let l = Array.unsafe_get h_data i in
    if Hash_gen.replace_bucket key data l eq_key then 
      begin 
        Array.unsafe_set h_data i (Cons{key; data; next=l});
        h.size <- h.size + 1;
        if h.size > Array.length h_data lsl 1 then Hash_gen.resize key_index h;
      end 

  let mem (h : _ t) key = 
    Hash_gen.small_bucket_mem 
      (Array.unsafe_get h.data (key_index h key))
      eq_key key 


  let of_list2 ks vs = 
    let len = List.length ks in 
    let map = create len in 
    List.iter2 (fun k v -> add map k v) ks vs ; 
    map

# 143 "ext/hash.cppo.ml"
end

end
module Bsb_pkg : sig 
#1 "bsb_pkg.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** [resolve cwd module_name], 
    [cwd] is current working directory, absolute path
    Trying to find paths to load [module_name]
    it is sepcialized for option [-bs-package-include] which requires
    [npm_package_name/lib/ocaml]

    it relies on [npm_config_prefix] env variable for global npm modules
*)

val resolve_bs_package : cwd:string -> Bsb_pkg_types.t -> string
(** @raise  when not found *)

val to_list : (Bsb_pkg_types.t -> string -> 'a) -> 'a list
(** used by watcher *)

end = struct
#1 "bsb_pkg.ml"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Filename.concat

type t = Bsb_pkg_types.t

(* TODO: be more restrict
   [bsconfig.json] does not always make sense,
   when resolving [ppx-flags]
*)
let make_sub_path (x : t) : string =
  Literals.node_modules // Bsb_pkg_types.to_string x

let node_paths : string list Lazy.t =
  lazy
    (try
       Ext_string.split (Sys.getenv "NODE_PATH")
         (if Sys.win32 then ';' else ':')
     with _ -> [])

(** It makes sense to have this function raise, when [bsb] could not resolve a package, it used to mean
    a failure
*)
let check_dir dir =
  match Sys.file_exists dir with true -> Some dir | false -> None

let resolve_bs_package_aux ~cwd (pkg : t) =
  (* First try to resolve recursively from the current working directory  *)
  let sub_path = make_sub_path pkg in
  let rec aux cwd =
    let abs_marker = cwd // sub_path in
    if Sys.file_exists abs_marker then abs_marker
    else
      let another_cwd = Filename.dirname cwd in
      (* TODO: may non-terminating when see symlinks *)
      if String.length another_cwd < String.length cwd then aux another_cwd
      else
        (* To the end try other possiblilities [NODE_PATH]*)
        match
          Ext_list.find_opt (Lazy.force node_paths) (fun dir ->
              check_dir (dir // Bsb_pkg_types.to_string pkg))
        with
        | Some resolved_dir -> resolved_dir
        | None -> Bsb_exception.package_not_found ~pkg ~json:None
  in
  aux cwd

module Coll = Hash.Make (struct
  type nonrec t = t

  let equal = Bsb_pkg_types.equal

  let hash (x : t) = Hashtbl.hash x
end)

let cache : string Coll.t = Coll.create 0

let to_list cb = Coll.to_list cache cb

(** TODO: collect all warnings and print later *)
let resolve_bs_package ~cwd (package : t) =
  match Coll.find_opt cache package with
  | None ->
      let result = resolve_bs_package_aux ~cwd package in
      Bsb_log.info "@{<info>Package@} %a -> %s@." Bsb_pkg_types.print package
        result;
      Coll.add cache package result;
      result
  | Some x ->
      let result = resolve_bs_package_aux ~cwd package in
      if not (Bsb_real_path.is_same_paths_via_io result x) then
        Bsb_log.warn
          "@{<warning>Duplicated package:@} %a %s (chosen) vs %s in %s @."
          Bsb_pkg_types.print package x result cwd;
      x

(** The package does not need to be a bspackage
    example:
    {[
      resolve_npm_package_file ~cwd "reason/refmt";;
      resolve_npm_package_file ~cwd "reason/refmt/xx/yy"
    ]}
    It also returns the path name
    Note the input [sub_path] is already converted to physical meaning path according to OS
*)
(* let resolve_npm_package_file ~cwd sub_path = *)
(*   let rec aux  cwd  =  *)
(*     let abs_marker =  cwd // Literals.node_modules // sub_path in  *)
(*     if Sys.file_exists abs_marker then Some abs_marker *)
(*     else  *)
(*       let cwd' = Filename.dirname cwd in  *)
(*       if String.length cwd' < String.length cwd then   *)
(*         aux cwd'  *)
(*       else  *)
(*         try  *)
(*           let abs_marker =  *)
(*             Sys.getenv "npm_config_prefix"  *)
(*             // "lib" // Literals.node_modules // sub_path in *)
(*           if Sys.file_exists abs_marker *)
(*           then Some  abs_marker *)
(*           else None *)
(*             (\* Bs_exception.error (Bs_package_not_found name) *\) *)
(*         with  *)
(*           Not_found -> None *)
(*           (\* Bs_exception.error (Bs_package_not_found name)           *\) *)
(*   in *)
(*   aux cwd *)

end
module Ext_js_suffix
= struct
#1 "ext_js_suffix.ml"
type t =
  | Js
  | Mjs
  | Cjs
  | Bs_js
  | Bs_mjs
  | Bs_cjs
  | Unknown_extension

let to_string (x : t) =
  match x with
  | Js -> Literals.suffix_js
  | Mjs -> Literals.suffix_mjs
  | Cjs -> Literals.suffix_cjs
  | Bs_js -> Literals.suffix_bs_js
  | Bs_mjs -> Literals.suffix_bs_mjs
  | Bs_cjs -> Literals.suffix_bs_cjs
  | Unknown_extension -> assert false

let of_string (x : string) : t =
  match () with
  | () when x = Literals.suffix_js -> Js
  | () when x = Literals.suffix_mjs -> Mjs
  | () when x = Literals.suffix_cjs -> Cjs
  | () when x = Literals.suffix_bs_js -> Bs_js
  | () when x = Literals.suffix_bs_mjs -> Bs_mjs
  | () when x = Literals.suffix_bs_cjs -> Bs_cjs
  | _ -> Unknown_extension

end
module Bsb_spec_set : sig 
#1 "bsb_spec_set.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
type format = Ext_module_system.t

type spec = { format : format; in_source : bool; suffix : Ext_js_suffix.t }

type t = private spec list

val empty : t

val add : spec -> t -> t

val singleton : spec -> t

val fold : (spec -> 'a -> 'a) -> t -> 'a -> 'a

val iter : (spec -> unit) -> t -> unit

end = struct
#1 "bsb_spec_set.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

[@@@warning "+9"]

(* TODO: sync up with {!Js_packages_info.module_system}  *)
type format = Ext_module_system.t = NodeJS | Es6 | Es6_global

type spec = { format : format; in_source : bool; suffix : Ext_js_suffix.t }

type t = spec list

let cmp (s1 : spec) ({ format; in_source; suffix } : spec) =
  let v = compare s1.format format in
  if v <> 0 then v
  else
    let v = compare s1.in_source in_source in
    if v <> 0 then v else compare s1.suffix suffix

let empty = []

let rec insert lst piviot =
  match lst with
  | [] -> [ piviot ]
  | x :: xs ->
      let v = cmp piviot x in
      if v = 0 then lst
      else if v < 0 then piviot :: lst
      else x :: insert xs piviot

let add spec specs =
  match specs with
  | [] -> [ spec ]
  | [ a ] ->
      let v = cmp spec a in
      if v < 0 then spec :: specs else if v = 0 then specs else [ a; spec ]
  | [ a; b ] ->
      let v = cmp spec a in
      if v < 0 then spec :: specs
      else if v = 0 then specs
      else
        let v1 = cmp spec b in
        if v < 0 then [ a; spec; b ]
        else if v1 = 0 then specs
        else [ a; b; spec ]
  | _ :: _ :: _ :: _ ->
      (* unlikely to happen *)
      insert specs spec

let singleton x = [ x ]

let rec fold f t acc = match t with [] -> acc | x :: xs -> fold f xs (f x acc)

let rec iter f t =
  match t with
  | [] -> ()
  | x :: xs ->
      f x;
      iter f xs

end
module Ext_filename : sig 
#1 "ext_filename.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* TODO:
   Change the module name, this code is not really an extension of the standard
    library but rather specific to JS Module name convention.
*)

(** An extension module to calculate relative path follow node/npm style. 
    TODO : this short name will have to change upon renaming the file.
*)

val is_dir_sep : char -> bool

val maybe_quote : string -> string

val chop_extension_maybe : string -> string

(* return an empty string if no extension found *)
val get_extension_maybe : string -> string

val new_extension : string -> string -> string

val chop_all_extensions_maybe : string -> string

(* OCaml specific abstraction*)
val module_name : string -> string

type module_info = { module_name : string; case : bool }

val as_module : basename:string -> module_info option

end = struct
#1 "ext_filename.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let is_dir_sep_unix c = c = '/'

let is_dir_sep_win_cygwin c = c = '/' || c = '\\' || c = ':'

let is_dir_sep = if Sys.unix then is_dir_sep_unix else is_dir_sep_win_cygwin

(* reference ninja.cc IsKnownShellSafeCharacter *)
let maybe_quote (s : string) =
  let noneed_quote =
    Ext_string.for_all s (function
      | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' | '_' | '+' | '-' | '.' | '/' | '@'
        ->
          true
      | _ -> false)
  in
  if noneed_quote then s else Filename.quote s

let chop_extension_maybe name =
  let rec search_dot i =
    if i < 0 || is_dir_sep (String.unsafe_get name i) then name
    else if String.unsafe_get name i = '.' then String.sub name 0 i
    else search_dot (i - 1)
  in
  search_dot (String.length name - 1)

let get_extension_maybe name =
  let name_len = String.length name in
  let rec search_dot name i name_len =
    if i < 0 || is_dir_sep (String.unsafe_get name i) then ""
    else if String.unsafe_get name i = '.' then String.sub name i (name_len - i)
    else search_dot name (i - 1) name_len
  in
  search_dot name (name_len - 1) name_len

let chop_all_extensions_maybe name =
  let rec search_dot i last =
    if i < 0 || is_dir_sep (String.unsafe_get name i) then
      match last with None -> name | Some i -> String.sub name 0 i
    else if String.unsafe_get name i = '.' then search_dot (i - 1) (Some i)
    else search_dot (i - 1) last
  in
  search_dot (String.length name - 1) None

let new_extension name (ext : string) =
  let rec search_dot name i ext =
    if i < 0 || is_dir_sep (String.unsafe_get name i) then name ^ ext
    else if String.unsafe_get name i = '.' then (
      let ext_len = String.length ext in
      let buf = Bytes.create (i + ext_len) in
      Bytes.blit_string name 0 buf 0 i;
      Bytes.blit_string ext 0 buf i ext_len;
      Bytes.unsafe_to_string buf)
    else search_dot name (i - 1) ext
  in
  search_dot name (String.length name - 1) ext

(** TODO: improve efficiency
    given a path, calcuate its module name 
    Note that `ocamlc.opt -c aa.xx.mli` gives `aa.xx.cmi`
    we can not strip all extensions, otherwise
    we can not tell the difference between "x.cpp.ml" 
    and "x.ml"
*)
let module_name name =
  let rec search_dot i name =
    if i < 0 then Ext_string.capitalize_ascii name
    else if String.unsafe_get name i = '.' then Ext_string.capitalize_sub name i
    else search_dot (i - 1) name
  in
  let name = Filename.basename name in
  let name_len = String.length name in
  search_dot (name_len - 1) name

type module_info = { module_name : string; case : bool }

let rec valid_module_name_aux name off len =
  if off >= len then true
  else
    let c = String.unsafe_get name off in
    match c with
    | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '\'' | '.' | '[' | ']' ->
        valid_module_name_aux name (off + 1) len
    | _ -> false

type state = Invalid | Upper | Lower

let valid_module_name name len =
  if len = 0 then Invalid
  else
    let c = String.unsafe_get name 0 in
    match c with
    | 'A' .. 'Z' -> if valid_module_name_aux name 1 len then Upper else Invalid
    | 'a' .. 'z' | '0' .. '9' | '_' | '[' | ']' ->
        if valid_module_name_aux name 1 len then Lower else Invalid
    | _ -> Invalid

let as_module ~basename =
  let rec search_dot i name name_len =
    if i < 0 then
      (* Input e.g, [a_b] *)
      match valid_module_name name name_len with
      | Invalid -> None
      | Upper -> Some { module_name = name; case = true }
      | Lower ->
          Some { module_name = Ext_string.capitalize_ascii name; case = false }
    else if String.unsafe_get name i = '.' then
      (*Input e.g, [A_b] *)
      match valid_module_name name i with
      | Invalid -> None
      | Upper ->
          Some { module_name = Ext_string.capitalize_sub name i; case = true }
      | Lower ->
          Some { module_name = Ext_string.capitalize_sub name i; case = false }
    else search_dot (i - 1) name name_len
  in
  let name_len = String.length basename in
  search_dot (name_len - 1) basename name_len

end
module Ext_js_file_kind
= struct
#1 "ext_js_file_kind.ml"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
type case = Upper | Little

type [@warning "-69"] t = { case : case; suffix : Ext_js_suffix.t }

let any_runtime_kind = { case = Little; suffix = Ext_js_suffix.Js }

end
module Ext_namespace : sig 
#1 "ext_namespace.mli"
(* Copyright (C) 2017- Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val try_split_module_name : string -> (string * string) option

(* Note  we have to output uncapitalized file Name,
   or at least be consistent, since by reading cmi file on Case insensitive OS, we don't really know it is `list.cmi` or `List.cmi`, so that `require (./list.js)` or `require(./List.js)`
   relevant issues: #1609, #913

   #1933 when removing ns suffix, don't pass the bound
   of basename
*)
val change_ext_ns_suffix : string -> string -> string

val js_name_of_modulename :
  string -> Ext_js_file_kind.case -> Ext_js_suffix.t -> string
(** [js_name_of_modulename ~little A-Ns]
*)

(* TODO handle cases like
   '@angular/core'
   its directory structure is like
   {[
     @angular
     |-------- core
   ]}
*)
val is_valid_npm_package_name : string -> bool

val namespace_of_package_name : string -> string

end = struct
#1 "ext_namespace.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let rec rindex_rec s i =
  if i < 0 then i
  else
    let char = String.unsafe_get s i in
    if Ext_filename.is_dir_sep char then -1
    else if char = Literals.ns_sep_char then i
    else rindex_rec s (i - 1)

let change_ext_ns_suffix name ext =
  let i = rindex_rec name (String.length name - 1) in
  if i < 0 then name ^ ext else String.sub name 0 i ^ ext
(* FIXME: micro-optimizaiton*)

let try_split_module_name name =
  let len = String.length name in
  let i = rindex_rec name (len - 1) in
  if i < 0 then None
  else Some (String.sub name (i + 1) (len - i - 1), String.sub name 0 i)

let js_name_of_modulename s (case : Ext_js_file_kind.case) suffix : string =
  let s =
    match case with Little -> Ext_string.uncapitalize_ascii s | Upper -> s
  in
  change_ext_ns_suffix s (Ext_js_suffix.to_string suffix)

(* https://docs.npmjs.com/files/package.json
   Some rules:
   The name must be less than or equal to 214 characters. This includes the scope for scoped packages.
   The name can't start with a dot or an underscore.
   New packages must not have uppercase letters in the name.
   The name ends up being part of a URL, an argument on the command line, and a folder name. Therefore, the name can't contain any non-URL-safe characters.
*)
let is_valid_npm_package_name (s : string) =
  let len = String.length s in
  len <= 214 (* magic number forced by npm *)
  && len > 0
  &&
  match String.unsafe_get s 0 with
  | 'a' .. 'z' | '@' ->
      Ext_string.for_all_from s 1 (fun x ->
          match x with
          | 'a' .. 'z' | '0' .. '9' | '_' | '-' -> true
          | _ -> false)
  | _ -> false

let namespace_of_package_name (s : string) : string =
  let len = String.length s in
  let buf = Ext_buffer.create len in
  let add capital ch =
    Ext_buffer.add_char buf (if capital then Char.uppercase_ascii ch else ch)
  in
  let rec aux capital off len =
    if off >= len then ()
    else
      let ch = String.unsafe_get s off in
      match ch with
      | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' ->
          add capital ch;
          aux false (off + 1) len
      | '/' | '-' -> aux true (off + 1) len
      | _ -> aux capital (off + 1) len
  in
  aux true 0 len;
  Ext_buffer.contents buf

end
module Bsb_package_specs : sig 
#1 "bsb_package_specs.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t

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

val get_list_of_output_js : t -> string -> string list

val package_flag_of_package_specs : t -> dirname:string -> string
(**
   Sample output: {[ -bs-package-output commonjs:lib/js/jscomp/test]}
*)

(* used to ensure each dir does exist *)
val list_dirs_by : t -> (string -> unit) -> unit

end = struct
#1 "bsb_package_specs.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Ext_path.combine

(*FIXME: use assoc list instead *)
module Spec_set = Bsb_spec_set

type t = {
  modules : Spec_set.t;
  runtime : string option;
      (* This has to be resolved as early as possible, since
         the path will be inherited in sub projects
      *)
}

let ( .?() ) = Map_string.find_opt

let bad_module_format_message_exn ~loc format =
  Bsb_exception.errorf ~loc
    "package-specs: `%s` isn't a valid output module format. It has to be one \
     of:  %s, %s or %s"
    format Literals.commonjs Literals.es6 Literals.es6_global

let supported_format (x : string) loc : Ext_module_system.t =
  if x = Literals.commonjs then NodeJS
  else if x = Literals.es6 then Es6
  else if x = Literals.es6_global then Es6_global
  else bad_module_format_message_exn ~loc x

let string_of_format (x : Ext_module_system.t) =
  match x with
  | NodeJS -> Literals.commonjs
  | Es6 -> Literals.es6
  | Es6_global -> Literals.es6_global

let rec from_array suffix (arr : Ext_json_types.t array) : Spec_set.t =
  let spec = ref Spec_set.empty in
  let has_in_source = ref false in
  Ext_array.iter arr (fun x ->
      let result = from_json_single suffix x in
      if result.in_source then
        if not !has_in_source then has_in_source := true
        else
          Bsb_exception.errorf ~loc:(Ext_json.loc_of x)
            "package-specs: we've detected two module formats that are both \
             configured to be in-source.";
      spec := Spec_set.add result !spec);
  !spec

(* TODO: FIXME: better API without mutating *)
and from_json_single suffix (x : Ext_json_types.t) : Bsb_spec_set.spec =
  match x with
  | Str { str = format; loc } ->
      { format = supported_format format loc; in_source = false; suffix }
  | Obj { map; loc } -> (
      match map.?("module") with
      | Some (Str { str = format }) ->
          let in_source =
            match map.?(Bsb_build_schemas.in_source) with
            | Some (True _) -> true
            | Some _ | None -> false
          in
          let suffix =
            match map.?("suffix") with
            | Some (Str { str = suffix; loc }) ->
                let s = Ext_js_suffix.of_string suffix in
                if s = Unknown_extension then
                  Bsb_exception.errorf ~loc "expect .js,.bs.js,.mjs or .cjs"
                else s
            | Some _ ->
                Bsb_exception.errorf ~loc:(Ext_json.loc_of x)
                  "expect a string field"
            | None -> suffix
          in
          { format = supported_format format loc; in_source; suffix }
      | Some _ ->
          Bsb_exception.errorf ~loc
            "package-specs: when the configuration is an object, `module` \
             field should be a string, not an array. If you want to pass \
             multiple module specs, try turning package-specs into an array of \
             objects (or strings) instead."
      | None ->
          Bsb_exception.errorf ~loc
            "package-specs: when the configuration is an object, the `module` \
             field is mandatory.")
  | _ ->
      Bsb_exception.errorf ~loc:(Ext_json.loc_of x)
        "package-specs: we expect either a string or an object."

let from_json suffix (x : Ext_json_types.t) : Spec_set.t =
  match x with
  | Arr { content; _ } -> from_array suffix content
  | _ -> Spec_set.singleton (from_json_single suffix x)

let bs_package_output = "-bs-package-output"

[@@@warning "+9"]

(* Assume input is valid
    coordinate with command line flag
    {[ -bs-package-output commonjs:lib/js/jscomp/test:.js ]}
*)
let package_flag ({ format; in_source; suffix } : Bsb_spec_set.spec) dir =
  Ext_string.inter2 bs_package_output
    (Ext_string.concat5 (string_of_format format) Ext_string.single_colon
       (if in_source then dir
       else Bsb_config.top_prefix_of_format format // dir)
       Ext_string.single_colon
       (Ext_js_suffix.to_string suffix))

(* FIXME: we should adapt it *)
let package_flag_of_package_specs (package_specs : t) ~(dirname : string) :
    string =
  let res =
    match (package_specs.modules :> Bsb_spec_set.spec list) with
    | [] -> Ext_string.empty
    | [ format ] ->
        Ext_string.inter2 Ext_string.empty (package_flag format dirname)
    | [ a; b ] ->
        Ext_string.inter3 Ext_string.empty (package_flag a dirname)
          (package_flag b dirname)
    | [ a; b; c ] ->
        Ext_string.inter4 Ext_string.empty (package_flag a dirname)
          (package_flag b dirname) (package_flag c dirname)
    | _ ->
        Spec_set.fold
          (fun format acc ->
            Ext_string.inter2 acc (package_flag format dirname))
          package_specs.modules Ext_string.empty
  in
  match package_specs.runtime with
  | None -> res
  | Some x -> Ext_string.inter3 res "-runtime" x

let default_package_specs suffix =
  Spec_set.singleton { format = NodeJS; in_source = false; suffix }

(**
    [get_list_of_output_js specs "src/hi/hello"]

*)
let get_list_of_output_js (package_specs : t)
    (output_file_sans_extension : string) =
  Spec_set.fold
    (fun (spec : Bsb_spec_set.spec) acc ->
      let basename =
        Ext_namespace.change_ext_ns_suffix output_file_sans_extension
          (Ext_js_suffix.to_string spec.suffix)
      in
      (if spec.in_source then Bsb_config.rev_lib_bs_prefix basename
      else Bsb_config.lib_bs_prefix_of_format spec.format // basename)
      :: acc)
    package_specs.modules []

let list_dirs_by (package_specs : t) (f : string -> unit) =
  Spec_set.iter
    (fun (spec : Bsb_spec_set.spec) ->
      if not spec.in_source then f (Bsb_config.top_prefix_of_format spec.format))
    package_specs.modules

type json_map = Ext_json_types.t Map_string.t

let extract_bs_suffix_exn (map : json_map) : Ext_js_suffix.t =
  match map.?(Bsb_build_schemas.suffix) with
  | None -> Js
  | Some (Str { str; loc }) ->
      let s = Ext_js_suffix.of_string str in
      if s = Unknown_extension then
        Bsb_exception.errorf ~loc
          "expect .js, .mjs, .cjs or .bs.js, .bs.mjs, .bs.cjs here"
      else s
  | Some config ->
      Bsb_exception.config_error config
        "expect a string exteion like \".js\" here"

let from_map ~(cwd : string) map =
  let suffix = extract_bs_suffix_exn map in
  let modules =
    match map.?(Bsb_build_schemas.package_specs) with
    | Some x -> from_json suffix x
    | None -> default_package_specs suffix
  in
  let runtime =
    match map.?(Bsb_build_schemas.external_stdlib) with
    | None -> None
    | Some (Str { str; _ }) ->
        Some
          (Bsb_pkg.resolve_bs_package ~cwd
             (Bsb_pkg_types.string_as_package str))
    | _ -> assert false
  in
  { runtime; modules }

end
module Bsb_package_kind
= struct
#1 "bsb_package_kind.ml"
(* Copyright (C) 2020 - Hongbo Zhang, Authors of ReScript 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

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

type t =
  | Toplevel
  | Dependency of dep_payload
  | Pinned_dependency of dep_payload
(* This package specs comes from the toplevel to
   override the current settings
*)

let encode_no_nl (x : t) =
  match x with
  | Toplevel -> "0"
  | Dependency x ->
      "1"
      ^ Bsb_package_specs.package_flag_of_package_specs x.package_specs
          ~dirname:"."
      ^ Bsb_jsx.encode_no_nl x.jsx
  | Pinned_dependency x ->
      "2"
      ^ Bsb_package_specs.package_flag_of_package_specs x.package_specs
          ~dirname:"."
      ^ Bsb_jsx.encode_no_nl x.jsx

end
module Bsc_warnings
= struct
#1 "bsc_warnings.ml"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(**
   See the meanings of the warning codes here: https://caml.inria.fr/pub/docs/manual-ocaml/comp.html#sec281

   - 30 Two labels or constructors of the same name are defined in two mutually recursive types.
   - 40 Constructor or label name used out of scope.

   - 6 Label omitted in function application.
   - 7 Method overridden.
   - 9 Missing fields in a record pattern. (*Not always desired, in some cases need [@@@warning "+9"] *)
   - 27 Innocuous unused variable: unused variable that is not bound with let nor as, and doesn’t start with an underscore (_) character.
   - 29 Unescaped end-of-line in a string constant (non-portable code).
   - 32 .. 39 Unused blabla
   - 44 Open statement shadows an already defined identifier.
   - 45 Open statement shadows an already defined label or constructor.
   - 48 Implicit elimination of optional arguments. https://caml.inria.fr/mantis/view.php?id=6352
   - 101 (bsb-specific) unsafe polymorphic comparison.
*)

(*
  The purpose of default warning set is to make it strict while
  not annoy user too much

  -4 Fragile pattern matching: matching that will remain complete even if additional con- structors are added to one of the variant types matched.
  We turn it off since common pattern
   {[
     match x with | A -> .. |  _ -> false
   ]}

   -9 Missing fields in a record pattern.
   only in some special cases that we need all fields being listed

   We encourage people to write code based on type based disambigution
   40,41,42 are enabled for compatiblity reasons  
   -40 Constructor or label name used out of scope
   This is intentional, we should never warn it
   - 41 Ambiguous constructor or label name.
     It is turned off since it prevents such cases below:
   {[
     type a = A |B 
     type b = A | B | C
   ]}
   - 42 Disambiguated constructor or label name (compatibility warning).

   - 50 Unexpected documentation comment.

   - 102 Bs_polymorphic_comparison
*)
let defaults_w = "+a-4-9-20-40-41-42-50-61-102"

let defaults_warn_error = "-a+5+6+101+109"
(*TODO: add +10*)

end
module Bsb_warning : sig 
#1 "bsb_warning.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t

val to_merlin_string : t -> string
(** Extra work is need to make merlin happy *)

val from_map : Ext_json_types.t Map_string.t -> t

val to_bsb_string : package_kind:Bsb_package_kind.t -> t -> string
(** [to_bsb_string not_dev warning]
*)

val use_default : t

end = struct
#1 "bsb_warning.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type warning_error =
  | Warn_error_false
  (* default [false] to make our changes non-intrusive *)
  | Warn_error_true
  | Warn_error_number of string

type t0 = { number : string option; error : warning_error }

type nonrec t = t0 option

let use_default = None

let prepare_warning_concat ~(beg : bool) s =
  let s = Ext_string.trim s in
  if s = "" then s
  else
    match s.[0] with
    | '0' .. '9' -> if beg then "-w +" ^ s else "+" ^ s
    | 'a' .. 'z' -> if beg then "-w " ^ s else "-" ^ s
    | 'A' .. 'Z' -> if beg then "-w " ^ s else "+" ^ s
    | _ -> if beg then "-w " ^ s else s

let to_merlin_string x =
  "-w " ^ Bsc_warnings.defaults_w
  ^
  let customize =
    match x with
    | Some { number = None } | None -> Ext_string.empty
    | Some { number = Some x } -> prepare_warning_concat ~beg:false x
  in
  if customize = "" then customize else customize ^ "-40-42-61"
(* see #4406 to avoid user pass A
   Sync up with {!Warnings.report}
*)

let from_map (m : Ext_json_types.t Map_string.t) =
  let number_opt = Map_string.find_opt m Bsb_build_schemas.number in
  let error_opt = Map_string.find_opt m Bsb_build_schemas.error in
  match (number_opt, error_opt) with
  | None, None -> None
  | _, _ ->
      let error =
        match error_opt with
        | Some (True _) -> Warn_error_true
        | Some (False _) -> Warn_error_false
        | Some (Str { str }) -> Warn_error_number str
        | Some x -> Bsb_exception.config_error x "expect true/false or string"
        | None -> Warn_error_false
        (* To make it less intrusive : warning error has to be enabled*)
      in
      let number =
        match number_opt with
        | Some (Str { str = number }) -> Some number
        | None -> None
        | Some x -> Bsb_exception.config_error x "expect a string"
      in
      Some { number; error }

let to_bsb_string ~(package_kind : Bsb_package_kind.t) warning =
  match package_kind with
  | Toplevel | Pinned_dependency _ -> (
      match warning with
      | None -> Ext_string.empty
      | Some warning -> (
          (match warning.number with
          | None -> Ext_string.empty
          | Some x -> prepare_warning_concat ~beg:true x)
          ^
          match warning.error with
          | Warn_error_true -> " -warn-error A"
          | Warn_error_number y -> " -warn-error " ^ y
          | Warn_error_false -> Ext_string.empty))
  | Dependency _ -> " -w a"
(* TODO: this is the current default behavior *)

end
module Bsb_config_types
= struct
#1 "bsb_config_types.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type dependency = {
  package_name : Bsb_pkg_types.t;
  package_install_path : string;
}

type dependencies = dependency list
type reason_react_jsx = Jsx_v3
(* string option  *)

type refmt = string option
type gentype_config = bool
type command = string
type ppx = { name : string; args : string list }

type t = {
  package_name : string;
  (* [captial-package] *)
  namespace : string option;
  (* CapitalPackage *)
  external_includes : string list;
  bsc_flags : string list;
  ppx_files : ppx list;
  pp_file : string option;
  bs_dependencies : dependencies;
  bs_dev_dependencies : dependencies;
  pinned_dependencies : Set_string.t;
  built_in_dependency : bool;
  warning : Bsb_warning.t;
  (*TODO: maybe we should always resolve rescript
    so that we can calculate correct relative path in
    [.merlin]
  *)
  refmt : refmt;
  js_post_build_cmd : string option;
  package_specs : Bsb_package_specs.t;
  file_groups : Bsb_file_groups.t;
  files_to_install : Bsb_db.module_info Queue.t;
  generate_merlin : bool;
  reason_react_jsx : reason_react_jsx option;
  jsx: Bsb_jsx.t;
  (* whether apply PPX transform or not*)
  generators : command Map_string.t;
  cut_generators : bool;
  (* note when used as a dev mode, we will always ignore it *)
  gentype_config : gentype_config;
}

end
module Ext_json_parse : sig 
#1 "ext_json_parse.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type error

val report_error : Format.formatter -> error -> unit

exception Error of Lexing.position * Lexing.position * error

val parse_json_from_string : string -> Ext_json_types.t

val parse_json_from_chan : string -> in_channel -> Ext_json_types.t

val parse_json_from_file : string -> Ext_json_types.t

end = struct
#1 "ext_json_parse.ml"
# 1 "ext/ext_json_parse.mll"
 
type error =
  | Illegal_character of char
  | Unterminated_string
  | Unterminated_comment
  | Illegal_escape of string
  | Unexpected_token 
  | Expect_comma_or_rbracket
  | Expect_comma_or_rbrace
  | Expect_colon
  | Expect_string_or_rbrace 
  | Expect_eof 
  (* | Trailing_comma_in_obj *)
  (* | Trailing_comma_in_array *)


let fprintf  = Format.fprintf
let report_error ppf = function
  | Illegal_character c ->
      fprintf ppf "Illegal character (%s)" (Char.escaped c)
  | Illegal_escape s ->
      fprintf ppf "Illegal backslash escape in string or character (%s)" s
  | Unterminated_string -> 
      fprintf ppf "Unterminated_string"
  | Expect_comma_or_rbracket ->
    fprintf ppf "Expect_comma_or_rbracket"
  | Expect_comma_or_rbrace -> 
    fprintf ppf "Expect_comma_or_rbrace"
  | Expect_colon -> 
    fprintf ppf "Expect_colon"
  | Expect_string_or_rbrace  -> 
    fprintf ppf "Expect_string_or_rbrace"
  | Expect_eof  -> 
    fprintf ppf "Expect_eof"
  | Unexpected_token 
    ->
    fprintf ppf "Unexpected_token"
  (* | Trailing_comma_in_obj  *)
  (*   -> fprintf ppf "Trailing_comma_in_obj" *)
  (* | Trailing_comma_in_array  *)
  (*   -> fprintf ppf "Trailing_comma_in_array" *)
  | Unterminated_comment 
    -> fprintf ppf "Unterminated_comment"
         

exception Error of Lexing.position * Lexing.position * error


let () = 
  Printexc.register_printer
    (function x -> 
     match x with 
     | Error (loc_start,loc_end,error) -> 
       Some (Format.asprintf 
          "@[%a:@ %a@ -@ %a)@]" 
          report_error  error
          Ext_position.print loc_start
          Ext_position.print loc_end
       )

     | _ -> None
    )





type token = 
  | Comma
  | Eof
  | False
  | Lbrace
  | Lbracket
  | Null
  | Colon
  | Number of string
  | Rbrace
  | Rbracket
  | String of string
  | True   
  
let error  (lexbuf : Lexing.lexbuf) e = 
  raise (Error (lexbuf.lex_start_p, lexbuf.lex_curr_p, e))


let lexeme_len (x : Lexing.lexbuf) =
  x.lex_curr_pos - x.lex_start_pos

let update_loc (lexbuf : Lexing.lexbuf) diff =
  let lex_curr_p = lexbuf.lex_curr_p in 
  lexbuf.lex_curr_p <-
    {
      lex_curr_p with
      pos_lnum = lex_curr_p.pos_lnum + 1;
      pos_bol = lex_curr_p.pos_cnum - diff;
    }

let char_for_backslash = function
  | 'n' -> '\010'
  | 'r' -> '\013'
  | 'b' -> '\008'
  | 't' -> '\009'
  | c -> c

let dec_code c1 c2 c3 =
  100 * (Char.code c1 - 48) + 10 * (Char.code c2 - 48) + (Char.code c3 - 48)

let hex_code c1 c2 =
  let d1 = Char.code c1 in
  let val1 =
    if d1 >= 97 then d1 - 87
    else if d1 >= 65 then d1 - 55
    else d1 - 48 in
  let d2 = Char.code c2 in
  let val2 =
    if d2 >= 97 then d2 - 87
    else if d2 >= 65 then d2 - 55
    else d2 - 48 in
  val1 * 16 + val2

let lf = '\010'

# 125 "ext/ext_json_parse.ml"
let __ocaml_lex_tables = {
  Lexing.lex_base =
   "\000\000\239\255\240\255\241\255\000\000\025\000\011\000\244\255\
    \245\255\246\255\247\255\248\255\249\255\000\000\000\000\000\000\
    \041\000\001\000\254\255\005\000\005\000\253\255\001\000\002\000\
    \252\255\000\000\000\000\003\000\251\255\001\000\003\000\250\255\
    \079\000\089\000\099\000\121\000\131\000\141\000\153\000\163\000\
    \001\000\253\255\254\255\023\000\255\255\006\000\246\255\189\000\
    \248\255\215\000\255\255\249\255\249\000\181\000\252\255\009\000\
    \063\000\075\000\234\000\251\255\032\001\250\255";
  Lexing.lex_backtrk =
   "\255\255\255\255\255\255\255\255\013\000\013\000\016\000\255\255\
    \255\255\255\255\255\255\255\255\255\255\016\000\016\000\016\000\
    \016\000\016\000\255\255\000\000\012\000\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\013\000\255\255\013\000\255\255\013\000\255\255\
    \255\255\255\255\255\255\001\000\255\255\255\255\255\255\008\000\
    \255\255\255\255\255\255\255\255\006\000\006\000\255\255\006\000\
    \001\000\002\000\255\255\255\255\255\255\255\255";
  Lexing.lex_default =
   "\001\000\000\000\000\000\000\000\255\255\255\255\255\255\000\000\
    \000\000\000\000\000\000\000\000\000\000\255\255\255\255\255\255\
    \255\255\255\255\000\000\255\255\020\000\000\000\255\255\255\255\
    \000\000\255\255\255\255\255\255\000\000\255\255\255\255\000\000\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \042\000\000\000\000\000\255\255\000\000\047\000\000\000\047\000\
    \000\000\051\000\000\000\000\000\255\255\255\255\000\000\255\255\
    \255\255\255\255\255\255\000\000\255\255\000\000";
  Lexing.lex_trans =
   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\019\000\018\000\018\000\019\000\017\000\019\000\255\255\
    \048\000\019\000\255\255\057\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \019\000\000\000\003\000\000\000\000\000\019\000\000\000\000\000\
    \050\000\000\000\000\000\043\000\008\000\006\000\033\000\016\000\
    \004\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\
    \005\000\005\000\007\000\004\000\005\000\005\000\005\000\005\000\
    \005\000\005\000\005\000\005\000\005\000\032\000\044\000\033\000\
    \056\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\
    \005\000\005\000\005\000\021\000\057\000\000\000\000\000\000\000\
    \020\000\000\000\000\000\012\000\000\000\011\000\032\000\056\000\
    \000\000\025\000\049\000\000\000\000\000\032\000\014\000\024\000\
    \028\000\000\000\000\000\057\000\026\000\030\000\013\000\031\000\
    \000\000\000\000\022\000\027\000\015\000\029\000\023\000\000\000\
    \000\000\000\000\039\000\010\000\039\000\009\000\032\000\038\000\
    \038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\
    \038\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\
    \034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\
    \034\000\034\000\034\000\034\000\034\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\037\000\000\000\037\000\000\000\
    \035\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\
    \036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\
    \036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\
    \036\000\036\000\036\000\036\000\036\000\036\000\036\000\255\255\
    \035\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\
    \038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\
    \038\000\038\000\038\000\038\000\038\000\000\000\000\000\255\255\
    \000\000\056\000\000\000\000\000\055\000\058\000\058\000\058\000\
    \058\000\058\000\058\000\058\000\058\000\058\000\058\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\054\000\
    \000\000\054\000\000\000\000\000\000\000\000\000\054\000\000\000\
    \002\000\041\000\000\000\000\000\000\000\255\255\046\000\053\000\
    \053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\
    \053\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\255\255\059\000\059\000\059\000\059\000\059\000\059\000\
    \059\000\059\000\059\000\059\000\000\000\000\000\000\000\000\000\
    \000\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\
    \060\000\060\000\060\000\054\000\000\000\000\000\000\000\000\000\
    \000\000\054\000\060\000\060\000\060\000\060\000\060\000\060\000\
    \000\000\000\000\000\000\000\000\000\000\054\000\000\000\000\000\
    \000\000\054\000\000\000\054\000\000\000\000\000\000\000\052\000\
    \061\000\061\000\061\000\061\000\061\000\061\000\061\000\061\000\
    \061\000\061\000\060\000\060\000\060\000\060\000\060\000\060\000\
    \000\000\061\000\061\000\061\000\061\000\061\000\061\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\061\000\061\000\061\000\061\000\061\000\061\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\255\255\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\255\255\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000";
  Lexing.lex_check =
   "\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\000\000\000\000\017\000\000\000\000\000\019\000\020\000\
    \045\000\019\000\020\000\055\000\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \000\000\255\255\000\000\255\255\255\255\019\000\255\255\255\255\
    \045\000\255\255\255\255\040\000\000\000\000\000\004\000\000\000\
    \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
    \000\000\000\000\000\000\006\000\006\000\006\000\006\000\006\000\
    \006\000\006\000\006\000\006\000\006\000\004\000\043\000\005\000\
    \056\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\
    \005\000\005\000\005\000\016\000\057\000\255\255\255\255\255\255\
    \016\000\255\255\255\255\000\000\255\255\000\000\005\000\056\000\
    \255\255\014\000\045\000\255\255\255\255\004\000\000\000\023\000\
    \027\000\255\255\255\255\057\000\025\000\029\000\000\000\030\000\
    \255\255\255\255\015\000\026\000\000\000\013\000\022\000\255\255\
    \255\255\255\255\032\000\000\000\032\000\000\000\005\000\032\000\
    \032\000\032\000\032\000\032\000\032\000\032\000\032\000\032\000\
    \032\000\033\000\033\000\033\000\033\000\033\000\033\000\033\000\
    \033\000\033\000\033\000\034\000\034\000\034\000\034\000\034\000\
    \034\000\034\000\034\000\034\000\034\000\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\035\000\255\255\035\000\255\255\
    \034\000\035\000\035\000\035\000\035\000\035\000\035\000\035\000\
    \035\000\035\000\035\000\036\000\036\000\036\000\036\000\036\000\
    \036\000\036\000\036\000\036\000\036\000\037\000\037\000\037\000\
    \037\000\037\000\037\000\037\000\037\000\037\000\037\000\047\000\
    \034\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\
    \038\000\038\000\038\000\039\000\039\000\039\000\039\000\039\000\
    \039\000\039\000\039\000\039\000\039\000\255\255\255\255\047\000\
    \255\255\049\000\255\255\255\255\049\000\053\000\053\000\053\000\
    \053\000\053\000\053\000\053\000\053\000\053\000\053\000\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\049\000\
    \255\255\049\000\255\255\255\255\255\255\255\255\049\000\255\255\
    \000\000\040\000\255\255\255\255\255\255\020\000\045\000\049\000\
    \049\000\049\000\049\000\049\000\049\000\049\000\049\000\049\000\
    \049\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\047\000\058\000\058\000\058\000\058\000\058\000\058\000\
    \058\000\058\000\058\000\058\000\255\255\255\255\255\255\255\255\
    \255\255\052\000\052\000\052\000\052\000\052\000\052\000\052\000\
    \052\000\052\000\052\000\049\000\255\255\255\255\255\255\255\255\
    \255\255\049\000\052\000\052\000\052\000\052\000\052\000\052\000\
    \255\255\255\255\255\255\255\255\255\255\049\000\255\255\255\255\
    \255\255\049\000\255\255\049\000\255\255\255\255\255\255\049\000\
    \060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\
    \060\000\060\000\052\000\052\000\052\000\052\000\052\000\052\000\
    \255\255\060\000\060\000\060\000\060\000\060\000\060\000\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\060\000\060\000\060\000\060\000\060\000\060\000\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\047\000\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\049\000\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\
    \255\255";
  Lexing.lex_base_code =
   "";
  Lexing.lex_backtrk_code =
   "";
  Lexing.lex_default_code =
   "";
  Lexing.lex_trans_code =
   "";
  Lexing.lex_check_code =
   "";
  Lexing.lex_code =
   "";
}

let rec lex_json buf lexbuf =
   __ocaml_lex_lex_json_rec buf lexbuf 0
and __ocaml_lex_lex_json_rec buf lexbuf __ocaml_lex_state =
  match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with
      | 0 ->
# 143 "ext/ext_json_parse.mll"
          ( lex_json buf lexbuf)
# 315 "ext/ext_json_parse.ml"

  | 1 ->
# 144 "ext/ext_json_parse.mll"
                   ( 
    update_loc lexbuf 0;
    lex_json buf  lexbuf
  )
# 323 "ext/ext_json_parse.ml"

  | 2 ->
# 148 "ext/ext_json_parse.mll"
                ( comment buf lexbuf)
# 328 "ext/ext_json_parse.ml"

  | 3 ->
# 149 "ext/ext_json_parse.mll"
         ( True)
# 333 "ext/ext_json_parse.ml"

  | 4 ->
# 150 "ext/ext_json_parse.mll"
          (False)
# 338 "ext/ext_json_parse.ml"

  | 5 ->
# 151 "ext/ext_json_parse.mll"
         (Null)
# 343 "ext/ext_json_parse.ml"

  | 6 ->
# 152 "ext/ext_json_parse.mll"
       (Lbracket)
# 348 "ext/ext_json_parse.ml"

  | 7 ->
# 153 "ext/ext_json_parse.mll"
       (Rbracket)
# 353 "ext/ext_json_parse.ml"

  | 8 ->
# 154 "ext/ext_json_parse.mll"
       (Lbrace)
# 358 "ext/ext_json_parse.ml"

  | 9 ->
# 155 "ext/ext_json_parse.mll"
       (Rbrace)
# 363 "ext/ext_json_parse.ml"

  | 10 ->
# 156 "ext/ext_json_parse.mll"
       (Comma)
# 368 "ext/ext_json_parse.ml"

  | 11 ->
# 157 "ext/ext_json_parse.mll"
        (Colon)
# 373 "ext/ext_json_parse.ml"

  | 12 ->
# 158 "ext/ext_json_parse.mll"
                      (lex_json buf lexbuf)
# 378 "ext/ext_json_parse.ml"

  | 13 ->
# 160 "ext/ext_json_parse.mll"
         ( Number (Lexing.lexeme lexbuf))
# 383 "ext/ext_json_parse.ml"

  | 14 ->
# 162 "ext/ext_json_parse.mll"
      (
  let pos = Lexing.lexeme_start_p lexbuf in
  scan_string buf pos lexbuf;
  let content = (Buffer.contents  buf) in 
  Buffer.clear buf ;
  String content 
)
# 394 "ext/ext_json_parse.ml"

  | 15 ->
# 169 "ext/ext_json_parse.mll"
       (Eof )
# 399 "ext/ext_json_parse.ml"

  | 16 ->
let
# 170 "ext/ext_json_parse.mll"
       c
# 405 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf lexbuf.Lexing.lex_start_pos in
# 170 "ext/ext_json_parse.mll"
          ( error lexbuf (Illegal_character c ))
# 409 "ext/ext_json_parse.ml"

  | __ocaml_lex_state -> lexbuf.Lexing.refill_buff lexbuf;
      __ocaml_lex_lex_json_rec buf lexbuf __ocaml_lex_state

and comment buf lexbuf =
   __ocaml_lex_comment_rec buf lexbuf 40
and __ocaml_lex_comment_rec buf lexbuf __ocaml_lex_state =
  match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with
      | 0 ->
# 172 "ext/ext_json_parse.mll"
              (lex_json buf lexbuf)
# 421 "ext/ext_json_parse.ml"

  | 1 ->
# 173 "ext/ext_json_parse.mll"
     (comment buf lexbuf)
# 426 "ext/ext_json_parse.ml"

  | 2 ->
# 174 "ext/ext_json_parse.mll"
       (error lexbuf Unterminated_comment)
# 431 "ext/ext_json_parse.ml"

  | __ocaml_lex_state -> lexbuf.Lexing.refill_buff lexbuf;
      __ocaml_lex_comment_rec buf lexbuf __ocaml_lex_state

and scan_string buf start lexbuf =
   __ocaml_lex_scan_string_rec buf start lexbuf 45
and __ocaml_lex_scan_string_rec buf start lexbuf __ocaml_lex_state =
  match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with
      | 0 ->
# 178 "ext/ext_json_parse.mll"
      ( () )
# 443 "ext/ext_json_parse.ml"

  | 1 ->
# 180 "ext/ext_json_parse.mll"
  (
        let len = lexeme_len lexbuf - 2 in
        update_loc lexbuf len;

        scan_string buf start lexbuf
      )
# 453 "ext/ext_json_parse.ml"

  | 2 ->
# 187 "ext/ext_json_parse.mll"
      (
        let len = lexeme_len lexbuf - 3 in
        update_loc lexbuf len;
        scan_string buf start lexbuf
      )
# 462 "ext/ext_json_parse.ml"

  | 3 ->
let
# 192 "ext/ext_json_parse.mll"
                                               c
# 468 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) in
# 193 "ext/ext_json_parse.mll"
      (
        Buffer.add_char buf (char_for_backslash c);
        scan_string buf start lexbuf
      )
# 475 "ext/ext_json_parse.ml"

  | 4 ->
let
# 197 "ext/ext_json_parse.mll"
                 c1
# 481 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1)
and
# 197 "ext/ext_json_parse.mll"
                               c2
# 486 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 2)
and
# 197 "ext/ext_json_parse.mll"
                                             c3
# 491 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 3)
and
# 197 "ext/ext_json_parse.mll"
                                                    s
# 496 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos (lexbuf.Lexing.lex_start_pos + 4) in
# 198 "ext/ext_json_parse.mll"
      (
        let v = dec_code c1 c2 c3 in
        if v > 255 then
          error lexbuf (Illegal_escape s) ;
        Buffer.add_char buf (Char.chr v);

        scan_string buf start lexbuf
      )
# 507 "ext/ext_json_parse.ml"

  | 5 ->
let
# 206 "ext/ext_json_parse.mll"
                        c1
# 513 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 2)
and
# 206 "ext/ext_json_parse.mll"
                                         c2
# 518 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 3) in
# 207 "ext/ext_json_parse.mll"
      (
        let v = hex_code c1 c2 in
        Buffer.add_char buf (Char.chr v);

        scan_string buf start lexbuf
      )
# 527 "ext/ext_json_parse.ml"

  | 6 ->
let
# 213 "ext/ext_json_parse.mll"
             c
# 533 "ext/ext_json_parse.ml"
= Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) in
# 214 "ext/ext_json_parse.mll"
      (
        Buffer.add_char buf '\\';
        Buffer.add_char buf c;

        scan_string buf start lexbuf
      )
# 542 "ext/ext_json_parse.ml"

  | 7 ->
# 221 "ext/ext_json_parse.mll"
      (
        update_loc lexbuf 0;
        Buffer.add_char buf lf;

        scan_string buf start lexbuf
      )
# 552 "ext/ext_json_parse.ml"

  | 8 ->
# 228 "ext/ext_json_parse.mll"
      (
        let ofs = lexbuf.lex_start_pos in
        let len = lexbuf.lex_curr_pos - ofs in
        Buffer.add_subbytes buf lexbuf.lex_buffer ofs len;

        scan_string buf start lexbuf
      )
# 563 "ext/ext_json_parse.ml"

  | 9 ->
# 236 "ext/ext_json_parse.mll"
      (
        error lexbuf Unterminated_string
      )
# 570 "ext/ext_json_parse.ml"

  | __ocaml_lex_state -> lexbuf.Lexing.refill_buff lexbuf;
      __ocaml_lex_scan_string_rec buf start lexbuf __ocaml_lex_state

;;

# 240 "ext/ext_json_parse.mll"
 






let  parse_json lexbuf =
  let buf = Buffer.create 64 in 
  let look_ahead = ref None in
  let token () : token = 
    match !look_ahead with 
    | None ->  
      lex_json buf lexbuf 
    | Some x -> 
      look_ahead := None ;
      x 
  in
  let push e = look_ahead := Some e in 
  let rec json (lexbuf : Lexing.lexbuf) : Ext_json_types.t = 
    match token () with 
    | True -> True lexbuf.lex_start_p
    | False -> False lexbuf.lex_start_p
    | Null -> Null lexbuf.lex_start_p
    | Number s ->  Flo {flo = s; loc = lexbuf.lex_start_p}  
    | String s -> Str { str = s; loc =    lexbuf.lex_start_p}
    | Lbracket -> parse_array  lexbuf.lex_start_p lexbuf.lex_curr_p [] lexbuf
    | Lbrace -> parse_map lexbuf.lex_start_p Map_string.empty lexbuf
    |  _ -> error lexbuf Unexpected_token

(* Note if we remove [trailing_comma] support 
    we should report errors (actually more work), for example 
    {[
    match token () with 
    | Rbracket ->
      if trailing_comma then
        error lexbuf Trailing_comma_in_array
      else
    ]} 
    {[
    match token () with 
    | Rbrace -> 
      if trailing_comma then
        error lexbuf Trailing_comma_in_obj
      else

    ]}   
 *)
  and parse_array   loc_start loc_finish acc lexbuf 
    : Ext_json_types.t =
    match token () with 
    | Rbracket ->
        Arr {loc_start ; content = Ext_array.reverse_of_list acc ; 
              loc_end = lexbuf.lex_curr_p }
    | x -> 
      push x ;
      let new_one = json lexbuf in 
      begin match token ()  with 
      | Comma -> 
          parse_array  loc_start loc_finish (new_one :: acc) lexbuf 
      | Rbracket 
        -> Arr {content = (Ext_array.reverse_of_list (new_one::acc));
                     loc_start ; 
                     loc_end = lexbuf.lex_curr_p }
      | _ -> 
        error lexbuf Expect_comma_or_rbracket
      end
  and parse_map loc_start  acc lexbuf : Ext_json_types.t = 
    match token () with 
    | Rbrace -> 
        Obj { map = acc ; loc = loc_start}
    | String key -> 
      begin match token () with 
      | Colon ->
        let value = json lexbuf in
        begin match token () with 
        | Rbrace -> Obj {map = Map_string.add acc key value  ; loc = loc_start}
        | Comma -> 
          parse_map loc_start  (Map_string.add acc key value ) lexbuf 
        | _ -> error lexbuf Expect_comma_or_rbrace
        end
      | _ -> error lexbuf Expect_colon
      end
    | _ -> error lexbuf Expect_string_or_rbrace
  in 
  let v = json lexbuf in 
  match token () with 
  | Eof -> v 
  | _ -> error lexbuf Expect_eof

let parse_json_from_string s = 
  parse_json (Lexing.from_string s )

let parse_json_from_chan fname in_chan = 
  let lexbuf = 
    Ext_position.lexbuf_from_channel_with_fname
    in_chan fname in 
  parse_json lexbuf 

let parse_json_from_file s = 
  let in_chan = open_in s in 
  let lexbuf = 
    Ext_position.lexbuf_from_channel_with_fname
    in_chan s in 
  match parse_json lexbuf with 
  | exception e -> close_in in_chan ; raise e
  | v  -> close_in in_chan;  v





# 690 "ext/ext_json_parse.ml"

end
module Bs_hash_stubs
= struct
#1 "bs_hash_stubs.pp.ml"


external hash_string :  string -> int = "caml_bs_hash_string" [@@noalloc];;

external hash_string_int :  string -> int  -> int = "caml_bs_hash_string_and_int" [@@noalloc];;

external hash_string_small_int :  string -> int  -> int = "caml_bs_hash_string_and_small_int" [@@noalloc];;

external hash_stamp_and_name : int -> string -> int = "caml_bs_hash_stamp_and_name" [@@noalloc];;

external hash_small_int : int -> int = "caml_bs_hash_small_int" [@@noalloc];;

external hash_int :  int  -> int = "caml_bs_hash_int" [@@noalloc];;

external string_length_based_compare : string -> string -> int  = "caml_string_length_based_compare" [@@noalloc];;

external    
  int_unsafe_blit : 
  int array -> int -> int array -> int -> int -> unit = "caml_int_array_blit" [@@noalloc];;

external set_as_old_file : string -> unit = "caml_stale_file"




end
module Hash_string : sig 
#1 "hash_string.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

include Hash_gen.S with type key = string

end = struct
#1 "hash_string.ml"
# 9 "ext/hash.cppo.ml"
type key = string
type 'a t = (key, 'a)  Hash_gen.t 
let key_index (h : _ t ) (key : key) =
  (Bs_hash_stubs.hash_string  key ) land (Array.length h.data - 1)
let eq_key = Ext_string.equal 

  
# 33 "ext/hash.cppo.ml"
  type ('a, 'b) bucket = ('a,'b) Hash_gen.bucket
  let create = Hash_gen.create
  let clear = Hash_gen.clear
  let reset = Hash_gen.reset
  let iter = Hash_gen.iter
  let to_list = Hash_gen.to_list
  let fold = Hash_gen.fold
  let length = Hash_gen.length
  (* let stats = Hash_gen.stats *)



  let add (h : _ t) key data =
    let i = key_index h key in
    let h_data = h.data in   
    Array.unsafe_set h_data i (Cons{key; data; next=Array.unsafe_get h_data i});
    h.size <- h.size + 1;
    if h.size > Array.length h_data lsl 1 then Hash_gen.resize key_index h

  (* after upgrade to 4.04 we should provide an efficient [replace_or_init] *)
  let add_or_update 
      (h : 'a t) 
      (key : key) 
      ~update:(modf : 'a -> 'a) 
      (default :  'a) : unit =
    let rec find_bucket (bucketlist : _ bucket) : bool =
      match bucketlist with
      | Cons rhs  ->
        if eq_key rhs.key key then begin rhs.data <- modf rhs.data; false end
        else find_bucket rhs.next
      | Empty -> true in
    let i = key_index h key in 
    let h_data = h.data in 
    if find_bucket (Array.unsafe_get h_data i) then
      begin 
        Array.unsafe_set h_data i  (Cons{key; data=default; next = Array.unsafe_get h_data i});
        h.size <- h.size + 1 ;
        if h.size > Array.length h_data lsl 1 then Hash_gen.resize key_index h 
      end

  let remove (h : _ t ) key =
    let i = key_index h key in
    let h_data = h.data in 
    Hash_gen.remove_bucket h i key ~prec:Empty (Array.unsafe_get h_data i) eq_key

  (* for short bucket list, [find_rec is not called ] *)
  let rec find_rec key (bucketlist : _ bucket) = match bucketlist with  
    | Empty ->
      raise Not_found
    | Cons rhs  ->
      if eq_key key rhs.key then rhs.data else find_rec key rhs.next

  let find_exn (h : _ t) key =
    match Array.unsafe_get h.data (key_index h key) with
    | Empty -> raise Not_found
    | Cons rhs  ->
      if eq_key key rhs.key then rhs.data else
        match rhs.next with
        | Empty -> raise Not_found
        | Cons rhs  ->
          if eq_key key rhs.key then rhs.data else
            match rhs.next with
            | Empty -> raise Not_found
            | Cons rhs ->
              if eq_key key rhs.key  then rhs.data else find_rec key rhs.next

  let find_opt (h : _ t) key =
    Hash_gen.small_bucket_opt eq_key key (Array.unsafe_get h.data (key_index h key))

  let find_key_opt (h : _ t) key =
    Hash_gen.small_bucket_key_opt eq_key key (Array.unsafe_get h.data (key_index h key))

  let find_default (h : _ t) key default = 
    Hash_gen.small_bucket_default eq_key key default (Array.unsafe_get h.data (key_index h key))

  let find_all (h : _ t) key =
    let rec find_in_bucket (bucketlist : _ bucket) = match bucketlist with 
      | Empty ->
        []
      | Cons rhs  ->
        if eq_key key rhs.key
        then rhs.data :: find_in_bucket rhs.next
        else find_in_bucket rhs.next in
    find_in_bucket (Array.unsafe_get h.data (key_index h key))


  let replace h key data =
    let i = key_index h key in
    let h_data = h.data in 
    let l = Array.unsafe_get h_data i in
    if Hash_gen.replace_bucket key data l eq_key then 
      begin 
        Array.unsafe_set h_data i (Cons{key; data; next=l});
        h.size <- h.size + 1;
        if h.size > Array.length h_data lsl 1 then Hash_gen.resize key_index h;
      end 

  let mem (h : _ t) key = 
    Hash_gen.small_bucket_mem 
      (Array.unsafe_get h.data (key_index h key))
      eq_key key 


  let of_list2 ks vs = 
    let len = List.length ks in 
    let map = create len in 
    List.iter2 (fun k v -> add map k v) ks vs ; 
    map


end
module Bsb_build_util : sig 
#1 "bsb_build_util.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val flag_concat : string -> string list -> string
(**
   Use:
   {[
     flag_concat "-ppx" [ppxs]
   ]}
*)

val ppx_flags : Bsb_config_types.ppx list -> string
(**
   Build quoted commandline arguments for bsc.exe for the given ppx flags

   Use:
   {[
     ppx_flags [ppxs]
   ]}
*)

val pp_flag : string -> string

val include_dirs : string list -> string
(**
   Build unquoted command line arguments for bsc.exe for the given include dirs

   Use:
   {[
     include_dirs [dirs]
   ]}
*)

val include_dirs_by : 'a list -> ('a -> string) -> string

val mkp : string -> unit

(* The path of [bsc] and [bsdep] is normalized so that the invokation of [./jscomp/bin/bsb.exe]
   and [bsb.exe] (combined with a dirty bsconfig.json) will not trigger unnecessary rebuild.

   The location of [bsc] and [bsdep] is configured by the combination of [Sys.executable_name]
   and [cwd].

   In theory, we should also check the integrity of [bsb.exe], if it is changed, the rebuild
   should be regen, but that is too much in practice, not only you need check the integrity of
   path of [bsb.exe] but also the timestamp, to make it 100% correct, also the integrity of
   [bsdep.exe] [bsc.exe] etc.
*)

val get_list_string_acc : Ext_json_types.t array -> string list -> string list

val get_list_string : Ext_json_types.t array -> string list

type top = Expect_none | Expect_name of string

type result = { path : string; checked : bool }

(* [resolve_bsb_magic_file]
   returns a tuple (path,checked)
   when checked is true, it means such file should exist without depending on env
*)
val resolve_bsb_magic_file : cwd:string -> desc:string -> string -> result

type package_context = { proj_dir : string; top : top }

val walk_all_deps :
  string -> pinned_dependencies:Set_string.t -> package_context Queue.t

end = struct
#1 "bsb_build_util.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let flag_concat flag xs =
  String.concat Ext_string.single_space
    (Ext_list.flat_map xs (fun x -> [ flag; x ]))

let ( // ) = Ext_path.combine

let ppx_flags (xs : Bsb_config_types.ppx list) =
  flag_concat "-ppx"
    (Ext_list.map xs (fun x ->
         if x.args = [] then Ext_filename.maybe_quote x.name
         else
           let fmt : _ format =
             if Ext_sys.is_windows_or_cygwin then "\"%s %s\"" else "'%s %s'"
           in
           Printf.sprintf fmt x.name (String.concat " " x.args)))

let pp_flag (xs : string) = "-pp " ^ Ext_filename.maybe_quote xs

let include_dirs dirs =
  String.concat Ext_string.single_space
    (Ext_list.flat_map dirs (fun x -> [ "-I"; Ext_filename.maybe_quote x ]))

let include_dirs_by dirs fn =
  String.concat Ext_string.single_space
    (Ext_list.flat_map dirs (fun x -> [ "-I"; Ext_filename.maybe_quote (fn x) ]))

(* we use lazy $src_root_dir *)

(* It does several conversion:
   First, it will convert unix path to windows backward on windows platform.
   Then if it is absolute path, it will do thing
   Else if it is relative path, it will be rebased on project's root directory *)

let convert_and_resolve_path : string -> string -> string =
  if Sys.unix then ( // )
  else fun cwd path ->
    if Ext_sys.is_windows_or_cygwin then
      let p = Ext_string.replace_slash_backward path in
      cwd // p
    else failwith ("Unknown OS :" ^ Sys.os_type)
(* we only need convert the path in the beginning *)

type result = { path : string; checked : bool }

(* Magic path resolution:
   foo => foo
   foo/ => /absolute/path/to/projectRoot/node_modules/foo
   foo/bar => /absolute/path/to/projectRoot/node_modules/foo/bar
   /foo/bar => /foo/bar
   ./foo/bar => /absolute/path/to/projectRoot/./foo/bar
   Input is node path, output is OS dependent (normalized) path
*)
let resolve_bsb_magic_file ~cwd ~desc p : result =
  let no_slash = Ext_string.no_slash_idx p in
  if no_slash < 0 then
    (* Single file FIXME: better error message for "" input *)
    { path = p; checked = false }
  else
    let first_char = String.unsafe_get p 0 in
    if Filename.is_relative p && first_char <> '.' then
      let package_name, rest = Bsb_pkg_types.extract_pkg_name_and_file p in
      let relative_path =
        if Ext_sys.is_windows_or_cygwin then
          Ext_string.replace_slash_backward rest
        else rest
      in
      (* let p = if Ext_sys.is_windows_or_cygwin then Ext_string.replace_slash_backward p else p in *)
      let package_dir = Bsb_pkg.resolve_bs_package ~cwd package_name in
      let path = package_dir // relative_path in
      if Sys.file_exists path then { path; checked = true }
      else (
        Bsb_log.error "@{<error>Could not resolve @} %s in %s@." p cwd;
        failwith (p ^ " not found when resolving " ^ desc))
    else
      (* relative path [./x/y]*)
      { path = convert_and_resolve_path cwd p; checked = true }

(** converting a file from Linux path format to Windows *)

(** 
   {[
     mkp "a/b/c/d";;
     mkp "/a/b/c/d"
   ]}
*)
let rec mkp dir =
  if not (Sys.file_exists dir) then
    let parent_dir = Filename.dirname dir in
    if parent_dir = Filename.current_dir_name then Unix.mkdir dir 0o777
      (* leaf node *)
    else (
      mkp parent_dir;
      Unix.mkdir dir 0o777)
  else if not @@ Sys.is_directory dir then
    failwith (dir ^ " exists but it is not a directory, plz remove it first")
  else ()

let get_list_string_acc (s : Ext_json_types.t array) acc =
  Ext_array.to_list_map_acc s acc (fun x ->
      match x with Str x -> Some x.str | _ -> None)

let get_list_string s = get_list_string_acc s []

(* Key is the path *)
let ( |? ) m (key, cb) = m |> Ext_json.test key cb

type top = Expect_none | Expect_name of string

type package_context = { proj_dir : string; top : top }

(**
   TODO: check duplicate package name
   ?use path as identity?

   Basic requirements
     1. cycle detection
     2. avoid duplication
     3. deterministic, since -make-world will also comes with -clean-world

*)

let pp_packages_rev ppf lst =
  Ext_list.rev_iter lst (fun s -> Format.fprintf ppf "%s " s)

let rec walk_all_deps_aux (visited : string Hash_string.t) (paths : string list)
    ~(top : top) (dir : string) (queue : _ Queue.t) ~pinned_dependencies =
  let bsconfig_json = dir // Literals.bsconfig_json in
  match Ext_json_parse.parse_json_from_file bsconfig_json with
  | Obj { map; loc } ->
      let cur_package_name =
        match Map_string.find_opt map Bsb_build_schemas.name with
        | Some (Str { str; loc }) ->
            (match top with
            | Expect_none -> ()
            | Expect_name s ->
                if s <> str then
                  Bsb_exception.errorf ~loc
                    "package name is expected to be %s but got %s" s str);
            str
        | Some _ | None ->
            Bsb_exception.errorf ~loc "package name missing in %s/bsconfig.json"
              dir
      in
      if Ext_list.mem_string paths cur_package_name then (
        Bsb_log.error "@{<error>Cyclic dependencies in package stack@}@.";
        exit 2);
      let package_stacks = cur_package_name :: paths in
      Bsb_log.info "@{<info>Package stack:@} %a @." pp_packages_rev
        package_stacks;
      if Hash_string.mem visited cur_package_name then
        Bsb_log.info "@{<info>Visited before@} %s@." cur_package_name
      else
        let explore_deps (deps : string) =
          map
          |? ( deps,
               `Arr
                 (fun (new_packages : Ext_json_types.t array) ->
                   Ext_array.iter new_packages (fun js ->
                       match js with
                       | Str { str = new_package } ->
                           let package_dir =
                             Bsb_pkg.resolve_bs_package ~cwd:dir
                               (Bsb_pkg_types.string_as_package new_package)
                           in
                           walk_all_deps_aux visited package_stacks
                             ~top:(Expect_name new_package) package_dir queue
                             ~pinned_dependencies
                       | _ ->
                           Bsb_exception.errorf ~loc "%s expect an array" deps))
             )
          |> ignore
        in
        explore_deps Bsb_build_schemas.bs_dependencies;
        (match top with
        | Expect_none -> explore_deps Bsb_build_schemas.bs_dev_dependencies
        | Expect_name n when Set_string.mem pinned_dependencies n ->
            explore_deps Bsb_build_schemas.bs_dev_dependencies
        | Expect_name _ -> ());
        Queue.add { top; proj_dir = dir } queue;
        Hash_string.add visited cur_package_name dir
  | _ -> ()

let walk_all_deps dir ~pinned_dependencies : package_context Queue.t =
  let visited = Hash_string.create 0 in
  let cb = Queue.create () in
  walk_all_deps_aux visited [] ~top:Expect_none dir cb ~pinned_dependencies;
  cb

end
module Bsb_global_paths : sig 
#1 "bsb_global_paths.mli"
(* Copyright (C) 2019 - Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val cwd : string

val bsc_dir : string

val vendor_bsc : string

val vendor_ninja : string

val vendor_bsdep : string

end = struct
#1 "bsb_global_paths.ml"
(* Copyright (C) 2019 - Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let cwd = Sys.getcwd ()

(**
   If [Sys.executable_name] gives an absolute path, 
   nothing needs to be done.

   If [Sys.executable_name] is not an absolute path, for example
   (rlwrap ./ocaml)
   it is a relative path, 
   it needs be adapted based on cwd

   if [Sys.executable_name] gives an absolute path, 
   nothing needs to be done
   if it is a relative path 

   there are two cases: 
   - bsb.exe
   - ./bsb.exe 
     The first should also not be touched
     Only the latter need be adapted based on project root  
*)

let bsc_dir =
  Filename.dirname
    (Ext_path.normalize_absolute_path
       (Ext_path.combine cwd Sys.executable_name))

let vendor_bsc = Filename.concat bsc_dir "bsc.exe"

let vendor_ninja = Filename.concat bsc_dir "ninja.exe"

let vendor_bsdep = Filename.concat bsc_dir "bsb_helper.exe";;

assert (Sys.file_exists bsc_dir)

end
module Bsb_db_util : sig 
#1 "bsb_db_util.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val conflict_module_info :
  string -> Bsb_db.module_info -> Bsb_db.module_info -> exn

val merge : Bsb_db.map -> Bsb_db.map -> Bsb_db.map

val sanity_check : Bsb_db.map -> unit

(** 
   Currently it is okay to have duplicated module, 
   In the future, we may emit a warning 
*)

val add_basename :
  dir:string ->
  Bsb_db.map ->
  ?error_on_invalid_suffix:Ext_position.t ->
  string ->
  Bsb_db.map

end = struct
#1 "bsb_db_util.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
type module_info = Bsb_db.module_info

type t = Bsb_db.map

let conflict_module_info modname (a : module_info) (b : module_info) =
  Bsb_exception.conflict_module modname a.dir b.dir

(* merge data info from two directories*)
let merge (acc : t) (sources : t) : t =
  Map_string.disjoint_merge_exn acc sources conflict_module_info

let sanity_check (map : t) =
  Map_string.iter map (fun m module_info ->
      if module_info.info = Intf then Bsb_exception.no_implementation m)

(* invariant check:
   ml and mli should have the same case, same path
*)
let check (x : module_info) name_sans_extension case syntax_kind
    (module_info : Bsb_db.info) =
  let x_ml_info = x.info in
  if
    x.name_sans_extension <> name_sans_extension
    || x.case <> case
    || x.syntax_kind <> syntax_kind
    || x_ml_info = module_info || x_ml_info = Impl_intf
  then
    Bsb_exception.invalid_spec
      (Printf.sprintf
         "implementation and interface have different path names or different \
          cases %s vs %s"
         x.name_sans_extension name_sans_extension);
  x.info <- Impl_intf;
  x

let warning_unused_file : _ format =
  "@{<warning>IGNORED@}: file %s under %s is ignored because it can't be \
   turned into a valid module name. \n\
   The build system transforms a file name into a module name by upper-casing \
   the first letter@."
(* TODO: add a link for more explanations *)

let is_editor_temporary_files basename = Ext_string.starts_with basename ".#"

(*
   Example: .#hi.ml
   Note for other files like <filename>~, <filename>.swp
   it does not pass the suffix rules
*)
let add_basename ~(dir : string) (map : t) ?error_on_invalid_suffix basename : t
    =
  if is_editor_temporary_files basename then map
  else
    let info = ref Bsb_db.Impl in
    let syntax_kind = ref Bsb_db.Ml in
    let invalid_suffix = ref false in
    let file_suffix = Ext_filename.get_extension_maybe basename in
    (match () with
    | _ when file_suffix = Literals.suffix_ml -> ()
    | _ when file_suffix = Literals.suffix_res -> syntax_kind := Res
    | _ when file_suffix = Literals.suffix_re -> syntax_kind := Reason
    | _ when file_suffix = Literals.suffix_mli -> info := Intf
    | _ when file_suffix = Literals.suffix_resi ->
        info := Intf;
        syntax_kind := Res
    | _ when file_suffix = Literals.suffix_rei ->
        info := Intf;
        syntax_kind := Reason
    | _ -> invalid_suffix := true);
    let info = !info in
    let syntax_kind = !syntax_kind in
    let invalid_suffix = !invalid_suffix in
    if invalid_suffix then
      match error_on_invalid_suffix with
      | None -> map
      | Some loc -> Bsb_exception.errorf ~loc "invalid suffix %s" basename
    else
      match Ext_filename.as_module ~basename:(Filename.basename basename) with
      | None ->
          Bsb_log.warn warning_unused_file basename dir;
          map
      | Some { module_name; case } ->
          let name_sans_extension =
            Filename.concat dir (Ext_filename.chop_extension_maybe basename)
          in
          let dir = Filename.dirname name_sans_extension in
          Map_string.adjust map module_name (fun opt_module_info ->
              match opt_module_info with
              | None -> { dir; name_sans_extension; info; syntax_kind; case }
              | Some x -> check x name_sans_extension case syntax_kind info)

end
module Ext_option : sig 
#1 "ext_option.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** Utilities for [option] type *)

val map : 'a option -> ('a -> 'b) -> 'b option

val iter : 'a option -> ('a -> unit) -> unit

val exists : 'a option -> ('a -> bool) -> bool

end = struct
#1 "ext_option.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let map v f = match v with None -> None | Some x -> Some (f x)

let iter v f = match v with None -> () | Some x -> f x

let exists v f = match v with None -> false | Some x -> f x

end
module Bsb_parse_sources : sig 
#1 "bsb_parse_sources.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val scan :
  package_kind:Bsb_package_kind.t ->
  root:string ->
  cut_generators:bool ->
  (* namespace : string option ->  *)
  ignored_dirs:Set_string.t ->
  Ext_json_types.t ->
  Bsb_file_groups.t
(** [scan .. cxt json]
    entry is to the [sources] in the schema    
    given a root, return an object which is
    all relative paths, this function will do the IO
*)

val clean_re_js : string -> unit
(** This function has some duplication 
    from [scan],
    the parsing assuming the format is 
    already valid
*)

end = struct
#1 "bsb_parse_sources.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type build_generator = Bsb_file_groups.build_generator

let ( .?() ) = Map_string.find_opt

(* type file_group = Bsb_file_groups.file_group *)

type t = Bsb_file_groups.t

let is_input_or_output (xs : build_generator list) (x : string) =
  Ext_list.exists xs (fun { input; output } ->
      let it_is y = y = x in
      Ext_list.exists input it_is || Ext_list.exists output it_is)

let errorf x fmt = Bsb_exception.errorf ~loc:(Ext_json.loc_of x) fmt

type cxt = {
  package_kind : Bsb_package_kind.t;
  is_dev : bool;
  cwd : string;
  root : string;
  cut_generators : bool;
  traverse : bool;
  (* namespace : string option; *)
  ignored_dirs : Set_string.t;
}

(** [public] has a list of modules, we do a sanity check to see if all the listed 
    modules are indeed valid module components
*)
let collect_pub_modules (xs : Ext_json_types.t array) (cache : Bsb_db.map) :
    Set_string.t =
  let set = ref Set_string.empty in
  for i = 0 to Array.length xs - 1 do
    let v = Array.unsafe_get xs i in
    match v with
    | Str { str; loc } ->
        if Map_string.mem cache str then set := Set_string.add !set str
        else
          Bsb_exception.errorf ~loc "%S in public is not an existing module" str
    | _ ->
        Bsb_exception.errorf ~loc:(Ext_json.loc_of v)
          "public expects a list of strings"
  done;
  !set

let extract_pub (input : Ext_json_types.t Map_string.t)
    (cur_sources : Bsb_db.map) : Bsb_file_groups.public =
  match input.?(Bsb_build_schemas.public) with
  | Some (Str { str = s } as x) ->
      if s = Bsb_build_schemas.export_all then Export_all
      else if s = Bsb_build_schemas.export_none then Export_none
      else errorf x "invalid str for %s " s
  | Some (Arr { content }) ->
      Export_set (collect_pub_modules content cur_sources)
  | Some config -> Bsb_exception.config_error config "expect array or string"
  | None -> Export_all

let extract_resources (input : Ext_json_types.t Map_string.t) : string list =
  match input.?(Bsb_build_schemas.resources) with
  | Some (Arr x) -> Bsb_build_util.get_list_string x.content
  | Some config -> Bsb_exception.config_error config "expect array "
  | None -> []

let extract_input_output (edge : Ext_json_types.t) : string list * string list =
  let error () =
    errorf edge {| invalid edge format, expect  ["output" , ":", "input" ]|}
  in
  match edge with
  | Arr { content } -> (
      match
        Ext_array.find_and_split content
          (fun x () -> match x with Str { str = ":" } -> true | _ -> false)
          ()
      with
      | No_split -> error ()
      | Split (output, input) ->
          ( Ext_array.to_list_map output (fun x ->
                match x with
                | Str { str = ":" } -> error ()
                | Str { str } -> Some str
                | _ -> None),
            Ext_array.to_list_map input (fun x ->
                match x with
                | Str { str = ":" } -> error ()
                | Str { str } ->
                    Some str
                    (* More rigirous error checking: It would trigger a ninja syntax error *)
                | _ -> None) ))
  | _ -> error ()

type json_map = Ext_json_types.t Map_string.t

let extract_generators (input : json_map) : build_generator list =
  match input.?(Bsb_build_schemas.generators) with
  | Some (Arr { content; loc_start = _ }) ->
      (* Need check is dev build or not *)
      Ext_array.fold_left content [] (fun acc x ->
          match x with
          | Obj { map } -> (
              match
                (map.?(Bsb_build_schemas.name), map.?(Bsb_build_schemas.edge))
              with
              | Some (Str command), Some edge ->
                  let output, input = extract_input_output edge in
                  { Bsb_file_groups.input; output; command = command.str }
                  :: acc
              | _ -> errorf x "Invalid generator format")
          | _ -> errorf x "Invalid generator format")
  | Some x -> errorf x "Invalid generator format"
  | None -> []

let extract_predicate (m : json_map) : string -> bool =
  let excludes =
    match m.?(Bsb_build_schemas.excludes) with
    | None -> []
    | Some (Arr { content = arr }) -> Bsb_build_util.get_list_string arr
    | Some x -> Bsb_exception.config_error x "excludes expect array "
  in
  let slow_re = m.?(Bsb_build_schemas.slow_re) in
  match (slow_re, excludes) with
  | Some (Str { str = s }), [] ->
      let re = Str.regexp s in
      fun name -> Str.string_match re name 0
  | Some (Str { str = s }), _ :: _ ->
      let re = Str.regexp s in
      fun name ->
        Str.string_match re name 0 && not (Ext_list.mem_string excludes name)
  | Some config, _ ->
      Bsb_exception.config_error config
        (Bsb_build_schemas.slow_re ^ " expect a string literal")
  | None, _ -> fun name -> not (Ext_list.mem_string excludes name)

(** [parsing_source_dir_map cxt input]
    Major work done in this function, 
    assume [not toplevel && not (Bsb_dir_index.is_lib_dir dir_index)]      
    is already checked, so we don't need check it again    
*)

(** This is the only place where we do some removal during scanning,
    configurabl
*)

(********************************************************************)
(* starts parsing *)
let rec parsing_source_dir_map ({ cwd = dir } as cxt)
    (input : Ext_json_types.t Map_string.t) : Bsb_file_groups.t =
  if Set_string.mem cxt.ignored_dirs dir then Bsb_file_groups.empty
  else
    let cur_globbed_dirs = ref false in
    let has_generators =
      match cxt with
      | {
       cut_generators = false;
       package_kind = Toplevel | Pinned_dependency _;
      } ->
          true
      | { cut_generators = false; package_kind = Dependency _ }
      | { cut_generators = true; _ } ->
          false
    in
    let scanned_generators = extract_generators input in
    let sub_dirs_field = input.?(Bsb_build_schemas.subdirs) in
    let base_name_array =
      lazy
        (cur_globbed_dirs := true;
         Sys.readdir (Filename.concat cxt.root dir))
    in
    let output_sources =
      Ext_list.fold_left
        (Ext_list.flat_map scanned_generators (fun x -> x.output))
        Map_string.empty
        (fun acc o -> Bsb_db_util.add_basename ~dir acc o)
    in
    let sources =
      match input.?(Bsb_build_schemas.files) with
      | None ->
          (* We should avoid temporary files *)
          Ext_array.fold_left (Lazy.force base_name_array) output_sources
            (fun acc basename ->
              if is_input_or_output scanned_generators basename then acc
              else Bsb_db_util.add_basename ~dir acc basename)
      | Some (Arr basenames) ->
          Ext_array.fold_left basenames.content output_sources
            (fun acc basename ->
              match basename with
              | Str { str = basename; loc } ->
                  Bsb_db_util.add_basename ~dir acc basename
                    ~error_on_invalid_suffix:loc
              | _ -> acc)
      | Some (Obj { map; loc = _ }) ->
          (* { excludes : [], slow_re : "" }*)
          let predicate = extract_predicate map in
          Ext_array.fold_left (Lazy.force base_name_array) output_sources
            (fun acc basename ->
              if
                is_input_or_output scanned_generators basename
                || not (predicate basename)
              then acc
              else Bsb_db_util.add_basename ~dir acc basename)
      | Some x ->
          Bsb_exception.config_error x "files field expect array or object "
    in
    let resources = extract_resources input in
    let public = extract_pub input sources in
    (* Doing recursive stuff *)
    let children =
      match (sub_dirs_field, cxt.traverse) with
      | None, true | Some (True _), _ ->
          let root = cxt.root in
          let parent = Filename.concat root dir in
          Ext_array.fold_left (Lazy.force base_name_array) Bsb_file_groups.empty
            (fun origin x ->
              if
                (not (Set_string.mem cxt.ignored_dirs x))
                && Ext_sys.is_directory_no_exn (Filename.concat parent x)
              then
                Bsb_file_groups.merge
                  (parsing_source_dir_map
                     {
                       cxt with
                       cwd =
                         Ext_path.concat cxt.cwd
                           (Ext_path.simple_convert_node_path_to_os_path x);
                       traverse = true;
                     }
                     Map_string.empty)
                  origin
              else origin)
      (* readdir parent avoiding scanning twice *)
      | None, false | Some (False _), _ -> Bsb_file_groups.empty
      | Some s, _ -> parse_sources cxt s
    in
    (* Do some clean up *)
    (* prune_staled_bs_js_files cxt sources ; *)
    Bsb_file_groups.cons
      ~file_group:
        {
          dir;
          sources;
          resources;
          public;
          is_dev = cxt.is_dev;
          generators = (if has_generators then scanned_generators else []);
        }
      ?globbed_dir:(if !cur_globbed_dirs then Some dir else None)
      children

and parsing_single_source ({ package_kind; is_dev; cwd } as cxt)
    (x : Ext_json_types.t) : t =
  match x with
  | Str { str = dir } -> (
      match (package_kind, is_dev) with
      | Dependency _, true -> Bsb_file_groups.empty
      | Dependency _, false | (Toplevel | Pinned_dependency _), _ ->
          parsing_source_dir_map
            {
              cxt with
              cwd =
                Ext_path.concat cwd
                  (Ext_path.simple_convert_node_path_to_os_path dir);
            }
            Map_string.empty)
  | Obj { map } -> (
      let current_dir_index =
        match map.?(Bsb_build_schemas.type_) with
        | Some (Str { str = "dev" }) -> true
        | Some _ ->
            Bsb_exception.config_error x {|type field expect "dev" literal |}
        | None -> is_dev
      in
      match (package_kind, current_dir_index) with
      | Dependency _, true -> Bsb_file_groups.empty
      | Dependency _, false | (Toplevel | Pinned_dependency _), _ ->
          let dir =
            match map.?(Bsb_build_schemas.dir) with
            | Some (Str { str }) ->
                if str = Literals.library_file then
                    Bsb_exception.config_error x (Printf.sprintf "dir field should be different from `%s`" Literals.library_file)
                else
                Ext_path.simple_convert_node_path_to_os_path str
            | Some x ->
                Bsb_exception.config_error x "dir expected to be a string"
            | None ->
                Bsb_exception.config_error x
                  ("required field :" ^ Bsb_build_schemas.dir ^ " missing")
          in

          parsing_source_dir_map
            {
              cxt with
              is_dev = current_dir_index;
              cwd = Ext_path.concat cwd dir;
            }
            map)
  | _ -> Bsb_file_groups.empty

and parsing_arr_sources cxt (file_groups : Ext_json_types.t array) =
  Ext_array.fold_left file_groups Bsb_file_groups.empty (fun origin x ->
      Bsb_file_groups.merge (parsing_single_source cxt x) origin)

and parse_sources (cxt : cxt) (sources : Ext_json_types.t) =
  match sources with
  | Arr file_groups -> parsing_arr_sources cxt file_groups.content
  | _ -> parsing_single_source cxt sources

let scan ~package_kind ~root ~cut_generators ~(* ~namespace  *)
                                             ignored_dirs x : t =
  parse_sources
    {
      ignored_dirs;
      package_kind;
      is_dev = false;
      cwd = Filename.current_dir_name;
      root;
      cut_generators;
      (* namespace; *)
      traverse = false;
    }
    x

(* Walk through to do some work *)
type walk_cxt = {
  cwd : string;
  root : string;
  traverse : bool;
  ignored_dirs : Set_string.t;
  gentype_language : string;
}

let rec walk_sources (cxt : walk_cxt) (sources : Ext_json_types.t) =
  match sources with
  | Arr { content } ->
      Ext_array.iter content (fun x -> walk_single_source cxt x)
  | x -> walk_single_source cxt x

and walk_single_source cxt (x : Ext_json_types.t) =
  match x with
  | Str { str = dir } ->
      let dir = Ext_path.simple_convert_node_path_to_os_path dir in
      walk_source_dir_map { cxt with cwd = Ext_path.concat cxt.cwd dir } None
  | Obj { map } -> (
      match map.?(Bsb_build_schemas.dir) with
      | Some (Str { str }) ->
          let dir = Ext_path.simple_convert_node_path_to_os_path str in
          walk_source_dir_map
            { cxt with cwd = Ext_path.concat cxt.cwd dir }
            map.?(Bsb_build_schemas.subdirs)
      | _ -> ())
  | _ -> ()

and walk_source_dir_map (cxt : walk_cxt) sub_dirs_field =
  let working_dir = Filename.concat cxt.root cxt.cwd in
  if not (Set_string.mem cxt.ignored_dirs cxt.cwd) then (
    let file_array = Sys.readdir working_dir in
    (* Remove .gen.js/.gen.tsx during clean up *)
    Ext_array.iter file_array (fun file ->
        let is_typescript = cxt.gentype_language = "typescript" in
        if
          (not is_typescript)
          && Ext_string.ends_with file Literals.suffix_gen_js
          || (is_typescript && Ext_string.ends_with file Literals.suffix_gen_tsx)
        then Sys.remove (Filename.concat working_dir file));
    let cxt_traverse = cxt.traverse in
    match (sub_dirs_field, cxt_traverse) with
    | None, true | Some (True _), _ ->
        Ext_array.iter file_array (fun f ->
            if
              (not (Set_string.mem cxt.ignored_dirs f))
              && Ext_sys.is_directory_no_exn (Filename.concat working_dir f)
            then
              walk_source_dir_map
                {
                  cxt with
                  cwd =
                    Ext_path.concat cxt.cwd
                      (Ext_path.simple_convert_node_path_to_os_path f);
                  traverse = true;
                }
                None)
    | None, _ | Some (False _), _ -> ()
    | Some s, _ -> walk_sources cxt s)

(* It makes use of the side effect when [walk_sources], removing suffix_re_js,
   TODO: make it configurable
*)
let clean_re_js root =
  match
    Ext_json_parse.parse_json_from_file
      (Filename.concat root Literals.bsconfig_json)
  with
  | Obj { map } ->
      let ignored_dirs =
        match map.?(Bsb_build_schemas.ignored_dirs) with
        | Some (Arr { content = x }) ->
            Set_string.of_list (Bsb_build_util.get_list_string x)
        | Some _ | None -> Set_string.empty
      in
      let gentype_language =
        match map.?(Bsb_build_schemas.gentypeconfig) with
        | None -> ""
        | Some (Obj { map }) -> (
            match map.?(Bsb_build_schemas.language) with
            | None -> ""
            | Some (Str { str }) -> str
            | Some _ -> "")
        | Some _ -> ""
      in
      Ext_option.iter map.?(Bsb_build_schemas.sources) (fun config ->
          try
            walk_sources
              {
                root;
                traverse = true;
                cwd = Filename.current_dir_name;
                ignored_dirs;
                gentype_language;
              }
              config
          with _ -> ())
  | _ -> ()
  | exception _ -> ()

end
module Bsb_config_parse : sig 
#1 "bsb_config_parse.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val deps_from_bsconfig : unit -> Bsb_package_specs.t * Bsb_jsx.t * Set_string.t

val interpret_json :
  package_kind:Bsb_package_kind.t -> per_proj_dir:string -> Bsb_config_types.t

end = struct
#1 "bsb_config_parse.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* let get_list_string = Bsb_build_util.get_list_string *)
let ( // ) = Ext_path.combine

let resolve_package cwd package_name =
  let x = Bsb_pkg.resolve_bs_package ~cwd package_name in
  {
    Bsb_config_types.package_name;
    package_install_path = x // Bsb_config.lib_ocaml;
  }

type json_map = Ext_json_types.t Map_string.t

(* Key is the path *)
let ( |? ) m (key, cb) = m |> Ext_json.test key cb
let ( .?() ) = Map_string.find_opt

(*TODO: it is a little mess that [cwd] and [project dir] are shared*)

let extract_package_name_and_namespace (map : json_map) : string * string option
    =
  let package_name =
    match map.?(Bsb_build_schemas.name) with
    | Some (Str { str = "_" } as config) ->
        Bsb_exception.config_error config "_ is a reserved package name"
    | Some (Str { str = name }) -> name
    | Some config ->
        Bsb_exception.config_error config "name expect a string field"
    | None -> Bsb_exception.invalid_spec "field name is required"
  in
  let namespace =
    match map.?(Bsb_build_schemas.namespace) with
    | None | Some (False _) -> None
    | Some (True _) ->
        Some (Ext_namespace.namespace_of_package_name package_name)
    | Some (Str { str }) ->
        (*TODO : check the validity of namespace *)
        Some (Ext_namespace.namespace_of_package_name str)
    | Some x ->
        Bsb_exception.config_error x "namespace field expects string or boolean"
  in
  (package_name, namespace)

(**
    There are two things to check:
   - the running bsb and vendoring bsb is the same
   - the running bsb need delete stale build artifacts
      (kinda check npm upgrade)

      Note if the setup is correct: 
      the running compiler and node_modules/rescript
      should be the same version, 
      The exact check is that the running compiler should have a 
      compatible runtime version installed, the location of the
      compiler is actually not relevant.
      We disable the check temporarily
      e.g,
      ```
      bsc -runtime runtime_dir@version
      ```
*)
let check_stdlib (map : json_map) : bool =
  (*built_in_package*)
  match map.?(Bsb_build_schemas.use_stdlib) with
  | Some (False _) -> false
  | None | Some _ -> true

let extract_gentype_config (map : json_map) : Bsb_config_types.gentype_config =
  match map.?(Bsb_build_schemas.gentypeconfig) with
  | None -> false
  | Some (Obj _) -> true
  | Some config ->
      Bsb_exception.config_error config "gentypeconfig expect an object"

let extract_refmt (map : json_map) cwd : Bsb_config_types.refmt =
  match map.?(Bsb_build_schemas.refmt) with
  | Some (Flo { flo } as config) -> (
      match flo with
      | "3" -> None
      | _ -> Bsb_exception.config_error config "expect version 3 only")
  | Some (Str { str }) ->
      Some
        (Bsb_build_util.resolve_bsb_magic_file ~cwd
           ~desc:Bsb_build_schemas.refmt str)
          .path
  | Some config -> Bsb_exception.config_error config "expect version 2 or 3"
  | None -> None

let extract_string (map : json_map) (field : string) cb =
  match map.?(field) with
  | None -> None
  | Some (Str { str }) -> cb str
  | Some config -> Bsb_exception.config_error config (field ^ " expect a string")

let extract_boolean (map : json_map) (field : string) (default : bool) : bool =
  match map.?(field) with
  | None -> default
  | Some (True _) -> true
  | Some (False _) -> false
  | Some config ->
      Bsb_exception.config_error config (field ^ " expect a boolean")

let extract_reason_react_jsx (map : json_map) =
  let default : Bsb_config_types.reason_react_jsx option ref = ref None in
  map
  |? ( Bsb_build_schemas.reason,
       `Obj
         (fun m ->
           match m.?(Bsb_build_schemas.react_jsx) with
           | Some (Flo { loc; flo }) -> (
               match flo with
               | "3" -> default := Some Jsx_v3
               | _ -> Bsb_exception.errorf ~loc "Unsupported jsx version %s" flo
               )
           | Some x ->
               Bsb_exception.config_error x
                 "Unexpected input (expect a version number) for jsx, note \
                  boolean is no longer allowed"
           | None -> ()) )
  |> ignore;
  !default

let extract_warning (map : json_map) =
  match map.?(Bsb_build_schemas.warnings) with
  | None -> Bsb_warning.use_default
  | Some (Obj { map }) -> Bsb_warning.from_map map
  | Some config -> Bsb_exception.config_error config "expect an object"

let extract_ignored_dirs (map : json_map) : Set_string.t =
  match map.?(Bsb_build_schemas.ignored_dirs) with
  | None -> Set_string.empty
  | Some (Arr { content }) ->
      Set_string.of_list (Bsb_build_util.get_list_string content)
  | Some config -> Bsb_exception.config_error config "expect an array of string"

let extract_pinned_dependencies (map : json_map) : Set_string.t =
  match map.?(Bsb_build_schemas.pinned_dependencies) with
  | None -> Set_string.empty
  | Some (Arr { content }) ->
      Set_string.of_list (Bsb_build_util.get_list_string content)
  | Some config -> Bsb_exception.config_error config "expect an array of string"

let extract_generators (map : json_map) =
  let generators = ref Map_string.empty in
  (match map.?(Bsb_build_schemas.generators) with
  | None -> ()
  | Some (Arr { content = s }) ->
      generators :=
        Ext_array.fold_left s Map_string.empty (fun acc json ->
            match json with
            | Obj { map = m; loc } -> (
                match
                  (m.?(Bsb_build_schemas.name), m.?(Bsb_build_schemas.command))
                with
                | Some (Str { str = name }), Some (Str { str = command }) ->
                    Map_string.add acc name command
                | _, _ ->
                    Bsb_exception.errorf ~loc
                      {| generators exepect format like { "name" : "cppo",  "command"  : "cppo $in -o $out"} |}
                )
            | _ -> acc)
  | Some config ->
      Bsb_exception.config_error config
        (Bsb_build_schemas.generators ^ " expect an array field"));
  !generators

let extract_dependencies (map : json_map) cwd (field : string) :
    Bsb_config_types.dependencies =
  match map.?(field) with
  | None -> []
  | Some (Arr { content = s }) ->
      Ext_list.map (Bsb_build_util.get_list_string s) (fun s ->
          resolve_package cwd (Bsb_pkg_types.string_as_package s))
  | Some config -> Bsb_exception.config_error config (field ^ " expect an array")

(* return an empty array if not found *)
let extract_string_list (map : json_map) (field : string) : string list =
  match map.?(field) with
  | None -> []
  | Some (Arr { content = s }) -> Bsb_build_util.get_list_string s
  | Some config -> Bsb_exception.config_error config (field ^ " expect an array")

let extract_ppx (map : json_map) (field : string) ~(cwd : string) :
    Bsb_config_types.ppx list =
  match map.?(field) with
  | None -> []
  | Some (Arr { content }) ->
      let resolve s =
        if s = "" then
          Bsb_exception.invalid_spec "invalid ppx, empty string found"
        else
          (Bsb_build_util.resolve_bsb_magic_file ~cwd
             ~desc:Bsb_build_schemas.ppx_flags s)
            .path
      in
      Ext_array.to_list_f content (fun x ->
          match x with
          | Str x -> { Bsb_config_types.name = resolve x.str; args = [] }
          | Arr { content } -> (
              let xs = Bsb_build_util.get_list_string content in
              match xs with
              | [] -> Bsb_exception.config_error x " empty array is not allowed"
              | name :: args -> { Bsb_config_types.name = resolve name; args })
          | config ->
              Bsb_exception.config_error config
                (field ^ "expect each item to be either string or array"))
  | Some config -> Bsb_exception.config_error config (field ^ " expect an array")

let extract_js_post_build (map : json_map) cwd : string option =
  let js_post_build_cmd = ref None in
  map
  |? ( Bsb_build_schemas.js_post_build,
       `Obj
         (fun m ->
           m
           |? ( Bsb_build_schemas.cmd,
                `Str
                  (fun s ->
                    js_post_build_cmd :=
                      Some
                        (Bsb_build_util.resolve_bsb_magic_file ~cwd
                           ~desc:Bsb_build_schemas.js_post_build s)
                          .path) )
           |> ignore) )
  |> ignore;
  !js_post_build_cmd

(** ATT: make sure such function is re-entrant. 
    With a given [cwd] it works anywhere*)
let interpret_json ~(package_kind : Bsb_package_kind.t) ~(per_proj_dir : string)
    : Bsb_config_types.t =
  (* we should not resolve it too early,
      since it is external configuration, no {!Bsb_build_util.convert_and_resolve_path}
  *)

  (* When we plan to add more deps here,
     Make sure check it is consistent that for nested deps, we have a
     quck check by just re-parsing deps
     Make sure it works with [-make-world] [-clean-world]
  *)

  (* Setting ninja is a bit complex
     1. if [build.ninja] does use [ninja] we need set a variable
     2. we need store it so that we can call ninja correctly
  *)
  match
    Ext_json_parse.parse_json_from_file (per_proj_dir // Literals.bsconfig_json)
  with
  | Obj { map } -> (
      let package_name, namespace = extract_package_name_and_namespace map in
      let refmt = extract_refmt map per_proj_dir in
      let gentype_config = extract_gentype_config map in

      (* This line has to be before any calls to Bsb_global_backend.backend, because it'll read the entries
           array from the bsconfig and set the backend_ref to the first entry, if any. *)

      (* The default situation is empty *)
      let built_in_package : bool = check_stdlib map in

      let pp_flags : string option =
        extract_string map Bsb_build_schemas.pp_flags (fun p ->
            if p = "" then
              Bsb_exception.invalid_spec "invalid pp, empty string found"
            else
              Some
                (Bsb_build_util.resolve_bsb_magic_file ~cwd:per_proj_dir
                   ~desc:Bsb_build_schemas.pp_flags p)
                  .path)
      in
      let reason_react_jsx = extract_reason_react_jsx map in
      let bs_dependencies =
        extract_dependencies map per_proj_dir Bsb_build_schemas.bs_dependencies
      in
      let bs_dev_dependencies =
        match package_kind with
        | Toplevel | Pinned_dependency _ ->
            extract_dependencies map per_proj_dir
              Bsb_build_schemas.bs_dev_dependencies
        | Dependency _ -> []
      in
      let pinned_dependencies = extract_pinned_dependencies map in
      match map.?(Bsb_build_schemas.sources) with
      | Some sources ->
          let cut_generators =
            extract_boolean map Bsb_build_schemas.cut_generators false
          in
          let groups =
            Bsb_parse_sources.scan ~ignored_dirs:(extract_ignored_dirs map)
              ~package_kind ~root:per_proj_dir ~cut_generators
              (* ~namespace *)
              sources
          in
          let bsc_flags = extract_string_list map Bsb_build_schemas.bsc_flags in
          let jsx = Bsb_jsx.from_map map in
          let jsx, bsc_flags =
            match package_kind with
            | Pinned_dependency x | Dependency x ->
                if List.mem package_name x.jsx.v3_dependencies then
                  ( { jsx with version = Some Jsx_v3 },
                    "-open ReactV3" :: bsc_flags )
                else (x.jsx, bsc_flags)
            | _ -> (jsx, bsc_flags)
          in
          {
            pinned_dependencies;
            gentype_config;
            package_name;
            namespace;
            warning = extract_warning map;
            external_includes =
              extract_string_list map Bsb_build_schemas.bs_external_includes;
            bsc_flags;
            ppx_files =
              extract_ppx map ~cwd:per_proj_dir Bsb_build_schemas.ppx_flags;
            pp_file = pp_flags;
            bs_dependencies;
            bs_dev_dependencies;
            (*
            reference for quoting
             {[
               let tmpfile = Filename.temp_file "ocamlpp" "" in
               let comm = Printf.sprintf "%s %s > %s"
                   pp (Filename.quote sourcefile) tmpfile
               in
             ]}
          *)
            refmt;
            js_post_build_cmd = extract_js_post_build map per_proj_dir;
            package_specs =
              (match package_kind with
              | Toplevel -> Bsb_package_specs.from_map ~cwd:per_proj_dir map
              | Pinned_dependency x | Dependency x -> x.package_specs);
            file_groups = groups;
            files_to_install = Queue.create ();
            built_in_dependency = built_in_package;
            generate_merlin =
              extract_boolean map Bsb_build_schemas.generate_merlin false;
            reason_react_jsx;
            jsx;
            generators = extract_generators map;
            cut_generators;
          }
      | None ->
          Bsb_exception.invalid_spec "no sources specified in bsconfig.json")
  | _ -> Bsb_exception.invalid_spec "bsconfig.json expect a json object {}"

let deps_from_bsconfig () =
  let json = Ext_json_parse.parse_json_from_file Literals.bsconfig_json in
  match json with
  | Obj { map } ->
      ( Bsb_package_specs.from_map ~cwd:Bsb_global_paths.cwd map,
        Bsb_jsx.from_map map,
        extract_pinned_dependencies map )
  | _ -> assert false

end
module Bsb_unix : sig 
#1 "bsb_unix.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type command = { cmd : string; cwd : string; args : string array }

val command_fatal_error : command -> int -> unit

val run_command_execv : command -> int

val remove_dir_recursive : string -> unit

end = struct
#1 "bsb_unix.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type command = { cmd : string; cwd : string; args : string array }

let log cmd =
  Bsb_log.info "@{<info>Entering@} %s @." cmd.cwd;
  Bsb_log.info "@{<info>Cmd:@} ";
  Bsb_log.info_args cmd.args

let command_fatal_error cmd eid =
  Bsb_log.error "@{<error>Failure:@} %s \nLocation: %s@." cmd.cmd cmd.cwd;
  exit eid

let run_command_execv_unix cmd : int =
  match Unix.fork () with
  | 0 ->
      log cmd;
      Unix.chdir cmd.cwd;
      Unix.execv cmd.cmd cmd.args
  | pid -> (
      match Unix.waitpid [] pid with
      | _, process_status -> (
          match process_status with
          | Unix.WEXITED eid -> eid
          | Unix.WSIGNALED _ | Unix.WSTOPPED _ ->
              Bsb_log.error "@{<error>Interrupted:@} %s@." cmd.cmd;
              2))

(** TODO: the args are not quoted, here 
    we are calling a very limited set of `bsb` commands, so that 
    we are safe
*)
let run_command_execv_win (cmd : command) =
  let old_cwd = Unix.getcwd () in
  log cmd;
  Unix.chdir cmd.cwd;
  let eid =
    Sys.command
      (String.concat Ext_string.single_space
         (Filename.quote cmd.cmd :: (List.tl @@ Array.to_list cmd.args)))
  in
  Bsb_log.info "@{<info>Leaving@} %s => %s  @." cmd.cwd old_cwd;
  Unix.chdir old_cwd;
  eid

(** it assume you have permissions, so always catch it to fail 
    gracefully
*)
let run_command_execv =
  if Ext_sys.is_windows_or_cygwin then run_command_execv_win
  else run_command_execv_unix

let rec remove_dir_recursive dir =
  match Sys.is_directory dir with
  | true ->
      let files = Sys.readdir dir in
      for i = 0 to Array.length files - 1 do
        remove_dir_recursive (Filename.concat dir (Array.unsafe_get files i))
      done;
      Unix.rmdir dir
  | false -> Sys.remove dir
  | exception _ -> ()

end
module Bsb_clean : sig 
#1 "bsb_clean.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** clean bsc generated artifacts.
    TODO: clean stale in source js artifacts
*)

val clean_bs_deps : string -> unit

val clean_self : string -> unit

end = struct
#1 "bsb_clean.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Ext_path.combine

let ninja_clean proj_dir =
  try
    let cmd = Bsb_global_paths.vendor_ninja in
    let lib_artifacts_dir = Bsb_config.lib_bs in
    let cwd = proj_dir // lib_artifacts_dir in
    if Sys.file_exists cwd then
      let eid =
        Bsb_unix.run_command_execv { cmd; args = [| cmd; "-t"; "clean" |]; cwd }
      in
      if eid <> 0 then Bsb_log.warn "@{<warning>Failed@}@."
  with e -> Bsb_log.warn "@{<warning>Failed@}: %s @." (Printexc.to_string e)

let clean_bs_garbage proj_dir =
  Bsb_log.info "@{<info>Cleaning:@} in %s@." proj_dir;
  let try_remove x =
    let x = proj_dir // x in
    if Sys.file_exists x then Bsb_unix.remove_dir_recursive x
  in
  try
    Bsb_parse_sources.clean_re_js proj_dir;
    (* clean re.js files*)
    ninja_clean proj_dir;
    Ext_list.iter Bsb_config.all_lib_artifacts try_remove
  with e ->
    Bsb_log.warn "@{<warning>Failed@} to clean due to %s" (Printexc.to_string e)

let clean_bs_deps proj_dir =
  let _, _, pinned_dependencies = Bsb_config_parse.deps_from_bsconfig () in
  let queue = Bsb_build_util.walk_all_deps proj_dir ~pinned_dependencies in
  Queue.iter
    (fun (pkg_cxt : Bsb_build_util.package_context) ->
      (* whether top or not always do the cleaning *)
      clean_bs_garbage pkg_cxt.proj_dir)
    queue

let clean_self proj_dir = clean_bs_garbage proj_dir

end
module Ext_io : sig 
#1 "ext_io.mli"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val load_file : string -> string

val rev_lines_of_file : string -> string list

val rev_lines_of_chann : in_channel -> string list

val write_file : string -> string -> unit

end = struct
#1 "ext_io.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(** on 32 bit , there are 16M limitation *)
let load_file f =
  Ext_pervasives.finally (open_in_bin f) ~clean:close_in (fun ic ->
      let n = in_channel_length ic in
      let s = Bytes.create n in
      really_input ic s 0 n;
      Bytes.unsafe_to_string s)

let rev_lines_of_chann chan =
  let rec loop acc chan =
    match input_line chan with
    | line -> loop (line :: acc) chan
    | exception End_of_file ->
        close_in chan;
        acc
  in
  loop [] chan

let rev_lines_of_file file =
  Ext_pervasives.finally ~clean:close_in (open_in_bin file) rev_lines_of_chann

let write_file f content =
  Ext_pervasives.finally ~clean:close_out (open_out_bin f) (fun oc ->
      output_string oc content)

end
module Bsb_ninja_check : sig 
#1 "bsb_ninja_check.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(**
   This module is used to check whether [build.ninja] needs
   be regenerated. Everytime [bsb] run [regenerate_ninja], 
   bsb will try to [check] if it is needed, 
   if needed, bsb will regenerate ninja file and store the 
   metadata again
*)

type check_result =
  | Good
  | Bsb_file_corrupted
  | Bsb_file_not_exist  (** We assume that it is a clean repo *)
  | Bsb_source_directory_changed
  | Bsb_bsc_version_mismatch
  | Bsb_forced
  | Bsb_package_kind_inconsistent
  | Other of string

val pp_check_result : Format.formatter -> check_result -> unit

val record :
  package_kind:Bsb_package_kind.t ->
  per_proj_dir:string ->
  file:string ->
  config:Bsb_config_types.t ->
  string list ->
  unit
(** [record cwd file relevant_file_or_dirs]
    The data structure we decided to whether regenerate [build.ninja] 
    or not. 
    Note that if we don't record absolute path,  ninja will not notice  its build spec changed, 
    it will not trigger  rebuild behavior, 
    It may not be desired behavior, since there is some subtlies here (__FILE__ or __dirname)

    We serialize such data structure and call {!check} to decide
    [build.ninja] should be regenerated
*)

val check :
  package_kind:Bsb_package_kind.t ->
  per_proj_dir:string ->
  forced:bool ->
  file:string ->
  check_result
(** check if [build.ninja] should be regenerated *)

end = struct
#1 "bsb_ninja_check.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

[@@@warning "+9"]

(* float_of_string_opt *)
external hexstring_of_float : float -> int -> char -> string
  = "caml_hexstring_of_float"

let hex_of_float f = hexstring_of_float f (-1) '-'

(* This should not lose any preicision *)
(* let id (f : float) =
    float_of_string (hex_of_float f) = f
*)

type check_result =
  | Good
  | Bsb_file_corrupted
  | Bsb_file_not_exist  (** We assume that it is a clean repo *)
  | Bsb_source_directory_changed
  | Bsb_bsc_version_mismatch
  | Bsb_forced
  | Bsb_package_kind_inconsistent
  | Other of string

let pp_check_result fmt (check_resoult : check_result) =
  Format.pp_print_string fmt
    (match check_resoult with
    | Good -> "OK"
    | Bsb_file_corrupted -> "Stored data corrupted"
    | Bsb_file_not_exist -> "Dependencies information missing"
    | Bsb_source_directory_changed -> "Bsb source directory changed"
    | Bsb_bsc_version_mismatch -> "Bsc or bsb version mismatch"
    | Bsb_forced -> "Bsb forced rebuild"
    | Bsb_package_kind_inconsistent -> "The package was built in different mode"
    | Other s -> s)

let rec check_aux cwd (xs : string list) =
  match xs with
  | [] -> Good
  | "===" :: rest -> check_global_atime rest
  | item :: rest -> (
      match Ext_string.split item '\t' with
      | [ file; stamp ] ->
          let stamp = float_of_string stamp in
          let cur_file = Filename.concat cwd file in
          let stat = Unix.stat cur_file in
          if stat.st_mtime <= stamp then check_aux cwd rest else Other cur_file
      | _ -> Bsb_file_corrupted)

and check_global_atime rest =
  match rest with
  | [] -> Good
  | item :: rest -> (
      match Ext_string.split item '\t' with
      | [ file; stamp ] ->
          let stamp = float_of_string stamp in
          let cur_file = file in
          let stat = Unix.stat cur_file in
          if stat.st_atime <= stamp then check_global_atime rest
          else Other cur_file
      | _ -> Bsb_file_corrupted)

(* TODO: for such small data structure, maybe text format is better *)

let record_global_atime buf name =
  let stamp = (Unix.stat name).st_atime in
  Ext_buffer.add_string_char buf name '\t';
  Ext_buffer.add_string_char buf (hex_of_float stamp) '\n'

let record ~(package_kind : Bsb_package_kind.t) ~per_proj_dir ~file
    ~(config : Bsb_config_types.t) (file_or_dirs : string list) : unit =
  let buf = Ext_buffer.create 1_000 in
  Ext_buffer.add_string_char buf Bs_version.version '\n';
  Ext_buffer.add_string_char buf per_proj_dir '\n';
  Ext_buffer.add_string_char buf
    (Bsb_package_kind.encode_no_nl package_kind)
    '\n';
  Ext_list.iter file_or_dirs (fun f ->
      Ext_buffer.add_string_char buf f '\t';
      Ext_buffer.add_string_char buf
        (hex_of_float (Unix.stat (Filename.concat per_proj_dir f)).st_mtime)
        '\n');
  Ext_buffer.add_string buf "===\n";
  record_global_atime buf Sys.executable_name;
  Ext_list.iter config.ppx_files (fun { name; args = _ } ->
      try record_global_atime buf name
      with _ -> (* record the ppx files as a best effort *)
                ());
  let oc = open_out_bin file in
  Ext_buffer.output_buffer oc buf;
  close_out oc

(** check time stamp for all files
    TODO: those checks system call can be saved later
    Return a reason
    Even forced, we still need walk through a little
    bit in case we found a different version of compiler
*)
let check ~(package_kind : Bsb_package_kind.t) ~(per_proj_dir : string) ~forced
    ~file : check_result =
  match open_in_bin file with
  (* Windows binary mode*)
  | exception _ -> Bsb_file_not_exist
  | ic -> (
      match List.rev (Ext_io.rev_lines_of_chann ic) with
      | exception _ -> Bsb_file_corrupted
      | version :: source_directory :: package_kind_str :: dir_or_files -> (
          if version <> Bs_version.version then Bsb_bsc_version_mismatch
          else if per_proj_dir <> source_directory then
            Bsb_source_directory_changed
          else if forced then Bsb_forced (* No need walk through *)
          else if Bsb_package_kind.encode_no_nl package_kind <> package_kind_str
          then Bsb_package_kind_inconsistent
          else
            try check_aux per_proj_dir dir_or_files
            with e ->
              Bsb_log.info "@{<info>Stat miss %s@}@." (Printexc.to_string e);
              Bsb_file_not_exist)
      | _ -> Bsb_file_corrupted)

end
module Bsb_db_encode : sig 
#1 "bsb_db_encode.mli"
(* Copyright (C) 2019 - Present Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val encode : Bsb_db.t -> Ext_buffer.t -> unit

val write_build_cache : dir:string -> Bsb_db.t -> string

end = struct
#1 "bsb_db_encode.ml"
(* Copyright (C) 2019 - Present Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let bsbuild_cache = Literals.bsbuild_cache

let nl buf = Ext_buffer.add_char buf '\n'

(* IDEAS:
   Pros:
   - could be even shortened to a single byte
     Cons:
   - decode would allocate
   - code too verbose
   - not readable
*)

let make_encoding length buf : Ext_buffer.t -> int -> unit =
  let max_range = (length lsl 1) + 1 in
  if max_range <= 0xff then (
    Ext_buffer.add_char buf '1';
    Ext_buffer.add_int_1)
  else if max_range <= 0xff_ff then (
    Ext_buffer.add_char buf '2';
    Ext_buffer.add_int_2)
  else if length <= 0x7f_ff_ff then (
    Ext_buffer.add_char buf '3';
    Ext_buffer.add_int_3)
  else if length <= 0x7f_ff_ff_ff then (
    Ext_buffer.add_char buf '4';
    Ext_buffer.add_int_4)
  else assert false

(* Make sure [tmp_buf1] and [tmp_buf2] is cleared ,
   they are only used to control the order.
   Strictly speaking, [tmp_buf1] is not needed
*)
let encode_single (db : Bsb_db.map) (buf : Ext_buffer.t) =
  (* module name section *)
  let len = Map_string.cardinal db in
  Ext_buffer.add_string_char buf (string_of_int len) '\n';
  if len <> 0 then (
    let mapping = Hash_string.create 50 in
    Map_string.iter db (fun name { dir } ->
        Ext_buffer.add_string_char buf name '\n';
        if not (Hash_string.mem mapping dir) then
          Hash_string.add mapping dir (Hash_string.length mapping));
    let length = Hash_string.length mapping in
    let rev_mapping = Array.make length "" in
    Hash_string.iter mapping (fun k i -> Array.unsafe_set rev_mapping i k);
    (* directory name section *)
    Ext_array.iter rev_mapping (fun s -> Ext_buffer.add_string_char buf s '\t');
    nl buf;
    (* module name info section *)
    let len_encoding = make_encoding length buf in
    Map_string.iter db (fun _ module_info ->
        len_encoding buf
          ((Hash_string.find_exn mapping module_info.dir lsl 1)
          + (Obj.magic (module_info.case : bool) : int)));
    nl buf)

let encode (dbs : Bsb_db.t) buf =
  encode_single dbs.lib buf;
  encode_single dbs.dev buf

(* shall we avoid writing such file (checking the digest)?
   It is expensive to start scanning the whole code base,
   we should we avoid it in the first place, if we do start scanning,
   this operation seems affordable
*)
let write_build_cache ~dir (bs_files : Bsb_db.t) : string =
  let oc = open_out_bin (Filename.concat dir bsbuild_cache) in
  let buf = Ext_buffer.create 100_000 in
  encode bs_files buf;
  Ext_buffer.output_buffer oc buf;
  close_out oc;
  let digest = Ext_buffer.digest buf in
  Digest.to_hex digest

end
module Ext_digest : sig 
#1 "ext_digest.mli"
(* Copyright (C) 2019- Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val length : int

val hex_length : int

end = struct
#1 "ext_digest.ml"
(* Copyright (C) 2019- Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let length = 16

let hex_length = 32

end
module Bsb_namespace_map_gen : sig 
#1 "bsb_namespace_map_gen.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val output : dir:string -> string -> Bsb_file_groups.file_groups -> unit
(** [output dir namespace file_groups]
    when [build.ninja] is generated, we output a module map [.mlmap] file 
    such [.mlmap] file will be consumed by [bsc.exe] to generate [.cmi] file
*)

end = struct
#1 "bsb_namespace_map_gen.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Ext_path.combine

let write_file fname digest contents =
  let oc = open_out_bin fname in
  Digest.output oc digest;
  output_char oc '\n';
  Ext_buffer.output_buffer oc contents;
  close_out oc

(*
   TODO:
   sort filegroupts to ensure deterministic behavior

   if [.bsbuild] is not changed
   [.mlmap] does not need to be changed too

*)
let output ~dir (namespace : string) (file_groups : Bsb_file_groups.file_groups)
    =
  let fname = namespace ^ Literals.suffix_mlmap in
  let buf = Ext_buffer.create 10000 in
  Ext_list.iter file_groups (fun x ->
      Map_string.iter x.sources (fun k _ ->
          Ext_buffer.add_string_char buf k '\n'));
  (* let contents = Buffer.contents buf in    *)
  let digest = Ext_buffer.digest buf in
  let fname = dir // fname in
  if Sys.file_exists fname then (
    let ic = open_in_bin fname in
    let old_digest = really_input_string ic Ext_digest.length in
    close_in ic;
    if old_digest <> digest then write_file fname digest buf)
  else write_file fname digest buf

end
module Bsb_ninja_global_vars
= struct
#1 "bsb_ninja_global_vars.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* Invariant: the two string literal has
   to be "a" and "$a"
*)

(* let src_root_dir = "g_root"

   let lazy_src_root_dir = "$g_root" *)
let g_finger = "g_finger"

end
module Bsb_ninja_rule : sig 
#1 "bsb_ninja_rule.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t
(** The complexity comes from the fact that we allow custom rules which could
    conflict with our custom built-in rules
*)

val get_name : t -> out_channel -> string

(***********************************************************)

type builtin = {
  build_ast : t;
  build_ast_from_re : t;
  (* platform dependent, on Win32,
      invoking cmd.exe
  *)
  copy_resources : t;
  (* Rules below all need restat *)
  build_bin_deps : t;
  build_bin_deps_dev : t;
  mj : t;
  mj_dev : t;
  mij : t;
  mij_dev : t;
  mi : t;
  mi_dev : t;
  build_package : t;
  customs : t Map_string.t;
}
(** A list of existing rules *)

(***********************************************************)

(** rules are generally composed of built-in rules and customized rules, there are two design choices:
    1. respect custom rules with the same name, then we need adjust our built-in
    rules dynamically in case the conflict.
    2. respect our built-in rules, then we only need re-load custom rules for each bsconfig.json
*)

type command = string

(* Since now we generate ninja files per bsconfig.json in a single process,
    we must make sure it is re-entrant
*)
val make_custom_rules :
  gentype_config:Bsb_config_types.gentype_config ->
  has_postbuild:string option ->
  pp_file:string option ->
  has_builtin:bool ->
  reason_react_jsx:Bsb_config_types.reason_react_jsx option ->
  jsx:Bsb_jsx.t ->
  digest:string ->
  package_specs:Bsb_package_specs.t ->
  refmt:string option ->
  namespace:string option ->
  package_name:string ->
  warnings:string ->
  ppx_files:Bsb_config_types.ppx list ->
  bsc_flags:string ->
  dpkg_incls:string ->
  lib_incls:string ->
  dev_incls:string ->
  bs_dependencies:Bsb_config_types.dependencies ->
  bs_dev_dependencies:Bsb_config_types.dependencies ->
  command Map_string.t ->
  builtin

end = struct
#1 "bsb_ninja_rule.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t = {
  mutable used : bool;
  (* rule_name : string;  *)
  name : out_channel -> string;
}

let get_name (x : t) oc = x.name oc

let print_rule (oc : out_channel) ?description ?(restat : unit option)
    ?(dyndep : unit option) ~command name =
  output_string oc "rule ";
  output_string oc name;
  output_string oc "\n";
  output_string oc "  command = ";
  output_string oc command;
  output_string oc "\n";
  if dyndep <> None then output_string oc "  dyndep = 1\n";
  if restat <> None then output_string oc "  restat = 1\n";
  match description with
  | None -> ()
  | Some description ->
      output_string oc "  description = ";
      output_string oc description;
      output_string oc "\n"

(** allocate an unique name for such rule*)
let define ~command ?dyndep ?restat rule_name : t =
  let rec self =
    {
      used = false;
      (* rule_name ; *)
      name =
        (fun oc ->
          if not self.used then (
            print_rule oc ?dyndep ?restat ~command rule_name;
            self.used <- true);
          rule_name);
    }
  in

  self

type command = string

type builtin = {
  build_ast : t;  (** TODO: Implement it on top of pp_flags *)
  build_ast_from_re : t;
  (* build_ast_from_rei : t ; *)
  (* platform dependent, on Win32,
      invoking cmd.exe
  *)
  copy_resources : t;
  (* Rules below all need restat *)
  build_bin_deps : t;
  build_bin_deps_dev : t;
  mj : t;
  mj_dev : t;
  mij : t;
  mij_dev : t;
  mi : t;
  mi_dev : t;
  build_package : t;
  customs : t Map_string.t;
}

let make_custom_rules ~(gentype_config : Bsb_config_types.gentype_config)
    ~(has_postbuild : string option) ~(pp_file : string option)
    ~(has_builtin : bool)
    ~(reason_react_jsx : Bsb_config_types.reason_react_jsx option)
    ~(jsx : Bsb_jsx.t) ~(digest : string) ~(package_specs : Bsb_package_specs.t)
    ~(refmt : string option)
    ~(namespace : string option) ~package_name ~warnings
    ~(ppx_files : Bsb_config_types.ppx list) ~bsc_flags ~(dpkg_incls : string)
    ~(lib_incls : string) ~(dev_incls : string) ~bs_dependencies
    ~bs_dev_dependencies (custom_rules : command Map_string.t) : builtin =
  let bs_dep = Ext_filename.maybe_quote Bsb_global_paths.vendor_bsdep in
  let bsc = Ext_filename.maybe_quote Bsb_global_paths.vendor_bsc in
  (* FIXME: We don't need set [-o ${out}] when building ast
      since the default is already good -- it does not*)
  let buf = Ext_buffer.create 100 in
  let ns_flag = match namespace with None -> "" | Some n -> " -bs-ns " ^ n in
  let mk_ml_cmj_cmd ~(read_cmi : [ `yes | `is_cmi | `no ]) ~is_dev ~postbuild :
      string =
    Ext_buffer.clear buf;
    Ext_buffer.add_string buf bsc;
    Ext_buffer.add_string buf ns_flag;
    if read_cmi = `yes then Ext_buffer.add_string buf " -bs-read-cmi";
    (* The include order matters below *)
    if is_dev then Ext_buffer.add_char_string buf ' ' dev_incls;
    Ext_buffer.add_char_string buf ' ' lib_incls;
    if is_dev then Ext_buffer.add_char_string buf ' ' dpkg_incls;
    if not has_builtin then Ext_buffer.add_string buf " -nostdlib";
    Ext_buffer.add_char_string buf ' ' bsc_flags;
    Ext_buffer.add_char_string buf ' ' warnings;
    (* we need "-w a" in the end position to take effect
       in non-toplevel mode
    *)
    (match gentype_config with
    | false -> ()
    | true -> Ext_buffer.add_string buf " -bs-gentype");
    if read_cmi <> `is_cmi then (
      Ext_buffer.add_string buf " -bs-package-name ";
      Ext_buffer.add_string buf (Ext_filename.maybe_quote package_name);
      Ext_buffer.add_string buf
        (Bsb_package_specs.package_flag_of_package_specs package_specs
           ~dirname:"$in_d"));
    (match (bs_dependencies, bs_dev_dependencies) with
    | [], [] -> ()
    | _, _ ->
        Ext_buffer.add_string buf " -bs-v";
        Ext_buffer.add_ninja_prefix_var buf Bsb_ninja_global_vars.g_finger);
    Ext_buffer.add_string buf " $i";
    (match postbuild with
    | None -> ()
    | Some cmd ->
        Ext_buffer.add_string buf " && ";
        Ext_buffer.add_string buf cmd;
        Ext_buffer.add_string buf " $out_last");
    Ext_buffer.contents buf
  in
  let mk_ast =
    Ext_buffer.clear buf;
    Ext_buffer.add_string buf bsc;
    Ext_buffer.add_char_string buf ' ' warnings;
    Ext_buffer.add_string buf " -bs-v ";
    Ext_buffer.add_string buf Bs_version.version;
    (match ppx_files with
    | [] -> ()
    | _ ->
        Ext_list.iter ppx_files (fun x ->
            match string_of_float (Unix.stat x.name).st_mtime with
            | exception _ -> ()
            | st -> Ext_buffer.add_char_string buf ',' st);
        Ext_buffer.add_char_string buf ' ' (Bsb_build_util.ppx_flags ppx_files));
    (match refmt with
    | None -> ()
    | Some x ->
        Ext_buffer.add_string buf " -bs-refmt ";
        Ext_buffer.add_string buf (Ext_filename.maybe_quote x));
    (match pp_file with
    | None -> ()
    | Some flag ->
        Ext_buffer.add_char_string buf ' ' (Bsb_build_util.pp_flag flag));
    (match (reason_react_jsx, jsx.version) with
    | _, Some Jsx_v3 -> Ext_buffer.add_string buf " -bs-jsx 3"
    | _, Some Jsx_v4 -> Ext_buffer.add_string buf " -bs-jsx 4"
    | Some Jsx_v3, None -> Ext_buffer.add_string buf " -bs-jsx 3"
    | None, None -> ());
    (match jsx.module_ with
    | None -> ()
    | Some React -> Ext_buffer.add_string buf " -bs-jsx-module react");
    (match jsx.mode with
    | None -> ()
    | Some Classic -> Ext_buffer.add_string buf " -bs-jsx-mode classic"
    | Some Automatic -> Ext_buffer.add_string buf " -bs-jsx-mode automatic");

    Ext_buffer.add_char_string buf ' ' bsc_flags;
    Ext_buffer.add_string buf " -absname -bs-ast -o $out $i";
    Ext_buffer.contents buf
  in
  let build_ast = define ~command:mk_ast "ast" in
  let build_ast_from_re = define ~command:mk_ast "astj" in

  let copy_resources =
    define
      ~command:
        (if Ext_sys.is_windows_or_cygwin then "cmd.exe /C copy /Y $i $out >NUL"
        else "cp $i $out")
      "copy_resource"
  in

  let build_bin_deps =
    define ~restat:()
      ~command:(bs_dep ^ " -hash " ^ digest ^ ns_flag ^ " $in")
      "deps"
    (*
       it seems we already have restat = 1
       now it is an implicit dependency, we need avoid write duplicated files
    *)
  in
  let build_bin_deps_dev =
    define ~restat:()
      ~command:(bs_dep ^ " -g -hash " ^ digest ^ ns_flag ^ " $in")
      "deps_dev"
  in
  let aux ~name ~read_cmi ~postbuild =
    ( define
        ~command:(mk_ml_cmj_cmd ~read_cmi ~is_dev:false ~postbuild)
        ~dyndep:() ~restat:() (* Always restat when having mli *) name,
      define
        ~command:(mk_ml_cmj_cmd ~read_cmi ~is_dev:true ~postbuild)
        ~dyndep:() ~restat:()
        (* Always restat when having mli *) (name ^ "_dev") )
  in

  let mj, mj_dev = aux ~name:"mj" ~read_cmi:`yes ~postbuild:has_postbuild in
  let mij, mij_dev = aux ~read_cmi:`no ~name:"mij" ~postbuild:has_postbuild in
  let mi, mi_dev = aux ~read_cmi:`is_cmi ~postbuild:None ~name:"mi" in
  let build_package =
    define
      ~command:(bsc ^ " -w -49 -color always -no-alias-deps  $i")
      ~restat:() "build_package"
  in
  {
    build_ast;
    build_ast_from_re;
    (* platform dependent, on Win32,
        invoking cmd.exe
    *)
    copy_resources;
    (* Rules below all need restat *)
    build_bin_deps;
    build_bin_deps_dev;
    mj;
    mj_dev;
    mij;
    mi;
    mij_dev;
    mi_dev;
    build_package;
    customs =
      Map_string.mapi custom_rules (fun name command ->
          define ~command ("custom_" ^ name));
  }

end
module Bsb_ninja_targets : sig 
#1 "bsb_ninja_targets.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val output_build :
  outputs:string list ->
  inputs:string list ->
  rule:Bsb_ninja_rule.t ->
  out_channel ->
  unit
(** output should always be marked explicitly,
    otherwise the build system can not figure out clearly
    however, for the command we don't need pass `-o`
*)

val phony :
  ?order_only_deps:string list ->
  inputs:string list ->
  output:string ->
  out_channel ->
  unit

val output_finger : string -> string -> out_channel -> unit

end = struct
#1 "bsb_ninja_targets.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let oc_list xs oc =
  Ext_list.iter xs (fun s ->
      output_string oc Ext_string.single_space;
      output_string oc s)

let output_build ~outputs ~inputs ~rule oc =
  let rule = Bsb_ninja_rule.get_name rule oc in
  (* Trigger building if not used *)
  output_string oc "o";
  oc_list outputs oc;
  output_string oc " : ";
  output_string oc rule;
  oc_list inputs oc;
  output_string oc "\n"

let phony ?(order_only_deps = []) ~inputs ~output oc =
  output_string oc "o ";
  output_string oc output;
  output_string oc " : ";
  output_string oc "phony";
  oc_list inputs oc;
  if order_only_deps <> [] then (
    output_string oc " ||";
    oc_list order_only_deps oc);
  output_string oc "\n"

let output_finger key value oc =
  output_string oc key;
  output_string oc " := ";
  output_string oc value;
  output_string oc "\n"

end
module Ext_namespace_encode : sig 
#1 "ext_namespace_encode.mli"
(* Copyright (C) 2020- Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val make : ?ns:string -> string -> string
(** [make ~ns:"Ns" "a" ]
    A typical example would return "a-Ns"
    Note the namespace comes from the output of [namespace_of_package_name]
*)

end = struct
#1 "ext_namespace_encode.ml"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let make ?ns cunit =
  match ns with None -> cunit | Some ns -> cunit ^ Literals.ns_sep ^ ns

end
module Bsb_ninja_file_groups : sig 
#1 "bsb_ninja_file_groups.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val handle_files_per_dir :
  out_channel ->
  rules:Bsb_ninja_rule.builtin ->
  package_specs:Bsb_package_specs.t ->
  files_to_install:Bsb_db.module_info Queue.t ->
  namespace:string option ->
  Bsb_file_groups.file_group ->
  unit

end = struct
#1 "bsb_ninja_file_groups.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Ext_path.combine

let handle_generators oc (group : Bsb_file_groups.file_group) custom_rules =
  let map_to_source_dir x = Bsb_config.proj_rel (group.dir // x) in
  Ext_list.iter group.generators (fun { output; input; command } ->
      (*TODO: add a loc for better error message *)
      match Map_string.find_opt custom_rules command with
      | None ->
          Ext_fmt.failwithf ~loc:__LOC__ "custom rule %s used but  not defined"
            command
      | Some rule ->
          Bsb_ninja_targets.output_build oc
            ~outputs:(Ext_list.map output map_to_source_dir)
            ~inputs:(Ext_list.map input map_to_source_dir)
            ~rule)

type suffixes = { impl : string; intf : string }

let re_suffixes = { impl = Literals.suffix_re; intf = Literals.suffix_rei }

let ml_suffixes = { impl = Literals.suffix_ml; intf = Literals.suffix_mli }

let res_suffixes = { impl = Literals.suffix_res; intf = Literals.suffix_resi }

let emit_module_build (rules : Bsb_ninja_rule.builtin)
    (package_specs : Bsb_package_specs.t) (is_dev : bool) oc namespace
    (module_info : Bsb_db.module_info) : unit =
  let has_intf_file = module_info.info = Impl_intf in
  let config, ast_rule =
    match module_info.syntax_kind with
    | Reason -> (re_suffixes, rules.build_ast_from_re)
    | Ml -> (ml_suffixes, rules.build_ast)
    | Res -> (res_suffixes, rules.build_ast_from_re)
    (* FIXME: better names *)
  in
  let filename_sans_extension = module_info.name_sans_extension in
  let input_impl =
    Bsb_config.proj_rel (filename_sans_extension ^ config.impl)
  in
  let input_intf =
    Bsb_config.proj_rel (filename_sans_extension ^ config.intf)
  in
  let output_ast = filename_sans_extension ^ Literals.suffix_ast in
  let output_iast = filename_sans_extension ^ Literals.suffix_iast in
  let output_d = filename_sans_extension ^ Literals.suffix_d in
  let output_filename_sans_extension =
    Ext_namespace_encode.make ?ns:namespace filename_sans_extension
  in
  let output_cmi = output_filename_sans_extension ^ Literals.suffix_cmi in
  let output_cmj = output_filename_sans_extension ^ Literals.suffix_cmj in
  let output_js =
    Bsb_package_specs.get_list_of_output_js package_specs
      output_filename_sans_extension
  in

  Bsb_ninja_targets.output_build oc ~outputs:[ output_ast ]
    ~inputs:[ input_impl ] ~rule:ast_rule;
  Bsb_ninja_targets.output_build oc ~outputs:[ output_d ]
    ~inputs:
      (if has_intf_file then [ output_ast; output_iast ] else [ output_ast ])
    ~rule:(if is_dev then rules.build_bin_deps_dev else rules.build_bin_deps);
  if has_intf_file then (
    Bsb_ninja_targets.output_build oc
      ~outputs:
        [ output_iast ]
        (* TODO: we can get rid of absloute path if we fixed the location to be
            [lib/bs], better for testing?
        *)
      ~inputs:[ input_intf ] ~rule:ast_rule;
    Bsb_ninja_targets.output_build oc ~outputs:[ output_cmi ]
      ~inputs:[ output_iast ]
      ~rule:(if is_dev then rules.mi_dev else rules.mi));
  let rule =
    if has_intf_file then if is_dev then rules.mj_dev else rules.mj
    else if is_dev then rules.mij_dev
    else rules.mij
  in
  Bsb_ninja_targets.output_build oc
    ~outputs:
      (if has_intf_file then output_cmj :: output_js
      else output_cmj :: output_cmi :: output_js)
    ~inputs:
      (if has_intf_file then [ output_ast; output_cmi ] else [ output_ast ])
    ~rule

let handle_files_per_dir oc ~(rules : Bsb_ninja_rule.builtin) ~package_specs
    ~files_to_install ~(namespace : string option)
    (group : Bsb_file_groups.file_group) : unit =
  let is_dev = group.is_dev in
  handle_generators oc group rules.customs;
  let installable =
    match group.public with
    | Export_all -> fun _ -> true
    | Export_none -> fun _ -> false
    | Export_set set -> fun module_name -> Set_string.mem set module_name
  in
  Map_string.iter group.sources (fun module_name module_info ->
      if installable module_name && not is_dev then
        Queue.add module_info files_to_install;
      emit_module_build rules package_specs is_dev oc namespace module_info)

(* ;
   Bsb_ninja_targets.phony
   oc ~order_only_deps:[] ~inputs:[] ~output:group.dir *)

(* pseuduo targets per directory *)

end
module Bsb_ninja_gen : sig 
#1 "bsb_ninja_gen.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val output_ninja_and_namespace_map :
  per_proj_dir:string ->
  package_kind:Bsb_package_kind.t ->
  Bsb_config_types.t ->
  unit
(** 
   generate ninja file based on [cwd] 
*)

end = struct
#1 "bsb_ninja_gen.ml"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Ext_path.combine

(* we need copy package.json into [_build] since it does affect build output
   it is a bad idea to copy package.json which requires to copy js files
*)

(* let dash_i = "-I" *)

let get_bsc_flags (bsc_flags : string list) : string =
  String.concat Ext_string.single_space bsc_flags

let emit_bsc_lib_includes (bs_dependencies : Bsb_config_types.dependencies)
    (source_dirs : string list) external_includes (namespace : _ option) :
    string =
  (* TODO: bsc_flags contain stdlib path which is in the latter position currently *)
  let all_includes source_dirs =
    source_dirs
    @ Ext_list.map bs_dependencies (fun x -> x.package_install_path)
    @ (* for external includes, if it is absolute path, leave it as is
         for relative path './xx', we need '../.././x' since we are in
         [lib/bs], [build] is different from merlin though
      *)
    Ext_list.map external_includes (fun x ->
        if Filename.is_relative x then Bsb_config.rev_lib_bs_prefix x else x)
  in

  Bsb_build_util.include_dirs
    (all_includes
       (if namespace = None then source_dirs
       else Filename.current_dir_name :: source_dirs
         (*working dir is [lib/bs] we include this path to have namespace mapping*)))

let output_static_resources (static_resources : string list) copy_rule oc =
  Ext_list.iter static_resources (fun output ->
      Bsb_ninja_targets.output_build oc ~outputs:[ output ]
        ~inputs:[ Bsb_config.proj_rel output ]
        ~rule:copy_rule);
  if static_resources <> [] then
    Bsb_ninja_targets.phony oc ~order_only_deps:static_resources ~inputs:[]
      ~output:Literals.build_ninja

(*
  FIXME: check if the trick still works
  phony build.ninja : | resources 
*)
let mark_rescript oc = output_string oc "rescript = 1\n"

let output_installation_file cwd_lib_bs namespace files_to_install =
  let install_oc = open_out_bin (cwd_lib_bs // "install.ninja") in
  mark_rescript install_oc;
  let o s = output_string install_oc s in
  let[@inline] oo suffix ~dest ~src =
    o "o ";
    o dest;
    o suffix;
    o " : cp ";
    o src;
    o suffix;
    o "\n"
  in
  let bs = ".." // "bs" in
  let sb = ".." // ".." in
  o
    (if Ext_sys.is_windows_or_cygwin then
     "rule cp\n\
     \  command = cmd.exe /C copy /Y $i $out >NUL\n\
      rule touch\n\
     \ command = cmd.exe /C type nul >>$out & copy $out+,, >NUL\n"
    else "rule cp\n  command = cp $i $out\nrule touch\n command = touch $out\n");
  let essentials = Ext_buffer.create 1_000 in
  files_to_install
  |> Queue.iter
       (fun ({ name_sans_extension; syntax_kind; info } : Bsb_db.module_info) ->
         let base = Filename.basename name_sans_extension in
         let dest = Ext_namespace_encode.make ?ns:namespace base in
         let ns_origin =
           Ext_namespace_encode.make ?ns:namespace name_sans_extension
         in
         let src = bs // ns_origin in
         oo Literals.suffix_cmi ~dest ~src;
         oo Literals.suffix_cmj ~dest ~src;
         oo Literals.suffix_cmt ~dest ~src;

         Ext_buffer.add_string essentials dest;
         Ext_buffer.add_string_char essentials Literals.suffix_cmi ' ';
         Ext_buffer.add_string essentials dest;
         Ext_buffer.add_string_char essentials Literals.suffix_cmj ' ';

         let suffix =
           match syntax_kind with
           | Ml -> Literals.suffix_ml
           | Reason -> Literals.suffix_re
           | Res -> Literals.suffix_res
         in
         oo suffix ~dest:base ~src:(sb // name_sans_extension);
         match info with
         | Intf -> assert false
         | Impl -> ()
         | Impl_intf ->
             let suffix_b =
               match syntax_kind with
               | Ml -> Literals.suffix_mli
               | Reason -> Literals.suffix_rei
               | Res -> Literals.suffix_resi
             in
             oo suffix_b ~dest:base ~src:(sb // name_sans_extension);
             oo Literals.suffix_cmti ~dest ~src);
  (match namespace with
  | None -> ()
  | Some dest ->
      let src = bs // dest in
      oo Literals.suffix_cmi ~dest ~src;
      oo Literals.suffix_cmj ~dest ~src;
      oo Literals.suffix_cmt ~dest ~src;
      Ext_buffer.add_string essentials dest;
      Ext_buffer.add_string_char essentials Literals.suffix_cmi ' ';
      Ext_buffer.add_string essentials dest;
      Ext_buffer.add_string essentials Literals.suffix_cmj);
  Ext_buffer.add_char essentials '\n';
  o "build install.stamp : touch ";
  Ext_buffer.output_buffer install_oc essentials;
  close_out install_oc

let output_ninja_and_namespace_map ~per_proj_dir ~package_kind
    ({
       package_name;
       external_includes;
       bsc_flags;
       pp_file;
       ppx_files;
       bs_dependencies;
       bs_dev_dependencies;
       refmt;
       js_post_build_cmd;
       package_specs;
       file_groups = { files = bs_file_groups };
       files_to_install;
       built_in_dependency;
       reason_react_jsx;
       jsx;
       generators;
       namespace;
       warning;
       gentype_config;
     } :
      Bsb_config_types.t) : unit =
  let lib_artifacts_dir = Bsb_config.lib_bs in
  let cwd_lib_bs = per_proj_dir // lib_artifacts_dir in

  let warnings = Bsb_warning.to_bsb_string ~package_kind warning in
  let bsc_flags = get_bsc_flags bsc_flags in
  let dpkg_incls =
    Bsb_build_util.include_dirs_by bs_dev_dependencies (fun x ->
        x.package_install_path)
  in
  let bs_groups : Bsb_db.t =
    { lib = Map_string.empty; dev = Map_string.empty }
  in
  let source_dirs : string list Bsb_db.cat = { lib = []; dev = [] } in
  let static_resources =
    Ext_list.fold_left bs_file_groups []
      (fun (acc_resources : string list) { sources; dir; resources; is_dev } ->
        if is_dev then (
          bs_groups.dev <- Bsb_db_util.merge bs_groups.dev sources;
          source_dirs.dev <- dir :: source_dirs.dev)
        else (
          bs_groups.lib <- Bsb_db_util.merge bs_groups.lib sources;
          source_dirs.lib <- dir :: source_dirs.lib);
        Ext_list.map_append resources acc_resources (fun x -> dir // x))
  in
  let lib = bs_groups.lib in
  let dev = bs_groups.dev in
  Bsb_db_util.sanity_check lib;
  Bsb_db_util.sanity_check dev;
  Map_string.iter dev (fun k a ->
      if Map_string.mem lib k then
        raise (Bsb_db_util.conflict_module_info k a (Map_string.find_exn lib k)));
  let dev_incls = Bsb_build_util.include_dirs source_dirs.dev in
  let digest = Bsb_db_encode.write_build_cache ~dir:cwd_lib_bs bs_groups in
  let lib_incls =
    emit_bsc_lib_includes bs_dependencies source_dirs.lib external_includes
      namespace
  in
  let rules : Bsb_ninja_rule.builtin =
    Bsb_ninja_rule.make_custom_rules ~refmt ~gentype_config
      ~has_postbuild:js_post_build_cmd ~pp_file ~has_builtin:built_in_dependency
      ~reason_react_jsx ~jsx ~package_specs ~namespace ~digest ~package_name
      ~warnings ~ppx_files ~bsc_flags ~dpkg_incls (* dev dependencies *)
      ~lib_incls (* its own libs *)
      ~dev_incls (* its own devs *)
      ~bs_dependencies ~bs_dev_dependencies generators
  in

  let oc = open_out_bin (cwd_lib_bs // Literals.build_ninja) in
  mark_rescript oc;
  let finger_file (x : Bsb_config_types.dependency) =
    x.package_install_path // "install.stamp"
  in
  Ext_list.iter bs_dependencies (fun x ->
      Bsb_ninja_targets.output_finger Bsb_ninja_global_vars.g_finger
        (finger_file x) oc);
  Ext_list.iter bs_dev_dependencies (fun x ->
      Bsb_ninja_targets.output_finger Bsb_ninja_global_vars.g_finger
        (finger_file x) oc);
  output_static_resources static_resources rules.copy_resources oc;
  (* Generate build statement for each file *)
  Ext_list.iter bs_file_groups (fun files_per_dir ->
      Bsb_ninja_file_groups.handle_files_per_dir oc ~rules ~package_specs
        ~files_to_install ~namespace files_per_dir);
  Ext_option.iter namespace (fun ns ->
      let namespace_dir = per_proj_dir // lib_artifacts_dir in
      Bsb_namespace_map_gen.output ~dir:namespace_dir ns bs_file_groups;
      Bsb_ninja_targets.output_build oc
        ~outputs:[ ns ^ Literals.suffix_cmi ]
        ~inputs:[ ns ^ Literals.suffix_mlmap ]
        ~rule:rules.build_package);
  close_out oc;
  output_installation_file cwd_lib_bs namespace files_to_install

end
module Ext_json_noloc : sig 
#1 "ext_json_noloc.mli"
(* Copyright (C) 2017- Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type t = private
  | True
  | False
  | Null
  | Flo of string
  | Str of string
  | Arr of t array
  | Obj of t Map_string.t

val true_ : t

val false_ : t

val null : t

val str : string -> t

val flo : string -> t

val arr : t array -> t

val obj : t Map_string.t -> t

val kvs : (string * t) list -> t

val to_string : t -> string

val to_channel : out_channel -> t -> unit

val to_file : string -> t -> unit

end = struct
#1 "ext_json_noloc.ml"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(* This file is only used in bsb watcher searlization *)
type t =
  | True
  | False
  | Null
  | Flo of string
  | Str of string
  | Arr of t array
  | Obj of t Map_string.t

(** poor man's serialization *)
let naive_escaped (unmodified_input : string) : string =
  let n = ref 0 in
  let len = String.length unmodified_input in
  for i = 0 to len - 1 do
    n :=
      !n
      +
      match String.unsafe_get unmodified_input i with
      | '\"' | '\\' | '\n' | '\t' | '\r' | '\b' -> 2
      | _ -> 1
  done;
  if !n = len then unmodified_input
  else
    let result = Bytes.create !n in
    n := 0;
    for i = 0 to len - 1 do
      let open Bytes in
      (match String.unsafe_get unmodified_input i with
      | ('\"' | '\\') as c ->
          unsafe_set result !n '\\';
          incr n;
          unsafe_set result !n c
      | '\n' ->
          unsafe_set result !n '\\';
          incr n;
          unsafe_set result !n 'n'
      | '\t' ->
          unsafe_set result !n '\\';
          incr n;
          unsafe_set result !n 't'
      | '\r' ->
          unsafe_set result !n '\\';
          incr n;
          unsafe_set result !n 'r'
      | '\b' ->
          unsafe_set result !n '\\';
          incr n;
          unsafe_set result !n 'b'
      | c -> unsafe_set result !n c);
      incr n
    done;
    Bytes.unsafe_to_string result

let quot x = "\"" ^ naive_escaped x ^ "\""

let true_ = True

let false_ = False

let null = Null

let str s = Str s

let flo s = Flo s

let arr s = Arr s

let obj s = Obj s

let kvs s = Obj (Map_string.of_list s)

let rec encode_buf (x : t) (buf : Buffer.t) : unit =
  let a str = Buffer.add_string buf str in
  match x with
  | Null -> a "null"
  | Str s -> a (quot s)
  | Flo s ->
      a s
      (*
         since our parsing keep the original float representation, we just dump it as is, there is no cases like [nan] *)
  | Arr content -> (
      match content with
      | [||] -> a "[]"
      | _ ->
          a "[ ";
          encode_buf (Array.unsafe_get content 0) buf;
          for i = 1 to Array.length content - 1 do
            a " , ";
            encode_buf (Array.unsafe_get content i) buf
          done;
          a " ]")
  | True -> a "true"
  | False -> a "false"
  | Obj map ->
      if Map_string.is_empty map then a "{}"
      else (
        (*prerr_endline "WEIRD";
          prerr_endline (string_of_int @@ Map_string.cardinal map ); *)
        a "{ ";
        let (_ : int) =
          Map_string.fold map 0 (fun k v i ->
              if i <> 0 then a " , ";
              a (quot k);
              a " : ";
              encode_buf v buf;
              i + 1)
        in
        a " }")

let to_string x =
  let buf = Buffer.create 1024 in
  encode_buf x buf;
  Buffer.contents buf

let to_channel (oc : out_channel) x =
  let buf = Buffer.create 1024 in
  encode_buf x buf;
  Buffer.output_buffer oc buf

let to_file name v =
  let ochan = open_out_bin name in
  to_channel ochan v;
  close_out ochan

end
module Bsb_watcher_gen : sig 
#1 "bsb_watcher_gen.mli"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val generate_sourcedirs_meta : name:string -> Bsb_file_groups.t -> unit
(** This module try to generate some meta data so that
    everytime [bsconfig.json] is reload, we can re-read
    such meta data changes in the watcher.

    Another way of doing it is processing [bsconfig.json] 
    directly in [watcher] but that would 
    mean the duplication of logic in [bsb] and [bsb_watcher]
*)

end = struct
#1 "bsb_watcher_gen.ml"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let kvs = Ext_json_noloc.kvs

let arr = Ext_json_noloc.arr

let str = Ext_json_noloc.str

let generate_sourcedirs_meta ~name (res : Bsb_file_groups.t) =
  let v =
    kvs
      [
        ("dirs", arr (Ext_array.of_list_map res.files (fun x -> str x.dir)));
        ( "generated",
          arr
            (Array.of_list
            @@ Ext_list.fold_left res.files [] (fun acc x ->
                   Ext_list.flat_map_append x.generators acc (fun x ->
                       Ext_list.map x.output str))) );
        ( "pkgs",
          arr
            (Array.of_list
               (Bsb_pkg.to_list (fun pkg path ->
                    arr [| str (Bsb_pkg_types.to_string pkg); str path |]))) );
      ]
  in
  Ext_json_noloc.to_file name v

end
module Bsb_ninja_regen : sig 
#1 "bsb_ninja_regen.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val regenerate_ninja :
  package_kind:Bsb_package_kind.t ->
  forced:bool ->
  per_proj_dir:string ->
  Bsb_config_types.t option
(** Regenerate ninja file by need based on [.bsdeps]
    return None if we dont need regenerate
    otherwise return Some info
*)

end = struct
#1 "bsb_ninja_regen.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let bsdeps = ".bsdeps"

let ( // ) = Ext_path.combine

(** Regenerate ninja file by need based on [.bsdeps]
    return None if we dont need regenerate
    otherwise return Some info
*)
let regenerate_ninja ~(package_kind : Bsb_package_kind.t) ~forced ~per_proj_dir
    : Bsb_config_types.t option =
  let lib_artifacts_dir = Bsb_config.lib_bs in
  let lib_bs_dir = per_proj_dir // lib_artifacts_dir in
  let output_deps = lib_bs_dir // bsdeps in
  let check_result =
    Bsb_ninja_check.check ~package_kind ~per_proj_dir ~forced ~file:output_deps
  in
  match check_result with
  | Good -> None (* Fast path, no need regenerate ninja *)
  | Bsb_forced | Bsb_bsc_version_mismatch | Bsb_package_kind_inconsistent
  | Bsb_file_corrupted | Bsb_file_not_exist | Bsb_source_directory_changed
  | Other _ ->
      Bsb_log.info "@{<info>BSB check@} build spec : %a @."
        Bsb_ninja_check.pp_check_result check_result;
      if check_result = Bsb_bsc_version_mismatch then (
        Bsb_log.warn
          "@{<info>Different compiler version@}: clean current repo@.";
        Bsb_clean.clean_bs_deps per_proj_dir;
        Bsb_clean.clean_self per_proj_dir);

      let config : Bsb_config_types.t =
        Bsb_config_parse.interpret_json ~package_kind ~per_proj_dir
      in
      (* create directory, lib/bs, lib/js, lib/es6 etc *)
      Bsb_build_util.mkp lib_bs_dir;
      Bsb_package_specs.list_dirs_by config.package_specs (fun x ->
          let dir = per_proj_dir // x in
          (*Unix.EEXIST error*)
          if not (Sys.file_exists dir) then Unix.mkdir dir 0o777);
      (match package_kind with
      | Toplevel ->
          Bsb_watcher_gen.generate_sourcedirs_meta
            ~name:(lib_bs_dir // Literals.sourcedirs_meta)
            config.file_groups
      | Pinned_dependency _ (* FIXME: seems need to be watched *) | Dependency _
        ->
          ());

      Bsb_ninja_gen.output_ninja_and_namespace_map ~per_proj_dir ~package_kind
        config;
      (* PR2184: we still need record empty dir
          since it may add files in the future *)
      Bsb_ninja_check.record ~package_kind ~per_proj_dir ~config
        ~file:output_deps
        (Literals.bsconfig_json :: config.file_groups.globbed_dirs);
      Some config

end
module Bsb_regex : sig 
#1 "bsb_regex.mli"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val global_substitute :
  string -> reg:string -> (string -> string list -> string) -> string
(** Used in `bsb -init` command *)

end = struct
#1 "bsb_regex.ml"
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let string_after s n = String.sub s n (String.length s - n)

(* There seems to be a bug in {!Str.global_substitute}
   {[
     Str.global_substitute (Str.regexp "\\${rescript:\\([-a-zA-Z0-9]+\\)}") (fun x -> (x^":found")) {|   ${rescript:hello-world}  ${rescript:x} ${x}|}  ;;
     - : bytes =
     "      ${rescript:hello-world}  ${rescript:x} ${x}:found     ${rescript:hello-world}  ${rescript:x} ${x}:found ${x}"
   ]}
*)
let global_substitute text ~reg:expr repl_fun =
  let text_len = String.length text in
  let expr = Str.regexp expr in
  let rec replace accu start last_was_empty =
    let startpos = if last_was_empty then start + 1 else start in
    if startpos > text_len then string_after text start :: accu
    else
      match Str.search_forward expr text startpos with
      | exception Not_found -> string_after text start :: accu
      | pos ->
          let end_pos = Str.match_end () in
          let matched = Str.matched_string text in
          let groups =
            let rec aux n acc =
              match Str.matched_group n text with
              | exception (Not_found | Invalid_argument _) -> acc
              | v -> aux (succ n) (v :: acc)
            in
            aux 1 []
          in
          let repl_text = repl_fun matched groups in
          replace
            (repl_text :: String.sub text start (pos - start) :: accu)
            end_pos (end_pos = pos)
  in
  String.concat "" (List.rev (replace [] 0 false))

end
module Bsb_templates : sig 
#1 "bsb_templates.mli"
type  node = 
  | Dir of string *  node list 
  | File of string *  string


val root : node list 
end = struct
#1 "bsb_templates.ml"

type  node = 
  | Dir of string *  node list 
  | File of string *  string  
let root = ([
   Dir("basic",[
    File(".gitignore",
    "*.exe\n\
    *.obj\n\
    *.out\n\
    *.compile\n\
    *.native\n\
    *.byte\n\
    *.cmo\n\
    *.annot\n\
    *.cmi\n\
    *.cmx\n\
    *.cmt\n\
    *.cmti\n\
    *.cma\n\
    *.a\n\
    *.cmxa\n\
    *.obj\n\
    *~\n\
    *.annot\n\
    *.cmj\n\
    *.bak\n\
    lib/bs\n\
    *.mlast\n\
    *.mliast\n\
    .vscode\n\
    .merlin\n\
    .bsb.lock\n\
    /node_modules/\n\
    "
    );
    File("README.md",
    "\n\
    \n\
    # Build\n\
    ```\n\
    npm run build\n\
    ```\n\
    \n\
    # Watch\n\
    \n\
    ```\n\
    npm run watch\n\
    ```\n\
    \n\
    "
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"sources\": {\n\
    \    \"dir\" : \"src\",\n\
    \    \"subdirs\" : true\n\
    \  },\n\
    \  \"package-specs\": {\n\
    \    \"module\": \"es6\",\n\
    \    \"in-source\": true\n\
    \  },\n\
    \  \"suffix\": \".bs.js\"\n\
    }\n\
    "
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"clean\": \"rescript clean\",\n\
    \    \"build\": \"rescript build\",\n\
    \    \"watch\": \"rescript build -w\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\"\n\
    \  }\n\
    }"
    );
    Dir("src",[
     File("Demo.res",
     "\n\
     Js.log(\"Hello, ReScript\")"
     )        
    ])        
   ]);
   Dir("basic-reason",[
    File(".gitignore",
    ".DS_Store\n\
    .merlin\n\
    .bsb.lock\n\
    npm-debug.log\n\
    /lib/bs/\n\
    /node_modules/\n\
    "
    );
    File("README.md",
    "# Basic Reason Template\n\
    \n\
    Hello! This project allows you to quickly get started with ReScript using Reason syntax. If you wanted a more sophisticated version, try the `react` template (`bsb -theme react -init .`).\n\
    \n\
    # Build\n\
    \n\
    ```bash\n\
    # for yarn\n\
    yarn build\n\
    \n\
    # for npm\n\
    npm run build\n\
    ```\n\
    \n\
    # Build + Watch\n\
    \n\
    ```bash\n\
    # for yarn\n\
    yarn start\n\
    \n\
    # for npm\n\
    npm run start\n\
    ```\n\
    \n\
    "
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"sources\": {\n\
    \    \"dir\" : \"src\",\n\
    \    \"subdirs\" : true\n\
    \  },\n\
    \  \"package-specs\": {\n\
    \    \"module\": \"commonjs\",\n\
    \    \"in-source\": true\n\
    \  },\n\
    \  \"suffix\": \".bs.js\",\n\
    \  \"bs-dependencies\": [\n\
    \n\
    \  ],\n\
    \  \"warnings\": {\n\
    \    \"error\" : \"+101\"\n\
    \  },\n\
    \  \"namespace\": true,\n\
    \  \"refmt\": 3\n\
    }\n\
    "
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"start\": \"bsb -make-world -w\",\n\
    \    \"clean\": \"bsb -clean-world\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     File("Demo.re",
     "Js.log(\"Hello, ReScript!\");\n\
     "
     )        
    ])        
   ]);
   Dir("generator",[
    File(".gitignore",
    "*.exe\n\
    *.obj\n\
    *.out\n\
    *.compile\n\
    *.native\n\
    *.byte\n\
    *.cmo\n\
    *.annot\n\
    *.cmi\n\
    *.cmx\n\
    *.cmt\n\
    *.cmti\n\
    *.cma\n\
    *.a\n\
    *.cmxa\n\
    *.obj\n\
    *~\n\
    *.annot\n\
    *.cmj\n\
    *.bak\n\
    lib/bs\n\
    *.mlast\n\
    *.mliast\n\
    .vscode\n\
    .merlin\n\
    .bsb.lock\n\
    /node_modules/\n\
    "
    );
    File("README.md",
    "\n\
    \n\
    # Build\n\
    ```\n\
    npm run build\n\
    ```\n\
    \n\
    # Watch\n\
    \n\
    ```\n\
    npm run watch\n\
    ```\n\
    \n\
    \n\
    # Editor\n\
    If you use `vscode`, Press `Windows + Shift + B` it will build automatically"
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"sources\": {\n\
    \    \"dir\": \"src\",\n\
    \    \"generators\": [{\n\
    \      \"name\": \"cpp\",\n\
    \      \"edge\": [\"test.ml\", \":\", \"test.cpp.ml\"]\n\
    \    }],\n\
    \    \"subdirs\": true  \n\
    \  },\n\
    \  \"generators\": [{\n\
    \    \"name\" : \"cpp\",\n\
    \    \"command\": \"sed 's/OCAML/3/' $in > $out\"\n\
    \  }],\n\
    \  \"bs-dependencies\" : [\n\
    \  ]\n\
    }"
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"clean\": \"bsb -clean-world\",\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"watch\": \"bsb -make-world -w\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     File("demo.ml",
     "\n\
     \n\
     let () = Js.log \"Hello, ReScript\""
     );
     File("test.cpp.ml",
     "\n\
     (* \n\
     #define FS_VAL(name,ty) external name : ty = \"\" [@@bs.module \"fs\"]\n\
     \n\
     \n\
     FS_VAL(readdirSync, string -> string array)\n\
     \ *)\n\
     \n\
     \n\
     \ let ocaml = OCAML"
     )        
    ])        
   ]);
   Dir("minimal",[
    File(".gitignore",
    ".DS_Store\n\
    .merlin\n\
    .bsb.lock\n\
    npm-debug.log\n\
    /lib/bs/\n\
    /node_modules/"
    );
    File("README.md",
    "\n\
    \  # ${rescript:name}"
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"sources\": {\n\
    \      \"dir\": \"src\",\n\
    \      \"subdirs\": true\n\
    \  }\n\
    }"
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"clean\": \"bsb -clean-world\",\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"start\": \"bsb -make-world -w\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     File("main.ml",
     ""
     )        
    ])        
   ]);
   Dir("node",[
    File(".gitignore",
    "*.exe\n\
    *.obj\n\
    *.out\n\
    *.compile\n\
    *.native\n\
    *.byte\n\
    *.cmo\n\
    *.annot\n\
    *.cmi\n\
    *.cmx\n\
    *.cmt\n\
    *.cmti\n\
    *.cma\n\
    *.a\n\
    *.cmxa\n\
    *.obj\n\
    *~\n\
    *.annot\n\
    *.cmj\n\
    *.bak\n\
    lib/bs\n\
    *.mlast\n\
    *.mliast\n\
    .vscode\n\
    .merlin\n\
    .bsb.lock\n\
    /node_modules/\n\
    "
    );
    File("README.md",
    "\n\
    \n\
    # Build\n\
    ```\n\
    npm run build\n\
    ```\n\
    \n\
    # Watch\n\
    \n\
    ```\n\
    npm run watch\n\
    ```\n\
    \n\
    \n\
    # Editor\n\
    If you use `vscode`, Press `Windows + Shift + B` it will build automatically\n\
    "
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"sources\": {\n\
    \      \"dir\": \"src\",\n\
    \      \"subdirs\" : true\n\
    \  },\n\
    \  \"package-specs\": {\n\
    \    \"module\": \"commonjs\",\n\
    \    \"in-source\": true\n\
    \  },\n\
    \  \"suffix\": \".bs.js\",\n\
    \  \"bs-dependencies\": [\n\
    \   ]\n\
    }"
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"clean\": \"bsb -clean-world\",\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"watch\": \"bsb -make-world -w\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     File("demo.ml",
     "\n\
     \n\
     let () = Js.log \"Hello, ReScript\""
     )        
    ])        
   ]);
   Dir("react-hooks",[
    File(".gitignore",
    ".DS_Store\n\
    .merlin\n\
    .bsb.lock\n\
    npm-debug.log\n\
    /lib/bs/\n\
    /node_modules/\n\
    /bundleOutput/"
    );
    File("README.md",
    "# ReasonReact Template & Examples\n\
    \n\
    This is:\n\
    - A template for your new ReasonReact project.\n\
    - A collection of thin examples illustrating ReasonReact usage.\n\
    - Extra helper documentation for ReasonReact (full ReasonReact docs [here](https://reasonml.github.io/reason-react/)).\n\
    \n\
    `src` contains 4 sub-folders, each an independent, self-contained ReasonReact example. Feel free to delete any of them and shape this into your project! This template's more malleable than you might be used to =).\n\
    \n\
    The point of this template and examples is to let you understand and personally tweak the entirely of it. We **don't** give you an opaque, elaborate mega build setup just to put some boxes on the screen. It strikes to stay transparent, learnable, and simple. You're encouraged to read every file; it's a great feeling, having the full picture of what you're using and being able to touch any part.\n\
    \n\
    ## Run\n\
    \n\
    ```sh\n\
    npm install\n\
    npm run server\n\
    # in a new tab\n\
    npm start\n\
    ```\n\
    \n\
    Open a new web page to `http://localhost:8000/`. Change any `.re` file in `src` to see the page auto-reload. **You don't need any bundler when you're developing**!\n\
    \n\
    **How come we don't need any bundler during development**? We highly encourage you to open up `index.html` to check for yourself!\n\
    \n\
    # Features Used\n\
    \n\
    |                           | Blinking Greeting | Reducer from ReactJS Docs | Fetch Dog Pictures | Reason Using JS Using Reason |\n\
    |---------------------------|-------------------|---------------------------|--------------------|------------------------------|\n\
    | No props                  |                   | ✓                         |                    |                              |\n\
    | Has props                 |                   |                           |                    | ✓                            |\n\
    | Children props            | ✓                 |                           |                    |                              |\n\
    | No state                  |                   |                           |                    | ✓                            |\n\
    | Has state                 | ✓                 |                           |  ✓                 |                              |\n\
    | Has state with useReducer |                   | ✓                         |                    |                              |\n\
    | ReasonReact using ReactJS |                   |                           |                    | ✓                            |\n\
    | ReactJS using ReasonReact |                   |                           |                    | ✓                            |\n\
    | useEffect                 | ✓                 |                           |  ✓                 |                              |\n\
    | Dom attribute             | ✓                 | ✓                         |                    | ✓                            |\n\
    | Styling                   | ✓                 | ✓                         |  ✓                 | ✓                            |\n\
    | React.array               |                   |                           |  ✓                 |                              |\n\
    \n\
    # Bundle for Production\n\
    \n\
    We've included a convenience `UNUSED_webpack.config.js`, in case you want to ship your project to production. You can rename and/or remove that in favor of other bundlers, e.g. Rollup.\n\
    \n\
    We've also provided a barebone `indexProduction.html`, to serve your bundle.\n\
    \n\
    ```sh\n\
    npm install webpack webpack-cli\n\
    # rename file\n\
    mv UNUSED_webpack.config.js webpack.config.js\n\
    # call webpack to bundle for production\n\
    ./node_modules/.bin/webpack\n\
    open indexProduction.html\n\
    ```\n\
    \n\
    # Handle Routing Yourself\n\
    \n\
    To serve the files, this template uses a minimal dependency called `moduleserve`. A URL such as `localhost:8000/scores/john` resolves to the file `scores/john.html`. If you'd like to override this and handle URL resolution yourself, change the `server` command in `package.json` from `moduleserve ./ --port 8000` to `moduleserve ./ --port 8000 --spa` (for \"single page application\"). This will make `moduleserve` serve the default `index.html` for any URL. Since `index.html` loads `Index.bs.js`, you can grab hold of the URL in the corresponding `Index.re` and do whatever you want.\n\
    \n\
    By the way, ReasonReact comes with a small [router](https://reasonml.github.io/reason-react/docs/en/router) you might be interested in.\n\
    "
    );
    File("UNUSED_webpack.config.js",
    "const path = require('path');\n\
    \n\
    module.exports = {\n\
    \  entry: './src/Index.bs.js',\n\
    \  // If you ever want to use webpack during development, change 'production'\n\
    \  // to 'development' as per webpack documentation. Again, you don't have to\n\
    \  // use webpack or any other bundler during development! Recheck README if\n\
    \  // you didn't know this\n\
    \  mode: 'production',\n\
    \  output: {\n\
    \    path: path.join(__dirname, \"bundleOutput\"),\n\
    \    filename: 'index.js',\n\
    \  },\n\
    };"
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"reason\": {\n\
    \    \"react-jsx\": 3\n\
    \  },\n\
    \  \"sources\": {\n\
    \    \"dir\" : \"src\",\n\
    \    \"subdirs\" : true\n\
    \  },\n\
    \  \"bsc-flags\": [\"-bs-super-errors\", \"-bs-no-version-header\"],\n\
    \  \"package-specs\": [{\n\
    \    \"module\": \"commonjs\",\n\
    \    \"in-source\": true\n\
    \  }],\n\
    \  \"suffix\": \".bs.js\",\n\
    \  \"namespace\": true,\n\
    \  \"bs-dependencies\": [\n\
    \    \"reason-react\"\n\
    \  ],\n\
    \  \"refmt\": 3\n\
    }\n\
    "
    );
    File("index.html",
    "<!DOCTYPE html>\n\
    <html lang=\"en\">\n\
    <head>\n\
    \  <meta charset=\"UTF-8\">\n\
    \  <title>ReasonReact Examples</title>\n\
    </head>\n\
    <body>\n\
    \  <script>\n\
    \    // stub a variable ReactJS checks. ReactJS assumes you're using a bundler, NodeJS or similar system that provides it the `process.env.NODE_ENV` variable.\n\
    \    window.process = {\n\
    \      env: {\n\
    \        NODE_ENV: 'development'\n\
    \      }\n\
    \    };\n\
    \  </script>\n\
    \n\
    \  <!-- This is https://github.com/marijnh/moduleserve, the secret sauce that allows us not need to bundle things during development, and have instantaneous iteration feedback, without any hot-reloading or extra build pipeline needed. -->\n\
    \  <script src=\"/moduleserve/load.js\" data-module=\"/src/Index.bs.js\"></script>\n\
    \  <!-- Our little watcher. Super clean. Check it out! -->\n\
    \  <script src=\"/watcher.js\"></script>\n\
    </body>\n\
    </html>\n\
    "
    );
    File("indexProduction.html",
    "<!DOCTYPE html>\n\
    <html lang=\"en\">\n\
    <head>\n\
    \  <meta charset=\"UTF-8\">\n\
    \  <title>ReasonReact Examples</title>\n\
    </head>\n\
    <body>\n\
    \  <script src=\"./bundleOutput/index.js\"></script>\n\
    </body>\n\
    </html>\n\
    "
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"start\": \"bsb -make-world -w -ws _ \",\n\
    \    \"clean\": \"bsb -clean-world\",\n\
    \    \"server\": \"moduleserve ./ --port 8000\",\n\
    \    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\",\n\
    \    \"ReasonReact\",\n\
    \    \"reason-react\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"dependencies\": {\n\
    \    \"react\": \"^16.8.1\",\n\
    \    \"react-dom\": \"^16.8.1\",\n\
    \    \"reason-react\": \">=0.7.1\"\n\
    \  },\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\",\n\
    \    \"moduleserve\": \"^0.9.0\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     Dir("BlinkingGreeting",[
      File("BlinkingGreeting.re",
      "[@react.component]\n\
      let make = (~children) => {\n\
      \  let (show, setShow) = React.useState(() => true);\n\
      \n\
      \  // Notice that instead of `useEffect`, we have `useEffect0`. See\n\
      \  // reasonml.github.io/reason-react/docs/en/components#hooks for more info\n\
      \  React.useEffect0(() => {\n\
      \    let id =\n\
      \      Js.Global.setInterval(\n\
      \        () => setShow(previousShow => !previousShow),\n\
      \        1000,\n\
      \      );\n\
      \n\
      \    Some(() => Js.Global.clearInterval(id));\n\
      \  });\n\
      \n\
      \  let style =\n\
      \    if (show) {\n\
      \      ReactDOMRe.Style.make(~opacity=\"1\", ~transition=\"opacity 1s\", ());\n\
      \    } else {\n\
      \      ReactDOMRe.Style.make(~opacity=\"0\", ~transition=\"opacity 1s\", ());\n\
      \    };\n\
      \n\
      \  <div style> children </div>;\n\
      };\n\
      "
      )        
     ]);
     File("ExampleStyles.re",
     "let reasonReactBlue = \"#48a9dc\";\n\
     \n\
     // The {j|...|j} feature is just string interpolation, from\n\
     // bucklescript.github.io/docs/en/interop-cheatsheet#string-unicode-interpolation\n\
     // This allows us to conveniently write CSS, together with variables, by\n\
     // constructing a string\n\
     let style = {j|\n\
     \  body {\n\
     \    background-color: rgb(224, 226, 229);\n\
     \    display: flex;\n\
     \    flex-direction: column;\n\
     \    align-items: center;\n\
     \  }\n\
     \  button {\n\
     \    background-color: white;\n\
     \    color: $reasonReactBlue;\n\
     \    box-shadow: 0 0 0 1px $reasonReactBlue;\n\
     \    border: none;\n\
     \    padding: 8px;\n\
     \    font-size: 16px;\n\
     \  }\n\
     \  button:active {\n\
     \    background-color: $reasonReactBlue;\n\
     \    color: white;\n\
     \  }\n\
     \  .container {\n\
     \    margin: 12px 0px;\n\
     \    box-shadow: 0px 4px 16px rgb(200, 200, 200);\n\
     \    width: 720px;\n\
     \    border-radius: 12px;\n\
     \    font-family: sans-serif;\n\
     \  }\n\
     \  .containerTitle {\n\
     \    background-color: rgb(242, 243, 245);\n\
     \    border-radius: 12px 12px 0px 0px;\n\
     \    padding: 12px;\n\
     \    font-weight: bold;\n\
     \  }\n\
     \  .containerContent {\n\
     \    background-color: white;\n\
     \    padding: 16px;\n\
     \    border-radius: 0px 0px 12px 12px;\n\
     \  }\n\
     |j};\n\
     "
     );
     Dir("FetchedDogPictures",[
      File("FetchedDogPictures.re",
      "[@bs.val] external fetch: string => Js.Promise.t('a) = \"fetch\";\n\
      \n\
      type state =\n\
      \  | LoadingDogs\n\
      \  | ErrorFetchingDogs\n\
      \  | LoadedDogs(array(string));\n\
      \n\
      [@react.component]\n\
      let make = () => {\n\
      \  let (state, setState) = React.useState(() => LoadingDogs);\n\
      \n\
      \  // Notice that instead of `useEffect`, we have `useEffect0`. See\n\
      \  // reasonml.github.io/reason-react/docs/en/components#hooks for more info\n\
      \  React.useEffect0(() => {\n\
      \    Js.Promise.(\n\
      \      fetch(\"https://dog.ceo/api/breeds/image/random/3\")\n\
      \      |> then_(response => response##json())\n\
      \      |> then_(jsonResponse => {\n\
      \           setState(_previousState => LoadedDogs(jsonResponse##message));\n\
      \           Js.Promise.resolve();\n\
      \         })\n\
      \      |> catch(_err => {\n\
      \           setState(_previousState => ErrorFetchingDogs);\n\
      \           Js.Promise.resolve();\n\
      \         })\n\
      \      |> ignore\n\
      \    );\n\
      \n\
      \    // Returning None, instead of Some(() => ...), means we don't have any\n\
      \    // cleanup to do before unmounting. That's not 100% true. We should\n\
      \    // technically cancel the promise. Unofortunately, there's currently no\n\
      \    // way to cancel a promise. Promises in general should be way less used\n\
      \    // for React components; but since folks do use them, we provide such an\n\
      \    // example here. In reality, this fetch should just be a plain callback,\n\
      \    // with a cancellation API\n\
      \    None;\n\
      \  });\n\
      \n\
      \  <div\n\
      \    style={ReactDOMRe.Style.make(\n\
      \      ~height=\"120px\",\n\
      \      ~display=\"flex\",\n\
      \      ~alignItems=\"center\",\n\
      \      ~justifyContent=\"center\",\n\
      \      (),\n\
      \    )}>\n\
      \    {switch (state) {\n\
      \     | ErrorFetchingDogs => React.string(\"An error occurred!\")\n\
      \     | LoadingDogs => React.string(\"Loading...\")\n\
      \     | LoadedDogs(dogs) =>\n\
      \       dogs\n\
      \       ->Belt.Array.mapWithIndex((i, dog) => {\n\
      \           let imageStyle =\n\
      \             ReactDOMRe.Style.make(\n\
      \               ~height=\"120px\",\n\
      \               ~width=\"100%\",\n\
      \               ~marginRight=i === Js.Array.length(dogs) - 1 ? \"0px\" : \"8px\",\n\
      \               ~borderRadius=\"8px\",\n\
      \               ~boxShadow=\"0px 4px 16px rgb(200, 200, 200)\",\n\
      \               ~backgroundSize=\"cover\",\n\
      \               ~backgroundImage={j|url($dog)|j},\n\
      \               ~backgroundPosition=\"center\",\n\
      \               (),\n\
      \             );\n\
      \           <div key=dog style=imageStyle />;\n\
      \         })\n\
      \       ->React.array\n\
      \     }}\n\
      \  </div>;\n\
      };\n\
      "
      )        
     ]);
     File("Index.re",
     "// Entry point\n\
     \n\
     [@bs.val] external document: Js.t({..}) = \"document\";\n\
     \n\
     // We're using raw DOM manipulations here, to avoid making you read\n\
     // ReasonReact when you might precisely be trying to learn it for the first\n\
     // time through the examples later.\n\
     let style = document##createElement(\"style\");\n\
     document##head##appendChild(style);\n\
     style##innerHTML #= ExampleStyles.style;\n\
     \n\
     let makeContainer = text => {\n\
     \  let container = document##createElement(\"div\");\n\
     \  container##className #= \"container\";\n\
     \n\
     \  let title = document##createElement(\"div\");\n\
     \  title##className #= \"containerTitle\";\n\
     \  title##innerText #= text;\n\
     \n\
     \  let content = document##createElement(\"div\");\n\
     \  content##className #= \"containerContent\";\n\
     \n\
     \  let () = container##appendChild(title);\n\
     \  let () = container##appendChild(content);\n\
     \  let () = document##body##appendChild(container);\n\
     \n\
     \  content;\n\
     };\n\
     \n\
     // All 4 examples.\n\
     ReactDOMRe.render(\n\
     \  <BlinkingGreeting>\n\
     \    {React.string(\"Hello!\")}\n\
     \  </BlinkingGreeting>,\n\
     \  makeContainer(\"Blinking Greeting\"),\n\
     );\n\
     \n\
     ReactDOMRe.render(\n\
     \  <ReducerFromReactJSDocs />,\n\
     \  makeContainer(\"Reducer From ReactJS Docs\"),\n\
     );\n\
     \n\
     ReactDOMRe.render(\n\
     \  <FetchedDogPictures />,\n\
     \  makeContainer(\"Fetched Dog Pictures\"),\n\
     );\n\
     \n\
     ReactDOMRe.render(\n\
     \  <ReasonUsingJSUsingReason />,\n\
     \  makeContainer(\"Reason Using JS Using Reason\"),\n\
     );\n\
     "
     );
     Dir("ReasonUsingJSUsingReason",[
      File("ReactJSCard.js",
      "// In this Interop example folder, we have:\n\
      // - A ReasonReact component, ReasonReactCard.re\n\
      // - Used by a ReactJS component, ReactJSCard.js (this file)\n\
      // - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re\n\
      // - ReasonUsingJSUsingReason.re is used by Index.re\n\
      \n\
      var ReactDOM = require('react-dom');\n\
      var React = require('react');\n\
      \n\
      var ReasonReactCard = require('./ReasonReactCard.bs').make;\n\
      \n\
      var ReactJSComponent = function() {\n\
      \  let backgroundColor = \"rgba(0, 0, 0, 0.05)\";\n\
      \  let padding = \"12px\";\n\
      \n\
      \  // We're not using JSX here, to avoid folks needing to install the related\n\
      \  // React toolchains just for this example.\n\
      \  // <div style={...}>\n\
      \  //   <div style={...}>This is a ReactJS card</div>\n\
      \  //   <ReasonReactCard style={...} />\n\
      \  // </div>\n\
      \  return React.createElement(\n\
      \    \"div\",\n\
      \    {style: {backgroundColor, padding, borderRadius: \"8px\"}},\n\
      \    React.createElement(\"div\", {style: {marginBottom: \"8px\"}}, \"This is a ReactJS card\"),\n\
      \    React.createElement(ReasonReactCard, {style: {backgroundColor, padding, borderRadius: \"4px\"}}),\n\
      \  )\n\
      };\n\
      ReactJSComponent.displayName = \"MyBanner\";\n\
      \n\
      module.exports = ReactJSComponent;\n\
      "
      );
      File("ReasonReactCard.re",
      "// In this Interop example folder, we have:\n\
      // - A ReasonReact component, ReasonReactCard.re (this file)\n\
      // - Used by a ReactJS component, ReactJSCard.js\n\
      // - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re\n\
      // - ReasonUsingJSUsingReason.re is used by Index.re\n\
      \n\
      [@react.component]\n\
      let make = (~style) => {\n\
      \  <div style> {React.string(\"This is a ReasonReact card\")} </div>;\n\
      };\n\
      "
      );
      File("ReasonUsingJSUsingReason.re",
      "// In this Interop example folder, we have:\n\
      // - A ReasonReact component, ReasonReactCard.re\n\
      // - Used by a ReactJS component, ReactJSCard.js\n\
      // - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re (this file)\n\
      // - ReasonUsingJSUsingReason.re is used by Index.re\n\
      \n\
      // All you need to do to use a ReactJS component in ReasonReact, is to write the lines below!\n\
      // reasonml.github.io/reason-react/docs/en/components#import-from-js\n\
      [@react.component] [@bs.module]\n\
      external make: unit => React.element = \"./ReactJSCard\";\n\
      "
      )        
     ]);
     Dir("ReducerFromReactJSDocs",[
      File("ReducerFromReactJSDocs.re",
      "// This is the ReactJS documentation's useReducer example, directly ported over\n\
      // https://reactjs.org/docs/hooks-reference.html#usereducer\n\
      \n\
      // A little extra we've put, because the ReactJS example has no styling\n\
      let leftButtonStyle = ReactDOMRe.Style.make(~borderRadius=\"4px 0px 0px 4px\", ~width=\"48px\", ());\n\
      let rightButtonStyle = ReactDOMRe.Style.make(~borderRadius=\"0px 4px 4px 0px\", ~width=\"48px\", ());\n\
      let containerStyle = ReactDOMRe.Style.make(~display=\"flex\", ~alignItems=\"center\", ~justifyContent=\"space-between\", ());\n\
      \n\
      // Record and variant need explicit declarations.\n\
      type state = {count: int};\n\
      \n\
      type action =\n\
      \  | Increment\n\
      \  | Decrement;\n\
      \n\
      let initialState = {count: 0};\n\
      \n\
      let reducer = (state, action) => {\n\
      \  switch (action) {\n\
      \  | Increment => {count: state.count + 1}\n\
      \  | Decrement => {count: state.count - 1}\n\
      \  };\n\
      };\n\
      \n\
      [@react.component]\n\
      let make = () => {\n\
      \  let (state, dispatch) = React.useReducer(reducer, initialState);\n\
      \n\
      \  // We can use a fragment here, but we don't, because we want to style the counter\n\
      \  <div style=containerStyle>\n\
      \    <div>\n\
      \      {React.string(\"Count: \")}\n\
      \      {React.string(string_of_int(state.count))}\n\
      \    </div>\n\
      \    <div>\n\
      \      <button style=leftButtonStyle onClick={_event => dispatch(Decrement)}>\n\
      \        {React.string(\"-\")}\n\
      \      </button>\n\
      \      <button style=rightButtonStyle onClick={_event => dispatch(Increment)}>\n\
      \        {React.string(\"+\")}\n\
      \      </button>\n\
      \    </div>\n\
      \  </div>;\n\
      };\n\
      "
      )        
     ])        
    ]);
    File("watcher.js",
    "// This is our simple, robust watcher. It hooks into the ReScript build\n\
    // system to listen for build events.\n\
    // See package.json's `start` script and `./node_modules/.bin/bsb --help`\n\
    \n\
    // Btw, if you change this file and reload the page, your browser cache\n\
    // _might_ not pick up the new version. If you're in Chrome, do Force Reload.\n\
    \n\
    var websocketReloader;\n\
    var LAST_SUCCESS_BUILD_STAMP =\n\
    \  localStorage.getItem(\"LAST_SUCCESS_BUILD_STAMP\") || 0;\n\
    // package.json's `start` script's `bsb -ws _` means it'll pipe build events\n\
    // through a websocket connection to a default port of 9999. This is\n\
    // configurable, e.g. `-ws 5000`\n\
    var webSocketPort = 9999;\n\
    \n\
    function setUpWebSocket() {\n\
    \  if (websocketReloader == null || websocketReloader.readyState !== 1) {\n\
    \    try {\n\
    \      websocketReloader = new WebSocket(\n\
    \        `ws://${window.location.hostname}:${webSocketPort}`\n\
    \      );\n\
    \      websocketReloader.onmessage = (message) => {\n\
    \        var newData = JSON.parse(message.data).LAST_SUCCESS_BUILD_STAMP;\n\
    \        if (newData > LAST_SUCCESS_BUILD_STAMP) {\n\
    \          LAST_SUCCESS_BUILD_STAMP = newData;\n\
    \          localStorage.setItem(\n\
    \            \"LAST_SUCCESS_BUILD_STAMP\",\n\
    \            LAST_SUCCESS_BUILD_STAMP\n\
    \          );\n\
    \          // Refresh the page! This will naturally re-run everything,\n\
    \          // including our moduleserve which will re-resolve all the modules.\n\
    \          // No stable build!\n\
    \          location.reload(true);\n\
    \        }\n\
    \      };\n\
    \    } catch (exn) {\n\
    \      console.error(\n\
    \        \"The watcher tried to connect to web socket, but failed. Here's the message:\"\n\
    \      );\n\
    \      console.error(exn);\n\
    \    }\n\
    \  }\n\
    }\n\
    \n\
    setUpWebSocket();\n\
    setInterval(setUpWebSocket, 2000);\n\
    "
    )        
   ]);
   Dir("react-starter",[
    File(".gitignore",
    ".DS_Store\n\
    .merlin\n\
    .bsb.lock\n\
    npm-debug.log\n\
    /lib/bs/\n\
    /node_modules/\n\
    *.bs.js\n\
    "
    );
    File("README.md",
    "# Reason react starter\n\
    \n\
    ## Run Project\n\
    \n\
    ```sh\n\
    npm install\n\
    npm start\n\
    # in another tab\n\
    npm run server\n\
    ```\n\
    \n\
    View the app in the browser at http://localhost:8000. Running in this environment provides hot reloading and support for routing; just edit and save the file and the browser will automatically refresh.\n\
    \n\
    To use a port other than 8000 set the `PORT` environment variable (`PORT=8080 npm run server`).\n\
    \n\
    ## Build for Production\n\
    \n\
    ```sh\n\
    npm run clean\n\
    npm run build\n\
    npm run webpack:production\n\
    ```\n\
    \n\
    This will replace the development artifact `build/Index.js` for an optimized version as well as copy `src/index.html` into `build/`. You can then deploy the contents of the `build` directory (`index.html` and `Index.js`).\n\
    \n\
    **To enable dead code elimination**, change `bsconfig.json`'s `package-specs` `module` from `\"commonjs\"` to `\"es6\"`. Then re-run the above 2 commands. This will allow Webpack to remove unused code.\n\
    "
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"reason-react-starter\",\n\
    \  \"reason\": {\n\
    \    \"react-jsx\": 3\n\
    \  },\n\
    \  \"sources\": {\n\
    \    \"dir\": \"src\",\n\
    \    \"subdirs\": true\n\
    \  },\n\
    \  \"bsc-flags\": [\"-bs-super-errors\", \"-bs-no-version-header\"],\n\
    \  \"package-specs\": [\n\
    \    {\n\
    \      \"module\": \"commonjs\",\n\
    \      \"in-source\": true\n\
    \    }\n\
    \  ],\n\
    \  \"suffix\": \".bs.js\",\n\
    \  \"namespace\": true,\n\
    \  \"bs-dependencies\": [\"reason-react\"],\n\
    \  \"refmt\": 3\n\
    }\n\
    "
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"start\": \"bsb -make-world -w -ws _ \",\n\
    \    \"clean\": \"bsb -clean-world\",\n\
    \    \"webpack\": \"webpack -w\",\n\
    \    \"webpack:production\": \"NODE_ENV=production webpack\",\n\
    \    \"server\": \"webpack-dev-server\",\n\
    \    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\",\n\
    \    \"ReasonReact\",\n\
    \    \"reason-react\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"dependencies\": {\n\
    \    \"react\": \"^17.0.1\",\n\
    \    \"react-dom\": \"^17.0.1\",\n\
    \    \"reason-react\": \"^0.9.1\"\n\
    \  },\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\",\n\
    \    \"css-loader\": \"^5.0.0\",\n\
    \    \"html-webpack-plugin\": \"^4.5.0\",\n\
    \    \"style-loader\": \"^2.0.0\",\n\
    \    \"webpack\": \"^4.44.2\",\n\
    \    \"webpack-cli\": \"^3.3.12\",\n\
    \    \"webpack-dev-server\": \"^3.11.0\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     File("App.re",
     "type state = {count: int};\n\
     \n\
     type action =\n\
     \  | Increment\n\
     \  | Decrement;\n\
     \n\
     let initialState = {count: 0};\n\
     \n\
     let reducer = (state, action) =>\n\
     \  switch (action) {\n\
     \  | Increment => {count: state.count + 1}\n\
     \  | Decrement => {count: state.count - 1}\n\
     \  };\n\
     \n\
     [@react.component]\n\
     let make = () => {\n\
     \  let (state, dispatch) = React.useReducer(reducer, initialState);\n\
     \n\
     \  <main>\n\
     \    {React.string(\"Simple counter with reducer\")}\n\
     \    <div>\n\
     \      <button onClick={_ => dispatch(Decrement)}>\n\
     \        {React.string(\"Decrement\")}\n\
     \      </button>\n\
     \      <span className=\"counter\">\n\
     \        {state.count |> string_of_int |> React.string}\n\
     \      </span>\n\
     \      <button onClick={_ => dispatch(Increment)}>\n\
     \        {React.string(\"Increment\")}\n\
     \      </button>\n\
     \    </div>\n\
     \  </main>;\n\
     };\n\
     "
     );
     File("Index.re",
     "[%bs.raw {|require(\"./index.css\")|}];\n\
     \n\
     ReactDOMRe.renderToElementWithId(<App />, \"root\");\n\
     "
     );
     File("index.css",
     "body {\n\
     \  margin: 0;\n\
     \  font-family: -apple-system, system-ui, \"Segoe UI\", Helvetica, Arial,\n\
     \    sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n\
     }\n\
     \n\
     main {\n\
     \  padding: 20px;\n\
     }\n\
     \n\
     .counter {\n\
     \  padding: 20px;\n\
     \  display: inline-block;\n\
     }\n\
     "
     );
     File("index.html",
     "<!DOCTYPE html>\n\
     <html lang=\"en\">\n\
     \  <head>\n\
     \    <meta charset=\"UTF-8\" />\n\
     \    <title>Reason react starter</title>\n\
     \  </head>\n\
     \  <body>\n\
     \    <div id=\"root\"></div>\n\
     \    <script src=\"/Index.js\"></script>\n\
     \  </body>\n\
     </html>\n\
     "
     )        
    ]);
    File("webpack.config.js",
    "const path = require(\"path\")\n\
    const HtmlWebpackPlugin = require(\"html-webpack-plugin\")\n\
    const outputDir = path.join(__dirname, \"build/\")\n\
    \n\
    const isProd = process.env.NODE_ENV === \"production\"\n\
    \n\
    module.exports = {\n\
    \  entry: \"./src/Index.bs.js\",\n\
    \  mode: isProd ? \"production\" : \"development\",\n\
    \  devtool: \"source-map\",\n\
    \  output: {\n\
    \    path: outputDir,\n\
    \    filename: \"Index.js\"\n\
    \  },\n\
    \  plugins: [\n\
    \    new HtmlWebpackPlugin({\n\
    \      template: \"src/index.html\",\n\
    \      inject: false\n\
    \    })\n\
    \  ],\n\
    \  devServer: {\n\
    \    compress: true,\n\
    \    contentBase: outputDir,\n\
    \    port: process.env.PORT || 8000,\n\
    \    historyApiFallback: true\n\
    \  },\n\
    \  module: {\n\
    \    rules: [\n\
    \      {\n\
    \        test: /\\.css$/,\n\
    \        use: [\"style-loader\", \"css-loader\"]\n\
    \      }\n\
    \    ]\n\
    \  }\n\
    }\n\
    "
    )        
   ]);
   Dir("tea",[
    File("README.md",
    "\n\
    \n\
    # Build\n\
    ```\n\
    npm run build\n\
    ```\n\
    \n\
    # Watch\n\
    \n\
    ```\n\
    npm run watch\n\
    ```\n\
    \n\
    create a http-server\n\
    \n\
    ```\n\
    npm install -g http-server\n\
    ```\n\
    \n\
    Edit the file and see the changes automatically reloaded in the browser\n\
    "
    );
    File("bsconfig.json",
    "{\n\
    \  \"name\": \"tea\",\n\
    \  \"version\": \"0.1.0\",\n\
    \  \"sources\": {\n\
    \    \"dir\" : \"src\",\n\
    \    \"subdirs\" : true\n\
    \  },\n\
    \  \"package-specs\": {\n\
    \    \"module\": \"commonjs\",\n\
    \    \"in-source\": true\n\
    \  },\n\
    \  \"suffix\": \".bs.js\",\n\
    \  \"bs-dependencies\": [\n\
    \      \"bucklescript-tea\"\n\
    \  ]\n\
    }\n\
    "
    );
    File("index.html",
    "<!DOCTYPE html>\n\
    <html lang=\"en\">\n\
    \  <head>\n\
    \    <meta charset=\"utf-8\">\n\
    \    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\
    \    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\
    \    <meta name=\"description\" content=\"\">\n\
    \    <meta name=\"author\" content=\"\">\n\
    \    <title>Bucklescript TEA Starter Kit</title>\n\
    \  </head>\n\
    \  \n\
    \n\
    \n\
    \  <body>\n\
    \    <div id=\"my-element\"> </div>\n\
    \    <script src=\"./loader.js\" type=\"module\" data-main=\"./src/main.bs.js\"></script>\n\
    \    <script src=\"./watcher.js\" type=\"module\"></script>\n\
    \    \n\
    \  </body>\n\
    </html>"
    );
    File("loader.js",
    "/* Copyright (C) 2018 Authors of ReScript\n\
    \ *\n\
    \ * This program is free software: you can redistribute it and/or modify\n\
    \ * it under the terms of the GNU Lesser General Public License as published by\n\
    \ * the Free Software Foundation, either version 3 of the License, or\n\
    \ * (at your option) any later version.\n\
    \ *\n\
    \ * In addition to the permissions granted to you by the LGPL, you may combine\n\
    \ * or link a \"work that uses the Library\" with a publicly distributed version\n\
    \ * of this file to produce a combined library or application, then distribute\n\
    \ * that combined work under the terms of your choosing, with no requirement\n\
    \ * to comply with the obligations normally placed on you by section 4 of the\n\
    \ * LGPL version 3 (or the corresponding section of a later version of the LGPL\n\
    \ * should you choose to use a later version).\n\
    \ *\n\
    \ * This program is distributed in the hope that it will be useful,\n\
    \ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
    \ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
    \ * GNU Lesser General Public License for more details.\n\
    \ *\n\
    \ * You should have received a copy of the GNU Lesser General Public License\n\
    \ * along with this program; if not, write to the Free Software\n\
    \ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */\n\
    \n\
    //@ts-check\n\
    \n\
    // @ts-ignore\n\
    window.process = { env: { NODE_ENV: \"dev\" } };\n\
    \n\
    // local to getPath\n\
    var relativeElement = document.createElement(\"a\");\n\
    var baseElement = document.createElement(\"base\");\n\
    document.head.appendChild(baseElement);\n\
    \n\
    export function BsGetPath(id, parent) {\n\
    \  var oldPath = baseElement.href;\n\
    \  baseElement.href = parent;\n\
    \  relativeElement.href = id;\n\
    \  var result = relativeElement.href;\n\
    \  baseElement.href = oldPath;\n\
    \  return result;\n\
    }\n\
    /**\n\
    \ *\n\
    \ * Given current link and its parent, return the new link\n\
    \ * @param {string} id\n\
    \ * @param {string} parent\n\
    \ * @return {string}\n\
    \ */\n\
    function getPathWithJsSuffix(id, parent) {\n\
    \  var oldPath = baseElement.href;\n\
    \  baseElement.href = parent;\n\
    \  relativeElement.href = id;\n\
    \  var result = addSuffixJsIfNot(relativeElement.href);\n\
    \  baseElement.href = oldPath;\n\
    \  return result;\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} x\n\
    \ */\n\
    function addSuffixJsIfNot(x) {\n\
    \  if (x.endsWith(\".js\")) {\n\
    \    return x;\n\
    \  } else {\n\
    \    return x + \".js\";\n\
    \  }\n\
    }\n\
    \n\
    var falsePromise = Promise.resolve(false);\n\
    var fetchConfig = { cache: \"no-cache\" };\n\
    // package.json semantics\n\
    // a string to module object\n\
    // from url -> module object\n\
    // Modules : Map<string, Promise < boolean | string >\n\
    // fetch the link:\n\
    // - if it is already fetched before, return the stored promise\n\
    //   otherwise create the promise which will be filled with the text if successful\n\
    //   or filled with boolean false when failed\n\
    var MODULES = new Map();\n\
    function cachedFetch(link) {\n\
    \  // console.info(link)\n\
    \  var linkResult = MODULES.get(link);\n\
    \  if (linkResult) {\n\
    \    return linkResult;\n\
    \  } else {\n\
    \    var p = fetch(link, fetchConfig).then((resp) => {\n\
    \      if (resp.ok) {\n\
    \        return resp.text();\n\
    \      } else {\n\
    \        return falsePromise;\n\
    \      }\n\
    \    });\n\
    \n\
    \    MODULES.set(link, p);\n\
    \    return p;\n\
    \  }\n\
    }\n\
    \n\
    // from location id -> url\n\
    // There are two rounds of caching:\n\
    // 1. if location and relative path is hit, no need to run\n\
    // 2. if location and relative path is not hit, but the resolved link is hit, no need\n\
    //     for network request\n\
    /**\n\
    \ * @type {Map<string, Map<string, Promise<any> > > }\n\
    \ */\n\
    var IDLocations = new Map();\n\
    \n\
    /**\n\
    \ * @type {Map<string, Map<string, any> > }\n\
    \ */\n\
    var SyncedIDLocations = new Map();\n\
    // Its value is an object\n\
    // { link : String }\n\
    // We will first mark it when visiting (to avoid duplicated computation)\n\
    // and populate its link later\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ * @param {string} location\n\
    \ */\n\
    function getIdLocation(id, location) {\n\
    \  var idMap = IDLocations.get(location);\n\
    \  if (idMap) {\n\
    \    return idMap.get(id);\n\
    \  }\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ * @param {string} location\n\
    \ */\n\
    function getIdLocationSync(id, location) {\n\
    \  var idMap = SyncedIDLocations.get(location);\n\
    \  if (idMap) {\n\
    \    return idMap.get(id);\n\
    \  }\n\
    }\n\
    \n\
    function countIDLocations() {\n\
    \  var count = 0;\n\
    \  for (let [k, vv] of IDLocations) {\n\
    \    for (let [kv, v] of vv) {\n\
    \      count += 1;\n\
    \    }\n\
    \  }\n\
    \  console.log(count, \"modules loaded\");\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ * @param {string} location\n\
    \ * @param {Function} cb\n\
    \ * @returns Promise<any>\n\
    \ */\n\
    function visitIdLocation(id, location, cb) {\n\
    \  var result;\n\
    \  var idMap = IDLocations.get(location);\n\
    \  if (idMap && (result = idMap.get(id))) {\n\
    \    return result;\n\
    \  } else {\n\
    \    result = new Promise((resolve) => {\n\
    \      return cb().then((res) => {\n\
    \        var idMap = SyncedIDLocations.get(location);\n\
    \        if (idMap) {\n\
    \          idMap.set(id, res);\n\
    \        } else {\n\
    \          SyncedIDLocations.set(location, new Map([[id, res]]));\n\
    \        }\n\
    \        return resolve(res);\n\
    \      });\n\
    \    });\n\
    \    if (idMap) {\n\
    \      idMap.set(id, result);\n\
    \    } else {\n\
    \      IDLocations.set(location, new Map([[id, result]]));\n\
    \    }\n\
    \    return result;\n\
    \  }\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} text\n\
    \ * @return {string[]}\n\
    \ */\n\
    function getDeps(text) {\n\
    \  var deps = [];\n\
    \  text.replace(\n\
    \    /(\\/\\*[\\w\\W]*?\\*\\/|\\/\\/[^\\n]*|[.$]r)|\\brequire\\s*\\(\\s*[\"']([^\"']*)[\"']\\s*\\)/g,\n\
    \    function (_, ignore, id) {\n\
    \      if (!ignore) deps.push(id);\n\
    \    }\n\
    \  );\n\
    \  return deps;\n\
    }\n\
    \n\
    // By using a named \"eval\" most browsers will execute in the global scope.\n\
    // http://www.davidflanagan.com/2010/12/global-eval-in.html\n\
    var globalEval = eval;\n\
    \n\
    // function parentURL(url) {\n\
    //     if (url.endsWith('/')) {\n\
    //         return url + '../'\n\
    //     } else {\n\
    //         return url + '/../'\n\
    //     }\n\
    // }\n\
    \n\
    // loader.js:23 http://localhost:8080/node_modules/react-dom/cjs/react-dom.development.js/..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//../ fbjs/lib/containsNode Promise {<pending>}\n\
    // 23:10:02.884 loader.js:23 http://localhost:8080/node_modules/react-dom/cjs/react-dom.development.js/..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//..//../ fbjs/lib/invariant Promise {<pending>}\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ * @param {string} parent\n\
    \ */\n\
    function getParentModulePromise(id, parent) {\n\
    \  var parentLink = BsGetPath(\"..\", parent);\n\
    \  if (parentLink === parent) {\n\
    \    return falsePromise;\n\
    \  }\n\
    \  return getPackageJsPromise(id, parentLink);\n\
    }\n\
    // In the beginning\n\
    // it is `resolveModule('./main.js', '')\n\
    // return the promise of link and text\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ */\n\
    function getPackageName(id) {\n\
    \  var index = id.indexOf(\"/\");\n\
    \  if (id[0] === \"@\") index = id.indexOf(\"/\", index + 1);\n\
    \  if (index === -1) {\n\
    \    return id;\n\
    \  }\n\
    \  return id.substring(0, index);\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} s\n\
    \ * @param {string} text\n\
    \ * @returns {undefined | string }\n\
    \ */\n\
    function isJustAPackageAndHasMainField(s, text) {\n\
    \  if (s.indexOf(\"/\") >= 0) {\n\
    \    return;\n\
    \  } else {\n\
    \    var mainField;\n\
    \    try {\n\
    \      mainField = JSON.parse(text).main;\n\
    \    } catch (_) {}\n\
    \    if (mainField === undefined) {\n\
    \      return;\n\
    \    } else {\n\
    \      return mainField;\n\
    \    }\n\
    \  }\n\
    }\n\
    function getPackageJsPromise(id, parent) {\n\
    \  var idNodeModulesPrefix = \"./node_modules/\" + id;\n\
    \  var link = getPathWithJsSuffix(idNodeModulesPrefix, parent);\n\
    \  if (parent.endsWith(\"node_modules/\")) {\n\
    \    // impossible that `node_modules/node_modules/xx/x\n\
    \    // return falsePromise\n\
    \    return getParentModulePromise(id, parent);\n\
    \  }\n\
    \n\
    \  var packageJson = BsGetPath(\n\
    \    `./node_modules/${getPackageName(id)}/package.json`,\n\
    \    parent\n\
    \  );\n\
    \n\
    \  return cachedFetch(packageJson).then(function (text) {\n\
    \    if (text !== false) {\n\
    \      var mainField;\n\
    \      if ((mainField = isJustAPackageAndHasMainField(id, text)) !== undefined) {\n\
    \        var packageLink = BsGetPath(\n\
    \          addSuffixJsIfNot(`./node_modules/${id}/${mainField}`),\n\
    \          parent\n\
    \        );\n\
    \        return cachedFetch(packageLink).then(function (text) {\n\
    \          if (text !== false) {\n\
    \            return { text, link: packageLink };\n\
    \          } else {\n\
    \            return getParentModulePromise(id, parent);\n\
    \          }\n\
    \        });\n\
    \      } else {\n\
    \        // package indeed exist\n\
    \        return cachedFetch(link).then(function (text) {\n\
    \          if (text !== false) {\n\
    \            return { text, link };\n\
    \          } else if (!id.endsWith(\".js\")) {\n\
    \            var linkNew = getPathWithJsSuffix(\n\
    \              idNodeModulesPrefix + `/index.js`,\n\
    \              parent\n\
    \            );\n\
    \            return cachedFetch(linkNew).then(function (text) {\n\
    \              if (text !== false) {\n\
    \                return { text, link: linkNew };\n\
    \              } else {\n\
    \                return getParentModulePromise(id, parent);\n\
    \              }\n\
    \            });\n\
    \          } else {\n\
    \            return getParentModulePromise(id, parent);\n\
    \          }\n\
    \        });\n\
    \      }\n\
    \    } else {\n\
    \      return getParentModulePromise(id, parent);\n\
    \    }\n\
    \  });\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ * @param {string} parent\n\
    \ * can return Promise <boolean | object>, false means\n\
    \ * this module can not be resolved\n\
    \ */\n\
    function getModulePromise(id, parent) {\n\
    \  var done = getIdLocation(id, parent);\n\
    \  if (!done) {\n\
    \    return visitIdLocation(id, parent, function () {\n\
    \      if (id[0] != \".\") {\n\
    \        // package path\n\
    \        return getPackageJsPromise(id, parent);\n\
    \      } else {\n\
    \        // relative path, one shot resolve\n\
    \        let link = getPathWithJsSuffix(id, parent);\n\
    \        return cachedFetch(link).then(function (text) {\n\
    \          if (text !== false) {\n\
    \            return { text, link };\n\
    \          } else if (!id.endsWith(\".js\")) {\n\
    \            // could be \"./dir\"\n\
    \            var newLink = getPathWithJsSuffix(id + \"/index.js\", parent);\n\
    \            return cachedFetch(newLink).then(function (text) {\n\
    \              if (text !== false) {\n\
    \                return { text, link: newLink };\n\
    \              } else {\n\
    \                throw new Error(` ${id} : ${parent} could not be resolved`);\n\
    \              }\n\
    \            });\n\
    \          } else {\n\
    \            throw new Error(` ${id} : ${parent} could not be resolved`);\n\
    \          }\n\
    \        });\n\
    \      }\n\
    \    });\n\
    \  } else {\n\
    \    return done;\n\
    \  }\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} id\n\
    \ * @param {string} parent\n\
    \ * @returns {Promise<any>}\n\
    \ */\n\
    function getAll(id, parent) {\n\
    \  return getModulePromise(id, parent).then(function (obj) {\n\
    \    if (obj) {\n\
    \      var deps = getDeps(obj.text);\n\
    \      return Promise.all(deps.map((x) => getAll(x, obj.link)));\n\
    \    } else {\n\
    \      throw new Error(`${id}@${parent} was not resolved successfully`);\n\
    \    }\n\
    \  });\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} text\n\
    \ * @param {string} parent\n\
    \ * @returns {Promise<any>}\n\
    \ */\n\
    function getAllFromText(text, parent) {\n\
    \  var deps = getDeps(text);\n\
    \  return Promise.all(deps.map((x) => getAll(x, parent)));\n\
    }\n\
    \n\
    var evaluatedModules = new Map();\n\
    \n\
    function loadSync(id, parent) {\n\
    \  var baseOrModule = getIdLocationSync(id, parent);\n\
    \  if (baseOrModule && baseOrModule.link !== undefined) {\n\
    \    if (evaluatedModules.has(baseOrModule.link)) {\n\
    \      return evaluatedModules.get(baseOrModule.link).exports;\n\
    \    }\n\
    \    if (!baseOrModule.exports) {\n\
    \      baseOrModule.exports = {};\n\
    \      globalEval(\n\
    \        `(function(require,exports,module){${baseOrModule.text}\\n})//# sourceURL=${baseOrModule.link}`\n\
    \      )(\n\
    \        function require(id) {\n\
    \          return loadSync(id, baseOrModule.link);\n\
    \        }, // require\n\
    \        (baseOrModule.exports = {}), // exports\n\
    \        baseOrModule // module\n\
    \      );\n\
    \    }\n\
    \    if (!evaluatedModules.has(baseOrModule.link)) {\n\
    \      evaluatedModules.set(baseOrModule.link, baseOrModule);\n\
    \    }\n\
    \    return baseOrModule.exports;\n\
    \  } else {\n\
    \    throw new Error(`${id} : ${parent} could not be resolved`);\n\
    \  }\n\
    }\n\
    \n\
    function genEvalName() {\n\
    \  return \"eval-\" + (\"\" + Math.random()).substr(2, 5);\n\
    }\n\
    /**\n\
    \ *\n\
    \ * @param {string} text\n\
    \ * @param {string} link\n\
    \ * In this case [text] evaluated result will not be cached\n\
    \ */\n\
    function loadTextSync(text, link) {\n\
    \  var baseOrModule = { exports: {}, text, link };\n\
    \  globalEval(\n\
    \    `(function(require,exports,module){${baseOrModule.text}\\n})//# sourceURL=${\n\
    \      baseOrModule.link\n\
    \    }/${genEvalName()}.js`\n\
    \  )(\n\
    \    function require(id) {\n\
    \      return loadSync(id, baseOrModule.link);\n\
    \    }, // require\n\
    \    baseOrModule.exports, // exports\n\
    \    baseOrModule // module\n\
    \  );\n\
    \  return baseOrModule.exports;\n\
    }\n\
    \n\
    /**\n\
    \ *\n\
    \ * @param {string} text\n\
    \ */\n\
    function BSloadText(text) {\n\
    \  console.time(\"Loading\");\n\
    \  var parent = BsGetPath(\".\", document.baseURI);\n\
    \  return getAllFromText(text, parent).then(function () {\n\
    \    var result = loadTextSync(text, parent);\n\
    \    console.timeEnd(\"Loading\");\n\
    \    return result;\n\
    \  });\n\
    }\n\
    \n\
    function load(id, parent) {\n\
    \  return getAll(id, parent).then(function () {\n\
    \    return loadSync(id, parent);\n\
    \  });\n\
    }\n\
    \n\
    export function BSload(id) {\n\
    \  var parent = BsGetPath(\".\", document.baseURI);\n\
    \  return load(id, parent);\n\
    }\n\
    \n\
    export var BSLoader = {\n\
    \  loadText: BSloadText,\n\
    \  load: BSload,\n\
    \  SyncedIDLocations: SyncedIDLocations,\n\
    };\n\
    \n\
    window.BSLoader = BSLoader;\n\
    \n\
    var main = document.querySelector(\"script[data-main]\");\n\
    if (main) {\n\
    \  BSload(main.dataset.main);\n\
    }\n\
    "
    );
    File("package.json",
    "{\n\
    \  \"name\": \"${rescript:name}\",\n\
    \  \"version\": \"${rescript:proj-version}\",\n\
    \  \"scripts\": {\n\
    \    \"clean\": \"bsb -clean-world\",\n\
    \    \"build\": \"bsb -make-world\",\n\
    \    \"watch\": \"bsb -make-world -w -ws _\"\n\
    \  },\n\
    \  \"keywords\": [\n\
    \    \"ReScript\"\n\
    \  ],\n\
    \  \"author\": \"\",\n\
    \  \"license\": \"MIT\",\n\
    \  \"devDependencies\": {\n\
    \    \"${rescript:platform}\": \"^${rescript:bs-version}\"\n\
    \  },\n\
    \  \"dependencies\": {\n\
    \    \"bucklescript-tea\": \"^0.7.4\"\n\
    \  }\n\
    }\n\
    "
    );
    Dir("src",[
     File("demo.ml",
     "(* This line opens the Tea.App modules into the current scope for Program access functions and types *)\n\
     open Tea.App\n\
     \n\
     (* This opens the Elm-style virtual-dom functions and types into the current scope *)\n\
     open Tea.Html\n\
     \n\
     (* Let's create a new type here to be our main message type that is passed around *)\n\
     type msg =\n\
     \  | Increment  (* This will be our message to increment the counter *)\n\
     \  | Decrement  (* This will be our message to decrement the counter *)\n\
     \  | Reset      (* This will be our message to reset the counter to 0 *)\n\
     \  | Set of int (* This will be our message to set the counter to a specific value *)\n\
     \  [@@bs.deriving {accessors}] (* This is a nice quality-of-life addon from Bucklescript, it will generate function names for each constructor name, optional, but nice to cut down on code, this is unused in this example but good to have regardless *)\n\
     \n\
     (* This is optional for such a simple example, but it is good to have an `init` function to define your initial model default values, the model for Counter is just an integer *)\n\
     let init () = 4\n\
     \n\
     (* This is the central message handler, it takes the model as the first argument *)\n\
     let update model = function (* These should be simple enough to be self-explanatory, mutate the model based on the message, easy to read and follow *)\n\
     \  | Increment -> model + 1\n\
     \  | Decrement -> model - 1\n\
     \  | Reset -> 0\n\
     \  | Set v -> v\n\
     \n\
     (* This is just a helper function for the view, a simple function that returns a button based on some argument *)\n\
     let view_button title msg =\n\
     \  button\n\
     \    [ onClick msg\n\
     \    ]\n\
     \    [ text title\n\
     \    ]\n\
     \n\
     (* This is the main callback to generate the virtual-dom.\n\
     \  This returns a virtual-dom node that becomes the view, only changes from call-to-call are set on the real DOM for efficiency, this is also only called once per frame even with many messages sent in within that frame, otherwise does nothing *)\n\
     let view model =\n\
     \  div\n\
     \    []\n\
     \    [ span\n\
     \        [ style \"text-weight\" \"bold\" ]\n\
     \        [ text (string_of_int model) ]\n\
     \    ; br []\n\
     \    ; view_button \"Increment\" Increment\n\
     \    ; br []\n\
     \    ; view_button \"Decrement\" Decrement\n\
     \    ; br []\n\
     \    ; view_button \"Set to 2\" (Set 42)\n\
     \    ; br []\n\
     \    ; if model <> 0 then view_button \"Reset\" Reset else noNode\n\
     \    ]\n\
     \n\
     (* This is the main function, it can be named anything you want but `main` is traditional.\n\
     \  The Program returned here has a set of callbacks that can easily be called from\n\
     \  Bucklescript or from javascript for running this main attached to an element,\n\
     \  or even to pass a message into the event loop.  You can even expose the\n\
     \  constructors to the messages to javascript via the above [@@bs.deriving {accessors}]\n\
     \  attribute on the `msg` type or manually, that way even javascript can use it safely. *)\n\
     let main =\n\
     \  beginnerProgram { (* The beginnerProgram just takes a set model state and the update and view functions *)\n\
     \    model = init (); (* Since model is a set value here, we call our init function to generate that value *)\n\
     \    update;\n\
     \    view;\n\
     \  }"
     );
     File("main.ml",
     "\n\
     \n\
     \n\
     Js.Global.setTimeout\n\
     \  (fun _ -> \n\
     \  Demo.main (Web.Document.getElementById \"my-element\") () \n\
     \  |. ignore\n\
     \  )  \n\
     0"
     )        
    ]);
    File("watcher.js",
    "\n\
    \n\
    var wsReloader;\n\
    var LAST_SUCCESS_BUILD_STAMP = (localStorage.getItem('LAST_SUCCESS_BUILD_STAMP') || 0)\n\
    var WS_PORT = 9999; // configurable\n\
    \n\
    function setUpWebScoket() {\n\
    \    if (wsReloader == null || wsReloader.readyState !== 1) {\n\
    \        try {\n\
    \            wsReloader = new WebSocket(`ws://${window.location.hostname}:${WS_PORT}`)\n\
    \            wsReloader.onmessage = (msg) => {\n\
    \                var newData = JSON.parse(msg.data).LAST_SUCCESS_BUILD_STAMP\n\
    \                if (newData > LAST_SUCCESS_BUILD_STAMP) {\n\
    \                    LAST_SUCCESS_BUILD_STAMP = newData\n\
    \                    localStorage.setItem('LAST_SUCCESS_BUILD_STAMP', LAST_SUCCESS_BUILD_STAMP)\n\
    \                    location.reload(true)\n\
    \                }\n\
    \n\
    \            }\n\
    \        } catch (exn) {\n\
    \            console.error(\"web socket failed connect\")\n\
    \        }\n\
    \    }\n\
    };\n\
    \n\
    setUpWebScoket();\n\
    setInterval(setUpWebScoket, 2000);"
    )        
   ])
])
end
module Bsb_theme_init : sig 
#1 "bsb_theme_init.mli"
(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val init_sample_project : cwd:string -> theme:string -> string -> unit

val list_themes : unit -> unit

end = struct
#1 "bsb_theme_init.ml"
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
 * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

type file_type = Directory | Non_directory_file | Non_exists

let classify_file name =
  let exists = Sys.file_exists name in
  if exists then if Sys.is_directory name then Directory else Non_directory_file
  else Non_exists

let replace s env : string =
  Bsb_regex.global_substitute s ~reg:"\\${rescript:\\([-a-zA-Z0-9]+\\)}"
    (fun (_s : string) templates ->
      match templates with
      | key :: _ -> Hash_string.find_exn env key
      | _ -> assert false)

let ( // ) = Filename.concat

let enter_dir cwd x action =
  Unix.chdir x;
  match action () with
  | exception e ->
      Unix.chdir cwd;
      raise e
  | v -> v

let mkdir_or_not_if_exists dir =
  match classify_file dir with
  | Directory -> ()
  | Non_directory_file ->
      Format.fprintf Format.err_formatter
        "%s expected to be added as dir but exist file is not a dir" dir
  | Non_exists -> Unix.mkdir dir 0o777

let rec process_theme_aux env cwd (x : Bsb_templates.node) =
  match x with
  | File (name, content) ->
      let new_file = cwd // name in
      if not @@ Sys.file_exists new_file then
        Ext_io.write_file new_file (replace content env)
  | Dir (current, nodes) ->
      let new_cwd = cwd // current in
      mkdir_or_not_if_exists new_cwd;
      List.iter (fun x -> process_theme_aux env new_cwd x) nodes

let list_themes () =
  Format.fprintf Format.std_formatter "Available themes: @.";
  Ext_list.iter Bsb_templates.root (fun x ->
      match x with
      | Dir (x, _) -> Format.fprintf Format.std_formatter "%s@." x
      | _ -> ())

(* @raise [Not_found] *)
let process_themes env theme proj_dir (themes : Bsb_templates.node list) =
  match
    Ext_list.find_first themes (fun x ->
        match x with Dir (dir, _) -> dir = theme | File _ -> false)
  with
  | None ->
      list_themes ();
      Bsb_arg.bad_arg ("theme " ^ theme ^ " not found")
  | Some (Dir (_theme, nodes)) ->
      List.iter (fun node -> process_theme_aux env proj_dir node) nodes
  | Some _ -> assert false

(** TODO: run npm link *)
let init_sample_project ~cwd ~theme name =
  let env = Hash_string.create 0 in
  List.iter
    (fun (k, v) -> Hash_string.add env k v)
    [
      ("proj-version", "0.1.0");
      ("bs-version", Bs_version.version);
      ("bsb", Filename.current_dir_name // "node_modules" // ".bin" // "bsb");
      ("platform", !Bs_version.package_name);
    ];
  let action _ =
    process_themes env theme Filename.current_dir_name Bsb_templates.root
  in
  match name with
  | "." ->
      let name = Filename.basename cwd in
      if Ext_namespace.is_valid_npm_package_name name then (
        Hash_string.add env "name" name;
        action ())
      else (
        Format.fprintf Format.err_formatter
          "@{<error>Invalid package name@} %S@}: the project name must be both \
           a valid npm package name and a valid name as namespace@."
          name;
        exit 2)
  | _ ->
      if Ext_namespace.is_valid_npm_package_name name then (
        match classify_file name with
        | Non_directory_file ->
            Format.fprintf Format.err_formatter
              "@{<error>%s already exists but it is not a directory@}@." name;
            exit 2
        | Directory ->
            Format.fprintf Format.std_formatter
              "Adding files into existing dir %s@." name;
            Hash_string.add env "name" name;
            enter_dir cwd name action
        | Non_exists ->
            Format.fprintf Format.std_formatter "Making directory %s@." name;
            Unix.mkdir name 0o777;
            Hash_string.add env "name" name;
            enter_dir cwd name action)
      else (
        Format.fprintf Format.err_formatter
          "@{<error>Invalid package name@} %S.@} The project name must be a \
           valid npm name, thus can't contain upper-case letters, for example."
          name;
        exit 2)

end
module Bsb_world : sig 
#1 "bsb_world.mli"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

val make_world_deps :
  string -> Bsb_config_types.t option -> string array -> unit

end = struct
#1 "bsb_world.ml"
(* Copyright (C) 2017- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let ( // ) = Ext_path.combine
let vendor_ninja = Bsb_global_paths.vendor_ninja

let make_world_deps cwd (config : Bsb_config_types.t option)
    (ninja_args : string array) =
  let package_specs, jsx, pinned_dependencies =
    match config with
    | None ->
        (* When this running bsb does not read bsconfig.json,
           we will read such json file to know which [package-specs]
           it wants
        *)
        Bsb_config_parse.deps_from_bsconfig ()
    | Some config ->
        (config.package_specs, config.jsx, config.pinned_dependencies)
  in
  let args =
    if Ext_array.is_empty ninja_args then [| vendor_ninja |]
    else Array.append [| vendor_ninja |] ninja_args
  in
  let lib_artifacts_dir = Bsb_config.lib_bs in
  let queue = Bsb_build_util.walk_all_deps cwd ~pinned_dependencies in
  (* let oc = open_out_bin ".deps.log" in
     queue |> Queue.iter (fun ({top; proj_dir} : Bsb_build_util.package_context) ->
     match top with
     | Expect_none -> ()
     | Expect_name s ->
      output_string oc s ;
      output_string oc " : ";
      output_string oc proj_dir;
      output_string oc "\n"
     );
     close_out oc ; *)
  queue
  |> Queue.iter (fun ({ top; proj_dir } : Bsb_build_util.package_context) ->
         match top with
         | Expect_none -> ()
         | Expect_name s ->
             let is_pinned = Set_string.mem pinned_dependencies s in
             if is_pinned then print_endline ("Dependency pinned on " ^ s)
             else print_endline ("Dependency on " ^ s);
             let lib_bs_dir = proj_dir // lib_artifacts_dir in
             Bsb_build_util.mkp lib_bs_dir;
             let _config : _ option =
               Bsb_ninja_regen.regenerate_ninja
                 ~package_kind:
                   (if is_pinned then Pinned_dependency { package_specs; jsx }
                   else Dependency { package_specs; jsx })
                 ~per_proj_dir:proj_dir ~forced:false
             in
             let command =
               { Bsb_unix.cmd = vendor_ninja; cwd = lib_bs_dir; args }
             in
             let eid = Bsb_unix.run_command_execv command in
             if eid <> 0 then Bsb_unix.command_fatal_error command eid;
             (* When ninja is not regenerated, ninja will still do the build,
                still need reinstall check
                Note that we can check if ninja print "no work to do",
                then don't need reinstall more
             *)
             Bsb_log.info "@{<info>Installation started@}@.";
             let install_dir = proj_dir // "lib" // "ocaml" in
             Bsb_build_util.mkp install_dir;
             let install_command =
               {
                 Bsb_unix.cmd = vendor_ninja;
                 cwd = install_dir;
                 args =
                   [| vendor_ninja; "-f"; ".." // "bs" // "install.ninja" |];
               }
             in
             let eid = Bsb_unix.run_command_execv install_command in
             if eid <> 0 then Bsb_unix.command_fatal_error install_command eid;
             Bsb_log.info "@{<info>Installation finished@}@.");
  print_endline "Dependency Finished"

end
module Rescript_main : sig 
#1 "rescript_main.mli"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

end = struct
#1 "rescript_main.ml"
(* Copyright (C) 2020- Hongbo Zhang, Authors of ReScript
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition to the permissions granted to you by the LGPL, you may combine
 * or link a "work that uses the Library" with a publicly distributed version
 * of this file to produce a combined library or application, then distribute
 * that combined work under the terms of your choosing, with no requirement
 * to comply with the obligations normally placed on you by section 4 of the
 * LGPL version 3 (or the corresponding section of a later version of the LGPL
 * should you choose to use a later version).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let () = Bsb_log.setup ()

let current_theme = ref "basic"

let separator = "--"

let watch_mode = ref false

let make_world = ref false

let do_install = ref false

let force_regenerate = ref false

type spec = Bsb_arg.spec

let call_spec f : spec = Unit (Unit_call f)

let unit_set_spec b : spec = Unit (Unit_set b)

let string_set_spec s : spec = String (String_set s)

let failed_annon ~rev_args =
  match rev_args with
  | x :: _ -> Bsb_arg.bad_arg ("Don't know what to do with " ^ x)
  | _ -> ()

(*Note that [keepdepfile] only makes sense when combined with [deps] for optimization*)

(**  Invariant: it has to be the last command of [bsb] *)
let exec_command_then_exit (type t) (command : string) : t =
  Bsb_log.info "@{<info>CMD:@} %s@." command;
  exit (Sys.command command)

(* Execute the underlying ninja build call, then exit (as opposed to keep watching) *)
let ninja_command_exit (type t) (ninja_args : string array) : t =
  let ninja_args_len = Array.length ninja_args in
  let lib_artifacts_dir = Bsb_config.lib_bs in
  if Ext_sys.is_windows_or_cygwin then
    let path_ninja = Filename.quote Bsb_global_paths.vendor_ninja in
    exec_command_then_exit
      (if ninja_args_len = 0 then
       Ext_string.inter3 path_ninja "-C" lib_artifacts_dir
      else
        let args =
          Array.append [| path_ninja; "-C"; lib_artifacts_dir |] ninja_args
        in
        Ext_string.concat_array Ext_string.single_space args)
  else
    let ninja_common_args = [| "ninja.exe"; "-C"; lib_artifacts_dir |] in
    let args =
      if ninja_args_len = 0 then ninja_common_args
      else Array.append ninja_common_args ninja_args
    in
    Bsb_log.info_args args;
    Unix.execvp Bsb_global_paths.vendor_ninja args

(**
   Cache files generated:
   - .bsdircache in project root dir
   - .bsdeps in builddir

   What will happen, some flags are really not good
   ninja -C _build
*)
let clean_usage =
  "Usage: rescript.exe clean <options>\n\n\
   `rescript clean` only cleans the current project\n"

let build_usage =
  "Usage: rescript.exe build <options> -- <ninja_options>\n\n\
   `rescript build` implicitly builds dependencies if they aren't built\n\n\
   `rescript.exe -- -h` for Ninja options (internal usage only; unstable)\n"

let install_target () =
  let ( // ) = Filename.concat in
  let vendor_ninja = Bsb_global_paths.vendor_ninja in
  let install_dir = "lib" // "ocaml" in
  Bsb_build_util.mkp install_dir;
  let install_command =
    {
      Bsb_unix.cmd = vendor_ninja;
      cwd = install_dir;
      args = [| vendor_ninja; "-f"; ".." // "bs" // "install.ninja" |];
    }
  in
  let eid = Bsb_unix.run_command_execv install_command in
  if eid <> 0 then Bsb_unix.command_fatal_error install_command eid

(** check if every dependency installation dierctory is there 
    This is in hot path, so we want it to be fast.
    The heuristics is that if the installation directory is not there,
    we definitely need build the world.
    If it is there, we do not check if it is up-to-date, since that's
    going be slow, user can use `-with-deps` to enforce such check
*)
let check_deps_installation_directory (config_opt : Bsb_config_types.t option) :
    bool =
  match config_opt with
  | Some { bs_dependencies; bs_dev_dependencies } ->
      not
        (Ext_list.for_all bs_dependencies (fun x ->
             Ext_sys.is_directory_no_exn x.package_install_path)
        && Ext_list.for_all bs_dev_dependencies (fun x ->
               Ext_sys.is_directory_no_exn x.package_install_path))
  | None -> false

let build_subcommand ~start argv argv_len =
  let i = Ext_array.rfind_with_index argv Ext_string.equal separator in

  Bsb_arg.parse_exn ~usage:build_usage ~start
    ?finish:(if i < 0 then None else Some i)
    ~argv
    [|
      ("-w", unit_set_spec watch_mode, "Watch mode");
      ("-with-deps", unit_set_spec make_world, "Build dependencies explicitly");
      ( "-install",
        unit_set_spec do_install,
        "*internal* Install public interface files for dependencies " );
      (* This should be put in a subcommand
         previously it works with the implication `bsb && bsb -install`
      *)
      ( "-ws",
        string_set_spec (ref ""),
        "[host]:port set up host & port for WebSocket build notifications" );
      ( "-regen",
        unit_set_spec force_regenerate,
        "*internal* \n\
         Always regenerate build.ninja no matter bsconfig.json is changed or \
         not" );
      ("-verbose", call_spec Bsb_log.verbose, "Set the output to be verbose");
    |]
    failed_annon;

  let ninja_args =
    if i < 0 then [||] else Array.sub argv (i + 1) (argv_len - i - 1)
  in
  match ninja_args with
  | [| "-h" |] -> ninja_command_exit ninja_args
  | _ ->
      let config_opt =
        Bsb_ninja_regen.regenerate_ninja ~package_kind:Toplevel
          ~per_proj_dir:Bsb_global_paths.cwd ~forced:!force_regenerate
      in
      let implict_build = check_deps_installation_directory config_opt in
      (match (!make_world, implict_build) with
      | true, _ ->
          Bsb_world.make_world_deps Bsb_global_paths.cwd config_opt ninja_args
      | false, true ->
          Bsb_world.make_world_deps Bsb_global_paths.cwd config_opt [||]
      | false, false -> ());
      if !do_install then install_target ();
      if !watch_mode then exit 0 (* let the watcher do the build*)
      else ninja_command_exit ninja_args

let clean_subcommand ~start argv =
  Bsb_arg.parse_exn ~usage:clean_usage ~start ~argv
    [|
      ( "-with-deps",
        unit_set_spec make_world,
        "*internal* Clean dependencies too" );
      ("-verbose", call_spec Bsb_log.verbose, "Set the output to be verbose");
    |]
    failed_annon;
  Bsb_clean.clean_bs_deps Bsb_global_paths.cwd;
  Bsb_clean.clean_self Bsb_global_paths.cwd

let init_usage =
  "Usage: rescript init [folder-name]\n\n\
   `rescript init` adds a small ReScript project template to the current \
   folder\n\n\
   Pass folder-name to add them to a dedicated folder\n"

let init_subcommand ~start argv =
  Bsb_arg.parse_exn ~usage:init_usage ~start ~argv [||] (fun ~rev_args ->
      let location =
        match rev_args with
        | [ x ] -> x
        | last :: _another :: _ ->
            raise (Bsb_arg.Bad ("Don't know what to do with " ^ last))
        | [] -> "."
      in
      Bsb_theme_init.init_sample_project ~cwd:Bsb_global_paths.cwd
        ~theme:!current_theme location)

let list_files = ref false

let info_subcommand ~start argv =
  Bsb_arg.parse_exn ~usage:"query the project" ~start ~argv
    [| ("-list-files", unit_set_spec list_files, "list source files") |]
    (fun ~rev_args ->
      (match rev_args with
      | x :: _ -> raise (Bsb_arg.Bad ("Don't know what to do with " ^ x))
      | [] -> ());
      if !list_files then
        match
          Bsb_ninja_regen.regenerate_ninja ~package_kind:Toplevel ~forced:true
            ~per_proj_dir:Bsb_global_paths.cwd
        with
        | None -> assert false
        | Some { file_groups = { files } } ->
            Ext_list.iter files (fun { sources } ->
                Map_string.iter sources
                  (fun _ { info; syntax_kind; name_sans_extension } ->
                    let extensions =
                      match (syntax_kind, info) with
                      | _, Intf -> assert false
                      | Reason, Impl -> [ ".re" ]
                      | Reason, Impl_intf -> [ ".re"; ".rei" ]
                      | Ml, Impl -> [ ".ml" ]
                      | Ml, Impl_intf -> [ ".ml"; ".mli" ]
                      | Res, Impl -> [ ".res" ]
                      | Res, Impl_intf -> [ ".res"; ".resi" ]
                    in
                    Ext_list.iter extensions (fun x ->
                        print_endline (name_sans_extension ^ x)))))

(* see discussion #929, if we catch the exception, we don't have stacktrace... *)
let () =
  let argv = Sys.argv in
  let argv_len = Array.length argv in
  try
    if argv_len = 1 then (
      (* specialize this path which is used in watcher *)
      let config_opt =
        Bsb_ninja_regen.regenerate_ninja ~package_kind:Toplevel ~forced:false
          ~per_proj_dir:Bsb_global_paths.cwd
      in
      if !make_world || check_deps_installation_directory config_opt then
        Bsb_world.make_world_deps Bsb_global_paths.cwd config_opt [||];
      ninja_command_exit [||])
    else
      match argv.(1) with
      | "build" -> build_subcommand ~start:2 argv argv_len
      | "clean" -> clean_subcommand ~start:2 argv
      | "init" -> init_subcommand ~start:2 argv
      | "info" ->
          (* internal *)
          info_subcommand ~start:2 argv
      | first_arg ->
          prerr_endline @@ "Unknown subcommand or flags: " ^ first_arg;
          exit 1
  with
  | Bsb_exception.Error e ->
      Bsb_exception.print Format.err_formatter e;
      Format.pp_print_newline Format.err_formatter ();
      exit 2
  | Ext_json_parse.Error (start, _, e) ->
      Format.fprintf Format.err_formatter
        "File %S, line %d\n@{<error>Error:@} %a@." start.pos_fname
        start.pos_lnum Ext_json_parse.report_error e;
      exit 2
  | Bsb_arg.Bad s | Sys_error s ->
      Format.fprintf Format.err_formatter "@{<error>Error:@} %s@." s;
      exit 2
  | e -> Ext_pervasives.reraise e

end
