From 15e9328841ef4304aea34e3024e748d284a6f4f5 Mon Sep 17 00:00:00 2001 From: Braden Napier Date: Fri, 16 Nov 2018 03:52:08 -0800 Subject: [PATCH] commit resolve_alias feature add tests for resolve_alias --- src/commands/commandUtils.ml | 1 + src/commands/config/flowConfig.ml | 12 +++++ src/commands/config/flowConfig.mli | 1 + src/common/files.ml | 27 ++++++++-- src/common/files.mli | 3 ++ src/services/inference/module/module_js.ml | 24 ++++++--- .../.flowconfig | 3 ++ ...fig_module_system_node_resolve_dirname.exp | 53 +++++++++++++++++++ .../custom_resolve_dir/testproj/index.js | 3 ++ .../custom_resolve_dir/testproj2/index.js | 3 ++ .../node_modules/testproj/index.js | 3 ++ .../node_modules/testproj2/index.js | 3 ++ .../custom_resolve_dir/testproj2/index.js | 3 ++ .../subdir/sublevel.js | 7 +++ .../toplevel.js | 6 +++ 15 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 tests/config_module_system_node_resolve_alias/.flowconfig create mode 100644 tests/config_module_system_node_resolve_alias/config_module_system_node_resolve_dirname.exp create mode 100644 tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj/index.js create mode 100644 tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj2/index.js create mode 100644 tests/config_module_system_node_resolve_alias/node_modules/testproj/index.js create mode 100644 tests/config_module_system_node_resolve_alias/node_modules/testproj2/index.js create mode 100644 tests/config_module_system_node_resolve_alias/subdir/custom_resolve_dir/testproj2/index.js create mode 100644 tests/config_module_system_node_resolve_alias/subdir/sublevel.js create mode 100644 tests/config_module_system_node_resolve_alias/toplevel.js diff --git a/src/commands/commandUtils.ml b/src/commands/commandUtils.ml index b37a5b5a3ed..529ac344a11 100644 --- a/src/commands/commandUtils.ml +++ b/src/commands/commandUtils.ml @@ -564,6 +564,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 = CommandSpec.ArgSpec.( diff --git a/src/commands/config/flowConfig.ml b/src/commands/config/flowConfig.ml index 5e87f0be255..6a41f135039 100644 --- a/src/commands/config/flowConfig.ml +++ b/src/commands/config/flowConfig.ml @@ -69,6 +69,7 @@ module Opts = struct munge_underscores: bool; no_flowlib: bool; node_resolver_dirnames: string list; + node_resolver_aliases: string list; root_name: string option; saved_state_fetcher: Options.saved_state_fetcher; shm_dep_table_pow: int; @@ -155,6 +156,7 @@ module Opts = struct munge_underscores = false; no_flowlib = false; node_resolver_dirnames = ["node_modules"]; + node_resolver_aliases = []; root_name = None; saved_state_fetcher = Options.Dummy_fetcher; shm_dep_table_pow = 17; @@ -486,6 +488,15 @@ module Opts = struct 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.use_strict", boolean (fun opts v -> Ok { opts with modules_are_use_strict = v }); @@ -991,6 +1002,7 @@ let modules_are_use_strict c = c.options.Opts.modules_are_use_strict let munge_underscores c = c.options.Opts.munge_underscores let no_flowlib c = c.options.Opts.no_flowlib let node_resolver_dirnames c = c.options.Opts.node_resolver_dirnames +let node_resolver_aliases c = c.options.Opts.node_resolver_aliases let root_name c = c.options.Opts.root_name let saved_state_fetcher c = c.options.Opts.saved_state_fetcher let shm_dep_table_pow c = c.options.Opts.shm_dep_table_pow diff --git a/src/commands/config/flowConfig.mli b/src/commands/config/flowConfig.mli index 4de1d362abf..33838da2530 100644 --- a/src/commands/config/flowConfig.mli +++ b/src/commands/config/flowConfig.mli @@ -75,6 +75,7 @@ val modules_are_use_strict: config -> bool val munge_underscores: config -> bool val no_flowlib: config -> bool val node_resolver_dirnames: config -> string list +val node_resolver_aliases: config -> string list val required_version: config -> string option val root_name: config -> string option val saved_state_fetcher: config -> Options.saved_state_fetcher diff --git a/src/common/files.ml b/src/common/files.ml index 2574a8e45d5..4c747e20bf0 100644 --- a/src/common/files.ml +++ b/src/common/files.ml @@ -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 @@ -28,6 +29,7 @@ let lib_paths options = options.lib_paths let module_file_exts options = options.module_file_exts 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 let node_modules_containers = ref SSet.empty @@ -104,6 +106,9 @@ 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) @@ -177,7 +182,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 = Core_list.map ~f:is_prefix paths in let rec process sz (acc, symlinks) files dir stack = if sz >= max_files then @@ -193,7 +198,7 @@ let make_next_files_and_symlinks then process (sz+1) (real :: acc, symlinks) files dir stack else process sz (acc, symlinks) files dir stack | Dir (path, is_symlink) -> - if node_module_filter file + if node_module_filter file || module_alias_filter file then node_modules_containers := SSet.add (Filename.dirname file) !node_modules_containers; let dirfiles = Array.to_list @@ try_readdir path in let symlinks = @@ -229,13 +234,14 @@ let make_next_files_and_symlinks of `paths`. *) let make_next_files_following_symlinks ~node_module_filter + ~module_alias_filter ~path_filter ~realpath_filter ~error_filter paths = let paths = Core_list.map ~f:Path.to_string paths in let cb = ref (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 ) in let symlinks = ref SSet.empty in let seen_symlinks = ref SSet.empty in @@ -254,7 +260,7 @@ let make_next_files_following_symlinks symlinks := SSet.empty; (* since we're following a symlink, use realpath_filter for both *) cb := make_next_files_and_symlinks - ~node_module_filter ~path_filter:realpath_filter ~realpath_filter ~error_filter paths; + ~node_module_filter ~module_alias_filter ~path_filter:realpath_filter ~realpath_filter ~error_filter paths; rec_cb () end in @@ -273,6 +279,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 [] else options.lib_paths in let libs, filter = match options.default_lib_dir with | None -> libs, is_valid_path ~options @@ -291,6 +298,7 @@ let init ?(flowlibs_only=false) (options: options) = let filter' path = path = lib_str || filter path in make_next_files_following_symlinks ~node_module_filter + ~module_alias_filter ~path_filter:filter' ~realpath_filter:filter' ~error_filter:(fun _ -> true) @@ -364,6 +372,7 @@ let watched_paths options = *) 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 _ -> true else wanted ~options libs in (* The directories from which we start our search *) @@ -399,7 +408,7 @@ let make_next_files ~root ~all ~subdir ~options ~libs = ) in make_next_files_following_symlinks - ~node_module_filter ~path_filter ~realpath_filter ~error_filter:filter starting_points + ~node_module_filter ~module_alias_filter ~path_filter ~realpath_filter ~error_filter:filter starting_points let is_windows_root root = Sys.win32 && @@ -527,6 +536,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 *) diff --git a/src/common/files.mli b/src/common/files.mli index 9f25e7d2f0f..a99cbd1b147 100644 --- a/src/common/files.mli +++ b/src/common/files.mli @@ -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 @@ -28,6 +29,7 @@ val lib_paths: options -> Path.t list val module_file_exts: options -> SSet.t 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 ref @@ -106,6 +108,7 @@ val filename_from_string: options: options -> string -> File_key.t 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: diff --git a/src/services/inference/module/module_js.ml b/src/services/inference/module/module_js.ml index 74768112696..42ba25e4879 100644 --- a/src/services/inference/module/module_js.ml +++ b/src/services/inference/module/module_js.ml @@ -407,12 +407,24 @@ module Node = struct lazy_seq [ lazy ( if SSet.mem dir node_modules_containers then - lazy_seq (Files.node_resolver_dirnames file_options |> Core_list.map ~f:(fun dirname -> - lazy (resolve_relative - ~options ~reader - loc ?resolution_acc dir (spf "%s%s%s" dirname Filename.dir_sep r) - ) - )) + lazy_seq([ + lazy ( + lazy_seq (Files.node_resolver_aliases file_options |> Core_list.map ~f:(fun dirname -> + lazy (resolve_relative + ~options + loc ?resolution_acc dir (spf "%s%s%s" dirname Filename.dir_sep r) + ) + )) + ); + lazy ( + lazy_seq (Files.node_resolver_dirnames file_options |> Core_list.map ~f:(fun dirname -> + lazy (resolve_relative + ~options ~reader + loc ?resolution_acc dir (spf "%s%s%s" dirname Filename.dir_sep r) + ) + )) + ); + ]) else None ); diff --git a/tests/config_module_system_node_resolve_alias/.flowconfig b/tests/config_module_system_node_resolve_alias/.flowconfig new file mode 100644 index 00000000000..20da9977f24 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/.flowconfig @@ -0,0 +1,3 @@ +[options] +module.system.node.resolve_dirname=node_modules +module.system.node.resolve_alias=custom_resolve_dir diff --git a/tests/config_module_system_node_resolve_alias/config_module_system_node_resolve_dirname.exp b/tests/config_module_system_node_resolve_alias/config_module_system_node_resolve_dirname.exp new file mode 100644 index 00000000000..3b04ff95125 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/config_module_system_node_resolve_dirname.exp @@ -0,0 +1,53 @@ +Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ subdir/sublevel.js:6:2 + +Cannot cast name to string literal custom_resolve_dir/testproj2 because string +literal subdir/custom_resolve_dir/testproj2 [1] is incompatible with string literal +custom_resolve_dir/testproj2 [2]. + + subdir/sublevel.js + 3│ import {name} from "testproj2"; + 4│ + 5│ (name: "subdir/custom_resolve_dir/testproj2"); + [2] 6│ (name: "custom_resolve_dir/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! + 7│ (name: "node_modules/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! + 8│ + + subdir/custom_resolve_dir/testproj2/index.js + [1] 3│ export var name: "subdir/custom_resolve_dir/testproj2" = "subdir/custom_resolve_dir/testproj2"; + + +Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ subdir/sublevel.js:7:2 + +Cannot cast name to string literal node_modules/testproj2 because string literal +subdir/custom_resolve_dir/testproj2 [1] is incompatible with string literal +node_modules/testproj2 [2]. + + subdir/sublevel.js + 4│ + 5│ (name: "subdir/custom_resolve_dir/testproj2"); + 6│ (name: "custom_resolve_dir/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! + [2] 7│ (name: "node_modules/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! + 8│ + + subdir/custom_resolve_dir/testproj2/index.js + [1] 3│ export var name: "subdir/custom_resolve_dir/testproj2" = "subdir/custom_resolve_dir/testproj2"; + + +Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ toplevel.js:6:2 + +Cannot cast name to string literal node_modules/testproj because string literal +custom_resolve_dir/testproj [1] is incompatible with string literal +node_modules/testproj [2]. + + toplevel.js + 3│ import {name} from "testproj"; + 4│ + 5│ (name: "custom_resolve_dir/testproj"); + [2] 6│ (name: "node_modules/testproj"); // Error: Resolve from resolve_alias first! + + custom_resolve_dir/testproj/index.js + [1] 3│ export var name: "custom_resolve_dir/testproj" = "custom_resolve_dir/testproj"; + + + +Found 3 errors \ No newline at end of file diff --git a/tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj/index.js b/tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj/index.js new file mode 100644 index 00000000000..124474deaaf --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "custom_resolve_dir/testproj" = "custom_resolve_dir/testproj"; diff --git a/tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj2/index.js b/tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj2/index.js new file mode 100644 index 00000000000..620347ab2f1 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/custom_resolve_dir/testproj2/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "custom_resolve_dir/testproj2" = "custom_resolve_dir/testproj2"; diff --git a/tests/config_module_system_node_resolve_alias/node_modules/testproj/index.js b/tests/config_module_system_node_resolve_alias/node_modules/testproj/index.js new file mode 100644 index 00000000000..04064ab9e18 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/node_modules/testproj/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "node_modules/testproj" = "node_modules/testproj"; diff --git a/tests/config_module_system_node_resolve_alias/node_modules/testproj2/index.js b/tests/config_module_system_node_resolve_alias/node_modules/testproj2/index.js new file mode 100644 index 00000000000..592604c3880 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/node_modules/testproj2/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "node_modules/testproj2" = "node_modules/testproj2"; diff --git a/tests/config_module_system_node_resolve_alias/subdir/custom_resolve_dir/testproj2/index.js b/tests/config_module_system_node_resolve_alias/subdir/custom_resolve_dir/testproj2/index.js new file mode 100644 index 00000000000..af4294a36c2 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/subdir/custom_resolve_dir/testproj2/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "subdir/custom_resolve_dir/testproj2" = "subdir/custom_resolve_dir/testproj2"; diff --git a/tests/config_module_system_node_resolve_alias/subdir/sublevel.js b/tests/config_module_system_node_resolve_alias/subdir/sublevel.js new file mode 100644 index 00000000000..19bc2d993c7 --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/subdir/sublevel.js @@ -0,0 +1,7 @@ +// @flow + +import {name} from "testproj2"; + +(name: "subdir/custom_resolve_dir/testproj2"); +(name: "custom_resolve_dir/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! +(name: "node_modules/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! diff --git a/tests/config_module_system_node_resolve_alias/toplevel.js b/tests/config_module_system_node_resolve_alias/toplevel.js new file mode 100644 index 00000000000..03ee0aae2fa --- /dev/null +++ b/tests/config_module_system_node_resolve_alias/toplevel.js @@ -0,0 +1,6 @@ +// @flow + +import {name} from "testproj"; + +(name: "custom_resolve_dir/testproj"); +(name: "node_modules/testproj"); // Error: Resolve from resolve_alias first! \ No newline at end of file