Skip to content

Commit 2f51ae8

Browse files
author
Marc Neuhaus
committed
Add Stylus loader
1 parent bf49baf commit 2f51ae8

File tree

7 files changed

+161
-0
lines changed

7 files changed

+161
-0
lines changed

index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,26 @@ const publicApi = {
497497
return this;
498498
},
499499

500+
/**
501+
* Call this if you plan on loading stylus files.
502+
*
503+
* Encore.enableStylusLoader();
504+
*
505+
* Or pass options to the loader
506+
*
507+
* Encore.enableStylusLoader(function(options) {
508+
* // https://github.com/shama/stylus-loader
509+
* });
510+
*
511+
* @param {function} StylusLoaderOptionsCallback
512+
* @return {exports}
513+
*/
514+
enableStylusLoader(stylusLoaderOptionsCallback = () => {}) {
515+
webpackConfig.enableStylusLoader(stylusLoaderOptionsCallback);
516+
517+
return this;
518+
},
519+
500520
/**
501521
* Configure babel, without needing a .babelrc file.
502522
*

lib/WebpackConfig.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class WebpackConfig {
5454
this.useFontsLoader = true;
5555
this.usePostCssLoader = false;
5656
this.useLessLoader = false;
57+
this.useStylusLoader = false;
5758
this.useSassLoader = false;
5859
this.useReact = false;
5960
this.usePreact = false;
@@ -74,6 +75,7 @@ class WebpackConfig {
7475
this.postCssLoaderOptionsCallback = () => {};
7576
this.sassLoaderOptionsCallback = () => {};
7677
this.lessLoaderOptionsCallback = () => {};
78+
this.stylusLoaderOptionsCallback = () => {};
7779
this.babelConfigurationCallback = () => {};
7880
this.vueLoaderOptionsCallback = () => {};
7981
this.tsConfigurationCallback = () => {};
@@ -344,6 +346,16 @@ class WebpackConfig {
344346
this.lessLoaderOptionsCallback = lessLoaderOptionsCallback;
345347
}
346348

349+
enableStylusLoader(stylusLoaderOptionsCallback = () => {}) {
350+
this.useStylusLoader = true;
351+
352+
if (typeof stylusLoaderOptionsCallback !== 'function') {
353+
throw new Error('Argument 1 to enableStylusLoader() must be a callback function.');
354+
}
355+
356+
this.stylusLoaderOptionsCallback = stylusLoaderOptionsCallback;
357+
}
358+
347359
enableReactPreset() {
348360
this.useReact = true;
349361
}

lib/config-generator.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const pathUtil = require('./config/path-util');
1515
const cssLoaderUtil = require('./loaders/css');
1616
const sassLoaderUtil = require('./loaders/sass');
1717
const lessLoaderUtil = require('./loaders/less');
18+
const stylusLoaderUtil = require('./loaders/stylus');
1819
const babelLoaderUtil = require('./loaders/babel');
1920
const tsLoaderUtil = require('./loaders/typescript');
2021
const vueLoaderUtil = require('./loaders/vue');
@@ -186,6 +187,13 @@ class ConfigGenerator {
186187
});
187188
}
188189

190+
if (this.webpackConfig.useStylusLoader) {
191+
rules.push({
192+
test: /\.stylus/,
193+
use: extractText.extract(this.webpackConfig, stylusLoaderUtil.getLoaders(this.webpackConfig))
194+
});
195+
}
196+
189197
if (this.webpackConfig.useVueLoader) {
190198
rules.push({
191199
test: /\.vue$/,

lib/features.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ const features = {
2626
packages: ['less-loader'],
2727
description: 'load LESS files'
2828
},
29+
stylus: {
30+
method: 'enableStylusLoader()',
31+
packages: ['stylus-loader'],
32+
description: 'load Stylus files'
33+
},
2934
postcss: {
3035
method: 'enablePostCssLoader()',
3136
packages: ['postcss-loader'],

lib/loaders/stylus.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <fabien@symfony.com>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const loaderFeatures = require('../features');
13+
const cssLoader = require('./css');
14+
15+
/**
16+
* @param {WebpackConfig} webpackConfig
17+
* @param {bool} ignorePostCssLoader If true, postcss-loader will never be added
18+
* @return {Array} of loaders to use for Stylus files
19+
*/
20+
module.exports = {
21+
getLoaders(webpackConfig, ignorePostCssLoader = false) {
22+
loaderFeatures.ensurePackagesExist('stylus');
23+
24+
const config = {
25+
sourceMap: webpackConfig.useSourceMaps
26+
};
27+
28+
// allow options to be configured
29+
webpackConfig.stylusLoaderOptionsCallback.apply(
30+
// use config as the this variable
31+
config,
32+
[config]
33+
);
34+
35+
return [
36+
...cssLoader.getLoaders(webpackConfig, ignorePostCssLoader),
37+
{
38+
loader: 'stylus-loader',
39+
options: config
40+
},
41+
];
42+
}
43+
};

test/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ describe('Public API', () => {
161161

162162
});
163163

164+
describe('enableStylusLoader', () => {
165+
166+
it('must return the API object', () => {
167+
const returnedValue = api.enableStylusLoader();
168+
expect(returnedValue).to.equal(api);
169+
});
170+
171+
});
172+
164173
describe('setOutputPath', () => {
165174

166175
it('must return the API object', () => {

test/loaders/stylus.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <fabien@symfony.com>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const expect = require('chai').expect;
13+
const WebpackConfig = require('../../lib/WebpackConfig');
14+
const RuntimeConfig = require('../../lib/config/RuntimeConfig');
15+
const stylusLoader = require('../../lib/loaders/stylus');
16+
const cssLoader = require('../../lib/loaders/css');
17+
const sinon = require('sinon');
18+
19+
function createConfig() {
20+
const runtimeConfig = new RuntimeConfig();
21+
runtimeConfig.context = __dirname;
22+
runtimeConfig.babelRcFileExists = false;
23+
24+
return new WebpackConfig(runtimeConfig);
25+
}
26+
27+
describe('loaders/stylus', () => {
28+
it('getLoaders() basic usage', () => {
29+
const config = createConfig();
30+
config.enableSourceMaps(true);
31+
32+
// make the cssLoader return nothing
33+
sinon.stub(cssLoader, 'getLoaders')
34+
.callsFake(() => []);
35+
36+
const actualLoaders = stylusLoader.getLoaders(config);
37+
expect(actualLoaders).to.have.lengthOf(1);
38+
expect(actualLoaders[0].options.sourceMap).to.be.true;
39+
40+
cssLoader.getLoaders.restore();
41+
});
42+
43+
it('getLoaders() with options callback', () => {
44+
const config = createConfig();
45+
config.enableSourceMaps(true);
46+
47+
// make the cssLoader return nothing
48+
sinon.stub(cssLoader, 'getLoaders')
49+
.callsFake(() => []);
50+
51+
config.enableStylusLoader(function(stylusOptions) {
52+
stylusOptions.custom_option = 'foo';
53+
stylusOptions.other_option = true;
54+
});
55+
56+
const actualLoaders = stylusLoader.getLoaders(config);
57+
expect(actualLoaders[0].options).to.deep.equals({
58+
sourceMap: true,
59+
custom_option: 'foo',
60+
other_option: true
61+
});
62+
cssLoader.getLoaders.restore();
63+
});
64+
});

0 commit comments

Comments
 (0)