Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore non-JS requires like require('image!Kitten') #345

Closed
ide opened this issue Mar 28, 2015 · 28 comments
Closed

Ignore non-JS requires like require('image!Kitten') #345

ide opened this issue Mar 28, 2015 · 28 comments

Comments

@ide
Copy link

ide commented Mar 28, 2015

No description provided.

@ide
Copy link
Author

ide commented Apr 3, 2015

cc @amasad, perhaps the packager could autogenerate a Flow interface definition for the image files it fines.

@amasad
Copy link
Contributor

amasad commented Apr 3, 2015

@jeffmo is working on this, I think. We want to add a rule to flowconfig to redirect certain require patterns to a predefined mock.

@ide
Copy link
Author

ide commented Apr 3, 2015

Super. Looking forward to that.

@jeffmo
Copy link
Contributor

jeffmo commented Apr 3, 2015

Thanks for the ping. I'm currently focused on building out 'import type'/'export type' -- but I will begin working on this module-path redirection stuff right after that.

@brentvatne
Copy link

👍 also very interested in this

@gcazaciuc
Copy link

+1

@dead-claudia
Copy link

I think this is for Webpack users, if I've inferred correctly.

@ide
Copy link
Author

ide commented May 7, 2015

It's for anyone using the require.js spec's extensions incl RN.

@dead-claudia
Copy link

Ok. Makes sense.

jeffmo added a commit that referenced this issue May 14, 2015
…ving them

Summary: This adds a new config option `module.name_mapper` that allows users to specify a regexp -> replacement template tuple to be applied to any matching module names before the Flow system resolves the name and looks it up.

This has been asked for in many contexts (Webpack and Browserify allow users to specify a means of normalization of module names, for example). However, the most immediate use case is React Native, where they wish to have special kinds of require()s such as `require('image!kitty.png')`. These module names don't really exist, but a name mapper would be able to map them over to a mocked module interface (let's say `MockPNGImageModule`) so that the returned value can still be typed as it will actually work at runtime.

For the react native request, see: #345

Feel free to bikeshed the name of this config entry -- I'm not super tied to it.

Reviewed By: @gabelevi

Differential Revision: D2019856
@jeffmo
Copy link
Contributor

jeffmo commented May 14, 2015

Update: 625c25a just landed which adds a config option of the form:

[options]
module_name.name_mapper = 'regexp-pattern' -> 'replacement-pattern'

(This should go out to opam/brew/etc with the next release)

When using Flow with it's [default] "node" module system, the mapper that wins is the first mapper whose regexp pattern matches and whose generated replacement results in module identifier string that represents an actual/known module in the system.

For anyone who happens to use the "haste" module system, due to the way Flow lazily matches haste module definitions to module imports/requires, the config option isn't able to consider whether a replacement candidate is a valid module at the time the conversion happens -- so the haste module system will use the first mapper whose regexp string matches.

The regexp string is just passed directly to ocaml's built-in regexp utilities, so that can serve as the reference for various supported regexp tokens and replacement templates:

http://caml.inria.fr/pub/docs/manual-ocaml/libref/Str.html#VALregexp

@jeffmo jeffmo closed this as completed May 14, 2015
@ide
Copy link
Author

ide commented May 14, 2015

This is terrific @jeffmo.

@amasad - is an image source mock in the works?

@amasad
Copy link
Contributor

amasad commented May 14, 2015

@jeffmo: can replacement-pattern generate a relative module name?

@ide: Should be pretty simple. Just making this object a module: https://github.com/facebook/react-native/blob/master/packager/react-packager/src/Packager/index.js#L178-L186
I'll do it once I understand how the replacement-pattern works.

@jeffmo
Copy link
Contributor

jeffmo commented May 14, 2015

@amasad: Sure. It's a string match that happens just before the lookup, so the result of running the replacement should be the same as if you put the replacement directly into the code

@thealjey
Copy link

@jeffmo could you please provide an example of how module_name.name_mapper can actually be used, for the not so savvy? How to do a sass import, how to do an image import, etc.
thank you!

@gabelevi
Copy link
Contributor

@thealjey - there are a couple tests checked in to exercise module.name_mapper. You can search for them.

Taking the first example that, shows

module.name_mapper='1Doesnt\(Exist\)' -> '\1s'

This means it will treat require('1DoesntExist) as require('Exists'). Each test is a folder with a tiny little Flow world inside. So The config_module_name_rewrite_node test is making sure this works for node module systems. In that directory there is a file Exists.js which is being required via the various renames and A.js which tests out the feature.

@jedwards1211
Copy link
Contributor

Is there any way to simply ignore all imports of e.g. .sass files without implementing a mock of some sort?

@rossPatton
Copy link

backing up what @jedwards1211 said - it's a little cumbersome as is.

for instance, in my use case, i'm using webpack and css modules and i'm pulling in local yaml and local css like so:

import fields from '../_fields.yaml'
import classes from './reference.css'

// react down here

understandably , both of those imports throw flow errors

screen shot 2016-04-13 at 5 48 44 pm

pretend both of those files are in the screenshot

Looking up how to deal with this issue leads me to this url, and the flow docs

When I attempt to use the solution mentioned in the docs

module.name_mapper.extension= 'css' -> '<PROJECT_ROOT>/CSSFlowStub.js.flow'

I get this error (was mapper.extension removed?):

screen shot 2016-04-13 at 5 51 46 pm

i also tried the other doc example, the non shorthand approach, and while it doesn't error out, it also doesn't appear to do anything

for instance, I would expect this to throw a flow error:

// my undeclared, potentially null/undefined require
import classes from './myClasses.css'

// react down here
return <div className={classes.div} />

But it doesnt

So, yeah. The only way I can think to handle my particular case is to declare every single local require manually in my interface file, or use a flow ignore comment (which is what I currently do) or to manually re-assign and type all my non js imports like below

// this works, but still requires manually declaring in my interface file
const classes: Object = require('./reference.css')

// react
return <div className={classes.div} />

Is there an official way to handle non js / local imports? Or should I just continue to use require for those files? It just seems really tedious to manually declare and type every single non js asset

edit: forgot to mention my flow version. i'm on 0.22.1

@mikelambert
Copy link

mikelambert commented Dec 6, 2016

@rossPatton , pulling an example from the react-native codebase:
https://github.com/facebook/react-native/blob/master/.flowconfig
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'

Where this is the implementation of RelativeImageStub:
https://github.com/facebook/react-native/blob/master/Libraries/Image/RelativeImageStub.js

Since Flow loads all modules and uses that @providesModule, you could stick it anywhere in your code tree.

@AlexanderTserkovniy
Copy link

https://blog.iansinnott.com/getting-started-with-flow-and-webpack/ helped me to solve this problem.

@nerdyman
Copy link

nerdyman commented Apr 3, 2017

like @AlexanderTserkovniy said this linked helped.

This is what I ended up with:

In your .flowconfig:

[options]
module.name_mapper.extension='css' -> '<PROJECT_ROOT>/flow/extension-resolver.js.flow'
module.name_mapper.extension='vue' -> '<PROJECT_ROOT>/flow/extension-resolver.js.flow'

In flow/extension-resolver.js:

/* @flow */
declare export default { [key: string]: string }
// example usage
import Main from 'main.vue';

I still have to explicitly specify the file extension when importing but it works good enough for now.

@sibelius
Copy link

I using this on my .flowconfig

module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'

but flow keeps complaining about Require module not found

@mikaello
Copy link

mikaello commented Aug 2, 2017

@sibelius Instead of matching the whole module name, you can use extension matcher as suggested in previous posts. This works for me:

Change:

module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'

To:

module.name_mapper.extension='\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)' -> 'RelativeImageStub'

@jslz
Copy link

jslz commented Aug 27, 2017

Do we have to reset some cache or something maybe? I have never seen any of these things work. Just tried a bunch of stuff on a new project again, and again with no luck unfortunately. Using 0.53.1.

module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.name_mapper.extension='png' -> 'RelativeImageStub'
Error: src/screens/Screens.js:25
 25:       icon: require('App/src/assets/images/tabbar-x.png'),
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ App/src/assets/images/tabbar-x.png. Required module not found
$ ls src/assets/images/tabbar-x*png
src/assets/images/tabbar-x@2x.png

@jedwards1211
Copy link
Contributor

Requiring images of kittens should always be allowed 😸

@emily-curry
Copy link

@jslz @sibelius Are you using react-native? RelativeImageStub is a module exported by that project, so if you don't have it then that's why flow can't find it. Personally, I used that regex, and then created a stub module manually for it. In the root of my project, I have a file called libdefs.js:

declare module 'RelativeImageStub' { declare module.exports: string }

Then, the relevant portion of my .flowconfig:

[libs]
libdefs.js

[options]
module.name_mapper.extension='\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)' -> 'RelativeImageStub'

This got rid of Required module not found on requiring images for me. flow@0.54.1

@sibelius
Copy link

sibelius commented Nov 7, 2017

this one worked for me #345 (comment)

in the latest version of flow

@haipengz
Copy link

@mikaello It works for me. Thanks!

@rtt63
Copy link

rtt63 commented Sep 7, 2018

All of this actually didn't work for me. Btw I also have

[untyped]
.*/node_modules/react-native

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests