GithubHelp home page GithubHelp logo

css-modules-require-hook's Introduction

css-modules-require-hook

The require hook compiles CSS Modules in runtime. This is similar to Babel's babel/register. See the example: demo.

What is CSS Modules

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. Learn more in the article CSS Modules - Welcome to the Future by Glen Maddern.

Features

Compiling in runtime, universal usage.

Requirements

To use this tool we require Node.js v0.12.x (or higher).

Installation

$ npm i css-modules-require-hook

Usage

Now, there are two ways to attach hook: manually or using preset file.

The first one allows you to pass options manually after module was required. Example:

const hook = require('css-modules-require-hook');

hook({
  generateScopedName: '[name]__[local]___[hash:base64:5]',
});

// const styles = require('./icon.css');

The second one allows you to move options to the separate file cmrh.conf.js. Config file should be located in the same directory where executor is or in its ancestor directories. In that case hook will be attached right after the css-modules-require-hook/preset module will be required. Example:

// cmrh.conf.js
module.exports = {
  generateScopedName: '[name]__[local]___[hash:base64:5]',
};
require('css-modules-require-hook/preset');

// const styles = require('./icon.css');

Using with babel-node / ES6 Imports

You will need to create a cmrh.conf.js file within the directory as you are importing css-modules-require-hook.

// server.js
import csshook from 'css-modules-require-hook/preset' // import hook before routes
import routes from '/shared/views/routes'

// create server, etc
// cmrh.conf.js
module.exports = {
  // Same scope name as in webpack build
  generateScopedName: '[name]__[local]___[hash:base64:5]',
}

Development mode

Usually, Node.js caches all the require calls by default. In order to invalidate cache for the purpose of development you should set the environment variable NODE_ENV to development. For example:

$ NODE_ENV=development node server.js

Still you can use devMode option (see below) to override behavior which is imposed by environment variable.

Adding custom PostCSS plugins

var hook = require('css-modules-require-hook');
var cssnext = require('cssnext');

hook({
  prepend: [
    // adding CSS Next plugin
    cssnext(),
  ],
});

Specify custom pattern to build generic names

var hook = require('css-modules-require-hook');

hook({
  generateScopedName: '[name]__[local]___[hash:base64:5]',
});

Using Stylus as a preprocessor

var hook = require('css-modules-require-hook');
var stylus = require('stylus');

hook({
  extensions: ['.styl'],
  preprocessCss: function (css, filename) {
    return stylus(css)
      .set('filename', filename)
      .render();
  },
});

// var styles = require('./demo.styl');

Tuning (options)

To adjust the require hook you need to provide params to the exported function.

var hook = require('css-modules-require-hook');

hook({
  // append: [],
  // generateScopedName: function () {},
  // or any other options
  // see the list below
});

devMode boolean

Helps you to invalidate cache of all require calls. Usually used for the development purpose. Also overrides behavior, imposed by NODE_ENV environment variable. For example:

hook({
  devMode: false,
});

extensions array

Attach the require hook to additional file extensions (for example ['.scss']).

ignore function|regex|string

Provides possibility to exclude particular files from processing. Supports glob and regular expressions syntax. Also you may provide custom function.

preprocessCss function

In rare cases you may want to precompile styles, before they will be passed to the PostCSS pipeline. You should use synchronous transformations, since require function is synchronous.

hook({
  /**
   * @param  {string} css
   * @param  {string} filepath Absolute path to the file
   * @return {string}
   */
  preprocessCss: function (css, filepath) {
    return css;
  }
});

processCss function

In rare cases you may want to get compiled styles in runtime, so providing this option helps.

hook({
  /**
   * @param  {string} css
   * @param  {string} filepath Absolute path to the file
   */
  processCss: function (css, filepath) { /* */ }
});

processorOpts object

Provides possibility to pass custom options to the LazyResult instance. It can be usefull if you want to set the custom parser, for example: postcss-less.

const hook = require('css-modules-require-hook');
const lessParser = require('postcss-less').parse;

hook({
  extensions: '.less',
  processorOpts: {parser: lessParser},
});

camelCase boolean|string

Camelizes exported class names. Similar to css-loader?camelCase.

Available options: true, dashes, only, dashesOnly.

append array

Appends custom plugins to the end of the PostCSS pipeline. Since the require function is synchronous, you should provide synchronous plugins only.

prepend array

Prepends custom plugins to the beginning of the PostCSS pipeline. Since the require function is synchronous, you should provide synchronous plugins only.

use array

Provides the full list of PostCSS plugins to the pipeline. Providing this cancels append, prepend, createImportedName, generateScopedName options. Synchronous plugins only.

createImportedName function

Short alias for the postcss-modules-extract-imports plugin's createImportedName option.

generateScopedName string|function

Short alias for the postcss-modules-scope plugin's option. Helps you to specify the custom way to build generic names for the class selectors. You may also use a string pattern similar to the webpack's css-loader.

hook({
  generateScopedName: '[name]__[local]___[hash:base64:5]'
});

or

hook({
  /**
   * @param  {string} name     Usually a class name
   * @param  {string} filepath
   * @param  {string} css
   * @return {string}
   */
  generateScopedName: function (name, filepath, css) {
    return name;
  }
});

hashPrefix string

Short alias for the generic-names helper option. Provides additional hash uniqueness. Might be useful for projects with several stylesheets sharing a same name.

mode string

Short alias for the postcss-modules-local-by-default plugin's option.

resolve object

Changes the way the paths of ICSS imports will be resolved (@value a from './b.css' and composes a from './b.css'). Supports:

  • resolve.alias object
  • resolve.extensions array — default value is ['.css'].
  • resolve.modules array
  • resolve.mainFile string — default value is 'index.css'.
  • resolve.preserveSymlinks boolean — default value is false.

See the detailed description at: https://github.com/css-modules/postcss-modules-resolve-imports#options

rootDir string

Provides absolute path to the project directory. Providing this will result in better generated class names. It can be obligatory, if you run require hook and build tools (like css-modulesify) from different working directories.

Debugging

debug package is used for debugging. So to turn it on simply specify the DEBUG environment variable:

  • DEBUG=css-modules:fetch — to see resolved paths to the files.
  • DEBUG=css-modules:preset — to see whether config was found or not.
  • DEBUG=css-modules:setup — to see the new options list.
  • DEBUG=css-modules:* — to see everything.

Links

License

The MIT License

css-modules-require-hook's People

Contributors

joeybaker avatar josephfinlayson avatar longlho avatar nkbt avatar oisezrg avatar pascalduez avatar ro-savage avatar sapegin avatar sebastienbarre avatar sullenor avatar tatjsn avatar yazonnile avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

css-modules-require-hook's Issues

caching feature

Compiling modules is good. Loading compiled modules from cache looks slightly better. The goal is to estimate the usefulness of this feature.

Also good option will be to think about possible ways to test this feature.

Similar ideas: https://babeljs.io/docs/usage/require/

Add code coverage report

I am going to add isparta (forked istanbul, perfectly supports ES6), it works good with mocha (did it before for react-motion).

remove shortcut aliases?

Hi all? What to do you think about making more strong API with removing aliases (root and d for rootDir, u for use)? In first 2-3 months we can just add warning message of using deprecated options.

Better extension handling?

I have a loader which loads normal css(non local css) with extensions with accepts ".styl".
Then I have a loader which does the same but with local css ".l.styl"

I have tried extensions: [ '.l.styl', 'l.styl' ] but did not work correctly.

There is a ignore function property, but how about an include property for the plugin?

Watch mode

CSS map changes doesn't apply, if module was changed.
It's due all modules in node js exposed as cached objects.
All reference saved in require.cache.

This tiny helper, that we use for clear require.cache:

  /**
   * Clears node.js require cache by key.
   * @param {string} key Cache key.
   */
  function clearCacheKey (key) {
    delete require.cache[key];
  }

Cannot resolve module 'postcss-less'

Hi, I've started to get the following error in the build stage of my app.

ERROR in ./~/css-modules-require-hook/test/api/processorOpts.js
Module not found: Error: Cannot resolve module 'postcss-less' in /Users/peterm/Workspace/personal/react-lego/node_modules/css-modules-require-hook/test/api
 @ ./~/css-modules-require-hook/test/api/processorOpts.js 5:19-42

my config file is (no mention of less):

const sass = require('node-sass');
module.exports = {
  generateScopedName: '[path]___[name]__[local]___[hash:base64:5]',
  extensions: ['.scss'],
  preprocessCss: (data, filename) =>
    sass.renderSync({
      data,
      file: filename,
    }).css,
};

I see your package.json has a devDependency "postcss-less": "^0.2.0", which looks correct to me as you are using this module for tests.

I suspect the tests are being run accidentally. Is that a post install script that runs the tests which is inadvertently being run when people npm i this module?

this is the code changes i have done to get css-modules to work : peter-mouland/react-lego@63b535c

modern syntax

Thanks to @pocketjoso we found out, that its impossible to adjust options for the require hook in the modern syntax. According to the http://calculist.org/blog/2012/06/29/static-module-resolution/

This import is resolved at compile time — that is, before the script starts executing. All the imports and exports of the declarative module dependency graph are resolved before execution.

It makes futile to adjust it right after the import. So have to look for another options :(
#6 (comment)

Can not use classnames from postcss-style inheritance

Hello.
I'm using postcss to write styles and it's inheritance (with ampersand or not).
Code sample:

.root {
  propertyName: value;

  &.deepRooted {
    propertyName: value;    
  }
}

When I import this and try to use styles.deepRooted - it's undefined.

I tired to see what css text processCss method return - and the css is not compiled.

Is this ok? Should I precomplie my postcss by preprocessCss method invokation?

Example project

Much in the same spirit as the browserify, jspm and webpack demos, I'd love to see a simple project using css-modules-require-hook to load CSS and render templates with it on the server side.

Is that something you could take on, @sullenor?

scss @import fail

Hi,
I am using css-modules-require-hook on my koa.js server. I do server side rendering of react components. It worked fine when just using css but now we want to switch to Sass in order to use variables.

I am using the following code to compile scss on runtime:

// css modules
const cssRequireHook = require('css-modules-require-hook');
const sass = require('node-sass');
cssRequireHook({
    generateScopedName: '[name]__[local]___[hash:base64:5]',
    extensions: [ '.scss', '.css' ],
    preprocessCss: data => sass.renderSync({ data }).css
});

It works, but as soon as I try to @importany other .scss file it breaks

@import '../../styles/variables';

.myclass {
    background: $color-white;
    clear: both;
}

in variables.scss

$color-white: #000;

the error I get:

/Users/tmaximini/data/frontend-node/node_modules/node-sass/lib/index.js:424
  throw util._extend(new Error(), JSON.parse(result.error));
  ^

Error: File to import not found or unreadable: ../../styles/variables

the path is definitely correct, and webpack handles it fine on client. I don't use webpack on my server code so I hoped that the require-hook can somehow resolve these @imports.

Any idea how to achieve this?
thanks

Possibility to specify custom pattern for the classname

Nice that already works for me. Does this already has an option to define how class names are generated? Basically I’d need to get the same class names as with webpack using ?localIdentName='[local]--[hash:base64:5]' or a different variation of the setting.
by @maxhoffmann

I'm not sure whether this fits in here, but for those who use webpack with css-loader/locals: I forked css-loader and added a new parameter for localIdentName: sourceHash. This generate a hash from the localName and the source code, so the class names are stable.
https://github.com/Aluxian/css-loader
by @aluxian

Testing with karma and karma-webpack-with-fast-source-maps

I've been using css-modules with mocha, enzyme and css-modules-require-hook without any problems.

// Foo.js
import React from 'react';
import styles from './Foo.css';

const Foo = () => <div className={ styles.foo }>Foo component</div>;

export default Foo;
// Foo.css
.foo {
    color: rebeccapurple;
}
// Foo.test.js
import Foo from '../Foo';

describe('<Foo />', () => {
    it('should render proper markup', () => {
        const wrapper = shallow(<Foo />);

        expect(wrapper.find('.foo')).to.have.length(1);
    });
});

I started using Karma with karma-webpack-with-fast-source-maps and now the test fails because the styles in Foo.js is an array: [[376, '.Foo__foo___3QT3e { color: rebeccapurple; } ', '']]

I've tried importing the css-modules-require-hook in test-bundler.js for karma-webpack but that throws a bunch of errors.

WARNING in ./~/css-modules-require-hook/preset.js
Critical dependencies:
13:7-22 the request of a dependency is an expression
 @ ./~/css-modules-require-hook/preset.js 13:7-22

// many more warnings

ERROR in ./~/css-modules-require-hook/lib/index.js
Module not found: Error: Cannot resolve module 'fs' in /Users/qmmr/code/streetlife/composer/code/site/node_modules/css-modules-require-hook/lib
 @ ./~/css-modules-require-hook/lib/index.js 14:19-32

// more errors

PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
  SyntaxError: Unexpected token ')'

Is there a way to use css-modules-require-hook with karma-webpack?

usage with webpack's preprocessors ?

I couldn't get the hook to work with my webpack's configurations for preprocessed stylesheets (in stylus).

My webpack config:

{ test: /\.styl$/, loader: "style!css?modules&importLoaders=2&sourceMap&localIdentName=[name]-[local]-[hash:base64:5]!stylus!autoprefixer?browsers=last 2 version" }

the stylus file:

@import "./constants/colors"
@import "./constants/typography"
@import "./utils/normalize"

html
  -webkit-tap-highlight-color rgba(0, 0, 0, 0)
body
  color $color-primary
  background $color-white
  font-size $font-size
  font-family $font-family
  font-weight 300
html,
body,
#root
  height 100%
a
  color $color-secondary
h1
  font-weight 300
  text-align center

the component using the style:

import styles from "../style/Application.styl";

console.log(styles)

class App extends Component {
    .....
}

how i register the hook on server.js:

// Enable css modules to be loaded on the server side
require("css-modules-require-hook")({
  extensions: [".styl"]
});
....

when logging styles, I expected to see Object { root: ... }, however I just see an empty object { }.

referenced class name in composes not found

Hi. I'm using webpack but testing with mocha. How would you do unit tests with css-modules / css-loader? It was suggested I use css-modules-require-hook but I'm getting errors related to composes:

CssSyntaxError: postcss-modules-scope: /src/css/Vote.css:2:3: referenced class name "i__imported_large_0" in composes not found
.voteButton {
  composes: large from "./typography.css";

where src/css/Vote.css is:

.voteButton {
  composes: large from "./typography.css";
}

and src/css/typography.css is:

.large {
  font-size: 1.5rem;
}

and src/components/Vote.jsx (being tested) starts with:

import React from 'react/addons';
import styles from '../css/Vote.css';

I'm testing with:

    "test": "mocha --compilers js:babel-core/register --require ./test/test_helper.js 'test/**/*.@(js|jsx)'",

and my test/test_helper.js contains:

var hook = require('css-modules-require-hook');
var path = require('path');

hook({
  // setting root to the parent directory
  rootDir: path.join(__dirname, '..')
});

It fails the same way if I don't change rootDir...
Thank you. Keep up with the great work!

css-loader patterns

Since many ppl use the require hook together with webpack, I think we can make the simplier API for the require hook and provide opportunity to specify the same patterns as for the css-loader. So the user will be able to provide the string pattern for the generateScopedName option:

hook({
  generateScopedName: '[name]__[local]___[hash:base64:5]'
})

I'm going to make another module (What will be the name?) which will implement that. It will be possible to use it with css-modulesify also, so we will get the similar API in our tools. Module will use interpolatename function and solve the problem with the [local] pattern. Smthing like:

/**
 * @param  {string} name     Usually a class name
 * @param  {string} filepath
 * @return {string}
 */
function generate(name, filepath) {
  // return the new name
}

Also I will same the opportunity to provide custom functions.

https://github.com/css-modules/postcss-modules-scope/blob/master/src/index.js#L38
https://github.com/webpack/css-loader/blob/master/lib/processCss.js#L148-L150
https://github.com/webpack/css-loader/blob/master/lib/getLocalIdent.js

Adding more caching

In our particular use case, we have a couple of large CSS files (>300 KB) that are being required. Each of these takes a long time to parse (the longest about 14 seconds), which means that server start up time is really slow. To deal with this, what we ended up doing was creating a disk cache to avoid having to re-parse all of these huge files every time the server is started (instead they just require a couple of json files, which is very fast). This reduced the server start up time from 40 seconds to about 5 in our particular case.

I was wondering if this kind of functionality is something you'd be interested in incorporating into this repo? If so, I'd be happy to start working on a PR.

Use without css modules

Is there a way to <link> one big css/scss bundle and make components use those global styles without css-modules and other hipster require(.css) tricks? :-D

CSSNext support

I have Webpack set up using cssnext-loader and then css-loader?modules for the client-side. However, for the server there's no way to preprocess the CSS before the require hook runs it through PostCSS. How could I have CSSNext process the file first before it hits the require hook?

Is it best that this is made into it's own require hook or could there be an extra parameter passed in (preprocess) that allows you to run the CSS through CSSNext first (or possibly LESS/SASS??)

generateScopedName generates different hash than css-loader

Thanks, for making such cool feature as generateScopedName, but combining it with webpack's css-loader gives me different results.
Here is css-modules-require-hook config:

require('css-modules-require-hook')({
  generateScopedName: '[name]__[local]___[hash:base64:5]',
  mode: 'local'
});

And here is webpack's css-loader config:

{ 
      test: /\.css$/, 
      loader: 'style!css?modules&localIdentName=[name]__[local]___[hash:base64:5]!postcss',
      include: [
        path.resolve(__dirname, '..', 'app')
      ]
}

So hooks result is index__navitem___2ntrM, but css-loader's: index__navitem___1gRhv.

What can be wrong with config or is this hook's bug?

Deprecation messages

getting these messages when I load up app

Container#eachAtRule is deprecated. Use Container#walkAtRules instead.
Container#eachRule is deprecated. Use Container#walkRules instead.
Container#eachDecl is deprecated. Use Container#walkDecls instead.
Node#before is deprecated. Use Node#raws.before
Node#removeSelf is deprecated. Use Node#remove.

Server side: generateScopedName is not used while using Babel

If you use babel imports you have something like this:

import cssModulesRequireHook from 'css-modules-require-hook'
import cssnext from 'postcss-cssnext'
cssModulesRequireHook({
    generateScopedName: '[name]__[local]___[hash:base64:5]',
  prepend: [
    cssnext(),
  ],
});
import App from './components/app.js'

And you app.js imports some css.
When you use babel-node all the imports go up in the code therefore cssModulesRequireHook() is called after requiring of app.js resulting into omitting generateScopedName.

Solution: return hook as function to be called with options, just like node-jsx.

Cannot both control generateScopedName and use posstcss plugins.

The return in https://github.com/css-modules/css-modules-require-hook/blob/master/src/index.js#L58 means that the generateScopedName option is ignored when postcss plugins are configured (with use).

It looks like #34 was intended to address this, but as far as I can tell it doesn't. The return statement continues to be a problem.

Replacing

  if (customPlugins) {
    return void (plugins = customPlugins);
  }

  plugins = [];

with

  if (customPlugins) {
    plugins = customPlugins;
  } else {
    plugins = [];
  }

(or at least hacking in the equivalent in dist) works for me. I'm not sure if it would break any other use cases.

I have a feeling that I may be simply overlooking or misunderstanding something.

Set postcss deps as peerDeps?

It might be cleaner to have all the postcss deps as peerDeps? That way users of this module can set their own versions.

Using composes meet " markup generated different on client and server"

as title

require-hook work fine on isomorphic project.
but when I used "composes" syntax meet following warning:

『Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) tyle__header___3gZu_ test__test___3UOM0"
(server) tyle__header___3gZu_" data-reactid=".1g4...』

seems like composes class not render in server side, has any method to resolve it?


sorry I didn't notice to restart the project, close the issue.

incompatibility with async plugins

If you try to use require hook with any async plugin (for example postcss-import) you get error throw new Error('Use process(css).then(cb) to work with async plugins');

Support to resolve index.css

Hey there,

I've been using this library for a while now and it has been working very well.
But I started on a new project, where they organise some of their CSS on folders, like this:

/style/typography/index.css
/style/typography/medium.css
/style/typography/large.css

So we can compose like:

.item {
  /* webpack can resolve the index.css for you */
  composes: size-sm bold from 'style/typography';
  
  /* or you can compose the file directly */
  composes: size-md from 'style/typography/medium';
}

That works fine on webpack, but this hook does not try to resolve index.css files if the given path is a folder. Is it possible to add such feature? I could try to submit a PR to include this.

Let me know!

How to customize resolve rules in order to align with webpack configuration

On client side, webpack can use resolve.alias to helps you shorten module resolving rules. If I specify:

resolve: {
  alias: { common: path.resolve('some/path/to/common') }
}

I can write something like:

.example {
  composes: one-helper from 'common/helpers';
}

and webpack will resolve 'common/helpers' to './some/path/to/common', and it works very well on client side.

How to achieve this functionality on server-side by using css-module-require-hook? I've solved many types of modules, only except for css modules. Thanks in advance.

Question: What are the possible interfaces expected for processCss hook and how to use css-modules-require-hook to generate what I want?

Hi all,

I am trying to create a processor to transpile css/scss to ReactNative.StyleSheet objects. What I am not understanding is what is expected from a processCss function. Maybe I should use another phase to do what I want. I don't know indeed and that is why I would be pleased to hear from anyone some advice about.

Given the import myClasses from './my-classes.css'; When/where should I interfere to parse the last step before assigning some value to the variable myClasses? I guess, it is the right moment to get my css (string) convert into object and use the ReactNative.StyleSheet.create to generate what I want to consume from myClasses.

Thank you so much in advance

Auto installing the hook

I am going to use this hook for my Mocha tests alongside the babel-register for js. So, I supposed to run Mocha using the following command:

mocha --compilers js:babel-register,css:css-modules-require-hook

But it doesn't work because you export hook, but don't do actual register. So, it would be good to have an ability to setup require hook with default settings by passing a single require statement:

require('css-modules-require-hook/register');

What do you think about it?

How to load global variables, mixins, etc?

Hi, I'm using react-on-rails with react-css-modules and webpack. Original react-on-rails boilerplate provides setup using sass-resources-loader, which makes able to define resources with global variables or mixins for all css modules inside the project:

sassResources: ['./app/assets/styles/app-variables.scss', './app/assets/styles/app-mixins.scss'],

Everything works well, until I started testing of components. Original command raised Error: "selected" CSS module is undefined.. So I added --require part to the command:

"test": "NODE_PATH=./app mocha --compilers js:babel-core/register --require ./app/libs/testHelper.js --require ./app/libs/testNullCompiler.js --require ./app/libs/testCSSModulesHook.js 'app/**/*.spec.@(js|jsx)'",

Content of app/libs/testCSSModulesHook.js:

import hook from 'css-modules-require-hook';
import sass from 'node-sass';
hook({
  extensions: ['.scss'],
  preprocessCss: data => sass.renderSync({ data }).css,
});

After that testing command started to raise "Error: no mixin named screen" or "Error: no variable named $var"

Is there any way to define resources with global variables and mixins using css-modules-require-hook?

Electron Support

What would you guys think about a pull request to support Electron? My basic idea would be to detect if window.document.head exists and if it does exist to inject the styles into the head. Probably somewhere around here...

Usage in production and includePaths

Hey!
I have two questions:

First I have a hard time thinking about if and how to use this hook in production. I am using webpack with css modules for the client side and this require hook on the server side when doing server side rendering of react components. In my production environment I am planning to use ExtractTextPlugin to extract all the css. Currently we are still in development.

What I notice so far is that the require hook is setting the correct classes but it does not include any generated css in the server rendered templates. So I actually get flashes of unstyled content until client side (webpack) picks up, which then inlines the styles into the html. Is this expected behaviour?
Also are there any performance hogs to be expected in production (compiling scss in runtime sounds like it). What is best practice here for setting up this hook for both development and production?

second question:

I have lots of components and lots of scss files. In these files I am importing most of the times other .scss files such as variables and mixins from a shared folder.
Webpack allows a configuration for modulesDirectories, similar to this hook's includePaths.

The problem is, that with webpack I have to prepend imports from these custom directories with a tilde. e.g.:
@import '~styles/variables'; instead of @import '../../../../styles/variables';

The tilde prefix breaks this require hook, here it would work with just @import 'styles/variables';
Any idea how to solve this?

Here is my current setup:

const cssRequireHook = require('css-modules-require-hook');
const sass = require('node-sass');
const path = require('path');
const nameScope = process.env.NODE_ENV === 'test' ? '[local]' : '[name]__[local]___[hash:base64:5]';

cssRequireHook({
    generateScopedName: nameScope,
    extensions: ['.scss', '.css'],
    preprocessCss: (data, filename) => sass.renderSync({
        data,
        file: filename,
        includePaths: [path.resolve(__dirname, '../../client')]
    }).css
});

declare Node version requirement

Hi, thanks for nice library,
I was stuck with the error below for a while before I figured out that my Node version (0.10.38) didn't have the required path.parse method:

var parsed = (0, _path.parse)(pathname);
                               ^
TypeError: undefined is not a function
    at isModule (/Users/jso/criticalcss/node_modules/css-modules-require-hook/dist/index.js:113:32)

Error went away after upgrading to Node 0.12.7.


Perhaps you can declare this requirement a bit more explicitly in README somewhere (or guard and throw clearer error)?

Thanks again for great repo!

How to clone your demo?

$ git clone https://github.com/css-modules/css-modules-require-hook/tree/master/demo
Cloning into 'demo'...
fatal: repository 'https://github.com/css-modules/css-modules-require-hook/tree/master/demo/' not found

Improve code quality

Just thought about adding jscs and eslint. Also add scripts to lint files on the precommit hook and globally.

Warnings "require.extensions is not supported by webpack. Use a loader instead." and errors "Module not found: Error: Cannot resolve module 'fs'"

Doesn't work. Errors.
What is this and how to solve?

    WARNING in ./~/css-modules-require-hook/lib/index.js
    Critical dependencies:
    79:40-60 the request of a dependency is an expression
     @ ./~/css-modules-require-hook/lib/index.js 79:40-60

    WARNING in ./~/css-modules-require-hook/lib/attachHook.js
    require.extensions is not supported by webpack. Use a loader instead.

    WARNING in ./~/css-modules-require-hook/lib/attachHook.js
    require.extensions is not supported by webpack. Use a loader instead.

    ERROR in ./~/css-modules-require-hook/lib/index.js
    Module not found: Error: Cannot resolve module 'fs' in c:\Users\user\demo-app\node_modules\css-modules-require-hook\lib
    resolve module fs in c:\Users\user\demo-app\node_modules\css-modules-require-hook\lib
      looking for modules in c:\Users\user\demo-app\node_modules
        c:\Users\user\demo-app\node_modules\fs doesn't exist (module as directory)
        resolve 'file' fs in c:\Users\user\demo-app\node_modules
          resolve file
            c:\Users\user\demo-app\node_modules\fs.js doesn't exist
            c:\Users\user\demo-app\node_modules\fs doesn't exist
            c:\Users\user\demo-app\node_modules\fs.jsx doesn't exist
            c:\Users\user\demo-app\node_modules\fs.react doesn't exist
            c:\Users\user\demo-app\node_modules\fs.scss doesn't exist
            c:\Users\user\demo-app\node_modules\fs.json doesn't exist
            c:\Users\user\demo-app\node_modules\fs.jpeg doesn't exist
            c:\Users\user\demo-app\node_modules\fs.jpg doesn't exist
            c:\Users\user\demo-app\node_modules\fs.png doesn't exist
            c:\Users\user\demo-app\node_modules\fs.gif doesn't exist
            c:\Users\user\demo-app\node_modules\fs.svg doesn't exist

Specify options for postcss-modules-parser

I'd like to use https://github.com/gilt/postcss-less to handle less/css-modules files. This works when I modify the line 93 in lib/index.js as follows:

+ var lessParser = require('postcss-less').parse;

- var lazyResult = runner.process(source, assign({}, { from: filename }));
+ var lazyResult = runner.process(source, assign({}, { from: filename, parser: lessParser}));

It'd be nice to be able to hook into this options object from the public css-modules-require-hook API.
What do you think?

Seems to be working, but the hash is different in my extracted bundle.css and my html template class names

I am generating a styles.bundle.css using ExtractTextPlugin.

Here's an example class from styles.bundle.css: .styles__navbar___3V2fO{ //some styles }

Now in the HTML template that was rendered, here is the class name: <nav class="styles__navbar___2n0nV navbar navbar-default" data-reactid="2">

As you can see, the hashes are different between the styles.bundle.css and the class name in my template.

Here is a snippet from my application's server.js:

if (isProd) {
 
  require('css-modules-require-hook')({
    generateScopedName: '[name]__[local]___[hash:base64:5]',
    extensions: ['.scss', '.css'],
    preprocessCss: (data, file) => sass.renderSync({
      data,
      file
    }).css
  });

  const simpleCache = {};

  const configureStore = require('../../src/config/configure-store');
  const store = configureStore.default();
  const routes = require('../../src/config/app-routes').default;

  app.use(express.static(path.join(__dirname, '../', '../', 'dist/')));

  app.get('*', (req, res) => {

Can someone please help me understand what I am doing wrong? Also, please let me know if there is additional information I should provide to better help answer my question.

Thank you!

generateScopedName generates different hash than css-loader

Here is css-modules-require-hook config:

hook({
    generateScopedName: '[name]__[local]_[hash:base64:5]'
});

And here is webpack's css-loader config:

module: {
    loaders: [
        {
            test: /\.css?$/,
            loader: ExtractTextPlugin.extract({
                fallbackLoader: 'style',
                loader: [
                    `css?minimize=false&module&localIdentName=[name]__[local]_[hash:base64:5],
                    'csso?-comments&-restructure',
                    'postcss'
                ].join('!')
            })
        }
    ]
},
plugins: [
    new webpack.LoaderOptionsPlugin({
        test: /\.css/,
        options: {
            postcss: () => postcssConfig.defaults
        }
    }),
]

[email protected], [email protected], [email protected]

See what is passed in 2 cases, and that's what got
webpack:

UIHeaderPrimary__header_Qfcau [name]__header_[hash:base64:5] { regExp: undefined,
  hashPrefix: '',
  context: '/Users/amarshenko/job/tinkoff-portal-web/components/platform/ui/headerPrimary',
  content: 'UIHeaderPrimary.css+header' }

hook:

{ from: '/Users/amarshenko/job/tinkoff-portal-web/components/platform/ui/headerPrimary/UIHeaderPrimary.css' } { header: 'UIHeaderPrimary__header_1ETb8' }

process.cwd () at server and webpack are equal

How to use it on server-side?

My server.js file:

const hook = require('css-modules-require-hook');

hook({
    generateScopedName: '[name]__[local]___[hash:base64:5]'
});

require('babel-register')({ presets: ['react'] });

import express from 'express';
var app = express();

app.use(express.static('public'));
app.use(require('./routes/index'));

var PORT = 3001;
app.listen(PORT, () => {
    console.log('SERVER IS RUNNING on http://localhost:' + PORT);
});

routes/index.js

import React from 'react';
import { renderToString } from 'react-dom/server';
import Helmet from "react-helmet"
import { match, RouterContext } from 'react-router';
import { createStore} from 'redux';
import { Provider } from 'react-redux'
import express from 'express'
const router = express.Router();

import reducer from '../reducers';
import routes from './routes';

const store = createStore(reducer);

router.get('*', function(request, response) {

    match({
        routes,
        location: request.url },
        (error, redirect, props) => {
            if(error){
                res.status(500).send(err.message)
            }
            else if (redirect) {
                res.redirect(redirect.pathname + redirect.search)
            }
            else if (props) {
                var html = renderToString(
                    <Provider store={store}>
                        <RouterContext {...props}/>
                    </Provider>
                );
                let head = Helmet.rewind();
                response.send(html);
            } else {
                response.status(404).send('Not Found');
            }
        });

});

module.exports = router;

client.js file:

import { render } from 'react-dom'
import React from 'react'
import routes from './routes/routes'
import {createStore, applyMiddleware} from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'

import reducer from './reducers'

var store = createStore(
    reducer,
    composeWithDevTools(applyMiddleware(thunk))
);

console.log('hello world');

render(
    <Provider store={store}>
        {routes}
    </Provider>, document
);

When I open browser, many 'hello world' messages appear in console, after that

Uncaught Error: You're trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser quirks by rendering at the document root. You should look for environment dependent code in your components and ensure the props are the same client and server side

How to fix it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.