Skip to content

Commit

Permalink
Add stylus support (#1437)
Browse files Browse the repository at this point in the history
* Add jest-cli to dependencies

I was getting a module resolution error after installing. Test command was
throwing an error saying it couldn't find jest-cli so now its in deps.

* Add stylus plugin

* Add support for using stylus modules with the plugin

* Add a validation whitelist to allow the "stylus" key in webpack config

This is a special case but we may run in to other cases before upgrading to
webpack v3. In webpack v1 many plugins and loaders used top-level configuration
keys to handle custom configuration. webpack-validator does not like this, but
it does provide the option to whitelist keys, which is exactly what I did here.

I also added a (hopefully) helpful error message to clarify what went wrong in
the case of an invalid webpack config.

* Add example of stylus plugin usage to gatsby-plugin-stylus

* Do not validate stylus options object shape

Validating the shape felt like overkill, and it might prevent valid options form
being used if I missed something.

* Configure stylus to use module extension for overall gatsby consistency

* Update readme to reflect css module configuration change

* Add common-tags helper to improve long string formatting

* Remove debug code
  • Loading branch information
iansinnott authored and KyleAMathews committed Jul 12, 2017
1 parent 5a37ebd commit 0d3783c
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 12 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -30,6 +30,7 @@
"iflow-lodash": "^1.1.24",
"iflow-react-router": "^1.2.1",
"jest": "^20.0.4",
"jest-cli": "^20.0.4",
"lerna": "^2.0.0-rc.5",
"plop": "^1.7.4",
"prettier": "^1.5.2",
Expand Down
44 changes: 43 additions & 1 deletion packages/gatsby-plugin-stylus/README.md
@@ -1,3 +1,45 @@
# gatsby-plugin-stylus

Stub README
Provides drop-in support for Stylus with or without CSS Modules

## Install

`yarn add gatsby-plugin-stylus`

## How to use

1. Include the plugin in your `gatsby-config.js` file.
2. Write your stylesheets in Stylus (`.styl` files) and require/import them

### Without CSS Modules

```javascript
// in gatsby-config.js
plugins: [
`gatsby-plugin-stylus`
]
```

### With CSS Modules

Using CSS modules requires no additional configuration. Simply prepend `.module` to the extension. For example: `App.styl` -> `App.module.styl`. Any file with the `module` extension will use CSS modules.

### With Stylus plugins

This plugin has the same API as [stylus-loader](https://github.com/shama/stylus-loader#stylus-plugins), which means you can add stylus plugins with `use`:

```javascript
// in gatsby-config.js
const rupture = require('rupture');

module.exports = {
plugins: [
{
resolve: 'gatsby-plugin-stylus',
options: {
use: [rupture()],
},
},
],
};
```
25 changes: 16 additions & 9 deletions packages/gatsby-plugin-stylus/package.json
@@ -1,18 +1,25 @@
{
"name": "gatsby-plugin-stylus",
"description": "Gatsby support for Stylus",
"version": "1.0.1",
"description": "Stub description for gatsby-plugin-stylus",
"main": "index.js",
"scripts": {
"build": "babel src --out-dir . --ignore __tests__",
"watch": "babel -w src --out-dir . --ignore __tests__"
"author": "Ian Sinnott <ian@iansinnott.com>",
"dependencies": {
"babel-cli": "^6.24.1",
"extract-text-webpack-plugin": "^1.0.1",
"stylus": "^0.54.5",
"stylus-loader": "webpack1"
},
"devDependencies": {},
"keywords": [
"gatsby"
"gatsby",
"gatsby-plugin",
"stylus"
],
"author": "Kyle Mathews &lt;mathews.kyle@gmail.com&gt;",
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.24.1"
"main": "./gatsby-node.js",
"readme": "README.md",
"scripts": {
"build": "babel src --out-dir .",
"watch": "babel -w src --out-dir ."
}
}
120 changes: 120 additions & 0 deletions packages/gatsby-plugin-stylus/src/gatsby-node.js
@@ -0,0 +1,120 @@
/**
* Usage:
*
* // gatsby-config.js
* plugins: [
* `gatsby-plugin-stylus`,
* ],
*
* // Usage with options:
*
* // gatsby-config.js
* plugins: [
* {
* resolve: `gatsby-plugin-stylus`,
* options: {
* use: [],
* },
* },
* ],
*/
const ExtractTextPlugin = require(`extract-text-webpack-plugin`)

exports.modifyWebpackConfig = ({ config, stage }, options = {}) => {
const cssModulesConfProd = `css?modules&minimize&importLoaders=1`
const cssModulesConfDev =
`css?modules&importLoaders=1&localIdentName=[name]---[local]---[hash:base64:5]`

// Pass in stylus plugins regardless of stage.
if (Array.isArray(options.use)) {
config.merge(current => {
current.stylus = {
use: options.use,
}
return current
})
} else if (options.use) {
throw new Error(`gatsby-plugin-stylus "use" option passed with ${options.use}. Pass an array of stylus plugins instead`)
}

const stylusFiles = /\.styl$/
const stylusModulesFiles = /\.module\.styl$/

switch (stage) {
case `develop`: {
config.loader(`stylus`, {
test: stylusFiles,
exclude: stylusModulesFiles,
loaders: [`style`, `css`, `postcss`, `stylus`],
})
config.loader(`stylusModules`, {
test: stylusModulesFiles,
loaders: [`style`, cssModulesConfDev, `postcss`, `stylus`],
})
return config
}

case `build-css`: {
config.loader(`stylus`, {
test: stylusFiles,
exclude: stylusModulesFiles,
loader: ExtractTextPlugin.extract(`style`, [
`css?minimize`,
`postcss`,
`stylus`,
]),
})
config.loader(`stylusModules`, {
test: stylusModulesFiles,
loader: ExtractTextPlugin.extract(`style`, [
cssModulesConfProd,
`postcss`,
`stylus`,
]),
})
return config
}

case `develop-html`:
case `build-html`: {
const moduleLoader = ExtractTextPlugin.extract(`style`, [
cssModulesConfProd,
`postcss`,
`stylus`,
])

config.loader(`stylus`, {
test: stylusFiles,
exclude: stylusModulesFiles,
loader: `null`,
})
config.loader(`stylusModules`, {
test: stylusModulesFiles,
loader: moduleLoader,
})
return config
}

case `build-javascript`: {
const moduleLoader = ExtractTextPlugin.extract(`style`, [
cssModulesConfProd,
`stylus`,
])
config.loader(`stylus`, {
test: stylusFiles,
exclude: stylusModulesFiles,
loader: `null`,
})
config.loader(`stylusModules`, {
test: stylusModulesFiles,
loader: moduleLoader,
})

return config
}

default: {
return config
}
}
}
1 change: 1 addition & 0 deletions packages/gatsby/package.json
Expand Up @@ -29,6 +29,7 @@
"chokidar": "^1.7.0",
"chunk-manifest-webpack-plugin": "0.1.0",
"commander": "^2.9.0",
"common-tags": "^1.4.0",
"convert-hrtime": "^2.0.0",
"copyfiles": "^1.2.0",
"css-loader": "^0.26.1",
Expand Down
21 changes: 19 additions & 2 deletions packages/gatsby/src/utils/webpack-modify-validate.js
@@ -1,9 +1,19 @@
import _ from "lodash"
import invariant from "invariant"
import path from "path"
import validate from "webpack-validator"
import validate, { Joi } from "webpack-validator"
import stripIndent from 'common-tags/lib/stripIndent'
import apiRunnerNode from "./api-runner-node"

// We whitelist special config keys that are not part of a standard Webpack v1
// config but are in common usage. We should be able to completely remove this
// once we're on Webpack v3.
//
// For info on whitelisting with webpack-validator see:
// https://github.com/js-dxtools/webpack-validator#customizing
const validationWhitelist = Joi.object({
stylus: Joi.any(),
})

export default (async function ValidateWebpackConfig(config, stage) {
// We don't care about the return as plugins just mutate the config directly.
await apiRunnerNode(`modifyWebpackConfig`, { config, stage })
Expand All @@ -21,6 +31,7 @@ export default (async function ValidateWebpackConfig(config, stage) {

const validationState = validate(config.resolve(), {
returnValidation: true,
schemaExtension: validationWhitelist,
})

if (!validationState.error) {
Expand All @@ -35,5 +46,11 @@ export default (async function ValidateWebpackConfig(config, stage) {
console.log(`\n`)
})

console.log(stripIndent`
Your Webpack config does not appear to be valid. This could be because of
something you added or a plugin. If you don't recognize the invalid keys listed
above try removing plugins and rebuilding to identify the culprit.
`)

return process.exit(1)
})

0 comments on commit 0d3783c

Please sign in to comment.