Skip to content

Commit 92548f1

Browse files
committed
feature #301 Added support for the handlebars-loader (ogiammetta)
This PR was merged into the master branch. Discussion ---------- Added support for the handlebars-loader First PR on GitHub, any feedback is appreciated! :) Commits ------- 3aafa2e Added support for the handlebars-loader
2 parents 62b6aef + 3aafa2e commit 92548f1

14 files changed

+264
-5
lines changed

fixtures/js/handlebars.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var template = require('../templates/template.hbs');
2+
3+
document.getElementById('app').innerHTML = template({
4+
title: 'Welcome to Your Handlebars App'
5+
});

fixtures/templates/template.hbs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>{{ title }}</h1>

index.js

+21
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,27 @@ class Encore {
719719
return this;
720720
}
721721

722+
/**
723+
* Call this if you plan on loading Handlebars files.
724+
*
725+
* Encore.enableHandlebarsLoader();
726+
*
727+
* Or pass options to the loader
728+
*
729+
* Encore.enableHandlebarsLoader(function(options) {
730+
* // https://github.com/pcardune/handlebars-loader
731+
* // options.debug = true;
732+
* });
733+
*
734+
* @param {function} callback
735+
* @returns {Encore}
736+
*/
737+
enableHandlebarsLoader(callback = () => {}) {
738+
webpackConfig.enableHandlebarsLoader(callback);
739+
740+
return this;
741+
}
742+
722743
/**
723744
* Call this if you wish to disable the default
724745
* images loader.

lib/WebpackConfig.js

+12
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class WebpackConfig {
6565
this.useCoffeeScriptLoader = false;
6666
this.useForkedTypeScriptTypeChecking = false;
6767
this.useWebpackNotifier = false;
68+
this.useHandlebarsLoader = false;
6869

6970
// Features/Loaders options
7071
this.sassOptions = {
@@ -87,6 +88,7 @@ class WebpackConfig {
8788
this.vueLoaderOptionsCallback = () => {};
8889
this.tsConfigurationCallback = () => {};
8990
this.coffeeScriptConfigurationCallback = () => {};
91+
this.handlebarsConfigurationCallback = () => {};
9092

9193
// Plugins options
9294
this.cleanWebpackPluginPaths = ['**/*'];
@@ -446,6 +448,16 @@ class WebpackConfig {
446448
this.notifierPluginOptionsCallback = notifierPluginOptionsCallback;
447449
}
448450

451+
enableHandlebarsLoader(callback = () => {}) {
452+
this.useHandlebarsLoader = true;
453+
454+
if (typeof callback !== 'function') {
455+
throw new Error('Argument 1 to enableHandlebarsLoader() must be a callback function.');
456+
}
457+
458+
this.handlebarsConfigurationCallback = callback;
459+
}
460+
449461
disableImagesLoader() {
450462
this.useImagesLoader = false;
451463
}

lib/config-generator.js

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const babelLoaderUtil = require('./loaders/babel');
2121
const tsLoaderUtil = require('./loaders/typescript');
2222
const coffeeScriptLoaderUtil = require('./loaders/coffee-script');
2323
const vueLoaderUtil = require('./loaders/vue');
24+
const handlebarsLoaderUtil = require('./loaders/handlebars');
2425
// plugins utils
2526
const extractTextPluginUtil = require('./plugins/extract-text');
2627
const deleteUnusedEntriesPluginUtil = require('./plugins/delete-unused-entries');
@@ -242,6 +243,13 @@ class ConfigGenerator {
242243
});
243244
}
244245

246+
if (this.webpackConfig.useHandlebarsLoader) {
247+
rules.push({
248+
test: /\.(handlebars|hbs)$/,
249+
use: handlebarsLoaderUtil.getLoaders(this.webpackConfig)
250+
});
251+
}
252+
245253
this.webpackConfig.loaders.forEach((loader) => {
246254
rules.push(loader);
247255
});

lib/features.js

+5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ const features = {
7777
method: 'configureUrlLoader()',
7878
packages: ['url-loader'],
7979
description: 'use the url-loader'
80+
},
81+
handlebars: {
82+
method: 'enableHandlebarsLoader()',
83+
packages: ['handlebars', 'handlebars-loader'],
84+
description: 'load Handlebars files'
8085
}
8186
};
8287

lib/loaders/handlebars.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 applyOptionsCallback = require('../utils/apply-options-callback');
14+
15+
/**
16+
* @param {WebpackConfig} webpackConfig
17+
* @return {Array} of loaders to use for Handlebars
18+
*/
19+
module.exports = {
20+
getLoaders(webpackConfig) {
21+
loaderFeatures.ensurePackagesExist('handlebars');
22+
23+
const options = {};
24+
25+
return [
26+
{
27+
loader: 'handlebars-loader',
28+
options: applyOptionsCallback(webpackConfig.handlebarsConfigurationCallback, options)
29+
}
30+
];
31+
}
32+
};

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
"eslint-plugin-header": "^1.0.0",
6363
"eslint-plugin-node": "^4.2.2",
6464
"fork-ts-checker-webpack-plugin": "^0.2.7",
65+
"handlebars": "^4.0.11",
66+
"handlebars-loader": "^1.7.0",
6567
"http-server": "^0.9.0",
6668
"less": "^2.7.2",
6769
"less-loader": "^4.0.2",

test/WebpackConfig.js

+22
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,28 @@ describe('WebpackConfig object', () => {
685685
});
686686
});
687687

688+
describe('enableHandlebarsLoader', () => {
689+
690+
it('Call with no config', () => {
691+
const config = createConfig();
692+
config.enableHandlebarsLoader();
693+
694+
expect(config.useHandlebarsLoader).to.be.true;
695+
});
696+
697+
it('Pass config', () => {
698+
const config = createConfig();
699+
const callback = (options) => {
700+
options.debug = true;
701+
};
702+
config.enableHandlebarsLoader(callback);
703+
704+
expect(config.useHandlebarsLoader).to.be.true;
705+
expect(config.handlebarsConfigurationCallback).to.equal(callback);
706+
});
707+
708+
});
709+
688710
describe('addPlugin', () => {
689711
it('extends the current registered plugins', () => {
690712
const config = createConfig();

test/config-generator.js

+26
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,32 @@ describe('The config-generator function', () => {
354354
});
355355
});
356356

357+
describe('enableHandlebarsLoader() adds the handlebars-loader', () => {
358+
359+
it('without enableHandlebarsLoader()', () => {
360+
const config = createConfig();
361+
config.outputPath = '/tmp/output/public-path';
362+
config.publicPath = '/public-path';
363+
config.addEntry('main', './main');
364+
const actualConfig = configGenerator(config);
365+
366+
expect(JSON.stringify(actualConfig.module.rules)).to.not.contain('handlebars-loader');
367+
});
368+
369+
it('enableHandlebarsLoader()', () => {
370+
const config = createConfig();
371+
config.outputPath = '/tmp/output/public-path';
372+
config.publicPath = '/public-path';
373+
config.addEntry('main', './main');
374+
config.enableHandlebarsLoader();
375+
376+
const actualConfig = configGenerator(config);
377+
378+
expect(JSON.stringify(actualConfig.module.rules)).to.contain('handlebars-loader');
379+
});
380+
381+
});
382+
357383
describe('addLoader() adds a custom loader', () => {
358384
it('addLoader()', () => {
359385
const config = createConfig();

test/functional.js

+26
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,32 @@ module.exports = {
862862
});
863863
});
864864

865+
it('When configured, Handlebars is compiled', (done) => {
866+
const config = createWebpackConfig('www/build', 'dev');
867+
config.setPublicPath('/build');
868+
config.addEntry('main', ['./js/handlebars.js']);
869+
const testCallback = () => {};
870+
config.enableHandlebarsLoader(testCallback);
871+
872+
testSetup.runWebpack(config, () => {
873+
expect(config.outputPath).to.be.a.directory().with.deep.files([
874+
'main.js',
875+
'manifest.json'
876+
]);
877+
878+
testSetup.requestTestPage(
879+
path.join(config.getContext(), 'www'),
880+
[
881+
'build/main.js'
882+
],
883+
(browser) => {
884+
browser.assert.text('#app h1', 'Welcome to Your Handlebars App');
885+
done();
886+
}
887+
);
888+
});
889+
});
890+
865891
it('The output directory is cleaned between builds', (done) => {
866892
const config = createWebpackConfig('www/build', 'dev');
867893
config.setPublicPath('/build');

test/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ describe('Public API', () => {
260260

261261
});
262262

263+
describe('enableHandlebarsLoader', () => {
264+
265+
it('must return the API object', () => {
266+
const returnedValue = api.enableHandlebarsLoader();
267+
expect(returnedValue).to.equal(api);
268+
});
269+
270+
});
271+
263272
describe('disableImagesLoader', () => {
264273

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

test/loaders/handlebars.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 handlebarsLoader = require('../../lib/loaders/handlebars');
16+
17+
function createConfig() {
18+
const runtimeConfig = new RuntimeConfig();
19+
runtimeConfig.context = __dirname;
20+
runtimeConfig.babelRcFileExists = false;
21+
22+
return new WebpackConfig(runtimeConfig);
23+
}
24+
25+
describe('loaders/handlebars', () => {
26+
it('getLoaders() basic usage', () => {
27+
const config = createConfig();
28+
config.enableHandlebarsLoader();
29+
30+
const actualLoaders = handlebarsLoader.getLoaders(config);
31+
expect(actualLoaders).to.have.lengthOf(1);
32+
expect(actualLoaders[0].options).to.be.empty;
33+
});
34+
35+
it('getLoaders() with options callback', () => {
36+
const config = createConfig();
37+
config.enableHandlebarsLoader((options) => {
38+
options.debug = true;
39+
});
40+
41+
const actualLoaders = handlebarsLoader.getLoaders(config);
42+
expect(actualLoaders).to.have.lengthOf(1);
43+
expect(actualLoaders[0].options.debug).to.be.true;
44+
});
45+
46+
it('getLoaders() with options callback that returns an object', () => {
47+
const config = createConfig();
48+
config.enableHandlebarsLoader((options) => {
49+
options.debug = true;
50+
51+
// This should override the original config
52+
return { foo: true };
53+
});
54+
55+
const actualLoaders = handlebarsLoader.getLoaders(config);
56+
expect(actualLoaders).to.have.lengthOf(1);
57+
expect(actualLoaders[0].options).to.deep.equal({ foo: true });
58+
});
59+
});

0 commit comments

Comments
 (0)