I read the new doc about migrating to v2 (https://v2--gatsbyjs.netlify.com/docs/migrating-from-v1-to-v2/#manually-specify-postcss-plugins)
I would like to try v2 alpha, but not sure how to configure postcss plugins.
Is there any more detailed example on how to manually configure postcss plugins?
Thanks.
Good question - there isn't yet. This step is kind of a placeholder, the plan is to add a new plugin that will allow you to configure PostCSS for your project with a plain old PostCSS config file. Check out the issue discussing this: https://github.com/gatsbyjs/gatsby/issues/3284
If you're comfortable configuring webpack, you could do this using Gatsby's onCreateWebpackConfig
and setWebpackConfig
hooks. See the v2 docs on adding a custom webpack config for a bit more info. There's also an old PR that implements some of this functionality - which could be a useful reference.
I've tried to add postcss-import
and postcss-cssnext
plugins, but have some strange error: https://github.com/gatsbyjs/gatsby/pull/4428#issuecomment-399078500 :/
Additionally, after adding postcss-cssnext
it complains that it already includes autoprefixer
plugin, so it's included twice, because it's also added by default by Gatsby.
I realize that you can use actions.setWebpackConfig
instead of actions.replaceWebpackConfig
as long as you make sure the test reads as: /\.css$/
exactly (I was using /\.css/
).
I am having some issues migrating from v1 to v2. I trying to reconfigure the .css
test and add my own options:
actions.setWebpackConfig({
module: {
rules: [
{
test: /\.css$/,
use: [
loaders.style(),
loaders.css({ importLoaders: 1 }),
loaders.postcss()
],
},
I think the problem I am running into is that the .css
test is already defined by Gatsby, and this will simply add the loaders on top of the default stack. It seems like replaceWebpackConfig
might be required, but I am not comfortable nuking the whole config and potentially merging the default config with the override myself. Seems odd that would be the way to do it.
My usage is simply the following in components/Layout.js
:
import './../css/index.css'
And then in my index.css
I import a bunch of files.
Since cssnext is being deprecated I am trying to use https://preset-env.cssdb.org/ instead.
Basically I am just migrating from v1 to v2 and this is blocking me sadly.
As soon as I add any type of loader I am getting errors. It doesn't matter if I only have one loader or a ton.
actions.setWebpackConfig({
module: {
rules: [
{
test: /\.css$/,
use: [
loaders.style(),
loaders.miniCssExtract(),
loaders.css({ importLoaders: 1 }),
loaders.postcss(),
],
},
],
},
})
The error:
(5:1) Unknown word
3 | // load the styles
4 | var content = require("!!../../node_modules/style-loader/index.js??ref--10-1!../../node_modules/css-loader/index.js??ref--10-2!../../node_modules/postcss-loader/lib/index.js??postcss-3!../../node_modules/style-loader/index.js??ref--12-0!../../node_modules/css-loader/index.js??ref--12-1!./index.css");
> 5 | if(typeof content === 'string') content = [[module.id, content, '']];
| ^
6 | // Prepare cssTransformation
7 | var transform;
After trying to make this work for a while I am still getting the above error. In the default webpack config. oneOf: [rules.cssModules(), rules.css()]
is used. It seems that webpack-merge
is used within: https://github.com/gatsbyjs/gatsby/blob/1fb19f9ad16618acdac7eda33d295d8ceba7f393/packages/gatsby/src/redux/reducers/webpack.js.
When using setWebpackConfig
and adding a test for /\.css$/
with loaders the merge doesn't replace the oneOf
rule in the original default. I am definitely no master on Webpack so have no idea if this is bad or not, but it's def not working for me.
[
{
"test": {},
"exclude": {},
"use": [
{
"options": {
"cacheDirectory": true,
"babelrc": false,
"sourceType": "unambiguous",
"presets": [
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/preset-env/lib/index.js",
{
"loose": true,
"modules": false,
"useBuiltIns": "usage",
"targets": {
"browsers": [
">0.25%",
"not dead"
]
}
}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/preset-react/lib/index.js",
{
"useBuiltIns": true,
"pragma": "React.createElement",
"development": true
}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/preset-flow/lib/index.js",
{}
]
],
"plugins": [
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/babel-plugin-remove-graphql-queries/index.js",
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/react-hot-loader/babel.js",
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-proposal-export-default-from/lib/index.js",
{}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-proposal-class-properties/lib/index.js",
{
"loose": true
}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-syntax-dynamic-import/lib/index.js",
{}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-transform-runtime/lib/index.js",
{
"helpers": true,
"regenerator": true,
"polyfill": false
}
]
]
},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/babel-loader/lib/index.js"
}
]
},
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/json-loader/index.js"
},
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/yaml-loader/index.js"
}
]
},
{
"use": [
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/url-loader/dist/cjs.js",
"options": {
"limit": 10000,
"name": "static/[name]-[hash].[ext]"
}
}
],
"test": {}
},
{
"use": [
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/url-loader/dist/cjs.js",
"options": {
"limit": 10000,
"name": "static/[name]-[hash].[ext]"
}
}
],
"test": {}
},
{
"use": [
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/url-loader/dist/cjs.js",
"options": {
"name": "static/[name]-[hash].[ext]"
}
}
],
"test": {}
},
{
"oneOf": [
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/style-loader/index.js"
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/css-loader/index.js",
"options": {
"minimize": false,
"sourceMap": true,
"camelCase": "dashesOnly",
"localIdentName": "[name]--[local]--[hash:base64:5]",
"modules": true,
"importLoaders": 1
}
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/postcss-loader/lib/index.js",
"options": {
"ident": "postcss-1",
"sourceMap": true
}
}
]
},
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/style-loader/index.js"
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/css-loader/index.js",
"options": {
"minimize": false,
"sourceMap": true,
"camelCase": "dashesOnly",
"localIdentName": "[name]--[local]--[hash:base64:5]",
"importLoaders": 1
}
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/postcss-loader/lib/index.js",
"options": {
"ident": "postcss-2",
"sourceMap": true
}
}
]
}
]
}
]
[
{
"test": {},
"exclude": {},
"use": [
{
"options": {
"cacheDirectory": true,
"babelrc": false,
"sourceType": "unambiguous",
"presets": [
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/preset-env/lib/index.js",
{
"loose": true,
"modules": false,
"useBuiltIns": "usage",
"targets": {
"browsers": [
">0.25%",
"not dead"
]
}
}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/preset-react/lib/index.js",
{
"useBuiltIns": true,
"pragma": "React.createElement",
"development": true
}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/preset-flow/lib/index.js",
{}
]
],
"plugins": [
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/babel-plugin-remove-graphql-queries/index.js",
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/react-hot-loader/babel.js",
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-proposal-export-default-from/lib/index.js",
{}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-proposal-class-properties/lib/index.js",
{
"loose": true
}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-syntax-dynamic-import/lib/index.js",
{}
],
[
"/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/@babel/plugin-transform-runtime/lib/index.js",
{
"helpers": true,
"regenerator": true,
"polyfill": false
}
]
]
},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/babel-loader/lib/index.js"
}
]
},
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/json-loader/index.js"
},
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/yaml-loader/index.js"
}
]
},
{
"use": [
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/url-loader/dist/cjs.js",
"options": {
"limit": 10000,
"name": "static/[name]-[hash].[ext]"
}
}
],
"test": {}
},
{
"use": [
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/url-loader/dist/cjs.js",
"options": {
"limit": 10000,
"name": "static/[name]-[hash].[ext]"
}
}
],
"test": {}
},
{
"use": [
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/url-loader/dist/cjs.js",
"options": {
"name": "static/[name]-[hash].[ext]"
}
}
],
"test": {}
},
{
"oneOf": [
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/style-loader/index.js"
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/css-loader/index.js",
"options": {
"minimize": false,
"sourceMap": true,
"camelCase": "dashesOnly",
"localIdentName": "[name]--[local]--[hash:base64:5]",
"modules": true,
"importLoaders": 1
}
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/postcss-loader/lib/index.js",
"options": {
"ident": "postcss-1",
"sourceMap": true
}
}
]
},
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/style-loader/index.js"
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/css-loader/index.js",
"options": {
"minimize": false,
"sourceMap": true,
"camelCase": "dashesOnly",
"localIdentName": "[name]--[local]--[hash:base64:5]",
"importLoaders": 1
}
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/postcss-loader/lib/index.js",
"options": {
"ident": "postcss-2",
"sourceMap": true
}
}
]
}
]
},
{
"test": {},
"use": [
{
"loader": "raw-loader"
}
]
},
{
"test": {},
"use": [
{
"options": {},
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/style-loader/index.js"
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/css-loader/index.js",
"options": {
"minimize": false,
"sourceMap": true,
"camelCase": "dashesOnly",
"localIdentName": "[name]--[local]--[hash:base64:5]",
"importLoaders": 1
}
},
{
"loader": "/Users/jeroenransijn/dev/src/github.com/segmentio/evergreen/docs/node_modules/postcss-loader/lib/index.js",
"options": {
"ident": "postcss-3",
"sourceMap": true
}
}
]
}
]
This is finally working with me with postcss-cssnext
:
const path = require('path')
const webpack = require('webpack') // eslint-disable-line import/no-extraneous-dependencies
const postcssCssnext = require('postcss-cssnext')
const poscssImport = require('postcss-import')
exports.onCreateWebpackConfig = ({ actions, loaders, getConfig }) => {
actions.setWebpackConfig({
module: {
rules: [
{
test: /\.example/,
use: [{ loader: 'raw-loader' }],
},
{
test: /\.css$/,
use: [
loaders.miniCssExtract(),
// // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
loaders.css({ importLoaders: 1 }),
loaders.postcss({
ident: 'postcss',
plugins: () => [poscssImport(), postcssCssnext()],
}),
],
},
],
}
})
const configAfterSettings = getConfig()
// Losing my mind here.
const finalRules = configAfterSettings.module.rules.filter(rule => {
if (Object.prototype.hasOwnProperty.call(rule, 'oneOf')) {
// Nuke this rule.
return JSON.stringify(rule).indexOf('style-loader') === -1
}
return true
})
// So much for immutability.
configAfterSettings.module.rules = finalRules
actions.replaceWebpackConfig(configAfterSettings)
}
@jeroenransijn nice! Next steps would be using https://www.npmjs.com/package/postcss-load-config to load your plugins from a .postcssrc
file. This code could then be wrapped into gatsby-plugin-postcss
. Any interest in working on this? :-)
@jeroenransijn Hey! Do you have any idea why doing this throws an error:
exports.onCreateWebpackConfig = ({ actions, rules }) => {
actions.setWebpackConfig({
module: {
rules: [rules.postcss()],
},
});
};
Even though I'm not specifying any postcss plugins there, I'm still getting an error very similar to yours:
(1:1) Unknown word
> 1 | exports = module.exports = require("../../../../node_modules/gatsby/node_modules/css-loader/lib/css-base.js")(true);
| ^
2 | // imports
3 | exports.i(require("-!../../../../node_modules/gatsby/node_modules/css-loader/index.js??ref--11-0!../../../../node_modules/postcss-loader/lib/index.js??postcss-3!./@freeletics/web-package-particle/src/physics/variables.css"), "");
@ ./src/templates/IndexPage.module.css 4:14-337 18:2-22:4 18:336-22:3 19:20-343
I'll later try your solution, even though it looks a bit complex ;) Do you know if it replaces postcss config for all Gatsby stages? I'm not sure if it's really true, but I might need different postcss plugins in development and production builds (though maybe I won't if e.g. css-loader can minify css as well)
@szimek that is exactly the issue I ran into. The main problem is that there is already a loader for CSS/CSS modules in the default Gatsby Webpack config. setWebpackConfig
does not remove or replace the default config for that. You end up running two loaders on the same import. Which is the error you experiencing.
The following super ugly hack gets the config, nukes that rule, and then replaces the Webpack config:
const configAfterSettings = getConfig()
// Losing my mind here.
const finalRules = configAfterSettings.module.rules.filter(rule => {
if (Object.prototype.hasOwnProperty.call(rule, 'oneOf')) {
// Nuke this rule.
return JSON.stringify(rule).indexOf('style-loader') === -1
}
return true
})
// So much for immutability.
configAfterSettings.module.rules = finalRules
actions.replaceWebpackConfig(configAfterSettings)
I talked to Kyle about some ideas around this. Imo, preferably we would have something inside of Gatsby to nuke this rule in a more graceful matter. The two following things come to mind:
actions.removeDefaultWebpackCSSLoader()
Or something more like the following to allow disabling other default loaders. Although I am not sure if there is a use case for this.
actions.setDefaultWebpackLoaders({
css: false,
})
@KyleAMathews I don't mind giving the plugin a try, but I think we should have something in Gatbsy to do the above without having to rely on hacks. It might be I am missing something here that would be more graceful without changing Gatsby..
I don't think there should be problem generally with adding another css loader even without removing the existing one, It _should_ just handle the files first leaving nothing for the later plugin to handle unless i'm missing something (believe i've done this is exact thing before without problem) I'm wondering if errors are do to not handling the various build stages correctly. It's not enough to add a new loader ,you need to add them at the right stages, like the existing one (and less, sass, style plugins as well).
@jquense I am not all that seasoned in Webpack so I might be missing something obvious. When I was trying this I was actually surprised I was able to solve this problem by removing the current loader. I was expecting Webpack to be able to understand what was going on.
Additionally, I think less
, sass
are generally less of an issue because they occupy a different extension vs. adding more loaders to the same extension.
Currently Gatsby has slightly different webpack rules for handling CSS files depending on the command (develop
and build
) and each stage (develop-html
, develop
, build-javascript
etc.). Additionally, it handles .module.css
files slightly differently than .css
files.
I'm not sure if PostCSS config needs to change depending on the command or stage, but would it be enough if it was possible to completely replace PostCSS loader config only (e.g. via postcss.config.js
file or an object), without touching the rest? E.g. postcss-preset-env
seems to already include autoprefixer
, so it would be better to allow to replace the PostCSS config completely, rather than only extend the existing default one, without allowing to overwrite the defaults.
On one hand it probably is a bad idea to provide specific options for each loader (this might get quickly out of hand, e.g. it would be great if I could change localIdentName
in css-loader
to generate shorter class names in production build to save a few KB, others might want other stuff), but on the other, the CSS setup is a bit complicated, so replacing it all like @jeroenransijn is doing is a bit hacky and risky.
Gatsby does provide loaders.postcss(options)
method, but, if I understand it correctly, I can only use it to generate a new loader config, not to replace the existing one. Maybe there could some function that allows to replace a loader config and one could use switch (stage) {...}
to provide correct config for each stage.
@szimek @jeroenransijn I have created a simple plugin which does the followings:
postcss-loader
Now we can use postcss.config.js
without any issues.
Please check https://github.com/mdreizin/gatsby-plugin-postcss
@mdreizin do you mind helping maintain your plugin here in this repo? I'd been planning on writing something similar to this soon :-) I was planning on using https://github.com/michael-ciniawsky/postcss-load-config to add support for additional ways of handling postcss config.
If you're amenable to this, just PR the plugin here and add me as an owner on NPM (kylemathews)
@KyleAMathews No problems, I will make a new PR within a hour.
@mdreizin thanks so much! This is amazing!
@KyleAMathews as a note this is still removing the current rule https://github.com/mdreizin/gatsby-plugin-postcss/blob/master/src/gatsby-node.js#L9 :
originalConfig.module.rules = originalConfig.module.rules.filter(rule => {
if (Array.isArray(rule.oneOf)) {
return JSON.stringify(rule).indexOf('postcss-loader') === -1;
}
return true;
});
I am okay with this, but would like to highlight it might be nice to make it a feature of the framework to do this safely.
@jeroenransijn I also agree that we need to have some methods (or an approach) which help to remove existing loaders.
@KyleAMathews I have already granted NPM permissions and created a new PR. It is a start point for further discussions and improvements ;)
I thought about using postcss-load-config
and keep gatsby-plugin-postcss
as simple as possible, but decided to stick with postcss-loader
, because it use postcss-load-config
out of the box and solves a lot of existing issues.
@mdreizin Thank you!
Or something more like the following to allow disabling other default loaders. Although I am not sure if there is a use case for this.
actions.setDefaultWebpackLoaders({ css: false, })
@jeroenransijn I could definitely use this!
Until Gatsby's postcss customization is figured out (including different configs in development vs. production), I've been:
With Webpack v1 in Gatsby v1, I was able to remove the default loader at each stage and redefine it like this:
exports.modifyWebpackConfig = ({ config, stage }) => {
switch (stage) {
case `develop`:
// Remove default CSS loader
config.removeLoader(`css`)
// Remove postcss from Gatsby's dev process and ignore partials
config.loader(`css`, {
test: /\.css$/,
exclude: [
/src\/styles\/base/,
/src\/styles\/builds\/after-purgecss/,
/src\/styles\/components/,
/src\/styles\/plugins/,
/src\/styles\/reset/,
/src\/styles\/supports/,
/src\/styles\/utilities/
],
loaders: [`style`, `css`]
})
break
case `build-css`:
// Remove default CSS loader
config.removeLoader(`css`)
// Remove postcss from Gatsby's build process and ignore partials
config.loader(`css`, {
test: /\.css$/,
exclude: [
/src\/styles\/base/,
/src\/styles\/builds\/after-postcss/,
/src\/styles\/components/,
/src\/styles\/plugins/,
/src\/styles\/reset/,
/src\/styles\/supports/,
/src\/styles\/utilities/
],
loader: ExtractTextPlugin.extract([`css?minimize`])
})
break
}
return config
}
Unfortunately, this functionality does not seem to be available with Webpack v4 in Gatsby v2.
Any tips on how to duplicate this functionality in Gatsby v2 would be very helpful!
(A plugin that enables the same functionality without having to use the CLI tools or modify the Webpack config directly would obviously be fantastic.)
@ooloth none of that should be necessary in v2, Gatsby does not include postcss _except_ to autoprefix, which shouldn't affect any style flow. v1 had a fairly prescriptive postcss loader by default, not that has been removed in v2. I'm not sure what all your excludes are for tho, can you talk about why you need them?
@jquense Thanks for the clarification about postcss in v2. In my case, I need to run the precss
and tailwindcss
postcss plugins before the autoprefixer
plugin is run, so the v2 postcss setup may still interrupt some style flows. (I see above that a plugin is on its way to handle this, though.)
Regarding your question about all the excludes, they are there to facilitate my CSS workflow, which currently consists of one index.css
(which imports a number of CSS partials) that is run through the postcss CLI tool in development (which outputs styles/builds/after-postcss/output.css
) and the postcss and purgecss CLI tools in production (which outputs styles/builds/after-purgecss/output.css
).
In my layout component, I import the relevant CSS file for the current environment:
// Use PostCSS stylesheet in development and PostCSS/PurgeCSS stylesheet in production:
switch (process.env.NODE_ENV) {
case `development`:
require(`../styles/builds/after-postcss/output.css`)
break
case `production`:
require(`../styles/builds/after-purgecss/output.css`)
break
}
Basically, I just want to run a specific sequence of postcss plugins in development and then the same postcss plugins + purgecss in production.
In v1, I found the development spreadsheet would unfortunately still be inlined in production unless I added the excludes. I opted to "exclude" irrelevant files (rather than explicitly testing just for relevant ones) to make sure Gatsby still picked up CSS produced by other packages (e.g. typefaces
).
In v2, the webpack setup in my previous comment no longer works and once again the development version of my CSS is loading in production (slowing page load as it includes many unused styles).
I'm open to any advice here! A cleaner approach that just uses a single stylesheet (instead of separate dev and production stylesheets) and uses webpack to run postcss on it for development and postcss + purgecss for production would be lovely.
In my case, I need to run the precss and tailwindcss postcss plugins before the autoprefixer plugin is run, so the v2 postcss setup may still interrupt some style flows. (I see above that a plugin is on its way to handle this, though.)
You shouldn't need to update the existing loader then, adding a new loader on the end would be enough. You can either piggyback on the existing loader by adding just postcss loader (no css or style) or do the whole cahin and the files will be handled before the default one effectively ignore it.
Basically, I just want to run a specific sequence of postcss plugins in development and then the same postcss plugins + purgecss in production.
I think you are being too smart for webpack, try switching your switch
to a simpler if then
block like if (process.env.NODE_ENV === 'production')
I think you are being too smart for webpack, try switching your switch to a simpler if then block like if (process.env.NODE_ENV === 'production')
Can you clarify which switch
you're referring to?
(I had one in my layout
component and one in gatsby-node
above.)
@jquense You were absolutely right!
Updating from switch
to if
in my layout component fixed the issue with the dev stylesheet loading in production:
// Use PostCSS stylesheet in development and PostCSS/PurgeCSS stylesheet in production:
if (process.env.NODE_ENV === `development`) {
require(`../styles/builds/after-postcss/output.css`)
} else if (process.env.NODE_ENV === `production`) {
require(`../styles/builds/after-purgecss/output.css`)
}
No idea why that worked, but I'm happy. :)
Any advice about how to apply purgeCSS only in production (either via webpack or postcss) without having to use the CLI tools and generate two different stylesheets?
Glad that is working. If you can point me to a minimal purgeCSS setup I might be able to recommend something. But I'm not really familiar with it
@jquense Thanks!
PurgeCSS is a tool that removes unused selectors from a stylesheet to reduce file size (useful when using utility-class libraries like Tachyons or TailwindCSS). It's like purifyCSS (which there is already a Gatsby plugin for), but much more effective.
How to achieve the minimal setup is the question, but here are examples of each option:
I currently use the CLI tool (see example) to take the "after-postcss" version of my stylesheet and generate the "after-purgecss" version for production before each gatsby build
. Ideally, I'd like to move away from this approach.
PurgeCSS can also be used with Webpack (see example) or postcss (see example). Could you possibly recommend how to integrate one of these approaches with Gatsby (in production only)? I'm a bit confused about exactly how to add the webpack example to Gatsby's v2 webpack config or how to add a production-only postcss plugin to the upcoming gatsby-plugin-postcss
...
(Thanks in advance!)
It looks like you need to include the plugin you can use the gatsby API and stage
to only alter the config during the build (production) steps:
exports.onCreateWebpackConfig = ({ stage, actions }) => {
if (stage.includes('develop')) return
actions.setWebpackConfig({
plugins: [new PurgecssPlugin(yourOptions)]
})
}
@jquense Thanks again!
That got me started and I got PurgeCSS working in production like this:
const PurgeCssPlugin = require(`purgecss-webpack-plugin`)
const path = require(`path`)
const glob = require(`glob`)
const PATHS = {
src: path.join(__dirname, `src`)
}
// Nonessential options removed for brevity
const purgeCssConfig = {
paths: glob.sync(`${PATHS.src}/**/*.js`, { nodir: true }),
}
exports.onCreateWebpackConfig = ({ actions, stage }) => {
if (stage.includes(`develop`)) return
// Add PurgeCSS in production
if (stage.includes(`build`)) {
actions.setWebpackConfig({
plugins: [new PurgeCssPlugin(purgeCssConfig)]
})
}
}
Now my layout component can just import one file (generated by the postcss CLI) instead of using the conditional loading logic above:
import '../styles/builds/after-postcss/output.css'
I tried add adding some of the postcss examples above to gatsby-node
so I can drop the postcss CLI, but kept running into errors. Hopefully gatsby-plugin-postcss
will solve that!
I just noticed that the production css produced by gatsby-node
snippet in my last comment is not minified. (I also use gatsby-plugin-postcss
now instead of postcss-cli
.)
Any tips how to minify the CSS output in production?
As of the official release of v2 PostCSS seems to work like a charm.
I started off configuring various PostCSS plugins myself just to find out everything is done through postcss-preset-env
these days. Both manually declaring PostCSS plugins and doing all the transformations through the postcss-preset-env
works.
Here is the plugin config that is working well for me:
{
resolve: `gatsby-plugin-postcss`,
options: {
postCssPlugins: [
require(`postcss-preset-env`)({
stage: 1,
browsers: '< 1%'
})
],
},
},
Most helpful comment
@szimek that is exactly the issue I ran into. The main problem is that there is already a loader for CSS/CSS modules in the default Gatsby Webpack config.
setWebpackConfig
does not remove or replace the default config for that. You end up running two loaders on the same import. Which is the error you experiencing.The following super ugly hack gets the config, nukes that rule, and then replaces the Webpack config:
I talked to Kyle about some ideas around this. Imo, preferably we would have something inside of Gatsby to nuke this rule in a more graceful matter. The two following things come to mind:
Or something more like the following to allow disabling other default loaders. Although I am not sure if there is a use case for this.
@KyleAMathews I don't mind giving the plugin a try, but I think we should have something in Gatbsy to do the above without having to rely on hacks. It might be I am missing something here that would be more graceful without changing Gatsby..