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

Enable extension of webpack config by consumers #87

Merged
merged 5 commits into from Nov 24, 2015

Conversation

ChristopherBiscardi
Copy link
Contributor

Under the scheme in this PR, gatsby.config.js would have to export a function which accepts and returns a config object. The config object is a webpack-configurator config object, which accepts merging an existing webpack config as well as incrementally adding loaders and plugins.

  • Move webpack config from coffeescript to JS
  • Allows consumers to modify loaders and plugins based on stage.

Here is a gatsby.config.js which uses the new extensibility to add
css-modules and postcss support to a static site.

var ExtractTextPlugin = require("extract-text-webpack-plugin");

function configureDevelopment(config) {
  config.removeLoader('css');
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = 'style!css?modules!postcss'
    return cfg
  })
  config.merge({
    postcss: [
      require('postcss-import')(),
      require('postcss-cssnext')({
        browsers: 'last 2 versions'
      })
    ]
  })
}

function configureForJSBundle(config) {
  config.removeLoader('css');
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = 'css/locals?modules!postcss'
    return cfg
  })
}

function configureForHTMLGeneration(config) {
  config.removeLoader('css');
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = ExtractTextPlugin.extract('style', 'css?modules&minimize!postcss');
    return cfg
  })
  config.plugin('extract-css',
                ExtractTextPlugin,
                ["styles.css"]);
  config.merge({
    postcss: [
      require('postcss-import')(),
      require('postcss-cssnext')({
        browsers: 'last 2 versions'
      })
    ]
  })
}

module.exports = function(config, env) {

  switch(env) {
    case 'production':
      configureForJSBundle(config);
    break;
    case 'static':
      configureForHTMLGeneration(config);
    break;
    default:
      configureDevelopment(config);
    break;
  }

  return config;
}

Given pages/_template.jsx which imports and uses the following
pages/_template.css:

:root {
  --black: black;
}
.header {
    background: var(--black);
}

When running gatsby build, All css is extracted and turned into a
minified styles.css:

._2D1e_Piibk-xdIwKzGpKuS{background:#000}

The bundle.js and static html files have a div that looks like:

<div class="_2D1e_Piibk-xdIwKzGpKuS">
  ...
</div>

When running gatsby develop, the styles are included in the ,
unminified:

<style type="text/css">._2D1e_Piibk-xdIwKzGpKuS {
    background: black;
}</style>

Testing this PR

  1. clone the branch: https://github.com/ChristopherBiscardi/gatsby/tree/extensible-loaders
  2. In the project root, npm install -g
  3. git clone git@github.com:ChristopherBiscardi/gatsby-default-template-extensible.git && cd gatsby-default-template-extensible
  4. npm i && gatsby develop

TODO

  • gatsby throws if gatsby.config.js doesn't exist.
    • make it not throw
  • coffeescript had react-hot as a loader before, add that back.
  • cleanup some console.logs, etc that are laying around
  • Make sure Docs are accurately representing the process for adding loaders and plugins

@ChristopherBiscardi ChristopherBiscardi mentioned this pull request Nov 12, 2015
@ChristopherBiscardi
Copy link
Contributor Author

Using the new config extensions, it is possible to use css-modules and postcss. The following gatsby.config.js replaces the default css loader with a style-loader combined with css and postcss loaders. It then merges in an additional key postcss to handle the configuration for the postcss-loader.

module.exports = function(config) {

  // Remove the default css loader
  config.removeLoader('css');
  // Add our postcss/css-modules loader
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss';
    return cfg
  })

  // merge some postcss config into the cached webpack config
  config.merge({
    postcss: [
      require('postcss-cssnext')({
        browsers: 'last 2 versions'
      }),
      require('postcss-import')(),
      require('postcss-nested'),
      require('lost')
    ]
  });
  return config;
}

@syrusakbary
Copy link
Contributor

👍

Add webpack-configurator which enables passing an object to a
gatsby.config.js. This object has a few convenience functions for
managing loaders as well as plugins (and arbitrary keys) relating to
webpack configuration.

* Move webpack config to JS
* Allows consumers to modify loaders and plugins based on stage.

Here is a webpack config which uses the new extensibility to add
css-modules, classnames and postcss support to a static site.

```javascript
var ExtractTextPlugin = require("extract-text-webpack-plugin");

function configureDevelopment(config) {
  config.removeLoader('css');
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = 'style!css?modules!postcss'
    return cfg
  })
  config.merge({
    postcss: [
      require('postcss-import')(),
      require('postcss-cssnext')({
        browsers: 'last 2 versions'
      })
    ]
  })
}

function configureForJSBundle(config) {
  config.removeLoader('css');
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = 'css/locals?modules!postcss'
    return cfg
  })
}

function configureForHTMLGeneration(config) {
  config.removeLoader('css');
  config.loader('css', function(cfg) {
    cfg.test = /\.css$/;
    cfg.loader = ExtractTextPlugin.extract('style', 'css?modules&minimize!postcss');
    return cfg
  })
  config.plugin('extract-css',
                ExtractTextPlugin,
                ["styles.css"]);
  config.merge({
    postcss: [
      require('postcss-import')(),
      require('postcss-cssnext')({
        browsers: 'last 2 versions'
      })
    ]
  })
}

module.exports = function(config, env) {

  switch(env) {
    case 'production':
      configureForJSBundle(config);
    break;
    case 'static':
      configureForHTMLGeneration(config);
    break;
    default:
      configureDevelopment(config);
    break;
  }

  return config;
}

```

Given `pages/_template.jsx` which imports and uses the following
`pages/_template.css`:

```css
:root {
  --black: black;
}
.header {
    background: var(--black);
}
```

When running `gatsby build`, All css is extracted and turned into a
minified `styles.css`:

```css
._2D1e_Piibk-xdIwKzGpKuS{background:#000}
```

The bundle.js and static html files have a div that looks like:

```html
<div class="_2D1e_Piibk-xdIwKzGpKuS">
  ...
</div>
```

When running `gatsby develop`, the styles are included in the <head>,
unminified:

```html
<style type="text/css">._2D1e_Piibk-xdIwKzGpKuS {
    background: black;
}</style>
```
@ChristopherBiscardi
Copy link
Contributor Author

@KyleAMathews This is rebased on 0.7 and is in working condition/ready for a look.

and replaces it with a loader that uses css-modules.

```javascript
modules.exports = function(config, env) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll probably want to introduce this by explaining we're using webpack-configurator so they can go there to learn more about what options are available..

@KyleAMathews
Copy link
Contributor

Woah webpack-configurator is cool! I could only think of complicated & fragile ways for people to extend our base webpack config but that makes it pretty straightforward. 💯

One question from looking at your css setup—what are thoughts on making that the default for Gatsby + it's three starters? It seems PostCSS/CSS Modules has a lot of momentum and are full of good ideas. I like the idea of Gatsby having really solid defaults out of the box (that are also not overly complicated for a React/Webpack noob to pickup). Thoughts?

@ChristopherBiscardi
Copy link
Contributor Author

@KyleAMathews As far as PostCSS and CSS Modules, I think those are great defaults and can make it easier to handle things like url('') rewriting in css. We've been using them at docker on every new project since ~April/May and it's been working fantastically.

cssnext is the "pack" which I'd suggest including with gatsby.

Compared to the example above, I'd suggest something more like this in dev:

postcss: [
  require('postcss-import'),
  require('postcss-url'),
  require('postcss-cssnext'),
  require('postcss-browser-reporter')
]

and this in prod

postcss: [
  require('postcss-import'),
  require('postcss-url'),
  require('postcss-cssnext'),
  require('cssnano')
]

Going a little deeper, one of the really nice things about postcss is plugins such as postcss-colorblind. Given a switch to postcss, it might be nice to consider either CLI extensions, flag passthrough to gatsby.config.js, or bundling in core so that something like gatsby develop --colorblind=protanopia would work.

and since we're talking about build stuffs, I think using image-loader instead of null would be good (perhaps in combination with file/url-loaders)

@KyleAMathews
Copy link
Contributor

This sounds great! As I said, I'm behind on css so I'd really appreciate you adding your expertise here. Perhaps add the css setup in a follow-up PR? I could get rid of this then 😛 https://github.com/gatsbyjs/gatsby-starter-documentation/blob/master/html.jsx#L31-L68

Passing options like in your colorblind example is interesting. Perhaps give users some way of defining options on a per-site basis and then responding to them in their gatsby.config.js

And 💯 on image-loader, that would fix #18 :-D

@ChristopherBiscardi
Copy link
Contributor Author

sgtm, I'll put the css stuff and image-loader in follow-up PRs.

@ChristopherBiscardi
Copy link
Contributor Author

@KyleAMathews I think this is good to go. lmk if you feel there's anything else I should do here.

@patrykkopycinski
Copy link
Contributor

👍

@syrusakbary
Copy link
Contributor

👍 @KyleAMathews Graphene website will be powered soon by Gatsby! ;)

@KyleAMathews
Copy link
Contributor

@syrusakbary cool! Make sure to add a PR to get yourself listed to the site showcase in the README.

KyleAMathews added a commit that referenced this pull request Nov 24, 2015
Enable extension of webpack config by consumers
@KyleAMathews KyleAMathews merged commit 466f379 into gatsbyjs:master Nov 24, 2015
@KyleAMathews
Copy link
Contributor

Merged! Thanks, this is a huge improvement!

@ChristopherBiscardi
Copy link
Contributor Author

Nice! Thanks for the review!

@KyleAMathews
Copy link
Contributor

Just released 0.7.1

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

Successfully merging this pull request may close these issues.

None yet

4 participants