Skip to content

Commit

Permalink
module.system.node.root_relative_dirname lets you configure where r…
Browse files Browse the repository at this point in the history
…oot relative paths resolve to

Summary:
When we see

```
[options]
module.system.node.allow_root_relative=true
module.system.node.root_relative_dirname=first
module.system.node.root_relative_dirname=second
```

Then we'll resolve `import foo from 'path/to/foo'`

1. First in `<PROJECT_ROOT>/first/path/to/foo.js`
2. Second in `<PROJECT_ROOT>/second/path/to/foo.js`
3. Third in node_modules.

Reviewed By: jbrown215

Differential Revision: D18234623

fbshipit-source-id: f569e599a9fb0191ba959cda89166ab1b77fd758
  • Loading branch information
gabelevi authored and facebook-github-bot committed Oct 31, 2019
1 parent e3f2902 commit d17ed03
Show file tree
Hide file tree
Showing 26 changed files with 309 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/commands/commandUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,8 @@ let make_options ~flowconfig_name ~flowconfig ~lazy_mode ~root (options_flags :
opt_no_saved_state = options_flags.no_saved_state;
opt_node_resolver_allow_root_relative =
FlowConfig.node_resolver_allow_root_relative flowconfig;
opt_node_resolver_root_relative_dirnames =
FlowConfig.node_resolver_root_relative_dirnames flowconfig;
opt_arch;
opt_abstract_locations;
opt_cache_direct_dependents = FlowConfig.cache_direct_dependents flowconfig;
Expand Down
15 changes: 14 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_root_relative_dirnames: string list;
recursion_limit: int;
root_name: string option;
saved_state_fetcher: Options.saved_state_fetcher;
Expand Down Expand Up @@ -191,6 +192,7 @@ module Opts = struct
no_flowlib = false;
node_resolver_allow_root_relative = false;
node_resolver_dirnames = ["node_modules"];
node_resolver_root_relative_dirnames = [""];
recursion_limit = 10000;
root_name = None;
saved_state_fetcher = Options.Dummy_fetcher;
Expand Down Expand Up @@ -470,6 +472,15 @@ module Opts = struct
else
let node_resolver_dirnames = v :: opts.node_resolver_dirnames in
Ok { opts with node_resolver_dirnames }) );
( "module.system.node.root_relative_dirname",
string
~init:(fun opts -> { opts with node_resolver_root_relative_dirnames = [] })
~multiple:true
(fun opts v ->
let node_resolver_root_relative_dirnames =
v :: opts.node_resolver_root_relative_dirnames
in
Ok { opts with node_resolver_root_relative_dirnames }) );
("module.use_strict", boolean (fun opts v -> Ok { opts with modules_are_use_strict = v }));
("munge_underscores", boolean (fun opts v -> Ok { opts with munge_underscores = v }));
( "name",
Expand Down Expand Up @@ -997,7 +1008,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 @@ -1189,6 +1200,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_root_relative_dirnames c = c.options.Opts.node_resolver_root_relative_dirnames

let recursion_limit c = c.options.Opts.recursion_limit

let root_name c = c.options.Opts.root_name
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_root_relative_dirnames : config -> string list

val required_version : config -> string option

val recursion_limit : config -> int
Expand Down
3 changes: 3 additions & 0 deletions src/common/options.ml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ type t = {
opt_modules_are_use_strict: bool;
opt_munge_underscores: bool;
opt_node_resolver_allow_root_relative: bool;
opt_node_resolver_root_relative_dirnames: string list;
opt_no_saved_state: bool;
opt_profile: bool;
opt_quiet: bool;
Expand Down Expand Up @@ -203,6 +204,8 @@ let no_saved_state opts = opts.opt_no_saved_state

let node_resolver_allow_root_relative opts = opts.opt_node_resolver_allow_root_relative

let node_resolver_root_relative_dirnames opts = opts.opt_node_resolver_root_relative_dirnames

let recursion_limit opts = opts.opt_recursion_limit

let root opts = opts.opt_root
Expand Down
13 changes: 12 additions & 1 deletion src/services/inference/module/module_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,18 @@ module Node = struct
[
lazy
( if Options.node_resolver_allow_root_relative options then
resolve_relative ~options ~reader loc ?resolution_acc root_str import_str
lazy_seq
( Options.node_resolver_root_relative_dirnames options
|> Core_list.map ~f:(fun root_relative_dirname ->
lazy
(let root_str =
if root_relative_dirname = "" then
root_str
else
Filename.concat root_str root_relative_dirname
in
resolve_relative ~options ~reader loc ?resolution_acc root_str import_str))
)
else
None );
lazy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[options]
module.system.node.allow_root_relative=true
module.system.node.root_relative_dirname=first
module.system.node.root_relative_dirname=second
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "user_code" = "user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
Error ----------------------------------------------------------------------------------------------------- test.js:16:2

Cannot cast `ambiguous` to empty because string literal `first_user_code` [1] is incompatible with empty [2].

test.js:16:2
16| (ambiguous: empty)
^^^^^^^^^

References:
first/ambiguous.js:3:14
3| const value: "first_user_code" = "first_user_code";
^^^^^^^^^^^^^^^^^ [1]
test.js:16:13
16| (ambiguous: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:19:2

Cannot cast `sub_ambiguous` to empty because string literal `first_user_code` [1] is incompatible with empty [2].

test.js:19:2
19| (sub_ambiguous: empty)
^^^^^^^^^^^^^

References:
first/subdir/ambiguous.js:3:14
3| const value: "first_user_code" = "first_user_code";
^^^^^^^^^^^^^^^^^ [1]
test.js:19:17
19| (sub_ambiguous: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:23:2

Cannot cast `user_code` to empty because string literal `first_user_code` [1] is incompatible with empty [2].

test.js:23:2
23| (user_code: empty)
^^^^^^^^^

References:
first/user_code.js:3:14
3| const value: "first_user_code" = "first_user_code";
^^^^^^^^^^^^^^^^^ [1]
test.js:23:13
23| (user_code: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:26:2

Cannot cast `sub_user_code` to empty because string literal `first_user_code` [1] is incompatible with empty [2].

test.js:26:2
26| (sub_user_code: empty)
^^^^^^^^^^^^^

References:
first/subdir/user_code.js:3:14
3| const value: "first_user_code" = "first_user_code";
^^^^^^^^^^^^^^^^^ [1]
test.js:26:17
26| (sub_user_code: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:30:2

Cannot cast `second_only_user_code` to empty because string literal `second_only_user_code` [1] is incompatible with
empty [2].

test.js:30:2
30| (second_only_user_code: empty)
^^^^^^^^^^^^^^^^^^^^^

References:
second/second_only_user_code.js:3:14
3| const value: "second_only_user_code" = "second_only_user_code";
^^^^^^^^^^^^^^^^^^^^^^^ [1]
test.js:30:25
30| (second_only_user_code: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:33:2

Cannot cast `sub_second_only_user_code` to empty because string literal `second_only_user_code` [1] is incompatible with
empty [2].

test.js:33:2
33| (sub_second_only_user_code: empty)
^^^^^^^^^^^^^^^^^^^^^^^^^

References:
second/subdir/second_only_user_code.js:3:14
3| const value: "second_only_user_code" = "second_only_user_code";
^^^^^^^^^^^^^^^^^^^^^^^ [1]
test.js:33:29
33| (sub_second_only_user_code: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:37:2

Cannot cast `node_code` to empty because string literal `node_code` [1] is incompatible with empty [2].

test.js:37:2
37| (node_code: empty)
^^^^^^^^^

References:
node_modules/node_code.js:3:14
3| const value: "node_code" = "node_code";
^^^^^^^^^^^ [1]
test.js:37:13
37| (node_code: empty)
^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- test.js:40:2

Cannot cast `sub_node_code` to empty because string literal `node_code` [1] is incompatible with empty [2].

test.js:40:2
40| (sub_node_code: empty)
^^^^^^^^^^^^^

References:
node_modules/subdir/node_code.js:3:14
3| const value: "node_code" = "node_code";
^^^^^^^^^^^ [1]
test.js:40:17
40| (sub_node_code: empty)
^^^^^ [2]


Error ---------------------------------------------------------------------------------------------------- test.js:43:25

Cannot resolve module `nonexistent`.

43| import nonexistent from 'nonexistent'
^^^^^^^^^^^^^


Error ---------------------------------------------------------------------------------------------------- test.js:44:29

Cannot resolve module `subdir/nonexistent`.

44| import sub_nonexistent from 'subdir/nonexistent'
^^^^^^^^^^^^^^^^^^^^



Found 10 errors
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "first_user_code" = "first_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "first_user_code" = "first_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "first_user_code" = "first_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "first_user_code" = "first_user_code";
export default value;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "second_user_code" = "second_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "second_only_user_code" = "second_only_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "second_user_code" = "second_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "second_only_user_code" = "second_only_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "second_user_code" = "second_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "second_user_code" = "second_user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "user_code" = "user_code";
export default value;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const value: "user_code" = "user_code";
export default value;
44 changes: 44 additions & 0 deletions tests/config_module_system_node_root_relative_dirnames/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @flow

/**
* 1) All "ambiguous" code exists in first/, second/ and node_modules/.
* first/ wins
* 2) All "user_code" code exists in first/ and second/
* first/ wins
* 3) All "second_only" code exists only in second/
* second/ wins
* 4) All "node_code" code exists in only node_modules/
* node_modules/ wins
*/

// These exist in both user code and node_modules, but user code wins
import ambiguous from 'ambiguous';
(ambiguous: empty)

import sub_ambiguous from 'subdir/ambiguous';
(sub_ambiguous: empty)

// These exist in only user code
import user_code from 'user_code';
(user_code: empty)

import sub_user_code from 'subdir/user_code';
(sub_user_code: empty)

// These exist in only user code, and only in second/
import second_only_user_code from 'second_only_user_code';
(second_only_user_code: empty)

import sub_second_only_user_code from 'subdir/second_only_user_code';
(sub_second_only_user_code: empty)

// These exist in only node code
import node_code from 'node_code';
(node_code: empty)

import sub_node_code from 'subdir/node_code';
(sub_node_code: empty)

// These exist nowhere
import nonexistent from 'nonexistent'
import sub_nonexistent from 'subdir/nonexistent'
Loading

0 comments on commit d17ed03

Please sign in to comment.