facebook / flow

Adds static typing to JavaScript to improve developer productivity and code quality.
https://flow.org/
MIT License
22.09k stars 1.86k forks source link

module.name_mapper not working #1068

Open alebiavati opened 9 years ago

alebiavati commented 9 years ago

I can't get module.name_mapper to work on the example in the documentation: http://flowtype.org/docs/advanced-configuration.html#options

Flow version: 0.18.1

Folder structure:

.
├── interfaces/
│   └── ImageStub.js
├── .flowconfig
├── app.js
└── foo.jpg

Contents of .flowconfig:

[libs]
.*/interfaces/*

[options]
module.name_mapper='^image![a-zA-Z0-9$_]+$'->'ImageStub'

Contents of ImageStub.js:

declare module ImageStub {
  declare var exports: { [key: string]: string };
}

Contents of app.js:

/* @flow */

require('image!foo.jpg');

Here is the output when doing flow check:

app.js:3
  3: require('image!foo.jpg');
     ^^^^^^^^^^^^^^^^^^^^^^^^ image!foo.jpg. Required module not found

Found 1 error

To make sure that the regex pattern wasn't the problem I tried to dumb it down and just do the following:

Contents of .flowconfig:

[libs]
.*/interfaces/*

[options]
module.name_mapper='image'->'ImageStub'

Contents of ImageStub.js:

declare module ImageStub {
  declare var exports: { [key: string]: string };
}

Contents of app.js:

/* @flow */

require('image');

and again, this is the output of flow check:

app.js:3
  3: require('image');
     ^^^^^^^^^^^^^^^^ image. Required module not found

Found 1 error

Any ideas?

samwgoldman commented 9 years ago

This feature is covered by automated tests, which are still passing, so it's unlikely that the feature is wholesale not working.

One thing that is suspicious about the code you've pasted here is the [libs] section. You have this configured as a regex (.*/interfaces/*), but I don't think that's correct. Can you try with this:

[libs]
interfaces/
alebiavati commented 9 years ago

I was surprised too! Anyway, I just tried with interfaces/ and it doesn't work.

What else can I provide to debug this? maybe I should try cloning the source and running the unit tests on my machine?

alebiavati commented 9 years ago

Ok, so I checked out the project and built it on my machine. All tests pass, but when I looked at the test sources, there are no tests for module.name_mapper used to map images or css imports.

One of the tests that gets run is for the <PROJECT_ROOT> tag:

module.name_mapper='^\(.*\)' -> '<PROJECT_ROOT>/src/\1'

I tried copying the code for that test to run it within my test app and sure enough flow finds the module in the src/ directory, but it still shows the error for the image.

I also verified that interfaces/ are working by including another test module interface in app.js.

Here is the new source of my test app:

app.js:

/* @flow */

import 'testmodule';
import 'TestModuleInterface';
import 'image!foo.jpg';

.flowconfig:

[libs]
interfaces/

[options]
module.name_mapper='^image![a-zA-Z0-9$_]+$' -> 'ImageStub'
module.name_mapper='^\(.*\)' -> '<PROJECT_ROOT>/src/\1'

src/testmodule.js:

// @flow

export const test = 42;

interfaces/ImageStub.js:

declare module ImageStub {
  declare var exports: { [key: string]: string };
}

interfaces/TestModuleInterface.js:

declare module TestModuleInterface {
  declare var exports: { [key: string]: string };
}

And here is the output of flow check:

app.js:5
  5: import 'image!foo.jpg';
            ^^^^^^^^^^^^^^^ image!foo.jpg. Required module not found

Found 1 error
jeffmo commented 9 years ago

Your regexp doesn't include '.' in the character class

alebiavati commented 9 years ago

Tried fixing that too, it still didn't work:

module.name_mapper='^image!.*' -> 'ImageStub'

BUT, it worked when I added this line:

module.system=haste

Still, I think somebody should update the documentation because there are a few things here:

1) the regex pattern on the documentation is wrong. 2) the example provided for importing foo.jpg doesn't work unless module.system=haste is set.

PS: another way to do this without using module.system=haste is to install the empty npm package and then use the following in your .flowconfig:

module.name_mapper='^image!.*' -> 'empty/object'
alebiavati commented 9 years ago

credit to: https://gist.github.com/lambdahands/d19e0da96285b749f0ef

samwgoldman commented 9 years ago

Hypothesis: the name resolution works for modules found under node_modules, but not for lib files.

alebiavati commented 9 years ago

It might be that. It works using the haste module system, but I'm not sure I understand the difference between node and haste. Does node mean that flow resolves only names that are in the node_modules folder?

girvo commented 8 years ago

I'm still running into this; I'm attempting to map this: '.*\.scss' -> 'empty/object' but no matter what I try it simply doesn't seem to want to work. Any ideas?

[ignore]

[include]

[libs]

[options]
module.name_mapper='.*\.scss' -> 'empty/object'

This is with Flow v0.20.1

girvo commented 8 years ago

Ah, I worked it out. Turns out, it was working, but because npm install empty had been executed in the incorrect directory, it didn't exist. However: the error message was that the scss file didn't exist, not the rewritten module!

simnalamburt commented 8 years ago

+1 For this. I have same problem.

MoOx commented 8 years ago

I am also having issue with local png file

[ignore]
.*/node_modules/.*
.*/lib/.*
.*/.nyc_output/.*
.*/__tests__/.*

[libs]
node_modules/flow-interfaces/interfaces
node_modules/iflow-material-ui/index.js.flow
node_modules/iflow-color/index.js.flow
flow-interfaces

[options]
esproposal.class_instance_fields=enable
esproposal.class_static_fields=enable

module.name_mapper='.*\.(svg|png|jpg|gif)$' -> 'string'

and this code

// @flow 
import logoSVG from "./logo.png"

.png are string, per webpack config (url-loader)

MoOx commented 8 years ago

Related: https://github.com/facebook/flow/pull/1801#issuecomment-219719511

MoOx commented 8 years ago

@jeffmo give some pretty good explanation here https://github.com/facebook/flow/pull/1801#issuecomment-219756526

tl;dr: module.name_mapper allows you to make a replacement, so the name you map to need to be a real node module or an real file. The file needs to be a JS file, not a flow declaration.

So this issue is just a bad usage. I have created a gist and an answer on stackoverflow

I guess this issue can be closed now that there is enough material for people to understand this.

casertap commented 8 years ago

Still not working in my case.

[version]
0.30.0

[ignore]
.*/bin/.*
.*/build/.*
.*/config/.*
.*/coverage/.*
.*/node_modules/.*
.*/src/styles/.*

[include]
./node_modules/

[libs]
./node_modules/flow-interfaces/interfaces/react-redux.d.js
./node_modules/flow-interfaces/interfaces/CSSModule.d.js
./flow-typed/npm/
./interfaces/

[options]
module.system=haste
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
esproposal.decorators=ignore

module.name_mapper.extension='scss' -> 'CSSModule'
module.name_mapper='^inject!(.*)' ->  'empty/object'
module.name_mapper='.*\.\(jpg\|jpeg\|gif\|svg\|png\)$' -> 'Image'
module.name_mapper='^components$' -> '<PROJECT_ROOT>/src/components'
module.name_mapper='^containers$' -> '<PROJECT_ROOT>/src/containers'
module.name_mapper='^redux\/\(.*\)$' -> '<PROJECT_ROOT>/src/redux/\1'
module.name_mapper='^routes$' -> '<PROJECT_ROOT>/src/routes'
module.name_mapper='^utils\/\(.*\)$' -> '<PROJECT_ROOT>/src/utils/\1'
module.name_mapper='^interfaces\/\(.*\)$' -> '<PROJECT_ROOT>/src/interfaces/\1'
module.name_mapper='^tests\/\(.*\)$' -> '<PROJECT_ROOT>/src/tests/\1'

munge_underscores=true

suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe

suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-6]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*www[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-6]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*www[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy

I get

src/tests/unit/redux/modules/search-spec.js:371
[0] 371:       const module = require('inject!redux/modules/search')({
[0]                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inject!redux/modules/search. Required module not found
[0]
[0] src/tests/unit/redux/modules/suggestions-spec.js:132
[0] 132:       const module = require('inject!redux/modules/suggestions')({
[0]                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inject!redux/modules/suggestions. Required module not found
maksimsemenov commented 8 years ago

I have the same issue:

[ignore]
<PROJECT_ROOT>/node_modules/fbjs/.*

[options]
module.name_mapper='^components\/(.*)$' -> '<PROJECT_ROOT>/src/components/\1'
module.name_mapper='^constants\/(.*)$' -> '<PROJECT_ROOT>/src/constants/\1'
module.name_mapper='^actions\/(.*)$' -> '<PROJECT_ROOT>/src/actions/\1'
module.name_mapper='^reducers\/(.*)$' -> '<PROJECT_ROOT>/src/reducers/\1'
module.name_mapper='^types\/(.*)$' -> '<PROJECT_ROOT>/src/types/\1'
import type { DataFieldT } from 'types/DataFieldT'

import * as fieldTypes from 'constants/fieldTypes'
import * as relTypes from 'constants/relationshipTypes'
import * as DFN from 'constants/dataFieldNames'

I get:

types/DataFieldT Required module not found
constants/fieldTypes Required module not found
constants/relationshipTypes Required module not found
constants/dataFieldNames Required module not found

Any suggestions?

averyvery commented 8 years ago

Seeing something similar here, after just installing Flow today and starting to play around with it. I'm trying to use name_mapper to resolve some imports that are usually handled by webpack's modulesDirectories.

Project structure:

/apps
- /shared
- /roomDashboard

.flowconfig:

.*/node_modules/.*

[options]
module.name_mapper='^shared\/(.*)$' -> '<PROJECT_ROOT>/apps/shared/\1'

Error:

  2: import wrapWithPolyfills from 'shared/lib/wrapWithPolyfills';
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ shared/lib/wrapWithPolyfills. Required module not found

Is this the same issue as the ones above? Any ideas?

averyvery commented 8 years ago

I was able to get around my issue with...

module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=apps

...which mimics my modulesDirectories setup. Now Flow is running fine!

prewk commented 7 years ago

This kind of config still doesn't work for me: module.name_mapper='^components\/(.*)$' -> '<PROJECT_ROOT>/components/\1'

I'm not sure how to do it with resolve_dirname instead, module.system.node.resolve_dirname=<PROJECT_ROOT> doesn't work (and would resolve way too much stuff anyway)

I've resorted to creating symbolic links inside of node_modules to make Flow and Mocha work, but they are in the way most of the time so I'd rather not have them.

Freezystem commented 7 years ago

Using flowtype 0.39.0 I encountered a similar problem with lodash imports on a basic create-react-app project.

I'm doing:

import isEmpty from 'lodash/isEmpty';

and I get this error: [flow] lodash/isEmpty (Required module not found) so I tried:

module.name_mapper='^lodash/.+$' -> 'lodash'
(* and *)
module.name_mapper='^lodash/\(.+\)$' -> '<PROJECT_ROOT>/node_modules/lodash/\1'

but I always get the same error. Am I doing it wrong ?

As I can't figured out what was wrong I just fallback to:

import { isEmpty } from 'lodash';
nodkz commented 7 years ago

My working solution for module resolving in 0.45:

[options]
- module.name_mapper='^app\/(.*)$' -> '<PROJECT_ROOT>/app/\1' # does not work
+ module.name_mapper='^app\/\(.*\)$' -> '<PROJECT_ROOT>/app/\1' # works as expected
module.name_mapper='^i18n\/\(.*\)$' -> '<PROJECT_ROOT>/i18n/\1'
module.name_mapper='^lib\/\(.*\)$' -> '<PROJECT_ROOT>/lib/\1'
module.name_mapper='^mailer\/\(.*\)$' -> '<PROJECT_ROOT>/mailer/\1'
module.name_mapper='^schema\/\(.*\)$' -> '<PROJECT_ROOT>/schema/\1'
module.name_mapper='^mongoose-elasticsearch-xp\(.*\)$' -> '<PROJECT_ROOT>/lib/mongoose-elasticsearch-xp\1'

Be careful with escaping characters in regexp

Write this comment cause module.name_mapper works in some cases. And further googlers should not give up playing with this option.

sarkistlt commented 7 years ago

the same issue works fine with module.system=haste it was reported almost 2 year ago, when will this be fixed?

alex35mil commented 7 years ago

One more case:

// module
import coverImage from './images/cover.jpg?preset=cover';

// .flowconfig
module.name_mapper='^\(.*\)\.\(png\|jpe?g\)\?preset=cover$' -> 'CoverImageModule'

// app/declarations/CoverImageModule.js.flow
declare module CoverImageModule {
  declare var exports: {|
    srcset: string,
    fallback: string,
  |};
}

Works w/ module.system=haste, doesn't work w/ module.system=node.

Repo to reproduce: https://github.com/alexfedoseev/blog

nicotroia commented 7 years ago

module.system=haste worked for me

mikeLspohn commented 7 years ago

@Freezystem I'm having that same issue. Did you get is solved with resorting to importing just lodash?

Freezystem commented 7 years ago

I used the fallback method by importing lodash directly but it was on an old project I no longer maintain, maybe the bug is fixed now.

guiguan commented 7 years ago

Same here. node doesn't work, but haste works. Can someone please fix this?

mikeaustin commented 7 years ago

I can't seem to get name_mapper working either. I use webpack resolve.alias instead of using symbolic links (might have to use them?) to refer to a parent project.

module.name_mapper='^react-native$' -> '/../react-evitan/src/components/index.js'

I tried multiple ways with exact js, absolute paths, etc. If I substitute a known module, it does work. So to MoOx's point, is looking for modules only? Using ../ in an import works fine, so I would assume name_mapper would also.

jamietre commented 7 years ago

@mikeaustin coincidentally, I am trying to do exactly the same thing. I don't think you can use .. in the replacement string to traverse above <PROJECT_ROOT>, since in my experimenting the regex I'm using works fine with a different target that is within the project root. I'm not sure how you would resolve a folder above the project root other than with a symlink.

mikeaustin commented 7 years ago

@jamietre Interesting. I guess I could use symlinks, but I don't think they work on Windows? Also, using webpack aliases allows all our imported components to see the alias. With symlinks, I'd need to add one in each module's node_modules.

STRML commented 6 years ago

@jamietre Ensure ../folder_name is in your [include].

pronebird commented 6 years ago

I can't get this to work even with basic

module.name_mapper='^some-module$' -> '<PROJECT_ROOT>/empty.js'`

Flow 0.50

MoOx commented 6 years ago

Try module.name_mapper='^some-module\/\(.*\)$' -> '<PROJECT_ROOT>/empty.js'

CaelanStewart commented 5 years ago

As already mentioned by many, adding a separate name_mapper for each and every directory under src is tedious and prone to error.

It is not a good solution at all.

Is there no simple way whatsoever for us to be able to specify a ROOT directory where our code is and leave it at that?

This is clearly an ongoing problem and nothing seems to have been done about.

I'm having no luck with resolve_dirname.

Tried every permutation in this thread, haste or node.

There are just so many damn variables, and so many explanations that have too many knowledge dependencies to be able to understand. Nobody seems to have given a clear answer that overcomes to the burden of knowledge.

Please just add a mechanism where I can say: this is my root directory, just use this damn directory.

Considering it is build by Facebook, one should think there would be ample time and resources to improve it.