Skip to content

Commit 25d67d3

Browse files
committed
Merge remote-tracking branch 'origin/master'
* origin/master: add stylus dependency suggestion add basic configuration callback example update package.json and yarn.lock add more tests fix stylus file extension test fix jsdoc typo fix devDependencies Add Stylus loader
2 parents 65e8b32 + fb93a8f commit 25d67d3

13 files changed

+297
-117
lines changed

fixtures/css/h2_styles.styl

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
base = #f938ab
2+
h2
3+
color: saturation(base, 5%);

index.js

+21
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,27 @@ 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+
* // options.import = ['~library/index.styl'];
510+
* });
511+
*
512+
* @param {function} stylusLoaderOptionsCallback
513+
* @return {exports}
514+
*/
515+
enableStylusLoader(stylusLoaderOptionsCallback = () => {}) {
516+
webpackConfig.enableStylusLoader(stylusLoaderOptionsCallback);
517+
518+
return this;
519+
},
520+
500521
/**
501522
* Configure babel, without needing a .babelrc file.
502523
*

lib/WebpackConfig.js

+12
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

+8
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: /\.styl/,
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

+5
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', 'stylus'],
32+
description: 'load Stylus files'
33+
},
2934
postcss: {
3035
method: 'enablePostCssLoader()',
3136
packages: ['postcss-loader'],

lib/loaders/stylus.js

+43
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 <[email protected]>
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+
};

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
"preact-compat": "^3.17.0",
7171
"sass-loader": "^6.0.3",
7272
"sinon": "^2.3.4",
73+
"stylus": "^0.54.5",
74+
"stylus-loader": "^3.0.1",
7375
"ts-loader": "^2.1.0",
7476
"typescript": "^2.3.4",
7577
"vue": "^2.3.4",

test/WebpackConfig.js

+25
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,31 @@ describe('WebpackConfig object', () => {
519519
});
520520
});
521521

522+
describe('enableStylusLoader', () => {
523+
it('Calling method sets it', () => {
524+
const config = createConfig();
525+
config.enableStylusLoader();
526+
527+
expect(config.useStylusLoader).to.be.true;
528+
});
529+
530+
it('Calling with callback', () => {
531+
const config = createConfig();
532+
const callback = (stylusOptions) => {};
533+
config.enableStylusLoader(callback);
534+
535+
expect(config.stylusLoaderOptionsCallback).to.equal(callback);
536+
});
537+
538+
it('Calling with non-callback throws an error', () => {
539+
const config = createConfig();
540+
541+
expect(() => {
542+
config.enableStylusLoader('FOO');
543+
}).to.throw('must be a callback function');
544+
});
545+
});
546+
522547
describe('enablePreactPreset', () => {
523548
it('Without preact-compat', () => {
524549
const config = createConfig();

test/config-generator.js

+26
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,32 @@ describe('The config-generator function', () => {
307307
});
308308
});
309309

310+
describe('enableStylusLoader() adds the stylus-loader', () => {
311+
it('without enableStylusLoader()', () => {
312+
const config = createConfig();
313+
config.outputPath = '/tmp/output/public-path';
314+
config.publicPath = '/public-path';
315+
config.addEntry('main', './main');
316+
// do not enable the stylus loader
317+
318+
const actualConfig = configGenerator(config);
319+
320+
expect(JSON.stringify(actualConfig.module.rules)).to.not.contain('stylus-loader');
321+
});
322+
323+
it('enableStylusLoader()', () => {
324+
const config = createConfig();
325+
config.outputPath = '/tmp/output/public-path';
326+
config.publicPath = '/public-path';
327+
config.addEntry('main', './main');
328+
config.enableStylusLoader();
329+
330+
const actualConfig = configGenerator(config);
331+
332+
expect(JSON.stringify(actualConfig.module.rules)).to.contain('stylus-loader');
333+
});
334+
});
335+
310336
describe('addLoader() adds a custom loader', () => {
311337
it('addLoader()', () => {
312338
const config = createConfig();

test/functional.js

+18
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,24 @@ module.exports = {
657657
});
658658
});
659659

660+
it('stylus processes when enabled', (done) => {
661+
const config = createWebpackConfig('www/build', 'dev');
662+
config.setPublicPath('/build');
663+
config.addStyleEntry('styles', ['./css/h2_styles.styl']);
664+
config.enableStylusLoader();
665+
666+
testSetup.runWebpack(config, (webpackAssert) => {
667+
// check that stylus did its work!
668+
webpackAssert.assertOutputFileContains(
669+
'styles.css',
670+
// stylus logic inside will resolve to tis
671+
'color: #9e9399;'
672+
);
673+
674+
done();
675+
});
676+
});
677+
660678
it('Babel is executed on .js files', (done) => {
661679
const config = createWebpackConfig('www/build', 'dev');
662680
config.setPublicPath('/build');

test/index.js

+9
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

+64
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 <[email protected]>
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)