forked from rescript-lang/rescript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbsb_package_specs.ml
232 lines (208 loc) · 8.81 KB
/
bsb_package_specs.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition to the permissions granted to you by the LGPL, you may combine
* or link a "work that uses the Library" with a publicly distributed version
* of this file to produce a combined library or application, then distribute
* that combined work under the terms of your choosing, with no requirement
* to comply with the obligations normally placed on you by section 4 of the
* LGPL version 3 (or the corresponding section of a later version of the LGPL
* should you choose to use a later version).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
let ( // ) = Ext_path.combine
(*FIXME: use assoc list instead *)
module Spec_set = Bsb_spec_set
type t = {
modules : Spec_set.t;
runtime : string option;
(* This has to be resolved as early as possible, since
the path will be inherited in sub projects
*)
}
let ( .?() ) = Map_string.find_opt
let bad_module_format_message_exn ~loc format =
Bsb_exception.errorf ~loc
"package-specs: `%s` isn't a valid output module format. It has to be one \
of: %s or %s"
format Literals.esmodule Literals.commonjs
let deprecated_option ~loc x message =
let loc_end =
{loc with Lexing.pos_cnum = loc.Lexing.pos_cnum + String.length x}
in
let loc = {Warnings.loc_start = loc; loc_end; loc_ghost = false} in
Location.deprecated loc message
let supported_format (x : string) loc : Ext_module_system.t =
let _ =
if x = Literals.es6 || x = Literals.es6_global then
deprecated_option ~loc x
(Printf.sprintf "Option \"%s\" is deprecated. Use \"%s\" instead." x
Literals.esmodule)
in
if x = Literals.es6 || x = Literals.esmodule then Esmodule
else if x = Literals.commonjs then Commonjs
else if x = Literals.es6_global then Es6_global
else bad_module_format_message_exn ~loc x
let string_of_format (x : Ext_module_system.t) =
match x with
| Commonjs -> Literals.commonjs
| Esmodule -> Literals.esmodule
| Es6_global -> Literals.es6_global
let js_suffix_regexp = Str.regexp "[A-Za-z0-9-_.]*\\.[cm]?js"
let validate_js_suffix suffix = Str.string_match js_suffix_regexp suffix 0
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: detected two module formats that are both \
configured to be in-source.";
spec := Spec_set.add result !spec);
!spec
(* TODO: FIXME: better API without mutating *)
and from_json_single suffix (x : Ext_json_types.t) : Bsb_spec_set.spec =
match x with
| Str { str = format; loc } ->
{ format = supported_format format loc; in_source = false; suffix }
| Obj { map; loc } -> (
match map.?("module") with
| Some (Str { str = format }) ->
let in_source =
match map.?(Bsb_build_schemas.in_source) with
| Some (True _) -> true
| Some _ | None -> false
in
let suffix =
match map.?(Bsb_build_schemas.suffix) with
| Some (Str { str = suffix; _ }) when validate_js_suffix suffix -> suffix
| Some (Str {str; loc}) ->
Bsb_exception.errorf ~loc
("invalid suffix \"%s\". The suffix and may contain letters, digits, \"-\", \"_\" and \".\" and must end with .js, .mjs or .cjs.") str
| Some _ ->
Bsb_exception.errorf ~loc:(Ext_json.loc_of x)
"expected a string extension like \".js\""
| None -> suffix
in
{ format = supported_format format loc; in_source; suffix }
| Some _ ->
Bsb_exception.errorf ~loc
"package-specs: when the configuration is an object, `module` \
field should be a string, not an array. If you want to pass \
multiple module specs, try turning package-specs into an array of \
objects (or strings) instead."
| None ->
Bsb_exception.errorf ~loc
"package-specs: when the configuration is an object, the `module` \
field is mandatory.")
| _ ->
Bsb_exception.errorf ~loc:(Ext_json.loc_of x)
"package-specs: expected either a string or an object."
let from_json suffix (x : Ext_json_types.t) : Spec_set.t =
match x with
| Arr { content; _ } -> from_array suffix content
| _ -> Spec_set.singleton (from_json_single suffix x)
let bs_package_output = "-bs-package-output"
[@@@warning "+9"]
(* Assume input is valid
coordinate with command line flag
{[ -bs-package-output commonjs:lib/js/jscomp/test:.js ]}
*)
let package_flag ({ format; in_source; suffix } : Bsb_spec_set.spec) dir =
Ext_string.inter2 bs_package_output
(Ext_string.concat5 (string_of_format format) Ext_string.single_colon
(if in_source then dir
else Bsb_config.top_prefix_of_format format // dir)
Ext_string.single_colon
suffix)
(* FIXME: we should adapt it *)
let package_flag_of_package_specs (package_specs : t) ~(dirname : string) :
string =
let res =
match (package_specs.modules :> Bsb_spec_set.spec list) with
| [] -> Ext_string.empty
| [ format ] ->
Ext_string.inter2 Ext_string.empty (package_flag format dirname)
| [ a; b ] ->
Ext_string.inter3 Ext_string.empty (package_flag a dirname)
(package_flag b dirname)
| [ a; b; c ] ->
Ext_string.inter4 Ext_string.empty (package_flag a dirname)
(package_flag b dirname) (package_flag c dirname)
| _ ->
Spec_set.fold
(fun format acc ->
Ext_string.inter2 acc (package_flag format dirname))
package_specs.modules Ext_string.empty
in
match package_specs.runtime with
| None -> res
| Some x -> Ext_string.inter3 res "-runtime" x
let default_package_specs suffix =
(* TODO: swap default to Esmodule in v12 *)
Spec_set.singleton { format = Commonjs; in_source = false; suffix }
(**
[get_list_of_output_js specs "src/hi/hello"]
*)
let get_list_of_output_js (package_specs : t)
(output_file_sans_extension : string) =
Spec_set.fold
(fun (spec : Bsb_spec_set.spec) acc ->
let basename =
Ext_namespace.change_ext_ns_suffix output_file_sans_extension spec.suffix
in
(if spec.in_source then Bsb_config.rev_lib_bs_prefix basename
else Bsb_config.lib_bs_prefix_of_format spec.format // basename)
:: acc)
package_specs.modules []
let list_dirs_by (package_specs : t) (f : string -> unit) =
Spec_set.iter
(fun (spec : Bsb_spec_set.spec) ->
if not spec.in_source then f (Bsb_config.top_prefix_of_format spec.format))
package_specs.modules
type json_map = Ext_json_types.t Map_string.t
let extract_js_suffix_exn (map : json_map) : string =
let deprecation = "The \"suffix\" option at the top level is deprecated. Move the \"suffix\" setting into each \"package-specs\" entry." in
match map.?(Bsb_build_schemas.suffix) with
| None -> Literals.suffix_js
| Some (Str { str = suffix; loc }) when validate_js_suffix suffix ->
deprecated_option ~loc Literals.suffix_js deprecation;
suffix
| Some ((Str {str; loc}) as config) ->
deprecated_option ~loc Literals.suffix_js deprecation;
Bsb_exception.config_error config
("invalid suffix \"" ^ str ^ "\". The suffix and may contain letters, digits, \"-\", \"_\" and \".\" and must end with .js, .mjs or .cjs.")
| Some config ->
Bsb_exception.config_error config
"expected a string extension like \".js\""
let from_map ~(cwd : string) map =
let suffix = extract_js_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 }