Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/commands/commandUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ let file_options =
module_file_exts = FlowConfig.module_file_exts flowconfig;
module_resource_exts = FlowConfig.module_resource_exts flowconfig;
node_resolver_dirnames = FlowConfig.node_resolver_dirnames flowconfig;
node_resolver_aliases = FlowConfig.node_resolver_aliases flowconfig;
}

let ignore_flag prev =
Expand Down
13 changes: 12 additions & 1 deletion src/commands/config/flowConfig.ml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ module Opts = struct
no_flowlib: bool;
node_resolver_allow_root_relative: bool;
node_resolver_dirnames: string list;
node_resolver_aliases: string list;
node_resolver_root_relative_dirnames: string list;
recursion_limit: int;
root_name: string option;
Expand Down Expand Up @@ -192,6 +193,7 @@ module Opts = struct
no_flowlib = false;
node_resolver_allow_root_relative = false;
node_resolver_dirnames = ["node_modules"];
node_resolver_aliases = [];
node_resolver_root_relative_dirnames = [""];
recursion_limit = 10000;
root_name = None;
Expand Down Expand Up @@ -472,6 +474,13 @@ module Opts = struct
else
let node_resolver_dirnames = v :: opts.node_resolver_dirnames in
Ok { opts with node_resolver_dirnames }) );
( "module.system.node.resolve_alias",
string
~init:(fun opts -> { opts with node_resolver_aliases = [] })
~multiple:true
(fun opts v ->
let node_resolver_aliases = v :: opts.node_resolver_aliases in
Ok { opts with node_resolver_aliases }) );
( "module.system.node.root_relative_dirname",
string
~init:(fun opts -> { opts with node_resolver_root_relative_dirnames = [] })
Expand Down Expand Up @@ -1008,7 +1017,7 @@ let is_not_comment =
(* Line starts with ; *)
Str.regexp_string "\240\159\146\169";
(* Line starts with poop emoji *)

]
in
fun (_, line) ->
Expand Down Expand Up @@ -1200,6 +1209,8 @@ let node_resolver_allow_root_relative c = c.options.Opts.node_resolver_allow_roo

let node_resolver_dirnames c = c.options.Opts.node_resolver_dirnames

let node_resolver_aliases c = c.options.Opts.node_resolver_aliases

let node_resolver_root_relative_dirnames c = c.options.Opts.node_resolver_root_relative_dirnames

let recursion_limit c = c.options.Opts.recursion_limit
Expand Down
2 changes: 2 additions & 0 deletions src/commands/config/flowConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ val node_resolver_allow_root_relative : config -> bool

val node_resolver_dirnames : config -> string list

val node_resolver_aliases: config -> string list

val node_resolver_root_relative_dirnames : config -> string list

val required_version : config -> string option
Expand Down
25 changes: 22 additions & 3 deletions src/common/files.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type options = {
module_file_exts: SSet.t;
module_resource_exts: SSet.t;
node_resolver_dirnames: string list;
node_resolver_aliases: string list;
}

let default_lib_dir options = options.default_lib_dir
Expand All @@ -37,6 +38,8 @@ let module_resource_exts options = options.module_resource_exts

let node_resolver_dirnames options = options.node_resolver_dirnames

let node_resolver_aliases options = options.node_resolver_aliases

(* During node module resolution, we need to look for node_modules/ directories
* as we walk up the path. But checking each directory for them would be expensive.
* So instead we memorize which directories contain node_modules/ directories.
Expand Down Expand Up @@ -114,6 +117,8 @@ let is_valid_path =

let is_node_module options path = List.mem (Filename.basename path) options.node_resolver_dirnames

let is_module_alias options path = List.mem (Filename.basename path) options.node_resolver_aliases

let is_flow_file ~options =
let is_valid_path = is_valid_path ~options in
(fun path -> is_valid_path path && not (is_directory path))
Expand Down Expand Up @@ -193,7 +198,7 @@ let max_files = 1000

If kind_of_path fails, then we only emit a warning if error_filter passes *)
let make_next_files_and_symlinks
~node_module_filter ~path_filter ~realpath_filter ~error_filter paths =
~node_module_filter ~module_alias_filter ~path_filter ~realpath_filter ~error_filter paths =
let prefix_checkers = Base.List.map ~f:is_prefix paths in
let rec process sz (acc, symlinks) files dir stack =
if sz >= max_files then
Expand All @@ -215,7 +220,7 @@ let make_next_files_and_symlinks
else
process sz (acc, symlinks) files dir stack
| Dir (path, is_symlink) ->
if node_module_filter file then
if node_module_filter file || module_alias_filter file then
node_modules_containers :=
SMap.add
~combine:SSet.union
Expand Down Expand Up @@ -256,12 +261,13 @@ let make_next_files_and_symlinks
and including any directories that are symlinked to even if they are outside
of `paths`. *)
let make_next_files_following_symlinks
~node_module_filter ~path_filter ~realpath_filter ~error_filter paths =
~node_module_filter ~module_alias_filter ~path_filter ~realpath_filter ~error_filter paths =
let paths = Base.List.map ~f:Path.to_string paths in
let cb =
ref
(make_next_files_and_symlinks
~node_module_filter
~module_alias_filter
~path_filter
~realpath_filter
~error_filter
Expand Down Expand Up @@ -294,6 +300,7 @@ let make_next_files_following_symlinks
cb :=
make_next_files_and_symlinks
~node_module_filter
~module_alias_filter
~path_filter:realpath_filter
~realpath_filter
~error_filter
Expand All @@ -315,6 +322,7 @@ let get_all =

let init ?(flowlibs_only = false) (options : options) =
let node_module_filter = is_node_module options in
let module_alias_filter = is_module_alias options in
let libs =
if flowlibs_only then
[]
Expand All @@ -341,6 +349,7 @@ let init ?(flowlibs_only = false) (options : options) =
let filter' path = (path = lib_str || filter path) && not (is_json_file path) in
make_next_files_following_symlinks
~node_module_filter
~module_alias_filter
~path_filter:filter'
~realpath_filter:filter'
~error_filter:(fun _ -> true)
Expand Down Expand Up @@ -416,6 +425,7 @@ let watched_paths options = Path_matcher.stems options.includes
*)
let make_next_files ~root ~all ~subdir ~options ~libs =
let node_module_filter = is_node_module options in
let module_alias_filter = is_module_alias options in
let filter =
if all then
fun _ ->
Expand Down Expand Up @@ -454,6 +464,7 @@ let make_next_files ~root ~all ~subdir ~options ~libs =
in
make_next_files_following_symlinks
~node_module_filter
~module_alias_filter
~path_filter
~realpath_filter
~error_filter:filter
Expand Down Expand Up @@ -585,6 +596,14 @@ let is_within_node_modules ~root ~options path =
let node_resolver_dirnames = node_resolver_dirnames options |> SSet.of_list in
not (SSet.inter directories node_resolver_dirnames |> SSet.is_empty)

(* Given a path, we want to know if it's a resolve alias. *)
let is_within_alias_directory ~root ~options path =
(* We use paths that are relative to the root, so that we ignore ancestor directories *)
let path = relative_path (Path.to_string root) path in
let directories = Str.split dir_sep path |> SSet.of_list in
let node_resolver_aliases = node_resolver_aliases options |> SSet.of_list in
not (SSet.inter directories node_resolver_aliases |> SSet.is_empty)

(* realpath doesn't work for non-existent paths. So let's find the longest existent prefix, run
* realpath on that, and then append the rest to it
*)
Expand Down
5 changes: 5 additions & 0 deletions src/common/files.mli
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type options = {
module_file_exts: SSet.t;
module_resource_exts: SSet.t;
node_resolver_dirnames: string list;
node_resolver_aliases: string list;
}

val default_lib_dir : options -> Path.t option
Expand All @@ -37,6 +38,8 @@ val module_resource_exts : options -> SSet.t

val node_resolver_dirnames : options -> string list

val node_resolver_aliases : options -> string list

val node_modules_containers : SSet.t SMap.t ref

val global_file_name : string
Expand Down Expand Up @@ -124,6 +127,8 @@ val mkdirp : string -> Unix.file_perm -> unit

val is_within_node_modules : root:Path.t -> options:options -> string -> bool

val is_within_alias_directory : root:Path.t -> options:options -> string -> bool

val imaginary_realpath : string -> string

val canonicalize_filenames :
Expand Down
54 changes: 39 additions & 15 deletions src/services/inference/module/module_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ let choose_provider_and_warn_about_duplicates =
in
(defn, errmap)

let get_root ~options =
Options.root options |> Path.to_string |> Sys_utils.normalize_filename_dir_sep

(**
* A set of module.name_mapper config entry allows users to specify regexp
* matcher strings each with a template string in order to map the names of a
Expand All @@ -74,7 +77,7 @@ let choose_provider_and_warn_about_duplicates =
let module_name_candidates ~options =
Module_hashtables.memoize_with_module_name_candidates_cache ~f:(fun name ->
let mappers = Options.module_name_mappers options in
let root = Options.root options |> Path.to_string |> Sys_utils.normalize_filename_dir_sep in
let root = get_root options in
let map_name mapped_names (regexp, template) =
let new_name =
name
Expand Down Expand Up @@ -318,26 +321,47 @@ module Node = struct

let rec node_module ~options ~reader node_modules_containers file loc resolution_acc dir r =
let file_options = Options.file_options options in
let root = get_root options in
lazy_seq
[
lazy
(match SMap.get dir node_modules_containers with
| Some existing_node_modules_dirs ->
lazy_seq
( Files.node_resolver_dirnames file_options
|> Base.List.map ~f:(fun dirname ->
lazy
( if SSet.mem dirname existing_node_modules_dirs then
resolve_relative
~options
~reader
loc
?resolution_acc
dir
(spf "%s%s%s" dirname Filename.dir_sep r)
else
None )) )
| None -> None);
[
lazy
(lazy_seq
( Files.node_resolver_aliases file_options
|> Base.List.map ~f:(fun dirname ->
let modified_dirname =
dirname
|> Str.split_delim Files.project_root_token
|> String.concat root
in
lazy
(resolve_relative
~options
~reader
loc
?resolution_acc
dir
(spf "%s%s%s" modified_dirname Filename.dir_sep r))) ));
lazy
(lazy_seq
( Files.node_resolver_dirnames file_options
|> Base.List.map ~f:(fun dirname ->
lazy
( if SSet.mem dirname existing_node_modules_dirs then
resolve_relative
~options
~reader
loc
?resolution_acc
dir
(spf "%s%s%s" dirname Filename.dir_sep r))) ));
]
else
None );
lazy
(let parent_dir = Filename.dirname dir in
if dir = parent_dir then
Expand Down
5 changes: 5 additions & 0 deletions tests/config_module_system_node_resolve_alias/.flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[options]
module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=custom_node_modules
module.system.node.resolve_alias=custom_resolve_dir
module.system.node.resolve_alias=<PROJECT_ROOT>/root_resolve_dir
1 change: 1 addition & 0 deletions tests/config_module_system_node_resolve_alias/.testconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shell: test.sh
Loading