diff --git a/docs/docson/build-schema.json b/docs/docson/build-schema.json index e16071d685..21635750d7 100644 --- a/docs/docson/build-schema.json +++ b/docs/docson/build-schema.json @@ -481,6 +481,10 @@ "type": "boolean", "description": "(Experimental) whether to use the OCaml standard library. Default: true" }, + "external-stdlib" : { + "type" : "string", + "description": "Use the external stdlib library instead of the one shipped with the compiler package" + }, "bs-external-includes": { "type": "array", "items": { diff --git a/jscomp/bsb/bsb_build_schemas.ml b/jscomp/bsb/bsb_build_schemas.ml index 90cdeb56bf..a5a4bf5e1e 100644 --- a/jscomp/bsb/bsb_build_schemas.ml +++ b/jscomp/bsb/bsb_build_schemas.ml @@ -65,6 +65,7 @@ let export_none = "none" let use_stdlib = "use-stdlib" +let external_stdlib = "external-stdlib" let reason = "reason" let react_jsx = "react-jsx" diff --git a/jscomp/bsb/bsb_config_parse.ml b/jscomp/bsb/bsb_config_parse.ml index 69fcdb0523..dd4f185358 100644 --- a/jscomp/bsb/bsb_config_parse.ml +++ b/jscomp/bsb/bsb_config_parse.ml @@ -90,48 +90,26 @@ let extract_package_name_and_namespace - the running bsb and vendoring bsb is the same - the running bsb need delete stale build artifacts (kinda check npm upgrade) -*) -let check_version_exit (map : json_map) stdlib_path = - match Map_string.find_exn map Bsb_build_schemas.version with - | Str {str } -> - if str <> Bs_version.version then - begin - Format.fprintf Format.err_formatter - "@{bs-platform version mismatch@} Running bsb @{%s@} (%s) vs vendored @{%s@} (%s)@." - Bs_version.version - (Filename.dirname (Filename.dirname Sys.executable_name)) - str - stdlib_path - ; - exit 2 - end - | _ -> assert false - -let check_stdlib (map : json_map) cwd (*built_in_package*) = + + Note if the setup is correct: + the running compiler and node_modules/bs-platform + 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) (*built_in_package*) : bool = match map.?( Bsb_build_schemas.use_stdlib) with - | Some (False _) -> None + | Some (False _) -> false | None - | Some _ -> - begin - let current_package : Bsb_pkg_types.t = Global !Bs_version.package_name in - if Sys.getenv_opt "RES_SKIP_STDLIB_CHECK" = None then begin - let stdlib_path = - Bsb_pkg.resolve_bs_package ~cwd current_package in - let json_spec = - Ext_json_parse.parse_json_from_file - (* No exn raised: stdlib has package.json *) - (Filename.concat stdlib_path Literals.package_json) in - match json_spec with - | Obj {map} -> - check_version_exit map stdlib_path; - - | _ -> assert false - end; - Some { - Bsb_config_types.package_name = current_package; - package_install_path = Filename.dirname Bsb_global_paths.bsc_dir // Bsb_config.lib_ocaml; - } - end + | Some _ -> + true + @@ -355,7 +333,7 @@ let interpret_json array from the bsconfig and set the backend_ref to the first entry, if any. *) (* The default situation is empty *) - let built_in_package = check_stdlib map per_proj_dir in + let built_in_package : bool = check_stdlib map in let pp_flags : string option = extract_string map Bsb_build_schemas.pp_flags (fun p -> @@ -410,7 +388,7 @@ let interpret_json js_post_build_cmd = (extract_js_post_build map per_proj_dir); package_specs = (match package_kind with - | Toplevel -> Bsb_package_specs.from_map map + | Toplevel -> Bsb_package_specs.from_map ~cwd:per_proj_dir map | Pinned_dependency x | Dependency x -> x); file_groups = groups; @@ -434,7 +412,7 @@ let package_specs_from_bsconfig () = let json = Ext_json_parse.parse_json_from_file Literals.bsconfig_json in begin match json with | Obj {map} -> - Bsb_package_specs.from_map map, + Bsb_package_specs.from_map ~cwd:Bsb_global_paths.cwd map, extract_pinned_dependencies map | _ -> assert false end diff --git a/jscomp/bsb/bsb_config_types.ml b/jscomp/bsb/bsb_config_types.ml index ee508f615b..0627c18be5 100644 --- a/jscomp/bsb/bsb_config_types.ml +++ b/jscomp/bsb/bsb_config_types.ml @@ -61,7 +61,7 @@ type t = bs_dependencies : dependencies; bs_dev_dependencies : dependencies; pinned_dependencies : Set_string.t; - built_in_dependency : dependency option; + built_in_dependency : bool; warning : Bsb_warning.t; (*TODO: maybe we should always resolve bs-platform so that we can calculate correct relative path in diff --git a/jscomp/bsb/bsb_merlin_gen.ml b/jscomp/bsb/bsb_merlin_gen.ml index 3fe323fb8f..5eb9b01ad3 100644 --- a/jscomp/bsb/bsb_merlin_gen.ml +++ b/jscomp/bsb/bsb_merlin_gen.ml @@ -165,11 +165,13 @@ let merlin_file_gen ~per_proj_dir:(per_proj_dir:string) Buffer.add_string buffer merlin_b; Buffer.add_string buffer path ; ); - Ext_option.iter built_in_dependency (fun package -> - let path = package.package_install_path in - Buffer.add_string buffer (merlin_s ^ path ); - Buffer.add_string buffer (merlin_b ^ path) - ); + if built_in_dependency then ( + let path = + (Filename.dirname Bsb_global_paths.bsc_dir) + // "lib" //"ocaml" in + Buffer.add_string buffer (merlin_s ^ path ); + Buffer.add_string buffer (merlin_b ^ path) + ); let bsc_string_flag = bsc_flg_to_merlin_ocamlc_flg bsc_flags in Buffer.add_string buffer bsc_string_flag ; Buffer.add_string buffer (warning_to_merlin_flg warning); diff --git a/jscomp/bsb/bsb_ninja_gen.ml b/jscomp/bsb/bsb_ninja_gen.ml index 3e593a0b14..fcb77a3103 100644 --- a/jscomp/bsb/bsb_ninja_gen.ml +++ b/jscomp/bsb/bsb_ninja_gen.ml @@ -237,7 +237,7 @@ let output_ninja_and_namespace_map ~gentype_config ~has_postbuild:js_post_build_cmd ~pp_file - ~has_builtin:(built_in_dependency <> None) + ~has_builtin:built_in_dependency ~reason_react_jsx ~package_specs ~namespace diff --git a/jscomp/bsb/bsb_ninja_rule.ml b/jscomp/bsb/bsb_ninja_rule.ml index ad17af3993..e76b808612 100644 --- a/jscomp/bsb/bsb_ninja_rule.ml +++ b/jscomp/bsb/bsb_ninja_rule.ml @@ -172,7 +172,8 @@ let make_custom_rules if read_cmi <> `is_cmi then begin Ext_buffer.add_string buf " -bs-package-name "; Ext_buffer.add_string buf package_name; - Ext_buffer.add_string buf (Bsb_package_specs.package_flag_of_package_specs package_specs "$in_d") + Ext_buffer.add_string buf + (Bsb_package_specs.package_flag_of_package_specs package_specs ~dirname:"$in_d") end; begin match bs_dependencies, bs_dev_dependencies with | [], [] -> () diff --git a/jscomp/bsb/bsb_package_specs.ml b/jscomp/bsb/bsb_package_specs.ml index 598f7e9428..5f618e7da7 100644 --- a/jscomp/bsb/bsb_package_specs.ml +++ b/jscomp/bsb/bsb_package_specs.ml @@ -37,11 +37,18 @@ type spec = { suffix : Ext_js_suffix.t } +(*FIXME: use assoc list instead *) module Spec_set = Set.Make( struct type t = spec let compare = Pervasives.compare end) -type t = Spec_set.t +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 @@ -89,8 +96,8 @@ and from_json_single suffix (x : Ext_json_types.t) : spec = | Str {str = format; loc } -> {format = supported_format format loc ; in_source = false ; suffix } | Obj {map; loc} -> - begin match Map_string.find_exn map "module" with - | Str {str = format} -> + begin match map .?("module") with + | Some(Str {str = format}) -> let in_source = match map.?(Bsb_build_schemas.in_source) with | Some (True _) -> true @@ -108,13 +115,10 @@ and from_json_single suffix (x : Ext_json_types.t) : spec = Bsb_exception.errorf ~loc:(Ext_json.loc_of x) "expect a string field" | None -> suffix in {format = supported_format format loc ; in_source ; suffix} - | Arr _ -> - 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." - | _ -> + | Some _ -> Bsb_exception.errorf ~loc - "package-specs: the `module` field of the configuration object should be a string." - | exception _ -> + "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." end @@ -144,12 +148,16 @@ let package_flag ({format; in_source; suffix } : spec) dir = (Ext_js_suffix.to_string suffix) ) +(* FIXME: we should adapt it *) let package_flag_of_package_specs (package_specs : t) - (dirname : string ) : string = - Spec_set.fold (fun format acc -> + ~(dirname : string ) : string = + let res = Spec_set.fold (fun format acc -> Ext_string.inter2 acc (package_flag format dirname ) - ) package_specs Ext_string.empty - + ) package_specs.modules Ext_string.empty in + match package_specs.runtime with + | None -> res + | Some x -> + res ^ " -runtime " ^ x let default_package_specs suffix = Spec_set.singleton { format = NodeJS ; in_source = false; suffix } @@ -161,7 +169,7 @@ let default_package_specs suffix = *) let get_list_of_output_js - (package_specs : Spec_set.t) + (package_specs : t) (output_file_sans_extension : string) = Spec_set.fold @@ -174,17 +182,17 @@ let get_list_of_output_js (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 [] + ) package_specs.modules [] let list_dirs_by - (package_specs : Spec_set.t) + (package_specs : t) (f : string -> unit) = Spec_set.iter (fun (spec : spec) -> if not spec.in_source then f (Bsb_config.top_prefix_of_format spec.format) - ) package_specs + ) package_specs.modules type json_map = Ext_json_types.t Map_string.t @@ -201,10 +209,20 @@ let extract_bs_suffix_exn (map : json_map) : Ext_js_suffix.t = Bsb_exception.config_error config "expect a string exteion like \".js\" here" -let from_map map = +let from_map ~(cwd:string) map = let suffix = extract_bs_suffix_exn map in - match map.?(Bsb_build_schemas.package_specs) with + let modules = match map.?(Bsb_build_schemas.package_specs) with | Some x -> from_json suffix x - | None -> default_package_specs suffix + | 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 + } diff --git a/jscomp/bsb/bsb_package_specs.mli b/jscomp/bsb/bsb_package_specs.mli index 540a1364f3..3b7f933154 100644 --- a/jscomp/bsb/bsb_package_specs.mli +++ b/jscomp/bsb/bsb_package_specs.mli @@ -28,6 +28,7 @@ type t val from_map: + cwd:string -> Ext_json_types.t Map_string.t -> t val get_list_of_output_js : @@ -39,8 +40,11 @@ val get_list_of_output_js : Sample output: {[ -bs-package-output commonjs:lib/js/jscomp/test]} *) val package_flag_of_package_specs : - t -> string -> string + t -> + dirname:string -> + string +(* used to ensure each dir does exist *) val list_dirs_by : t -> (string -> unit) -> diff --git a/jscomp/bsb/bsb_pkg.mli b/jscomp/bsb/bsb_pkg.mli index 1ad08f65ee..86d4d50542 100644 --- a/jscomp/bsb/bsb_pkg.mli +++ b/jscomp/bsb/bsb_pkg.mli @@ -38,6 +38,7 @@ val resolve_bs_package : cwd:string -> Bsb_pkg_types.t -> string +(** used by watcher *) val to_list: (Bsb_pkg_types.t -> string -> diff --git a/jscomp/bsb/bsb_pkg_types.ml b/jscomp/bsb/bsb_pkg_types.ml index 48c4206dfb..ae88413168 100644 --- a/jscomp/bsb/bsb_pkg_types.ml +++ b/jscomp/bsb/bsb_pkg_types.ml @@ -95,7 +95,10 @@ let string_as_package (s : string) : t = if v = '@' then let scope_id = Ext_string.no_slash_idx s in - assert (scope_id > 0); + 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 diff --git a/jscomp/common/js_config.ml b/jscomp/common/js_config.ml index 43b87c1c81..aba3327e90 100644 --- a/jscomp/common/js_config.ml +++ b/jscomp/common/js_config.ml @@ -92,3 +92,4 @@ let as_ppx = ref false let mono_empty_array = ref true +let customize_runtime = ref None \ No newline at end of file diff --git a/jscomp/common/js_config.mli b/jscomp/common/js_config.mli index 69da84f706..719c49fa6d 100644 --- a/jscomp/common/js_config.mli +++ b/jscomp/common/js_config.mli @@ -100,3 +100,4 @@ val no_export: bool ref val as_ppx : bool ref val mono_empty_array : bool ref +val customize_runtime : string option ref \ No newline at end of file diff --git a/jscomp/core/js_name_of_module_id.ml b/jscomp/core/js_name_of_module_id.ml index 172d8f545a..9965b9db0d 100644 --- a/jscomp/core/js_name_of_module_id.ml +++ b/jscomp/core/js_name_of_module_id.ml @@ -80,7 +80,12 @@ let get_runtime_module_path (*Invariant: the package path to bs-platform, it is used to calculate relative js path *) - ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + (match !Js_config.customize_runtime with + | None -> + ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + | Some path -> + path //dep_path // js_file + ) @@ -142,6 +147,9 @@ let string_of_module_id which is guaranteed by [-bs-package-output] *) else + if Js_packages_info.is_runtime_package dep_package_info then + get_runtime_module_path dep_module_id current_package_info module_system + else begin match module_system with | NodeJS | Es6 -> dep_pkg.pkg_rel_path // js_file diff --git a/jscomp/core/js_packages_info.ml b/jscomp/core/js_packages_info.ml index 27eab46e07..a3a808fe61 100644 --- a/jscomp/core/js_packages_info.ml +++ b/jscomp/core/js_packages_info.ml @@ -94,7 +94,19 @@ let runtime_test_package_specs : t = { name = Pkg_runtime; module_systems = [] } -let same_package_by_name (x : t) (y : t) = x.name = y.name + +let same_package_by_name (x : t) (y : t) = + match x.name with + | Pkg_empty -> + y.name = Pkg_empty + | Pkg_runtime -> + y.name = Pkg_runtime + | Pkg_normal s -> + begin match y.name with + | Pkg_normal y -> s = y + | Pkg_empty | Pkg_runtime -> false + end + let is_runtime_package (x : t) = x.name = Pkg_runtime diff --git a/jscomp/main/js_main.ml b/jscomp/main/js_main.ml index 075b401397..dc83d42971 100644 --- a/jscomp/main/js_main.ml +++ b/jscomp/main/js_main.ml @@ -32,7 +32,18 @@ let setup_error_printer (syntax_kind : [ `ml | `reason | `rescript ])= - +let setup_runtime_path path = + let u0 = Filename.dirname path in + let std = Filename.basename path in + let _path = Filename.dirname u0 in + let rescript = Filename.basename u0 in + (match rescript.[0] with + | '@' -> (* scoped package *) + Bs_version.package_name := rescript ^ "/" ^ std; + | _ -> Bs_version.package_name := std + | exception _ -> + Bs_version.package_name := std); + Js_config.customize_runtime := Some path let handle_reason (type a) (kind : a Ml_binary.kind) sourcefile ppf = setup_error_printer `reason; @@ -443,6 +454,8 @@ let buckle_script_flags : (string * Bsc_args.spec * string) array = " Enable or disable error status for warnings according\n\ to . See option -w for the syntax of .\n\ Default setting is " ^ Bsc_warnings.defaults_warn_error; + "-runtime",string_call setup_runtime_path, + "*internal* Set the runtime directory"; "-make-runtime", unit_call Js_packages_state.make_runtime, "*internal* make runtime library"; "-make-runtime-test", unit_call Js_packages_state.make_runtime_test, diff --git a/lib/4.06.1/bsb.ml b/lib/4.06.1/bsb.ml index f25c7453c6..637cceab9d 100644 --- a/lib/4.06.1/bsb.ml +++ b/lib/4.06.1/bsb.ml @@ -1928,6 +1928,7 @@ let export_none = "none" let use_stdlib = "use-stdlib" +let external_stdlib = "external-stdlib" let reason = "reason" let react_jsx = "react-jsx" @@ -6068,7 +6069,10 @@ let string_as_package (s : string) : t = if v = '@' then let scope_id = Ext_string.no_slash_idx s in - assert (scope_id > 0); + 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 @@ -6557,36 +6561,55 @@ let () = ) end -module Ext_js_suffix -= struct -#1 "ext_js_suffix.ml" -type t = - | Js - | Bs_js - | Mjs - | Cjs - | Unknown_extension -let to_string (x : t) = - match x with - | Js -> Literals.suffix_js - | Bs_js -> Literals.suffix_bs_js - | Mjs -> Literals.suffix_mjs - | Cjs -> Literals.suffix_cjs - | Unknown_extension -> assert false +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 -let of_string (x : string) : t = - match () with - | () when x = Literals.suffix_js -> Js - | () when x = Literals.suffix_bs_js -> Bs_js - | () when x = Literals.suffix_mjs -> Mjs - | () when x = Literals.suffix_cjs -> Cjs - | _ -> Unknown_extension +type style + = FG of color + | BG of color + | Bold + | Dim +(** Input is the tag for example `@{@}` return escape code *) +val ansi_of_tag : string -> string -end -module Ext_filename : sig -#1 "ext_filename.mli" +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 @@ -6614,67 +6637,83 @@ module Ext_filename : sig +type color + = Black + | Red + | Green + | Yellow + | Blue + | Magenta + | Cyan + | White -(* TODO: - Change the module name, this code is not really an extension of the standard - library but rather specific to JS Module name convention. -*) - - - +type style + = FG of color + | BG of color + | Bold + | Dim -(** An extension module to calculate relative path follow node/npm style. - TODO : this short name will have to change upon renaming the file. -*) +(* let ansi_of_color = function + | Black -> "0" + | Red -> "1" + | Green -> "2" + | Yellow -> "3" + | Blue -> "4" + | Magenta -> "5" + | Cyan -> "6" + | White -> "7" *) -val is_dir_sep : - char -> bool +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" -val maybe_quote: - string -> - string + | BG Black -> "40" + | BG Red -> "41" + | BG Green -> "42" + | BG Yellow -> "43" + | BG Blue -> "44" + | BG Magenta -> "45" + | BG Cyan -> "46" + | BG White -> "47" -val chop_extension_maybe: - string -> - string + | Bold -> "1" + | Dim -> "2" -(* return an empty string if no extension found *) -val get_extension_maybe: - string -> - string -val new_extension: - string -> - string -> - string +(** 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] + | _ -> [] -val chop_all_extensions_maybe: - string -> - string +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" -(* OCaml specific abstraction*) -val module_name: - string -> - string +let reset_lit = "\x1b[0m" -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. +end +module Bsb_log : sig +#1 "bsb_log.mli" +(* Copyright (C) 2017 Authors of BuckleScript * * 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 @@ -6699,166 +6738,146 @@ end = struct * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) +val setup : unit -> unit +type level = + | Debug + | Info + | Warn + | Error -let is_dir_sep_unix c = c = '/' -let is_dir_sep_win_cygwin c = - c = '/' || c = '\\' || c = ':' +val log_level : level ref -let is_dir_sep = - if Sys.unix then is_dir_sep_unix else is_dir_sep_win_cygwin +type 'a fmt = Format.formatter -> ('a, Format.formatter, unit) format -> 'a -(* 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 +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 -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) +val info_args : string array -> unit -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 +end = struct +#1 "bsb_log.ml" +(* Copyright (C) 2017- Authors of BuckleScript + * + * 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 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 +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 -(** 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 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 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 +let setup () = + begin + 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 + end -type state = - | Invalid - | Upper - | Lower +type level = + | Debug + | Info + | Warn + | Error -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 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 + begin + 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 () + end + else () + -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" +module Bsb_real_path : sig +#1 "bsb_real_path.mli" + (* Copyright (C) 2020- Authors of BuckleScript - * + * * 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 @@ -6876,28 +6895,58 @@ module Ext_js_file_kind * 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 t = { - case : case; - suffix : Ext_js_suffix.t; -} +val is_same_paths_via_io : string -> string -> bool + +end = struct +#1 "bsb_real_path.ml" +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) -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 BuckleScript +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 @@ -6922,49 +6971,13 @@ module Ext_namespace : sig * 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 power_2_above : int -> int -> int - -(** [js_name_of_modulename ~little A-Ns] - *) -val js_name_of_modulename : - string -> - Ext_js_file_kind.case -> - Ext_js_suffix.t -> - string - -(* 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 - +val stats_to_string : Hashtbl.statistics -> string end = struct -#1 "ext_namespace.ml" - +#1 "ext_util.ml" (* Copyright (C) 2015-2016 Bloomberg Finance L.P. * * This program is free software: you can redistribute it and/or modify @@ -6989,396 +7002,419 @@ end = struct * 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))) +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 *) -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 ) +(* 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 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 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 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 +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 -end -module Bsb_package_specs : sig -#1 "bsb_package_specs.mli" -(* Copyright (C) 2017 Authors of BuckleScript - * - * 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 reset h = + h.size <- 0; + h.data <- Array.make h.initial_size Empty -type t +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 begin + 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 + begin match Array.unsafe_get ndata_tail nidx with + | Empty -> + Array.unsafe_set ndata nidx cell + | Cons tail -> + tail.next <- cell + end; + 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 + end -val from_map: - Ext_json_types.t Map_string.t -> t -val get_list_of_output_js : - t -> - string -> - string list +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 -(** - Sample output: {[ -bs-package-output commonjs:lib/js/jscomp/test]} -*) -val package_flag_of_package_specs : - t -> string -> string +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 -val list_dirs_by : - t -> - (string -> unit) -> - unit -end = struct -#1 "bsb_package_specs.ml" -(* Copyright (C) 2017 Authors of BuckleScript - * - * 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 to_list h f = + fold h [] (fun k data acc -> f k data :: acc) -let (//) = Ext_path.combine +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 -(* 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 -} +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 -module Spec_set = Set.Make( struct type t = spec - let compare = Pervasives.compare - end) -type t = Spec_set.t +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 (.?()) = 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 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 supported_format (x : string) loc = - 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 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 begin + h.size <- h.size - 1; + match prec with + | Empty -> Array.unsafe_set h.data i next + | Cons c -> c.next <- next + end + else remove_bucket h i key ~prec:buck next eq_key -let string_of_format (x : format) = - match x with - | NodeJS -> Literals.commonjs - | Es6 -> Literals.es6 - | Es6_global -> Literals.es6_global +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 -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 + 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 -(* TODO: FIXME: better API without mutating *) -and from_json_single suffix (x : Ext_json_types.t) : spec = - match x with - | Str {str = format; loc } -> - {format = supported_format format loc ; in_source = false ; suffix } - | Obj {map; loc} -> - begin match Map_string.find_exn map "module" with - | 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} - | Arr _ -> - 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." - | _ -> - Bsb_exception.errorf ~loc - "package-specs: the `module` field of the configuration object should be a string." - | exception _ -> - Bsb_exception.errorf ~loc - "package-specs: when the configuration is an object, the `module` field is mandatory." - end - | _ -> Bsb_exception.errorf ~loc:(Ext_json.loc_of x) - "package-specs: we expect either a string or an object." + (** 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_key_opt: 'a t -> key -> key option -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 ) + val find_default: 'a t -> key -> 'a -> 'a -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 } : 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) - ) + 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 -let package_flag_of_package_specs (package_specs : t) - (dirname : string ) : string = - Spec_set.fold (fun format acc -> - Ext_string.inter2 acc (package_flag format dirname ) - ) package_specs Ext_string.empty -let default_package_specs suffix = - Spec_set.singleton - { format = NodeJS ; in_source = false; suffix } -(** - [get_list_of_output_js specs "src/hi/hello"] +end +module Hash : sig +#1 "hash.mli" -*) -let get_list_of_output_js - (package_specs : Spec_set.t) - (output_file_sans_extension : string) - = - Spec_set.fold - (fun (spec : 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 [] +module Make (Key : Hashtbl.HashedType) : Hash_gen.S with type key = Key.t -let list_dirs_by - (package_specs : Spec_set.t) - (f : string -> unit) - = - Spec_set.iter (fun (spec : spec) -> - if not spec.in_source then - f (Bsb_config.top_prefix_of_format spec.format) - ) package_specs +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)) -type json_map = Ext_json_types.t Map_string.t +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 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 .bs.js, .js, .cjs, .mjs here" - else s - | Some config -> - Bsb_exception.config_error config - "expect a string exteion like \".js\" here" +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 from_map map = - let suffix = extract_bs_suffix_exn map in - match map.?(Bsb_build_schemas.package_specs) with - | Some x -> - from_json suffix x - | None -> default_package_specs suffix +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 -end -module Bsb_package_kind -= struct -#1 "bsb_package_kind.ml" -(* 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 +let mem (h : _ t) key = + Hash_gen.small_bucket_mem + (Array.unsafe_get h.data (key_index h key)) + eq_key key - * - * 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 = - | Toplevel - | Dependency of Bsb_package_specs.t - | Pinned_dependency of Bsb_package_specs.t - (* This package specs comes from the toplevel to - override the current settings - *) +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 -module Bsc_warnings -= struct -#1 "bsc_warnings.ml" -(* Copyright (C) 2020- Authors of BuckleScript - * + +end +module Bsb_pkg : sig +#1 "bsb_pkg.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 @@ -7395,70 +7431,37 @@ module Bsc_warnings * 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] -(** - 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 - ]} + it relies on [npm_config_prefix] env variable for global npm modules +*) - -9 Missing fields in a record pattern. - only in some special cases that we need all fields being listed +(** @raise when not found *) +val resolve_bs_package : + cwd:string -> Bsb_pkg_types.t -> string - 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*) +(** used by watcher *) +val to_list: + (Bsb_pkg_types.t -> + string -> + 'a + ) -> 'a list +end = struct +#1 "bsb_pkg.ml" -end -module Bsb_warning : sig -#1 "bsb_warning.mli" -(* Copyright (C) 2017 Authors of BuckleScript +(* Copyright (C) 2017- Authors of BuckleScript * * 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 @@ -7482,30 +7485,208 @@ module Bsb_warning : sig * 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 -type t +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 -(** Extra work is need to make merlin happy *) -val to_merlin_string : t -> string +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 -val from_map : Ext_json_types.t Map_string.t -> t -(** [to_bsb_string not_dev warning] +let to_list cb = + Coll.to_list cache cb + +(* Some package managers will implement "postinstall" caches, that do not + * keep their build artifacts in the local node_modules. Similar to + * npm_config_prefix, bs_custom_resolution allows these to specify the + * exact location of build cache, but on a per-package basis. Implemented as + * environment lookup to avoid invasive changes to bsconfig and mandates. *) +let custom_resolution = lazy + (match Sys.getenv "bs_custom_resolution" with + | exception Not_found -> false + | "true" -> true + | _ -> false) + +let pkg_name_as_variable package = + Bsb_pkg_types.to_string package + |> fun s -> Ext_string.split s '@' + |> String.concat "" + |> fun s -> Ext_string.split s '_' + |> String.concat "__" + |> fun s -> Ext_string.split s '/' + |> String.concat "__slash__" + |> fun s -> Ext_string.split s '.' + |> String.concat "__dot__" + |> fun s -> Ext_string.split s '-' + |> String.concat "_" + +(** TODO: collect all warnings and print later *) +let resolve_bs_package ~cwd (package : t) = + if Lazy.force custom_resolution then + begin + Bsb_log.info "@{Using Custom Resolution@}@."; + let custom_pkg_loc = pkg_name_as_variable package ^ "__install" in + let custom_pkg_location = lazy (Sys.getenv custom_pkg_loc) in + match Lazy.force custom_pkg_location with + | exception Not_found -> + begin + Bsb_log.error + "@{Custom resolution of package %s does not exist in var %s @}@." + (Bsb_pkg_types.to_string package) + custom_pkg_loc; + Bsb_exception.package_not_found ~pkg:package ~json:None + end + | path when not (Sys.file_exists path) -> + begin + Bsb_log.error + "@{Custom resolution of package %s does not exist on disk: %s=%s @}@." + (Bsb_pkg_types.to_string package) + custom_pkg_loc + path; + Bsb_exception.package_not_found ~pkg:package ~json:None + end + | path -> + begin + Bsb_log.info + "@{Custom Resolution of package %s in var %s found at %s@}@." + (Bsb_pkg_types.to_string package) + custom_pkg_loc + path; + path + end + end + else + match Coll.find_opt cache package with + | None -> + let result = resolve_bs_package_aux ~cwd package in + Bsb_log.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 + begin + Bsb_log.warn + "@{Duplicated package:@} %a %s (chosen) vs %s in %s @." + Bsb_pkg_types.print package x result cwd; + end; + 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 *) -val to_bsb_string : - package_kind:Bsb_package_kind.t -> - t -> - string +(* 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 *) -val use_default : t -end = struct -#1 "bsb_warning.ml" -(* Copyright (C) 2017 Authors of BuckleScript - * +end +module Ext_js_suffix += struct +#1 "ext_js_suffix.ml" +type t = + | Js + | Bs_js + | Mjs + | Cjs + | Unknown_extension +let to_string (x : t) = + match x with + | Js -> Literals.suffix_js + | Bs_js -> Literals.suffix_bs_js + | Mjs -> Literals.suffix_mjs + | Cjs -> Literals.suffix_cjs + | Unknown_extension -> assert false + + +let of_string (x : string) : t = + match () with + | () when x = Literals.suffix_js -> Js + | () when x = Literals.suffix_bs_js -> Bs_js + | () when x = Literals.suffix_mjs -> Mjs + | () when x = Literals.suffix_cjs -> Cjs + | _ -> Unknown_extension + + +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 @@ -7523,195 +7704,74 @@ end = struct * 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 +(* TODO: + Change the module name, this code is not really an extension of the standard + library but rather specific to JS Module name convention. +*) -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 } +(** An extension module to calculate relative path follow node/npm style. + TODO : this short name will have to change upon renaming the file. +*) -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 *) +val is_dir_sep : + char -> bool + +val maybe_quote: + string -> + string -end -module Bsb_config_types -= struct -#1 "bsb_config_types.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. *) +val chop_extension_maybe: + string -> + string +(* return an empty string if no extension found *) +val get_extension_maybe: + string -> + string -type dependency = - { - package_name : Bsb_pkg_types.t ; - package_install_path : string ; - } -type dependencies = dependency list +val new_extension: + string -> + string -> + string +val chop_all_extensions_maybe: + string -> + string +(* OCaml specific abstraction*) +val module_name: + string -> + string -type reason_react_jsx = - | Jsx_v3 - (* string option *) -type refmt = string option -type gentype_config = { - path : string (* resolved *) -} -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 : dependency option; - warning : Bsb_warning.t; - (*TODO: maybe we should always resolve bs-platform - 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; (* 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 option; - } +type module_info = { + module_name : string ; + case : bool; +} -end -module Ext_color : sig -#1 "ext_color.mli" + + +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 @@ -7736,133 +7796,206 @@ module Ext_color : sig * 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 -(** Input is the tag for example `@{@}` return escape code *) -val ansi_of_tag : string -> string -val reset_lit : string +let is_dir_sep_unix c = c = '/' +let is_dir_sep_win_cygwin c = + c = '/' || c = '\\' || c = ':' -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. *) +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) -type color - = Black - | Red - | Green - | Yellow - | Blue - | Magenta - | Cyan - | White +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 -type style - = FG of color - | BG of color - | Bold - | Dim +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 ansi_of_color = function - | Black -> "0" - | Red -> "1" - | Green -> "2" - | Yellow -> "3" - | Blue -> "4" - | Magenta -> "5" - | Cyan -> "6" - | White -> "7" *) +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 -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: 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; +} -(** 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 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 reset_lit = "\x1b[0m" +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- Authors of BuckleScript + * + * 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 t = { + case : case; + suffix : Ext_js_suffix.t; +} +let any_runtime_kind = { + case = Little; + suffix = Ext_js_suffix.Js +} end -module Bsb_log : sig -#1 "bsb_log.mli" -(* Copyright (C) 2017 Authors of BuckleScript +module Ext_namespace : sig +#1 "ext_namespace.mli" +(* Copyright (C) 2017- Authors of BuckleScript * * 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 @@ -7887,31 +8020,50 @@ module Bsb_log : sig * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) -val setup : unit -> unit -type level = - | Debug - | Info - | Warn - | Error +val try_split_module_name : + string -> (string * string ) option -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 +(* 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 -val verbose : unit -> unit -val debug : 'a log -val info : 'a log -val warn : 'a log -val error : 'a log + #1933 when removing ns suffix, don't pass the bound + of basename +*) +val change_ext_ns_suffix : + string -> + string -> + string -val info_args : string array -> unit + + +(** [js_name_of_modulename ~little A-Ns] + *) +val js_name_of_modulename : + string -> + Ext_js_file_kind.case -> + Ext_js_suffix.t -> + string + +(* 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 "bsb_log.ml" -(* Copyright (C) 2017- Authors of BuckleScript +#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 @@ -7937,96 +8089,94 @@ end = struct -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 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 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 () = - begin - 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 - end - -type level = - | Debug - | Info - | Warn - | Error +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 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 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) -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 +(* 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 info_args (args : string array) = - if int_of_level Info >= int_of_level !log_level then - begin - 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 () - end - else () - +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_real_path : sig -#1 "bsb_real_path.mli" - -(* Copyright (C) 2020- Authors of BuckleScript - * +module Bsb_package_specs : sig +#1 "bsb_package_specs.mli" +(* Copyright (C) 2017 Authors of BuckleScript + * * 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 @@ -8044,90 +8194,41 @@ module Bsb_real_path : sig * 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" -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 +type t -let is_same_paths_via_io a b = - if a = b - then true - else (real_path a) = (real_path b) -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 from_map: + cwd:string -> + Ext_json_types.t Map_string.t -> t - -val power_2_above : int -> int -> int +val get_list_of_output_js : + t -> + string -> + string list +(** + Sample output: {[ -bs-package-output commonjs:lib/js/jscomp/test]} +*) +val package_flag_of_package_specs : + t -> + dirname:string -> + string -val stats_to_string : Hashtbl.statistics -> string +(* used to ensure each dir does exist *) +val list_dirs_by : + t -> + (string -> unit) -> + unit end = struct -#1 "ext_util.ml" -(* Copyright (C) 2015-2016 Bloomberg Finance L.P. +#1 "bsb_package_specs.ml" +(* Copyright (C) 2017 Authors of BuckleScript * * 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 @@ -8151,419 +8252,380 @@ end = struct * 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))) -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 *) +let (//) = Ext_path.combine +(* TODO: sync up with {!Js_packages_info.module_system} *) +type format = Ext_module_system.t = + | NodeJS | Es6 | Es6_global -(* 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 begin - 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 - begin match Array.unsafe_get ndata_tail nidx with - | Empty -> - Array.unsafe_set ndata nidx cell - | Cons tail -> - tail.next <- cell - end; - 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 - end +type spec = { + format : format; + in_source : bool; + suffix : Ext_js_suffix.t +} +(*FIXME: use assoc list instead *) +module Spec_set = Set.Make( struct type t = spec + let compare = Pervasives.compare + end) +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 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 (.?()) = Map_string.find_opt -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 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 to_list h f = - fold h [] (fun k data acc -> f k data :: acc) +let supported_format (x : string) loc = + 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 : format) = + 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 -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 +(* TODO: FIXME: better API without mutating *) +and from_json_single suffix (x : Ext_json_types.t) : spec = + match x with + | Str {str = format; loc } -> + {format = supported_format format loc ; in_source = false ; suffix } + | Obj {map; loc} -> + begin 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." + end + | _ -> 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 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 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 } : 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 = 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 -> + res ^ " -runtime " ^ x +let default_package_specs suffix = + Spec_set.singleton + { format = NodeJS ; in_source = false; suffix } -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 +(** + [get_list_of_output_js specs "src/hi/hello"] -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 begin - h.size <- h.size - 1; - match prec with - | Empty -> Array.unsafe_set h.data i next - | Cons c -> c.next <- next - end - else remove_bucket h i key ~prec:buck next eq_key +*) +let get_list_of_output_js + (package_specs : t) + (output_file_sans_extension : string) + = + Spec_set.fold + (fun (spec : 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 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 +let list_dirs_by + (package_specs : t) + (f : string -> unit) + = + Spec_set.iter (fun (spec : 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 - 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 +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 .bs.js, .js, .cjs, .mjs here" + else s + | Some config -> + Bsb_exception.config_error config + "expect a string exteion like \".js\" here" - (** 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_key_opt: 'a t -> key -> key option +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 + } - 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 +module Bsb_package_kind += struct +#1 "bsb_package_kind.ml" +(* 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 + * + * 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 = + | Toplevel + | Dependency of Bsb_package_specs.t + | Pinned_dependency of Bsb_package_specs.t + (* This package specs comes from the toplevel to + override the current settings + *) 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 - +module Bsc_warnings += struct +#1 "bsc_warnings.ml" +(* Copyright (C) 2020- Authors of BuckleScript + * + * 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 -# 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 *) + * + * 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 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 +(** + See the meanings of the warning codes here: https://caml.inria.fr/pub/docs/manual-ocaml/comp.html#sec281 -(* 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 + - 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. -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 + - 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. +*) -(* 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 +(* + The purpose of default warning set is to make it strict while + not annoy user too much -let find_opt (h : _ t) key = - Hash_gen.small_bucket_opt eq_key key (Array.unsafe_get h.data (key_index h key)) + -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 + ]} -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)) + -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). -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)) + - 50 Unexpected documentation comment. -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)) + - 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 Authors of BuckleScript + * + * 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 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 +type t -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 +(** Extra work is need to make merlin happy *) +val to_merlin_string : t -> string -# 143 "ext/hash.cppo.ml" -end -end -module Bsb_pkg : sig -#1 "bsb_pkg.mli" -(* Copyright (C) 2015-2016 Bloomberg Finance L.P. - * +val from_map : Ext_json_types.t Map_string.t -> t + +(** [to_bsb_string not_dev warning] +*) +val to_bsb_string : + package_kind:Bsb_package_kind.t -> + t -> + string + +val use_default : t +end = struct +#1 "bsb_warning.ml" +(* Copyright (C) 2017 Authors of BuckleScript + * * 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 @@ -8581,35 +8643,112 @@ module Bsb_pkg : sig * 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] +type warning_error = + | Warn_error_false + (* default [false] to make our changes non-intrusive *) + | Warn_error_true + | Warn_error_number of string - it relies on [npm_config_prefix] env variable for global npm modules -*) +type t0 = { + number : string option; + error : warning_error +} -(** @raise when not found *) -val resolve_bs_package : - cwd:string -> Bsb_pkg_types.t -> string +type nonrec t = t0 option +let use_default = None -val to_list: - (Bsb_pkg_types.t -> - string -> - 'a - ) -> 'a list -end = struct -#1 "bsb_pkg.ml" +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 } -(* Copyright (C) 2017- Authors of BuckleScript + +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. * * 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 @@ -8633,174 +8772,62 @@ end = struct * 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) +type dependency = + { + package_name : Bsb_pkg_types.t ; + package_install_path : string ; + } +type dependencies = dependency list -let cache : string Coll.t = Coll.create 0 -let to_list cb = - Coll.to_list cache cb - -(* Some package managers will implement "postinstall" caches, that do not - * keep their build artifacts in the local node_modules. Similar to - * npm_config_prefix, bs_custom_resolution allows these to specify the - * exact location of build cache, but on a per-package basis. Implemented as - * environment lookup to avoid invasive changes to bsconfig and mandates. *) -let custom_resolution = lazy - (match Sys.getenv "bs_custom_resolution" with - | exception Not_found -> false - | "true" -> true - | _ -> false) -let pkg_name_as_variable package = - Bsb_pkg_types.to_string package - |> fun s -> Ext_string.split s '@' - |> String.concat "" - |> fun s -> Ext_string.split s '_' - |> String.concat "__" - |> fun s -> Ext_string.split s '/' - |> String.concat "__slash__" - |> fun s -> Ext_string.split s '.' - |> String.concat "__dot__" - |> fun s -> Ext_string.split s '-' - |> String.concat "_" +type reason_react_jsx = + | Jsx_v3 + (* string option *) -(** TODO: collect all warnings and print later *) -let resolve_bs_package ~cwd (package : t) = - if Lazy.force custom_resolution then - begin - Bsb_log.info "@{Using Custom Resolution@}@."; - let custom_pkg_loc = pkg_name_as_variable package ^ "__install" in - let custom_pkg_location = lazy (Sys.getenv custom_pkg_loc) in - match Lazy.force custom_pkg_location with - | exception Not_found -> - begin - Bsb_log.error - "@{Custom resolution of package %s does not exist in var %s @}@." - (Bsb_pkg_types.to_string package) - custom_pkg_loc; - Bsb_exception.package_not_found ~pkg:package ~json:None - end - | path when not (Sys.file_exists path) -> - begin - Bsb_log.error - "@{Custom resolution of package %s does not exist on disk: %s=%s @}@." - (Bsb_pkg_types.to_string package) - custom_pkg_loc - path; - Bsb_exception.package_not_found ~pkg:package ~json:None - end - | path -> - begin - Bsb_log.info - "@{Custom Resolution of package %s in var %s found at %s@}@." - (Bsb_pkg_types.to_string package) - custom_pkg_loc - path; - path - end - end - else - match Coll.find_opt cache package with - | None -> - let result = resolve_bs_package_aux ~cwd package in - Bsb_log.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 - begin - Bsb_log.warn - "@{Duplicated package:@} %a %s (chosen) vs %s in %s @." - Bsb_pkg_types.print package x result cwd; - end; - x +type refmt = string option +type gentype_config = { + path : string (* resolved *) +} +type command = string -(** 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 *) +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 bs-platform + 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; (* 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 option; + } end module Ext_json_parse : sig @@ -11119,48 +11146,26 @@ let extract_package_name_and_namespace - the running bsb and vendoring bsb is the same - the running bsb need delete stale build artifacts (kinda check npm upgrade) -*) -let check_version_exit (map : json_map) stdlib_path = - match Map_string.find_exn map Bsb_build_schemas.version with - | Str {str } -> - if str <> Bs_version.version then - begin - Format.fprintf Format.err_formatter - "@{bs-platform version mismatch@} Running bsb @{%s@} (%s) vs vendored @{%s@} (%s)@." - Bs_version.version - (Filename.dirname (Filename.dirname Sys.executable_name)) - str - stdlib_path - ; - exit 2 - end - | _ -> assert false -let check_stdlib (map : json_map) cwd (*built_in_package*) = + Note if the setup is correct: + the running compiler and node_modules/bs-platform + 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) (*built_in_package*) : bool = match map.?( Bsb_build_schemas.use_stdlib) with - | Some (False _) -> None + | Some (False _) -> false | None - | Some _ -> - begin - let current_package : Bsb_pkg_types.t = Global !Bs_version.package_name in - if Sys.getenv_opt "RES_SKIP_STDLIB_CHECK" = None then begin - let stdlib_path = - Bsb_pkg.resolve_bs_package ~cwd current_package in - let json_spec = - Ext_json_parse.parse_json_from_file - (* No exn raised: stdlib has package.json *) - (Filename.concat stdlib_path Literals.package_json) in - match json_spec with - | Obj {map} -> - check_version_exit map stdlib_path; - - | _ -> assert false - end; - Some { - Bsb_config_types.package_name = current_package; - package_install_path = Filename.dirname Bsb_global_paths.bsc_dir // Bsb_config.lib_ocaml; - } - end + | Some _ -> + true + @@ -11384,7 +11389,7 @@ let interpret_json array from the bsconfig and set the backend_ref to the first entry, if any. *) (* The default situation is empty *) - let built_in_package = check_stdlib map per_proj_dir in + let built_in_package : bool = check_stdlib map in let pp_flags : string option = extract_string map Bsb_build_schemas.pp_flags (fun p -> @@ -11439,7 +11444,7 @@ let interpret_json js_post_build_cmd = (extract_js_post_build map per_proj_dir); package_specs = (match package_kind with - | Toplevel -> Bsb_package_specs.from_map map + | Toplevel -> Bsb_package_specs.from_map ~cwd:per_proj_dir map | Pinned_dependency x | Dependency x -> x); file_groups = groups; @@ -11463,7 +11468,7 @@ let package_specs_from_bsconfig () = let json = Ext_json_parse.parse_json_from_file Literals.bsconfig_json in begin match json with | Obj {map} -> - Bsb_package_specs.from_map map, + Bsb_package_specs.from_map ~cwd:Bsb_global_paths.cwd map, extract_pinned_dependencies map | _ -> assert false end @@ -12014,11 +12019,13 @@ let merlin_file_gen ~per_proj_dir:(per_proj_dir:string) Buffer.add_string buffer merlin_b; Buffer.add_string buffer path ; ); - Ext_option.iter built_in_dependency (fun package -> - let path = package.package_install_path in - Buffer.add_string buffer (merlin_s ^ path ); - Buffer.add_string buffer (merlin_b ^ path) - ); + if built_in_dependency then ( + let path = + (Filename.dirname Bsb_global_paths.bsc_dir) + // "lib" //"ocaml" in + Buffer.add_string buffer (merlin_s ^ path ); + Buffer.add_string buffer (merlin_b ^ path) + ); let bsc_string_flag = bsc_flg_to_merlin_ocamlc_flg bsc_flags in Buffer.add_string buffer bsc_string_flag ; Buffer.add_string buffer (warning_to_merlin_flg warning); @@ -12940,7 +12947,8 @@ let make_custom_rules if read_cmi <> `is_cmi then begin Ext_buffer.add_string buf " -bs-package-name "; Ext_buffer.add_string buf package_name; - Ext_buffer.add_string buf (Bsb_package_specs.package_flag_of_package_specs package_specs "$in_d") + Ext_buffer.add_string buf + (Bsb_package_specs.package_flag_of_package_specs package_specs ~dirname:"$in_d") end; begin match bs_dependencies, bs_dev_dependencies with | [], [] -> () @@ -13757,7 +13765,7 @@ let output_ninja_and_namespace_map ~gentype_config ~has_postbuild:js_post_build_cmd ~pp_file - ~has_builtin:(built_in_dependency <> None) + ~has_builtin:built_in_dependency ~reason_react_jsx ~package_specs ~namespace diff --git a/lib/4.06.1/unstable/all_ounit_tests.ml b/lib/4.06.1/unstable/all_ounit_tests.ml index 2e3e0b94a5..10bb490351 100644 --- a/lib/4.06.1/unstable/all_ounit_tests.ml +++ b/lib/4.06.1/unstable/all_ounit_tests.ml @@ -7500,7 +7500,10 @@ let string_as_package (s : string) : t = if v = '@' then let scope_id = Ext_string.no_slash_idx s in - assert (scope_id > 0); + 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 diff --git a/lib/4.06.1/unstable/bspack.ml b/lib/4.06.1/unstable/bspack.ml index c632307ae1..a212d52572 100644 --- a/lib/4.06.1/unstable/bspack.ml +++ b/lib/4.06.1/unstable/bspack.ml @@ -11956,7 +11956,7 @@ val no_export: bool ref val as_ppx : bool ref val mono_empty_array : bool ref - +val customize_runtime : string option ref end = struct #1 "js_config.ml" (* Copyright (C) 2015-2016 Bloomberg Finance L.P. @@ -12051,7 +12051,7 @@ let as_ppx = ref false let mono_empty_array = ref true - +let customize_runtime = ref None end module Map_gen : sig #1 "map_gen.mli" diff --git a/lib/4.06.1/unstable/js_compiler.ml b/lib/4.06.1/unstable/js_compiler.ml index 730c8a208f..0af1f973a7 100644 --- a/lib/4.06.1/unstable/js_compiler.ml +++ b/lib/4.06.1/unstable/js_compiler.ml @@ -17892,7 +17892,7 @@ val no_export: bool ref val as_ppx : bool ref val mono_empty_array : bool ref - +val customize_runtime : string option ref end = struct #1 "js_config.ml" (* Copyright (C) 2015-2016 Bloomberg Finance L.P. @@ -17987,7 +17987,7 @@ let as_ppx = ref false let mono_empty_array = ref true - +let customize_runtime = ref None end module Bs_cmi_load = struct @@ -92280,7 +92280,19 @@ let runtime_test_package_specs : t = { name = Pkg_runtime; module_systems = [] } -let same_package_by_name (x : t) (y : t) = x.name = y.name + +let same_package_by_name (x : t) (y : t) = + match x.name with + | Pkg_empty -> + y.name = Pkg_empty + | Pkg_runtime -> + y.name = Pkg_runtime + | Pkg_normal s -> + begin match y.name with + | Pkg_normal y -> s = y + | Pkg_empty | Pkg_runtime -> false + end + let is_runtime_package (x : t) = x.name = Pkg_runtime @@ -98653,7 +98665,12 @@ let get_runtime_module_path (*Invariant: the package path to bs-platform, it is used to calculate relative js path *) - ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + (match !Js_config.customize_runtime with + | None -> + ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + | Some path -> + path //dep_path // js_file + ) @@ -98715,6 +98732,9 @@ let string_of_module_id which is guaranteed by [-bs-package-output] *) else + if Js_packages_info.is_runtime_package dep_package_info then + get_runtime_module_path dep_module_id current_package_info module_system + else begin match module_system with | NodeJS | Es6 -> dep_pkg.pkg_rel_path // js_file diff --git a/lib/4.06.1/unstable/js_refmt_compiler.ml b/lib/4.06.1/unstable/js_refmt_compiler.ml index 5d7c1e908d..d2d8c3ecc3 100644 --- a/lib/4.06.1/unstable/js_refmt_compiler.ml +++ b/lib/4.06.1/unstable/js_refmt_compiler.ml @@ -17892,7 +17892,7 @@ val no_export: bool ref val as_ppx : bool ref val mono_empty_array : bool ref - +val customize_runtime : string option ref end = struct #1 "js_config.ml" (* Copyright (C) 2015-2016 Bloomberg Finance L.P. @@ -17987,7 +17987,7 @@ let as_ppx = ref false let mono_empty_array = ref true - +let customize_runtime = ref None end module Bs_cmi_load = struct @@ -92280,7 +92280,19 @@ let runtime_test_package_specs : t = { name = Pkg_runtime; module_systems = [] } -let same_package_by_name (x : t) (y : t) = x.name = y.name + +let same_package_by_name (x : t) (y : t) = + match x.name with + | Pkg_empty -> + y.name = Pkg_empty + | Pkg_runtime -> + y.name = Pkg_runtime + | Pkg_normal s -> + begin match y.name with + | Pkg_normal y -> s = y + | Pkg_empty | Pkg_runtime -> false + end + let is_runtime_package (x : t) = x.name = Pkg_runtime @@ -98653,7 +98665,12 @@ let get_runtime_module_path (*Invariant: the package path to bs-platform, it is used to calculate relative js path *) - ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + (match !Js_config.customize_runtime with + | None -> + ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + | Some path -> + path //dep_path // js_file + ) @@ -98715,6 +98732,9 @@ let string_of_module_id which is guaranteed by [-bs-package-output] *) else + if Js_packages_info.is_runtime_package dep_package_info then + get_runtime_module_path dep_module_id current_package_info module_system + else begin match module_system with | NodeJS | Es6 -> dep_pkg.pkg_rel_path // js_file diff --git a/lib/4.06.1/whole_compiler.ml b/lib/4.06.1/whole_compiler.ml index 48d268230a..8efb087b8b 100644 --- a/lib/4.06.1/whole_compiler.ml +++ b/lib/4.06.1/whole_compiler.ml @@ -294325,7 +294325,7 @@ val no_export: bool ref val as_ppx : bool ref val mono_empty_array : bool ref - +val customize_runtime : string option ref end = struct #1 "js_config.ml" (* Copyright (C) 2015-2016 Bloomberg Finance L.P. @@ -294420,7 +294420,7 @@ let as_ppx = ref false let mono_empty_array = ref true - +let customize_runtime = ref None end module Ast_config : sig #1 "ast_config.mli" @@ -370595,7 +370595,19 @@ let runtime_test_package_specs : t = { name = Pkg_runtime; module_systems = [] } -let same_package_by_name (x : t) (y : t) = x.name = y.name + +let same_package_by_name (x : t) (y : t) = + match x.name with + | Pkg_empty -> + y.name = Pkg_empty + | Pkg_runtime -> + y.name = Pkg_runtime + | Pkg_normal s -> + begin match y.name with + | Pkg_normal y -> s = y + | Pkg_empty | Pkg_runtime -> false + end + let is_runtime_package (x : t) = x.name = Pkg_runtime @@ -381238,7 +381250,12 @@ let get_runtime_module_path (*Invariant: the package path to bs-platform, it is used to calculate relative js path *) - ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + (match !Js_config.customize_runtime with + | None -> + ((Filename.dirname (Filename.dirname Sys.executable_name)) // dep_path // js_file) + | Some path -> + path //dep_path // js_file + ) @@ -381300,6 +381317,9 @@ let string_of_module_id which is guaranteed by [-bs-package-output] *) else + if Js_packages_info.is_runtime_package dep_package_info then + get_runtime_module_path dep_module_id current_package_info module_system + else begin match module_system with | NodeJS | Es6 -> dep_pkg.pkg_rel_path // js_file @@ -435915,7 +435935,18 @@ let setup_error_printer (syntax_kind : [ `ml | `reason | `rescript ])= - +let setup_runtime_path path = + let u0 = Filename.dirname path in + let std = Filename.basename path in + let _path = Filename.dirname u0 in + let rescript = Filename.basename u0 in + (match rescript.[0] with + | '@' -> + Bs_version.package_name := rescript ^ "/" ^ std; + | _ -> Bs_version.package_name := std + | exception _ -> + Bs_version.package_name := std); + Js_config.customize_runtime := Some path let handle_reason (type a) (kind : a Ml_binary.kind) sourcefile ppf = setup_error_printer `reason; @@ -436324,6 +436355,8 @@ let buckle_script_flags : (string * Bsc_args.spec * string) array = " Enable or disable error status for warnings according\n\ to . See option -w for the syntax of .\n\ Default setting is " ^ Bsc_warnings.defaults_warn_error; + "-runtime",string_call setup_runtime_path, + "*internal* Set the runtime directory"; "-make-runtime", unit_call Js_packages_state.make_runtime, "*internal* make runtime library"; "-make-runtime-test", unit_call Js_packages_state.make_runtime_test,