Skip to content

Commit 18db6fd

Browse files
feat: automatically use fibers if it is possible
1 parent fab50ab commit 18db6fd

File tree

4 files changed

+152
-3
lines changed

4 files changed

+152
-3
lines changed

README.md

+44-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,50 @@ module.exports = {
172172
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.
173173
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.
174174

175-
To enable this, pass the `Fiber` class to the `sassOptions.fiber` option:
175+
We automatically inject the [`fibers`](https://github.com/laverdet/node-fibers) package (setup `sassOptions.fiber`) if is possible (i.e. you need install the [`fibers`](https://github.com/laverdet/node-fibers) package).
176+
177+
**package.json**
178+
179+
```json
180+
{
181+
"devDependencies": {
182+
"sass-loader": "^7.2.0",
183+
"sass": "^1.22.10",
184+
"fibers": "^4.0.1"
185+
}
186+
}
187+
```
188+
189+
You can disable automatically inject the [`fibers`](https://github.com/laverdet/node-fibers) package pass the `false` value for the `sassOptions.fiber` option.
190+
191+
**webpack.config.js**
192+
193+
```js
194+
module.exports = {
195+
module: {
196+
rules: [
197+
{
198+
test: /\.s[ac]ss$/i,
199+
use: [
200+
'style-loader',
201+
'css-loader',
202+
{
203+
loader: 'sass-loader',
204+
options: {
205+
implementation: require('sass'),
206+
sassOptions: {
207+
fiber: false,
208+
},
209+
},
210+
},
211+
],
212+
},
213+
],
214+
},
215+
};
216+
```
217+
218+
Also you can pass own the `fiber` value using this code:
176219

177220
**webpack.config.js**
178221

src/getSassOptions.js

+29-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function isProductionLikeMode(loaderContext) {
2121
* @param {string} content
2222
* @returns {Object}
2323
*/
24-
function getSassOptions(loaderContext, loaderOptions, content) {
24+
function getSassOptions(loaderContext, loaderOptions, content, implementation) {
2525
const options = cloneDeep(
2626
loaderOptions.sassOptions
2727
? typeof loaderOptions.sassOptions === 'function'
@@ -30,6 +30,34 @@ function getSassOptions(loaderContext, loaderOptions, content) {
3030
: {}
3131
);
3232

33+
const isDartSass = implementation.info.includes('dart-sass');
34+
35+
if (isDartSass) {
36+
const shouldTryToResolveFibers =
37+
!options.fibers && options.fibers !== false;
38+
39+
if (shouldTryToResolveFibers) {
40+
let fibers;
41+
42+
try {
43+
fibers = require.resolve('fibers');
44+
} catch (_error) {
45+
// Nothing
46+
}
47+
48+
if (fibers) {
49+
// eslint-disable-next-line global-require, import/no-dynamic-require
50+
options.fiber = require(fibers);
51+
}
52+
} else if (options.fibers === false) {
53+
// Don't pass the `fiber` option for `sass` (`Dart Sass`)
54+
delete options.fiber;
55+
}
56+
} else {
57+
// Don't pass the `fiber` option for `node-sass`
58+
delete options.fiber;
59+
}
60+
3361
options.data = loaderOptions.prependData
3462
? typeof loaderOptions.prependData === 'function'
3563
? loaderOptions.prependData(loaderContext) + os.EOL + content

src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function loader(content) {
3232
this.addDependency(path.normalize(file));
3333
};
3434

35-
const sassOptions = getSassOptions(this, options, content);
35+
const sassOptions = getSassOptions(this, options, content, implementation);
3636

3737
const shouldUseWebpackImporter =
3838
typeof options.webpackImporter === 'boolean'

test/sassOptions-option.test.js

+78
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,84 @@ describe('sassOptions option', () => {
175175
expect(stats.compilation.warnings).toMatchSnapshot('warnings');
176176
expect(stats.compilation.errors).toMatchSnapshot('errors');
177177
});
178+
179+
it(`should work with the "fibers" option (${implementationName}) (${syntax})`, async () => {
180+
const testId = getTestId('language', syntax);
181+
const options = {
182+
implementation: getImplementationByName(implementationName),
183+
sassOptions: {},
184+
};
185+
186+
if (semver.satisfies(process.version, '>= 10')) {
187+
// eslint-disable-next-line global-require
188+
options.sassOptions.fibers = require('fibers');
189+
}
190+
191+
const stats = await compile(testId, { loader: { options } });
192+
const codeFromBundle = getCodeFromBundle(stats);
193+
const codeFromSass = getCodeFromSass(testId, options);
194+
195+
expect(codeFromBundle.css).toBe(codeFromSass.css);
196+
expect(codeFromBundle.css).toMatchSnapshot('css');
197+
expect(stats.compilation.warnings).toMatchSnapshot('warnings');
198+
expect(stats.compilation.errors).toMatchSnapshot('errors');
199+
200+
dartSassSpy.mockRestore();
201+
});
202+
203+
it(`should use the "fibers" package if it is possible (${implementationName}) (${syntax})`, async () => {
204+
const dartSassSpy = jest.spyOn(dartSass, 'render');
205+
const testId = getTestId('language', syntax);
206+
const options = {
207+
implementation: getImplementationByName(implementationName),
208+
sassOptions: {},
209+
};
210+
211+
const stats = await compile(testId, { loader: { options } });
212+
213+
expect(getCodeFromBundle(stats).css).toBe(
214+
getCodeFromSass(testId, options).css
215+
);
216+
217+
if (
218+
semver.satisfies(process.version, '>= 10') &&
219+
implementationName === 'dart-sass'
220+
) {
221+
expect(dartSassSpy.mock.calls[0][0]).toHaveProperty('fibers');
222+
}
223+
224+
expect(stats.compilation.warnings).toMatchSnapshot('warnings');
225+
expect(stats.compilation.errors).toMatchSnapshot('errors');
226+
227+
dartSassSpy.mockRestore();
228+
});
229+
230+
it(`should don't use the "fibers" package when the "fibers" option is "false" (${implementationName}) (${syntax})`, async () => {
231+
const dartSassSpy = jest.spyOn(dartSass, 'render');
232+
const testId = getTestId('language', syntax);
233+
const options = {
234+
implementation: getImplementationByName(implementationName),
235+
sassOptions: { fibers: false },
236+
};
237+
238+
const stats = await compile(testId, { loader: { options } });
239+
240+
expect(getCodeFromBundle(stats).css).toBe(
241+
getCodeFromSass(testId, options).css
242+
);
243+
244+
if (
245+
semver.satisfies(process.version, '>= 10') &&
246+
implementationName === 'dart-sass'
247+
) {
248+
expect(dartSassSpy.mock.calls[0][0]).not.toHaveProperty('fibers');
249+
}
250+
251+
expect(stats.compilation.warnings).toMatchSnapshot('warnings');
252+
expect(stats.compilation.errors).toMatchSnapshot('errors');
253+
254+
dartSassSpy.mockRestore();
255+
});
178256
});
179257
});
180258
});

0 commit comments

Comments
 (0)