|
| 1 | +# node-sass-conditional-importer |
| 2 | + |
| 3 | +A conditional/dynamic importer for [node-sass]. It provides the ability to `@import` Sass files dynamically based on |
| 4 | +their (environment) extension prefix, similar to React Native's |
| 5 | +[platform-specific extensions][react-platform-specific-extensions] behaviour. |
| 6 | + |
| 7 | +[](https://www.npmjs.com/package/node-sass-conditional-importer) |
| 8 | +[](https://travis-ci.org/codebymikey/node-sass-conditional-importer) |
| 9 | + |
| 10 | +It reads in a list of `environments` extension prefixes, which it'll attempt to use over the default file. |
| 11 | + |
| 12 | +The example use case for this importer is as follows, say you have the following folder structure: |
| 13 | + |
| 14 | +``` |
| 15 | +scss |
| 16 | +├── custom |
| 17 | +│ ├── style.custom.scss |
| 18 | +│ ├── style.development.scss |
| 19 | +│ ├── style.production.scss |
| 20 | +│ └── style.scss |
| 21 | +└── main.scss |
| 22 | +``` |
| 23 | + |
| 24 | +And you want to import a different version of `style.scss` based on a given build environment/variable. |
| 25 | +This is not currently possible easily because Sass does not allow dynamic `@import`s |
| 26 | +[using interpolation][sass-no-dynamic-imports] or in [`if` statements][sass-no-if-imports]. |
| 27 | + |
| 28 | +This importer allows you to simply pass in your current environment into the importer, and it checks |
| 29 | +for whether the environment-specific override file exists before importing it. |
| 30 | + |
| 31 | +The `environments` will be a list of environments ordered by the priority with which they should be used. |
| 32 | + |
| 33 | +If none of the environment file overrides are available, then it falls back to the original file. |
| 34 | + |
| 35 | +## Usage |
| 36 | +### Configuration options |
| 37 | +* `environments`: An array of environment extensions to look up. e.g. |
| 38 | + ```javascript |
| 39 | + // process.env.NODE_ENV = 'production'; |
| 40 | + // Look for [`${file}.production.scss`, `${file}.fallback.scss`] |
| 41 | + [process.env.NODE_ENV, 'fallback'] |
| 42 | + ``` |
| 43 | + |
| 44 | +### [node-sass] |
| 45 | +This module hooks into [node-sass's importer api][node-sass-importer-api]. |
| 46 | +
|
| 47 | +```javascript |
| 48 | +var sass = require('node-sass'); |
| 49 | +var conditionalImporter = require('node-sass-conditional-importer'); |
| 50 | +
|
| 51 | +sass.render({ |
| 52 | + file: scssFilename, |
| 53 | + importer: [ |
| 54 | + conditionalImporter({ |
| 55 | + environments: [ |
| 56 | + // Search for `*.custom.scss` files first, |
| 57 | + // Followed `*.(development|production).scss` files. |
| 58 | + 'custom', |
| 59 | + process.env.NODE_ENV, |
| 60 | + ], |
| 61 | + }), |
| 62 | + // .. other importers |
| 63 | + ], |
| 64 | +}, function(err, result) { /*...*/ }); |
| 65 | +``` |
| 66 | +
|
| 67 | +### Webpack / [sass-loader](https://github.com/jtangelder/sass-loader) |
| 68 | +
|
| 69 | +#### Webpack v1 |
| 70 | +
|
| 71 | +```javascript |
| 72 | +import conditionalImporter from 'node-sass-conditional-importer'; |
| 73 | +
|
| 74 | +// Webpack config |
| 75 | +export default { |
| 76 | + module: { |
| 77 | + loaders: [{ |
| 78 | + test: /\.scss$/, |
| 79 | + loaders: ['style', 'css', 'sass'] |
| 80 | + }], |
| 81 | + }, |
| 82 | + sassLoader: { |
| 83 | + importer: conditionalImporter({ |
| 84 | + environments: [ |
| 85 | + // Import based on the NODE_ENV environment variable. |
| 86 | + process.env.NODE_ENV, |
| 87 | + ], |
| 88 | + }) |
| 89 | + } |
| 90 | +}; |
| 91 | +``` |
| 92 | +
|
| 93 | +#### Webpack v2 |
| 94 | +
|
| 95 | +```javascript |
| 96 | +import conditionalImporter from 'node-sass-conditional-importer'; |
| 97 | +
|
| 98 | +// Webpack config |
| 99 | +export default { |
| 100 | + module: { |
| 101 | + rules: [ |
| 102 | + { |
| 103 | + test: /\.scss$/, |
| 104 | + use: [ |
| 105 | + 'style-loader', |
| 106 | + { |
| 107 | + loader: 'css-loader', |
| 108 | + options: { |
| 109 | + importLoaders: 1 |
| 110 | + }, |
| 111 | + }, |
| 112 | + { |
| 113 | + loader: 'sass-loader', |
| 114 | + options: { |
| 115 | + importer: conditionalImporter({ |
| 116 | + environments: [ |
| 117 | + // Import based on the NODE_ENV environment variable. |
| 118 | + process.env.NODE_ENV, |
| 119 | + ], |
| 120 | + }), |
| 121 | + }, |
| 122 | + }, |
| 123 | + ], |
| 124 | + }, |
| 125 | + ], |
| 126 | + }, |
| 127 | +}; |
| 128 | +``` |
| 129 | +
|
| 130 | +## Custom resolver |
| 131 | +
|
| 132 | +Should you care to resolve paths using some kind of custom logic, for example, |
| 133 | +resolving `~/` relative to the project root or some other arbitrary directory, |
| 134 | +you can do it using the following: |
| 135 | +
|
| 136 | +`main.scss`: |
| 137 | +
|
| 138 | +```scss |
| 139 | +@import '~/dynamic.scss'; |
| 140 | +body { |
| 141 | + background: $background; |
| 142 | +} |
| 143 | +``` |
| 144 | +
|
| 145 | +`custom/dynamic.myenvironment.scss`: |
| 146 | +
|
| 147 | +```scss |
| 148 | +$background: red; |
| 149 | +``` |
| 150 | +
|
| 151 | +```js |
| 152 | +var path = require('path'); |
| 153 | +var sass = require('node-sass'); |
| 154 | +var conditionalImporter = require('node-sass-conditional-importer'); |
| 155 | +
|
| 156 | +sass.render({ |
| 157 | + file: './main.scss', |
| 158 | + importer: [ |
| 159 | + conditionalImporter({ |
| 160 | + environments: ['myenvironment'], |
| 161 | + resolver: function(dir, url) { |
| 162 | + return url.startsWith('~/') |
| 163 | + ? path.resolve(dir, 'custom', url.substr(2)) |
| 164 | + : path.resolve(dir, url); |
| 165 | + }, |
| 166 | + }) |
| 167 | + ], |
| 168 | +}, function(err, result) { console.log(err || result.css.toString()) }); |
| 169 | +``` |
| 170 | +
|
| 171 | +## Known issues |
| 172 | +
|
| 173 | +- With a folder structure like: |
| 174 | + ``` |
| 175 | + scss |
| 176 | + ├── custom |
| 177 | + │ ├── style.custom.scss |
| 178 | + │ ├── style.development.scss |
| 179 | + │ ├── style.production.scss |
| 180 | + │ └── style.scss |
| 181 | + └── main.scss |
| 182 | + ``` |
| 183 | +
|
| 184 | + A file like `style.production.scss` may not be used to import `style.scss` as it'll result in an import loop. |
| 185 | + |
| 186 | + The recommended solution is to create a shared include file like `_style--shared.scss` and import that instead. |
| 187 | + |
| 188 | +## Thanks to |
| 189 | +This importer is inspired by [node-sass-json-importer]. |
| 190 | + |
| 191 | +## 📄 License |
| 192 | + |
| 193 | +node-sass-conditional-importer is MIT licensed, as found in the [LICENSE][license] file. |
| 194 | + |
| 195 | +[node-sass]: https://github.com/sass/node-sass |
| 196 | +[react-platform-specific-extensions]: https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions |
| 197 | +[node-sass-importer-api]: https://github.com/sass/node-sass#importer--v200---experimental |
| 198 | +[node-sass-json-importer]: https://github.com/pmowrer/node-sass-json-importer |
| 199 | +[sass-no-dynamic-imports]: https://sass-lang.com/documentation/at-rules/import#interpolation |
| 200 | +[sass-no-if-imports]: https://stackoverflow.com/q/13879042 |
| 201 | +[license]: https://github.com/codebymikey/node-sass-conditional-importer/blob/master/LICENSE |
0 commit comments