Skip to content

Commit ca4a3d9

Browse files
voideanvaluefacebook-github-bot
authored andcommitted
haste name_reducers and paths
Summary: We want to be able to figure out the id of a haste module by using it's filename instead of having to read the file to extract the `providesModule` annotations. The plan is to completely kill the usage of `providesModule` at Facebook (internally and open source projects). I'm adding this as a configurable opt-in option (and preserving support for `providesModule`) for the sake of (a) non-fb consumers of our open source tools that rely on `providesModule` and (b) seamlessly rolling this out throughout Facebook projects (the plan is to add support for using the filename to all tools and then make the switch and remove the `providesModule` annotations over the next 3-4 weeks). `name_reducers` are basically a series of replacements that are applied in order to reduce a filename to a module id. These rules only apply to file paths that match the whitelist and do not match the blacklist. I had to use this instead of just using the basename (minus .js.flow) because react native has a bunch of platform specific extensions (`.ios`/`.android`/`.web`) that shouldn't be part of the module id. I've hardly written any OCaml in my life, so please do pardon (and point out) atrocities that I may have committed. Reviewed By: avikchaudhuri Differential Revision: D4488705 fbshipit-source-id: f16bb1a034fb86bf6e6461f9e5f1cf2564f57d1f
1 parent 157167c commit ca4a3d9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+564
-41
lines changed

src/commands/lsCommand.ml

+12
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,18 @@ let make_options ~root ~strip_root ~ignore_flag ~include_flag =
216216
opt_ignore_non_literal_requires = false;
217217
opt_max_header_tokens = FlowConfig.(
218218
flowconfig.options.Opts.max_header_tokens
219+
);
220+
opt_haste_name_reducers = FlowConfig.(
221+
flowconfig.options.Opts.haste_name_reducers
222+
);
223+
opt_haste_paths_blacklist = FlowConfig.(
224+
flowconfig.options.Opts.haste_paths_blacklist
225+
);
226+
opt_haste_paths_whitelist = FlowConfig.(
227+
flowconfig.options.Opts.haste_paths_whitelist
228+
);
229+
opt_haste_use_name_reducers = FlowConfig.(
230+
flowconfig.options.Opts.haste_use_name_reducers
219231
)
220232
}
221233

src/commands/serverCommands.ml

+12
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,18 @@ module OptionParser(Config : CONFIG) = struct
358358
);
359359
opt_max_header_tokens = FlowConfig.(
360360
flowconfig.options.Opts.max_header_tokens
361+
);
362+
opt_haste_name_reducers = FlowConfig.(
363+
flowconfig.options.Opts.haste_name_reducers
364+
);
365+
opt_haste_paths_blacklist = FlowConfig.(
366+
flowconfig.options.Opts.haste_paths_blacklist
367+
);
368+
opt_haste_paths_whitelist = FlowConfig.(
369+
flowconfig.options.Opts.haste_paths_whitelist
370+
);
371+
opt_haste_use_name_reducers = FlowConfig.(
372+
flowconfig.options.Opts.haste_use_name_reducers
361373
)
362374
} in
363375
Main.start options

src/common/flowConfig.ml

+69-24
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ module Opts = struct
5151
esproposal_decorators: Options.esproposal_feature_mode;
5252
esproposal_export_star_as: Options.esproposal_feature_mode;
5353
facebook_fbt: string option;
54+
haste_name_reducers: (Str.regexp * string) list;
55+
haste_paths_blacklist: string list;
56+
haste_paths_whitelist: string list;
57+
haste_use_name_reducers: bool;
5458
ignore_non_literal_requires: bool;
5559
moduleSystem: moduleSystem;
5660
module_name_mappers: (Str.regexp * string) list;
@@ -149,6 +153,10 @@ module Opts = struct
149153
esproposal_decorators = Options.ESPROPOSAL_WARN;
150154
esproposal_export_star_as = Options.ESPROPOSAL_WARN;
151155
facebook_fbt = None;
156+
haste_name_reducers = [(Str.regexp "^\\(.*/\\)?\\([a-zA-Z0-9$_.-]+\\)\\.js\\(\\.flow\\)?$", "\\2")];
157+
haste_paths_blacklist = ["\\(.*\\)?/node_modules/.*"];
158+
haste_paths_whitelist = ["<PROJECT_ROOT>/.*"];
159+
haste_use_name_reducers = false;
152160
ignore_non_literal_requires = false;
153161
moduleSystem = Node;
154162
module_name_mappers = [];
@@ -286,6 +294,18 @@ module Opts = struct
286294

287295
let optparse_filepath str =
288296
Path.make str
297+
298+
let optparse_mapping str =
299+
let regexp_str = "^'\\([^']*\\)'[ \t]*->[ \t]*'\\([^']*\\)'$" in
300+
let regexp = Str.regexp regexp_str in
301+
(if not (Str.string_match regexp str 0) then
302+
raise (UserError (
303+
"Expected a mapping of form: " ^
304+
"'single-quoted-string' -> 'single-quoted-string'"
305+
))
306+
);
307+
(Str.matched_group 1 str, Str.matched_group 2 str)
308+
289309
end
290310

291311
type config = {
@@ -448,6 +468,53 @@ let parse_options config lines =
448468
});
449469
}
450470

471+
|> define_opt "module.system.haste.name_reducers" {
472+
_initializer = INIT_FN (fun opts -> {
473+
opts with haste_name_reducers = [];
474+
});
475+
flags = [ALLOW_DUPLICATE];
476+
optparser = (fun str ->
477+
let (pattern, template) = optparse_mapping str in
478+
(Str.regexp pattern, template)
479+
);
480+
setter = (fun opts v -> {
481+
opts with haste_name_reducers = v::(opts.haste_name_reducers);
482+
});
483+
}
484+
485+
|> define_opt "module.system.haste.paths.blacklist" {
486+
_initializer = INIT_FN (fun opts -> {
487+
opts with haste_paths_blacklist = [];
488+
});
489+
flags = [ALLOW_DUPLICATE];
490+
optparser = optparse_string;
491+
setter = (fun opts v -> {
492+
opts with haste_paths_blacklist = v::(opts.haste_paths_blacklist);
493+
});
494+
}
495+
496+
|> define_opt "module.system.haste.paths.whitelist" {
497+
_initializer = INIT_FN (fun opts -> {
498+
opts with haste_paths_whitelist = [];
499+
});
500+
flags = [ALLOW_DUPLICATE];
501+
optparser = optparse_string;
502+
setter = (fun opts v -> {
503+
opts with haste_paths_whitelist = v::(opts.haste_paths_whitelist);
504+
});
505+
}
506+
507+
|> define_opt "module.system.haste.use_name_reducers" {
508+
_initializer = INIT_FN (fun opts -> {
509+
opts with haste_use_name_reducers = false;
510+
});
511+
flags = [];
512+
optparser = optparse_boolean;
513+
setter = (fun opts v ->
514+
{opts with haste_use_name_reducers = v;}
515+
);
516+
}
517+
451518
|> define_opt "log.file" {
452519
_initializer = USE_DEFAULT;
453520
flags = [];
@@ -499,18 +566,7 @@ let parse_options config lines =
499566
_initializer = USE_DEFAULT;
500567
flags = [ALLOW_DUPLICATE];
501568
optparser = (fun str ->
502-
let regexp_str = "^'\\([^']*\\)'[ \t]*->[ \t]*'\\([^']*\\)'$" in
503-
let regexp = Str.regexp regexp_str in
504-
(if not (Str.string_match regexp str 0) then
505-
raise (Opts.UserError (
506-
"Expected a mapping of form: " ^
507-
"'single-quoted-string' -> 'single-quoted-string'"
508-
))
509-
);
510-
511-
let pattern = Str.matched_group 1 str in
512-
let template = Str.matched_group 2 str in
513-
569+
let (pattern, template) = optparse_mapping str in
514570
(Str.regexp pattern, template)
515571
);
516572
setter = (fun opts v ->
@@ -523,18 +579,7 @@ let parse_options config lines =
523579
_initializer = USE_DEFAULT;
524580
flags = [ALLOW_DUPLICATE];
525581
optparser = (fun str ->
526-
let regexp_str = "^'\\([^']*\\)'[ \t]*->[ \t]*'\\([^']*\\)'$" in
527-
let regexp = Str.regexp regexp_str in
528-
(if not (Str.string_match regexp str 0) then
529-
raise (Opts.UserError (
530-
"Expected a mapping of form: " ^
531-
"'single-quoted-string' -> 'single-quoted-string'"
532-
))
533-
);
534-
535-
let file_ext = Str.matched_group 1 str in
536-
let template = Str.matched_group 2 str in
537-
582+
let (file_ext, template) = optparse_mapping str in
538583
(Str.regexp ("^\\(.*\\)\\." ^ (Str.quote file_ext) ^ "$"), template)
539584
);
540585
setter = (fun opts v ->

src/common/flowConfig.mli

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ module Opts : sig
2020
esproposal_decorators: Options.esproposal_feature_mode;
2121
esproposal_export_star_as: Options.esproposal_feature_mode;
2222
facebook_fbt: string option;
23+
haste_name_reducers: (Str.regexp * string) list;
24+
haste_paths_blacklist: string list;
25+
haste_paths_whitelist: string list;
26+
haste_use_name_reducers: bool;
2327
ignore_non_literal_requires: bool;
2428
moduleSystem: moduleSystem;
2529
module_name_mappers: (Str.regexp * string) list;

src/common/options.ml

+8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ type t = {
3737
opt_esproposal_decorators: esproposal_feature_mode;
3838
opt_esproposal_export_star_as: esproposal_feature_mode;
3939
opt_facebook_fbt: string option;
40+
opt_haste_name_reducers: (Str.regexp * string) list;
41+
opt_haste_paths_blacklist: string list;
42+
opt_haste_paths_whitelist: string list;
43+
opt_haste_use_name_reducers: bool;
4044
opt_ignore_non_literal_requires: bool;
4145
opt_ignores: (string * Str.regexp) list;
4246
opt_includes: Path_matcher.t;
@@ -94,6 +98,10 @@ let esproposal_class_instance_fields opts =
9498
opts.opt_esproposal_class_instance_fields
9599
let esproposal_decorators opts = opts.opt_esproposal_decorators
96100
let esproposal_export_star_as opts = opts.opt_esproposal_export_star_as
101+
let haste_name_reducers opts = opts.opt_haste_name_reducers
102+
let haste_paths_blacklist opts = opts.opt_haste_paths_blacklist
103+
let haste_paths_whitelist opts = opts.opt_haste_paths_whitelist
104+
let haste_use_name_reducers opts = opts.opt_haste_use_name_reducers
97105
let ignores opts = opts.opt_ignores
98106
let includes opts = opts.opt_includes
99107
let is_check_mode opts = opts.opt_check_mode

src/services/inference/module_js.ml

+51-17
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ let package_incompatible package ast =
261261
model both Haste and Node, but should be further generalized. *)
262262
module type MODULE_SYSTEM = sig
263263
(* Given a file and docblock info, make the name of the module it exports. *)
264-
val exported_module: filename -> Docblock.t -> Modulename.t
264+
val exported_module: Options.t -> filename -> Docblock.t -> Modulename.t
265265

266266
(* Given a file and a reference in it to an imported module, make the name of
267267
the module it refers to. If given an optional reference to an accumulator,
@@ -354,7 +354,7 @@ let lazy_seq: 'a option Lazy.t list -> 'a option =
354354
(*******************************)
355355

356356
module Node = struct
357-
let exported_module file _ =
357+
let exported_module _ file _ =
358358
if Loc.check_suffix file Files.flow_ext
359359
then Modulename.Filename (Loc.chop_suffix file Files.flow_ext)
360360
else Modulename.Filename file
@@ -520,27 +520,61 @@ module Haste: MODULE_SYSTEM = struct
520520
| Loc.ResourceFile file ->
521521
Str.string_match mock_path file 0
522522

523-
let rec exported_module file info =
523+
let expand_project_root_token options str =
524+
let root = Path.to_string (Options.root options)
525+
|> Sys_utils.normalize_filename_dir_sep in
526+
str
527+
|> Str.split_delim FlowConfig.project_root_token
528+
|> String.concat root
529+
|> Str.regexp
530+
531+
let is_haste_file options file =
532+
let matched_haste_paths_whitelist file = List.exists
533+
(fun r -> Str.string_match (expand_project_root_token options r) (string_of_filename file) 0)
534+
(Options.haste_paths_whitelist options) in
535+
let matched_haste_paths_blacklist file = List.exists
536+
(fun r -> Str.string_match (expand_project_root_token options r) (string_of_filename file) 0)
537+
(Options.haste_paths_blacklist options) in
538+
(matched_haste_paths_whitelist file) && not (matched_haste_paths_blacklist file)
539+
540+
let haste_name options file =
541+
let reduce_name name (regexp, template) =
542+
Str.global_replace regexp template name
543+
in
544+
List.fold_left
545+
reduce_name
546+
(string_of_filename file)
547+
(Options.haste_name_reducers options)
548+
549+
let rec exported_module options file info =
524550
if is_mock file
525551
then Modulename.String (short_module_name_of file)
552+
else if Options.haste_use_name_reducers options
553+
then
554+
if is_haste_file options file
555+
then Modulename.String (haste_name options file)
556+
else exported_non_haste_module options file
526557
else match Docblock.providesModule info with
527558
| Some m -> Modulename.String m
528559
| None ->
529560
(* If foo.js.flow doesn't have a @providesModule, then look at foo.js
530561
* and use its @providesModule instead *)
531-
if Loc.check_suffix file Files.flow_ext
532-
then
533-
let file_without_flow_ext = Loc.chop_suffix file Files.flow_ext in
534-
if Parsing_service_js.has_ast file_without_flow_ext
535-
then
536-
(** TODO [perf]: mark as expensive and investigate! **)
537-
let _, info =
538-
Parsing_service_js.get_ast_and_info_unsafe file_without_flow_ext in
539-
exported_module file_without_flow_ext info
540-
else
541-
Modulename.Filename (file_without_flow_ext)
542-
else
543-
Modulename.Filename file
562+
exported_non_haste_module options file
563+
564+
and exported_non_haste_module options file =
565+
if Loc.check_suffix file Files.flow_ext
566+
then
567+
let file_without_flow_ext = Loc.chop_suffix file Files.flow_ext in
568+
if Parsing_service_js.has_ast file_without_flow_ext
569+
then
570+
(** TODO [perf]: mark as expensive and investigate! **)
571+
let _, info =
572+
Parsing_service_js.get_ast_and_info_unsafe file_without_flow_ext in
573+
exported_module options file_without_flow_ext info
574+
else
575+
Modulename.Filename (file_without_flow_ext)
576+
else
577+
Modulename.Filename file
544578

545579
let expanded_name r =
546580
match Str.split_delim (Str.regexp_string "/") r with
@@ -624,7 +658,7 @@ let get_module_system opts =
624658

625659
let exported_module ~options file info =
626660
let module M = (val (get_module_system options)) in
627-
M.exported_module file info
661+
M.exported_module options file info
628662

629663
let imported_module ~options cx loc ?path_acc r =
630664
let module M = (val (get_module_system options)) in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[options]
2+
module.system=haste
3+
module.system.haste.use_name_reducers=true
4+
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\)\.js\(\.flow\)?$' -> '\1'
5+
module.system.haste.paths.whitelist=<PROJECT_ROOT>/.*
6+
module.system.haste.paths.blacklist=.*/node_modules/.*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shell: test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/* @flow */
2+
3+
class AImplementation {}
4+
export function foo(): AImplementation { return new AImplementation(); }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare class ADefinition {}
2+
declare export function foo(): ADefinition;

0 commit comments

Comments
 (0)