Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
FlowType and CSS Modules

Huh?

So basically FlowType doesn't know about CSS Modules, a really handy way of dealing with the plagues of CSS in codebases (global variables and dependency wackiness mainly).

What WebPack allows us to do is "require" CSS files and use their class names:

import styles from "my_styles.css";
import React from "react";

const MyComponent = React.createClass({
  render() {
    return <h1 className={styles.redHeader}>Hello!</h1>;
  }
});

Unfortunately, Flow will give us an error Required module not found because, well, let's be honest, importing CSS with JavaScript is pretty out of this world and a little bit crazy (i.e: this).

So here's what I did to fix that. Flow has a nice way of dealing with this in its options, namely one called module.name_mapper. Somebody was kind enough to make an npm module called empty that– you guessed it– returns empty objects and arrays. I'm pretty amazed to have found a use for this.

So as a fix, do this: Run npm install --save empty in your project directory.

Open your .flowconfig, and add the following under [options]:

module.name_mapper='.*\(.css\)' -> 'empty/object'

Ta-da! Another fun day in JavaScript land.

@mvolkmann

This comment has been minimized.

Show comment Hide comment
@mvolkmann

mvolkmann Feb 15, 2016

Sadly this didn't work for me and I'm not sure why. Has anyone else gotten this to work?

Sadly this didn't work for me and I'm not sure why. Has anyone else gotten this to work?

@jsermeno

This comment has been minimized.

Show comment Hide comment
@jsermeno

jsermeno Feb 21, 2016

I couldn't get this to work either, however there is a solution in this thread.

interfaces/global.js

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

.flowconfig

[include]
.*/src/.*

[libs]
./interfaces/global.js

[options]
module.name_mapper='.*\(.css\)' -> 'CSSModule'
module.system=haste
strip_root=true

Hope that helps somebody.

I couldn't get this to work either, however there is a solution in this thread.

interfaces/global.js

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

.flowconfig

[include]
.*/src/.*

[libs]
./interfaces/global.js

[options]
module.name_mapper='.*\(.css\)' -> 'CSSModule'
module.system=haste
strip_root=true

Hope that helps somebody.

@mvolkmann

This comment has been minimized.

Show comment Hide comment
@mvolkmann

mvolkmann Feb 23, 2016

Thanks so much! That works for me.

It seems like the "strip_root=true" line isn't needed for CSS imports to work.

Also, it seems the regex in the name_mapper can be simplified to '.+.css$'.

And one more thing, under [libs] I was able to just use "interfaces" without the ./ or referring explicitly to global.js. That allows me to put other files in that directory as well and have them be processed.

Thanks so much! That works for me.

It seems like the "strip_root=true" line isn't needed for CSS imports to work.

Also, it seems the regex in the name_mapper can be simplified to '.+.css$'.

And one more thing, under [libs] I was able to just use "interfaces" without the ./ or referring explicitly to global.js. That allows me to put other files in that directory as well and have them be processed.

@CrocoDillon

This comment has been minimized.

Show comment Hide comment
@CrocoDillon

CrocoDillon May 1, 2016

The dot in a regex needs to be escaped, otherwise it's a wildcard. I have module.name_mapper='.+\.s?css' -> 'CSSModule'.

Without module.system=haste this doesn't work though. Any side-effects from setting this option? According to the docs haste is a Reacte Native thing, my app is not for React Native though.

The dot in a regex needs to be escaped, otherwise it's a wildcard. I have module.name_mapper='.+\.s?css' -> 'CSSModule'.

Without module.system=haste this doesn't work though. Any side-effects from setting this option? According to the docs haste is a Reacte Native thing, my app is not for React Native though.

@ghost

This comment has been minimized.

Show comment Hide comment
@ghost

ghost Jun 18, 2016

The original post worked fine for me with these two notes:

The dot does need to be escaped like this: module.name_mapper='.*\(.css\)' -> 'empty/object'
empty/object is an actual npm module that you need to install: npm install --save-dev empty

ghost commented Jun 18, 2016

The original post worked fine for me with these two notes:

The dot does need to be escaped like this: module.name_mapper='.*\(.css\)' -> 'empty/object'
empty/object is an actual npm module that you need to install: npm install --save-dev empty

@nicolasartman

This comment has been minimized.

Show comment Hide comment
@nicolasartman

nicolasartman Jul 22, 2016

I couldn't get this to work until I manually added all the file types I wanted flow to recognize:

module.file_ext=.js
module.file_ext=.json
module.file_ext=.jsx
module.file_ext=.css
module.file_ext=.scss

I couldn't get this to work until I manually added all the file types I wanted flow to recognize:

module.file_ext=.js
module.file_ext=.json
module.file_ext=.jsx
module.file_ext=.css
module.file_ext=.scss
@tomtwo

This comment has been minimized.

Show comment Hide comment
@tomtwo

tomtwo Jul 27, 2016

Thanks @nicolasartman - that finally got it fixed for me.

tomtwo commented Jul 27, 2016

Thanks @nicolasartman - that finally got it fixed for me.

@Jero786

This comment has been minimized.

Show comment Hide comment
@Jero786

Jero786 Aug 5, 2016

@nicolasartman +1 Thanks! :)

Jero786 commented Aug 5, 2016

@nicolasartman +1 Thanks! :)

@jdelStrother

This comment has been minimized.

Show comment Hide comment
@jdelStrother

jdelStrother Aug 30, 2016

@nicolasartman +1 🎉

@stereobooster

This comment has been minimized.

Show comment Hide comment
@stereobooster

stereobooster Sep 20, 2016

Other solution would be to generate definition file from css. As it done for TypeScript https://github.com/Quramy/typed-css-modules

Other solution would be to generate definition file from css. As it done for TypeScript https://github.com/Quramy/typed-css-modules

@sgronblo

This comment has been minimized.

Show comment Hide comment
@sgronblo

sgronblo Sep 27, 2016

By "works", do you guys just mean that Flow manages to parse your code correctly? Have you checked whether Flow actually can check that the correct props are specified when you use your component in JSX for example?

I was able to do something similar to what was suggested here but that will apparently make import CssModules from 'react-css-modules; export default CssModules(MyComponent, styles, options) spit out a new Component which is missing the information about the props so Flow cannot check it in JSX.

I was trying to put together a more sophisticated workaround today but without success. Would be interesting to hear if you also had this problem and if you can manage to figure out how to get my solution to work. I documented my efforts here: facebook/flow#2536

By "works", do you guys just mean that Flow manages to parse your code correctly? Have you checked whether Flow actually can check that the correct props are specified when you use your component in JSX for example?

I was able to do something similar to what was suggested here but that will apparently make import CssModules from 'react-css-modules; export default CssModules(MyComponent, styles, options) spit out a new Component which is missing the information about the props so Flow cannot check it in JSX.

I was trying to put together a more sophisticated workaround today but without success. Would be interesting to hear if you also had this problem and if you can manage to figure out how to get my solution to work. I documented my efforts here: facebook/flow#2536

@sgronblo

This comment has been minimized.

Show comment Hide comment
@sgronblo

sgronblo Sep 28, 2016

If anyone still cares, there's been a resolution to this problem on the flow issue I linked in my previous message.

If anyone still cares, there's been a resolution to this problem on the flow issue I linked in my previous message.

@dlants

This comment has been minimized.

Show comment Hide comment
@dlants

dlants Nov 4, 2016

Just a heads up to anyone who has trouble getting this working, it didn't for me until I added module.system=haste

Got the idea from here: facebook/flow#1068

dlants commented Nov 4, 2016

Just a heads up to anyone who has trouble getting this working, it didn't for me until I added module.system=haste

Got the idea from here: facebook/flow#1068

@ckknight

This comment has been minimized.

Show comment Hide comment
@ckknight

ckknight Nov 30, 2016

In order to not have to declare CSSModule for every project, I've published an npm package that does so:

https://www.npmjs.com/package/css-module-flow

https://github.com/ckknight/css-module-flow

In order to not have to declare CSSModule for every project, I've published an npm package that does so:

https://www.npmjs.com/package/css-module-flow

https://github.com/ckknight/css-module-flow

@faceyspacey

This comment has been minimized.

Show comment Hide comment
@faceyspacey

faceyspacey Jan 16, 2017

but what if you're importing styles without the extension, eg: import styles from '../css/foo' where foo is actually foo.css?

module.file_ext=.css doesn't seem to help in that matter. In fact, for me, it makes flow completely not work in VS Code.

faceyspacey commented Jan 16, 2017

but what if you're importing styles without the extension, eg: import styles from '../css/foo' where foo is actually foo.css?

module.file_ext=.css doesn't seem to help in that matter. In fact, for me, it makes flow completely not work in VS Code.

@jeznag

This comment has been minimized.

Show comment Hide comment
@jeznag

jeznag Jun 14, 2017

Ghost's solution worked for me:

empty/object is an actual npm module that you need to install: npm install --save-dev empty

And then add this to .flowconfig

[options]
module.name_mapper='.*\(.s?css\)' -> 'empty/object'

jeznag commented Jun 14, 2017

Ghost's solution worked for me:

empty/object is an actual npm module that you need to install: npm install --save-dev empty

And then add this to .flowconfig

[options]
module.name_mapper='.*\(.s?css\)' -> 'empty/object'
@skovhus

This comment has been minimized.

Show comment Hide comment
@skovhus

skovhus Jun 20, 2017

What I really wanted was Flow to understand and typecheck my CSS Modules. So it would validate that a given identifier is actually in the .css file. Doing so would give us:

  • static type checks showing usage of non existing classes
  • editor autocompletion of CSS classes (for editors supporting Flow)

I actually just released a few tools to do this. Please give them a try! 😃

See:

skovhus commented Jun 20, 2017

What I really wanted was Flow to understand and typecheck my CSS Modules. So it would validate that a given identifier is actually in the .css file. Doing so would give us:

  • static type checks showing usage of non existing classes
  • editor autocompletion of CSS classes (for editors supporting Flow)

I actually just released a few tools to do this. Please give them a try! 😃

See:

@draganSm

This comment has been minimized.

Show comment Hide comment
@draganSm

draganSm Jul 6, 2017

@jeznag Thanks! Your post actually saved my day.

draganSm commented Jul 6, 2017

@jeznag Thanks! Your post actually saved my day.

@johhansantana

This comment has been minimized.

Show comment Hide comment
@johhansantana

johhansantana Jul 12, 2017

@nicolasartman that worked

@nicolasartman that worked

@ndbroadbent

This comment has been minimized.

Show comment Hide comment
@ndbroadbent

ndbroadbent Aug 19, 2017

@skovhus That's brilliant! Way better than just ignoring the errors. Thanks for building this!

@skovhus That's brilliant! Way better than just ignoring the errors. Thanks for building this!

@mechanicals

This comment has been minimized.

Show comment Hide comment
@mechanicals

mechanicals Oct 12, 2017

@jeznag: fantastic. you solution works for me..

@jeznag: fantastic. you solution works for me..

@Luna2442

This comment has been minimized.

Show comment Hide comment
@Luna2442

Luna2442 Dec 15, 2017

Why install another dumb package? Just do this :)

[options]
module.name_mapper='.*(.css.scss)' -> '{}'

Luna2442 commented Dec 15, 2017

Why install another dumb package? Just do this :)

[options]
module.name_mapper='.*(.css.scss)' -> '{}'

@piyushmahen

This comment has been minimized.

Show comment Hide comment
@piyushmahen

piyushmahen Feb 14, 2018

thanks @nicolasartman it works!

thanks @nicolasartman it works!

@dbchristopher

This comment has been minimized.

Show comment Hide comment
@dbchristopher

dbchristopher Feb 21, 2018

Thanks so much for sharing this!! You're a lifesaver.

Thanks so much for sharing this!! You're a lifesaver.

@Vages

This comment has been minimized.

Show comment Hide comment
@Vages

Vages Apr 19, 2018

Could not get your solution to work, @Luna2442. According to the documentation (https://flow.org/en/docs/config/options/#toc-module-name-mapper-regex-string),

module.name_mapper (regex -> string)

Specify a regular expression to match against module names, and a replacement pattern, separated by a ->.

For example:

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

This makes Flow treat require('image!foo.jpg') as if it were require('ImageStub').

These are OCaml regular expressions. Use \( and \) (slashes required!) to create a capturing group, which you can refer to in the replacement pattern as \1 (up to \9).

So (1) how did you get your regex capturing group to work without the required slashes and (2) how did you get Flow to ignore the .css imports by feeding it require('{}')?

Original solution works perfectly, @lambdahands

Vages commented Apr 19, 2018

Could not get your solution to work, @Luna2442. According to the documentation (https://flow.org/en/docs/config/options/#toc-module-name-mapper-regex-string),

module.name_mapper (regex -> string)

Specify a regular expression to match against module names, and a replacement pattern, separated by a ->.

For example:

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

This makes Flow treat require('image!foo.jpg') as if it were require('ImageStub').

These are OCaml regular expressions. Use \( and \) (slashes required!) to create a capturing group, which you can refer to in the replacement pattern as \1 (up to \9).

So (1) how did you get your regex capturing group to work without the required slashes and (2) how did you get Flow to ignore the .css imports by feeding it require('{}')?

Original solution works perfectly, @lambdahands

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment