Skip to content

Commit 8d679a4

Browse files
committed
POC for opt-in SASS loader
1 parent eac8fce commit 8d679a4

File tree

4 files changed

+89
-8
lines changed

4 files changed

+89
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports.default = function(source) {
2+
try {
3+
// XXX performance hit of calling require / require.resolve this for every file passed to this loader?
4+
require.resolve('node-sass');
5+
6+
// Options are passed to loader using the function `this`. We use `bind`
7+
// to transparently pass options coming from the webpack config to
8+
// sass-loader:
9+
// XXX performance hit of bind?
10+
const sassLoader = require('sass-loader').bind(this);
11+
12+
return sassLoader(source);
13+
} catch (er) {
14+
throw new Error(
15+
'It looks like you have required a .scss file. In order for this to work, you need to install node-sass and sass-loader first.'
16+
);
17+
}
18+
};

packages/react-dev-utils/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"printHostingInstructions.js",
3535
"WatchMissingNodeModulesPlugin.js",
3636
"WebpackDevServerUtils.js",
37-
"webpackHotDevClient.js"
37+
"webpackHotDevClient.js",
38+
"optinSassWebpackLoader.js"
3839
],
3940
"dependencies": {
4041
"@babel/code-frame": "7.0.0-beta.38",
@@ -59,7 +60,9 @@
5960
"text-table": "0.2.0"
6061
},
6162
"devDependencies": {
62-
"jest": "22.1.2"
63+
"jest": "22.1.2",
64+
"node-sass": "^4.7.2",
65+
"sass-loader": "^6.0.6"
6366
},
6467
"scripts": {
6568
"test": "jest"

packages/react-scripts/config/env.js

+33-6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,30 @@ dotenvFiles.forEach(dotenvFile => {
4848
}
4949
});
5050

51+
const appDirectory = fs.realpathSync(process.cwd());
52+
53+
/**
54+
* Utility to parse PATH-like strings to array
55+
*
56+
* Separators:
57+
* : in Unix (colon)
58+
* ; in Windows (semicolon)
59+
*
60+
* @param {String} paths
61+
* @returns {string}
62+
*/
63+
function parsePath(paths) {
64+
if (paths) {
65+
return paths
66+
.split(path.delimiter)
67+
.filter(folder => folder && !path.isAbsolute(folder))
68+
.map(folder => path.resolve(appDirectory, folder))
69+
.join(path.delimiter);
70+
}
71+
72+
return '';
73+
}
74+
5175
// We support resolving modules according to `NODE_PATH`.
5276
// This lets you use absolute paths in imports inside large monorepos:
5377
// https://github.com/facebookincubator/create-react-app/issues/253.
@@ -57,12 +81,14 @@ dotenvFiles.forEach(dotenvFile => {
5781
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
5882
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
5983
// We also resolve them to make sure all tools using them work consistently.
60-
const appDirectory = fs.realpathSync(process.cwd());
61-
process.env.NODE_PATH = (process.env.NODE_PATH || '')
62-
.split(path.delimiter)
63-
.filter(folder => folder && !path.isAbsolute(folder))
64-
.map(folder => path.resolve(appDirectory, folder))
65-
.join(path.delimiter);
84+
process.env.NODE_PATH = parsePath(process.env.NODE_PATH);
85+
86+
// node-sass supports additional paths to resolve imports from.
87+
// https://github.com/sass/node-sass/tree/v4.7.2#includepaths
88+
// It works similar to NODE_PATH, but for importing scss files.
89+
// We would like to assign an array to process.env variable, but
90+
// Node will implicitly convert value to string.
91+
process.env.SASS_INCLUDE_PATHS = parsePath(process.env.SASS_INCLUDE_PATHS);
6692

6793
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
6894
// injected into the application via DefinePlugin in Webpack configuration.
@@ -87,6 +113,7 @@ function getClientEnvironment(publicUrl) {
87113
PUBLIC_URL: publicUrl,
88114
}
89115
);
116+
90117
// Stringify all values so we can feed into Webpack DefinePlugin
91118
const stringified = {
92119
'process.env': Object.keys(raw).reduce((env, key) => {

packages/react-scripts/config/webpack.config.dev.js

+33
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,39 @@ module.exports = {
264264
},
265265
],
266266
},
267+
// "react-dev-utils/optinSassWebpackLoader" wraps sass-loader, trying to require
268+
// node-sass and sass-loader, and prompting the user to install them if it fails
269+
// "postcss" loader applies autoprefixer to our CSS.
270+
// "css" loader resolves paths in CSS and adds assets as dependencies.
271+
// "style" loader turns CSS into JS modules that inject <style> tags.
272+
// In production, we use a plugin to extract that CSS to a file, but
273+
// in development "style" loader enables hot editing of CSS.
274+
{
275+
test: /\.scss$/,
276+
use: [
277+
require.resolve('style-loader'),
278+
{
279+
loader: require.resolve('css-loader'),
280+
options: {
281+
importLoaders: 2,
282+
},
283+
},
284+
{
285+
loader: require.resolve('postcss-loader'),
286+
options: postCSSLoaderOptions,
287+
},
288+
{
289+
loader: require.resolve(
290+
'react-dev-utils/optinSassWebpackLoader'
291+
),
292+
options: {
293+
includePaths: process.env.SASS_INCLUDE_PATHS.split(
294+
path.delimiter
295+
),
296+
},
297+
},
298+
],
299+
},
267300
// Allows you to use two kinds of imports for SVG:
268301
// import logoUrl from './logo.svg'; gives you the URL.
269302
// import { ReactComponent as Logo } from './logo.svg'; gives you a component.

0 commit comments

Comments
 (0)