From 03302534d3661627cf51ab5f2d637af16d49fa52 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Tue, 20 Aug 2019 16:07:32 +0300 Subject: [PATCH] docs: standardize readme (#730) --- README.md | 354 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 230 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 1d15dcc7..968b5f94 100644 --- a/README.md +++ b/README.md @@ -19,27 +19,38 @@ Loads a Sass/SCSS file and compiles it to CSS. -Use the [css-loader](https://github.com/webpack-contrib/css-loader) or the [raw-loader](https://github.com/webpack-contrib/raw-loader) to turn it into a JS module and the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to extract it into a separate file. -Looking for the webpack 1 loader? Check out the [archive/webpack-1 branch](https://github.com/webpack-contrib/sass-loader/tree/archive/webpack-1). - ## Getting Started -```bash +To begin, you'll need to install `sass-loader`: + +```console npm install sass-loader node-sass webpack --save-dev ``` -The sass-loader requires [webpack](https://github.com/webpack) as a[`peerDependency`](https://docs.npmjs.com/files/package.json#peerdependencies) and it requires you to install either [Node Sass](https://github.com/sass/node-sass) or [Dart Sass](https://github.com/sass/dart-sass) on your own. +The sass-loader requires you to install either [Node Sass](https://github.com/sass/node-sass) or [Dart Sass](https://github.com/sass/dart-sass) on your own (more documentation you can find below). This allows you to control the versions of all your dependencies, and to choose which Sass implementation to use. -[node sass]: https://github.com/sass/node-sass -[dart sass]: http://sass-lang.com/dart-sass +- [node sass](https://github.com/sass/node-sass) +- [dart sass](http://sass-lang.com/dart-sass) -## Examples +Chain the sass-loader with the [css-loader](https://github.com/webpack-contrib/css-loader) and the [style-loader](https://github.com/webpack-contrib/style-loader) to immediately apply all styles to the DOM or the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to extract it into a separate file. + +Then add the loader to your `webpack` config. For example: -Chain the sass-loader with the [css-loader](https://github.com/webpack-contrib/css-loader) and the [style-loader](https://github.com/webpack-contrib/style-loader) to immediately apply all styles to the DOM. +**file.js** -```bash -npm install style-loader css-loader --save-dev +```js +import style from './style.scss'; +``` + +**file.scss** + +```scss +$body-color: red; + +body { + color: $body-color; +} ``` **webpack.config.js** @@ -49,11 +60,14 @@ module.exports = { module: { rules: [ { - test: /\.scss$/, + test: /\.s[ac]ss$/i, use: [ - 'style-loader', // creates style nodes from JS strings - 'css-loader', // translates CSS into CommonJS - 'sass-loader', // compiles Sass to CSS, using Node Sass by default + // Creates `style` nodes from JS strings + 'style-loader', + // Translates CSS into CommonJS + 'css-loader', + // Compiles Sass to CSS + 'sass-loader', ], }, ], @@ -61,7 +75,49 @@ module.exports = { }; ``` -You can also pass options directly to [Node Sass][] or [Dart Sass][]: +And run `webpack` via your preferred method. + +### Resolving `import` at-rules + +The webpack provides an [advanced mechanism to resolve files](https://webpack.js.org/concepts/module-resolution/). + +The sass-loader uses Sass's custom importer feature to pass all queries to the webpack resolving engine. Thus you can import your Sass modules from `node_modules`. Just prepend them with a `~` to tell webpack that this is not a relative import: + +```css +@import '~bootstrap'; +``` + +It's important to only prepend it with `~`, because `~/` resolves to the home directory. +The webpack needs to distinguish between `bootstrap` and `~bootstrap` because CSS and Sass files have no special syntax for importing relative files. +Writing `@import "file"` is the same as `@import "./file";` + +### Problems with `url(...)` + +Since sass implementations don't provide [url rewriting](https://github.com/sass/libsass/issues/532), all linked assets must be relative to the output. + +- If you pass the generated CSS on to the css-loader, all urls must be relative to the entry-file (e.g. `main.scss`). +- If you're just generating CSS without passing it to the css-loader, it must be relative to your web root. + +You will be disrupted by this first issue. It is natural to expect relative references to be resolved against the `.sass`/`.scss` file in which they are specified (like in regular `.css` files). + +Thankfully there are a two solutions to this problem: + +- Add the missing url rewriting using the [resolve-url-loader](https://github.com/bholloway/resolve-url-loader). Place it before the sass-loader in the loader chain. +- Library authors usually provide a variable to modify the asset path. [bootstrap-sass](https://github.com/twbs/bootstrap-sass) for example has an `$icon-font-path`. + +## Options + +By default all options passed to loader also passed to to [Node Sass](https://github.com/sass/node-sass) or [Dart Sass](http://sass-lang.com/dart-sass) + +> ℹ️ The `indentedSyntax` option has `true` value for the `sass` extension. +> ℹ️ Options such as `file` and `outFile` are unavailable. +> ℹ️ Only the "expanded" and "compressed" values of outputStyle are supported for `dart-sass`. +> ℹ We recommend don't use `sourceMapContents`, `sourceMapEmbed`, `sourceMapRoot` options because loader automatically setup this options. + +There is a slight difference between the `node-sass` and `sass` options. We recommend look documentation before used them: + +- [the Node Sass documentation](https://github.com/sass/node-sass/#options) for all available `node-sass` options. +- [the Dart Sass documentation](https://github.com/sass/dart-sass#javascript-api) for all available `sass` options. **webpack.config.js** @@ -70,17 +126,14 @@ module.exports = { module: { rules: [ { - test: /\.scss$/, + test: /\.s[ac]ss$/i, use: [ - { - loader: 'style-loader', - }, - { - loader: 'css-loader', - }, + 'style-loader', + 'css-loader', { loader: 'sass-loader', options: { + indentWidth: 4, includePaths: ['absolute/path/a', 'absolute/path/b'], }, }, @@ -91,7 +144,9 @@ module.exports = { }; ``` -See [the Node Sass documentation](https://github.com/sass/node-sass/blob/master/README.md#options) for all available Sass options. +### `implementation` + +The special `implementation` option determines which implementation of Sass to use. By default the loader resolve the implementation based on your dependencies. Just add required implementation to `package.json` (`node-sass` or `sass` package) and install dependencies. @@ -103,8 +158,8 @@ Example where the `sass-loader` loader uses the `sass` (`dart-sass`) implementat ```json { "devDependencies": { - "sass-loader": "*", - "sass": "*" + "sass-loader": "^7.2.0", + "sass": "^1.22.10" } } ``` @@ -116,54 +171,170 @@ Example where the `sass-loader` loader uses the `node-sass` implementation: ```json { "devDependencies": { - "sass-loader": "*", - "node-sass": "*" + "sass-loader": "^7.2.0", + "node-sass": "^4.0.0" } } ``` Beware the situation when `node-sass` and `sass` was installed, by default the `sass-loader` prefers `node-sass`, to avoid this situation use the `implementation` option. -The special `implementation` option determines which implementation of Sass to use. -It takes either a [Node Sass][] or a [Dart Sass][] module. For example, to use Dart Sass, you'd pass: +It takes either `node-sass` or `sass` (`Dart Sass`) module. + +For example, to use Dart Sass, you'd pass: ```js -// ... - { - loader: "sass-loader", - options: { - implementation: require("sass") - } - } -// ... +module.exports = { + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + // Prefer `dart-sass` + implementation: require('sass'), + }, + }, + ], + }, + ], + }, +}; ``` -Note that when using Dart Sass, **synchronous compilation is twice as fast as asynchronous compilation** by default, due to the overhead of asynchronous callbacks. -To avoid this overhead, you can use the [`fibers`](https://www.npmjs.com/package/fibers) package to call asynchronous importers from the synchronous code path. +Note that when using `sass` (`Dart Sass`), **synchronous compilation is twice as fast as asynchronous compilation** by default, due to the overhead of asynchronous callbacks. +To avoid this overhead, you can use the [fibers](https://www.npmjs.com/package/fibers) package to call asynchronous importers from the synchronous code path. + To enable this, pass the `Fiber` class to the `fiber` option: **webpack.config.js** ```js -const Fiber = require('fibers'); +module.exports = { + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + implementation: require('sass'), + fiber: require('fibers'), + }, + }, + ], + }, + ], + }, +}; +``` + +### `data` + +Type: `String|Function` +Default: `undefined` +Prepends `Sass`/`SCSS` code before the actual entry file. +In this case, the `sass-loader` will not override the `data` option but just append the entry's content. + +This is especially useful when some of your Sass variables depend on the environment: + +> ℹ Since you're injecting code, this will break the source mappings in your entry file. Often there's a simpler solution than this, like multiple Sass entry files. + +#### `String` + +```js module.exports = { module: { rules: [ { - test: /\.scss$/, + test: /\.s[ac]ss$/i, use: [ + 'style-loader', + 'css-loader', { - loader: 'style-loader', + loader: 'sass-loader', + options: { + data: '$env: ' + process.env.NODE_ENV + ';', + }, }, + ], + }, + ], + }, +}; +``` + +#### `Function` + +```js +module.exports = { + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + data: (loaderContext) => { + // More information about avalaible options https://webpack.js.org/api/loaders/ + const { resourcePath, rootContext } = loaderContext; + const relativePath = path.relative(rootContext, resourcePath); + + if (relativePath === 'styles/foo.scss') { + return '$value: 100px;'; + } + + return '$value: 200px;'; + }, + }, + }, + ], + }, + ], + }, +}; +``` + +### `sourceMap` + +Type: `Boolean` +Default: `false` + +Enables/Disables generation of source maps. + +They are not enabled by default because they expose a runtime overhead and increase in bundle size (JS source maps do not). + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', { loader: 'css-loader', + options: { + sourceMap: true, + }, }, { loader: 'sass-loader', options: { - implementation: require('sass'), - fiber: Fiber, + sourceMap: true, }, }, ], @@ -173,9 +344,18 @@ module.exports = { }; ``` -### In production +> ℹ In some rare case `node-sass` can output invalid source maps (it is `node-sass` bug), to avoid try to update node-sass to latest version or you can try to set the `outputStyle` option to `compressed` value. + +## Examples + +### Extracts CSS into separate files + +For production builds it's recommended to extract the CSS from your bundle being able to use parallel loading of CSS/JS resources later on. -Usually, it's recommended to extract the style sheets into a dedicated file in production using the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin). This way your styles are not dependent on JavaScript: +There are two possibilities to extract a style sheet from the bundle: + +- [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) (use this, when using webpack 4 configuration. Works in all use-cases) +- [extract-loader](https://github.com/peerigon/extract-loader) (simpler, but specialized on the css-loader's output) **webpack.config.js** @@ -186,7 +366,7 @@ module.exports = { module: { rules: [ { - test: /\.scss$/, + test: /\.s[ac]ss$/i, use: [ // fallback to style-loader in development process.env.NODE_ENV !== 'production' @@ -209,42 +389,9 @@ module.exports = { }; ``` -

Usage

- -### Imports - -webpack provides an [advanced mechanism to resolve files](https://webpack.js.org/concepts/module-resolution/). The sass-loader uses Sass's custom importer feature to pass all queries to the webpack resolving engine. Thus you can import your Sass modules from `node_modules`. Just prepend them with a `~` to tell webpack that this is not a relative import: - -```css -@import '~bootstrap/dist/css/bootstrap'; -``` - -It's important to only prepend it with `~`, because `~/` resolves to the home directory. webpack needs to distinguish between `bootstrap` and `~bootstrap` because CSS and Sass files have no special syntax for importing relative files. Writing `@import "file"` is the same as `@import "./file";` - -### Problems with `url(...)` - -Since Sass/[libsass](https://github.com/sass/libsass) does not provide [url rewriting](https://github.com/sass/libsass/issues/532), all linked assets must be relative to the output. - -- If you're just generating CSS without passing it to the css-loader, it must be relative to your web root. -- If you pass the generated CSS on to the css-loader, all urls must be relative to the entry-file (e.g. `main.scss`). - -More likely you will be disrupted by this second issue. It is natural to expect relative references to be resolved against the `.scss` file in which they are specified (like in regular `.css` files). Thankfully there are a two solutions to this problem: - -- Add the missing url rewriting using the [resolve-url-loader](https://github.com/bholloway/resolve-url-loader). Place it before the sass-loader in the loader chain. -- Library authors usually provide a variable to modify the asset path. [bootstrap-sass](https://github.com/twbs/bootstrap-sass) for example has an `$icon-font-path`. - -### Extracting style sheets - -Bundling CSS with webpack has some nice advantages like referencing images and fonts with hashed urls or [hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/) in development. In production, on the other hand, it's not a good idea to apply your style sheets depending on JS execution. Rendering may be delayed or even a [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) might be visible. Thus it's often still better to have them as separate files in your final production build. - -There are two possibilities to extract a style sheet from the bundle: - -- [extract-loader](https://github.com/peerigon/extract-loader) (simpler, but specialized on the css-loader's output) -- [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) (use this, when using webpack 4 configuration. Works in all use-cases) - ### Source maps -To enable CSS source maps, you'll need to pass the `sourceMap` option to the sass-loader _and_ the css-loader. Your `webpack.config.js` should look like this: +To enable CSS source maps, you'll need to pass the `sourceMap` option to the sass-loader _and_ the css-loader. **webpack.config.js** @@ -256,12 +403,7 @@ module.exports = { { test: /\.scss$/, use: [ - { - loader: 'style-loader', - options: { - sourceMap: true, - }, - }, + 'style-loader', { loader: 'css-loader', options: { @@ -283,42 +425,6 @@ module.exports = { If you want to edit the original Sass files inside Chrome, [there's a good blog post](https://medium.com/@toolmantim/getting-started-with-css-sourcemaps-and-in-browser-sass-editing-b4daab987fb0). Checkout [test/sourceMap](https://github.com/webpack-contrib/sass-loader/tree/master/test) for a running example. -### Environment variables - -If you want to prepend Sass code before the actual entry file, you can set the `data` option. In this case, the sass-loader will not override the `data` option but just append the entry's content. This is especially useful when some of your Sass variables depend on the environment: - -```javascript -{ - loader: "sass-loader", - options: { - data: "$env: " + process.env.NODE_ENV + ";" - } -} -``` - -The `data` option supports `Function` notation: - -```javascript -{ - loader: "sass-loader", - options: { - data: (loaderContext) => { - // More information about avalaible options https://webpack.js.org/api/loaders/ - const { resourcePath, rootContext } = loaderContext; - const relativePath = path.relative(rootContext,resourcePath); - - if (relativePath === "styles/foo.scss") { - return "$value: 100px;" - } - - return "$value: 200px;" - } - } -} -``` - -**Please note:** Since you're injecting code, this will break the source mappings in your entry file. Often there's a simpler solution than this, like multiple Sass entry files. - ## Contributing Please take a moment to read our contributing guidelines if you haven't yet done so.