Skip to content

Commit e1bce25

Browse files
Merge pull request #959 from engelfrost/feature/handlebars-helpers
Feature/handlebars helpers
2 parents da4f187 + f3fd540 commit e1bce25

File tree

8 files changed

+178
-41
lines changed

8 files changed

+178
-41
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = function(Handlebars) {
2+
Handlebars.registerHelper('test', function() {
3+
return 'This is a test helper';
4+
});
5+
};

packages/development-edition-engine-handlebars/patternlab-config.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,10 @@
8585
"excludedPatternStates": [],
8686
"excludedTags": []
8787
}
88-
]
88+
],
89+
"engines": {
90+
"handlebars": {
91+
"extend": "helpers/*.js"
92+
}
93+
}
8994
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{test}}

packages/engine-handlebars/README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# The Handlebars PatternEngine for Pattern Lab / Node
22

3-
To install the Handlebars PatternEngine in your edition, `npm install @pattern-lab/engine-handlebars` should do the trick.
3+
To install the Handlebars PatternEngine in your edition, `npm install --save @pattern-lab/engine-handlebars` should do the trick.
44

55
## Supported features
66

@@ -11,3 +11,34 @@ To install the Handlebars PatternEngine in your edition, `npm install @pattern-l
1111
* [x] [Pattern States](http://patternlab.io/docs/pattern-states.html)
1212
* [ ] [Pattern Parameters](http://patternlab.io/docs/pattern-parameters.html) (Accomplished instead using [native Handlebars partial arguments](http://handlebarsjs.com/partials.html))
1313
* [ ] [Style Modifiers](http://patternlab.io/docs/pattern-stylemodifier.html) (Accomplished instead using [native Handlebars partial arguments](http://handlebarsjs.com/partials.html))
14+
15+
## Helpers
16+
17+
To add custom [helpers](http://handlebarsjs.com/#helpers) or otherwise interact with Handlebars directly, create a file named `patternlab-handlebars-config.js` in the root of your Pattern Lab project, or override the default location by specifying one or several glob patterns in the Pattern Lab config:
18+
19+
```json
20+
{
21+
...
22+
"engines": {
23+
"handlebars": {
24+
"extend": [
25+
"handlebars-helpers.js",
26+
"helpers/**/*.js"
27+
]
28+
}
29+
}
30+
}
31+
```
32+
33+
Each file should export a function which takes Handlebars as an argument.
34+
35+
```js
36+
module.exports = function(Handlebars) {
37+
// Put helpers here
38+
39+
Handlebars.registerHelper('fullName', function(person) {
40+
// Example: person = {firstName: "Alan", lastName: "Johnson"}
41+
return person.firstName + " " + person.lastName;
42+
});
43+
};
44+
```

packages/engine-handlebars/lib/engine_handlebars.js

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,30 @@
2525
const fs = require('fs-extra');
2626
const path = require('path');
2727
const Handlebars = require('handlebars');
28+
const glob = require('glob');
2829

2930
// regexes, stored here so they're only compiled once
3031
const findPartialsRE = /{{#?>\s*([\w-\/.]+)(?:.|\s+)*?}}/g;
3132
const findListItemsRE = /({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g;
3233
const findAtPartialBlockRE = /{{#?>\s*@partial-block\s*}}/g;
3334

3435
function escapeAtPartialBlock(partialString) {
35-
var partial = partialString.replace(
36+
const partial = partialString.replace(
3637
findAtPartialBlockRE,
3738
'{{> @partial-block }}'
3839
);
3940
return partial;
4041
}
4142

42-
var engine_handlebars = {
43+
function loadHelpers(helpers) {
44+
helpers.forEach(globPattern => {
45+
glob.sync(globPattern).forEach(filePath => {
46+
require(path.join(process.cwd(), filePath))(Handlebars);
47+
});
48+
});
49+
}
50+
51+
const engine_handlebars = {
4352
engine: Handlebars,
4453
engineName: 'handlebars',
4554
engineFileExtension: ['.hbs', '.handlebars'],
@@ -54,7 +63,7 @@ var engine_handlebars = {
5463
Handlebars.registerPartial(partials);
5564
}
5665

57-
var compiled = Handlebars.compile(escapeAtPartialBlock(pattern.template));
66+
const compiled = Handlebars.compile(escapeAtPartialBlock(pattern.template));
5867

5968
return Promise.resolve(compiled(data));
6069
},
@@ -68,7 +77,7 @@ var engine_handlebars = {
6877

6978
// find and return any {{> template-name }} within pattern
7079
findPartials: function findPartials(pattern) {
71-
var matches = pattern.template.match(findPartialsRE);
80+
const matches = pattern.template.match(findPartialsRE);
7281
return matches;
7382
},
7483
findPartialsWithStyleModifiers: function() {
@@ -85,14 +94,14 @@ var engine_handlebars = {
8594
return [];
8695
},
8796
findListItems: function(pattern) {
88-
var matches = pattern.template.match(findListItemsRE);
97+
const matches = pattern.template.match(findListItemsRE);
8998
return matches;
9099
},
91100

92101
// given a pattern, and a partial string, tease out the "pattern key" and
93102
// return it.
94103
findPartial: function(partialString) {
95-
var partial = partialString.replace(findPartialsRE, '$1');
104+
const partial = partialString.replace(findPartialsRE, '$1');
96105
return partial;
97106
},
98107

@@ -122,6 +131,36 @@ var engine_handlebars = {
122131
this.spawnFile(config, '_00-head.hbs');
123132
this.spawnFile(config, '_01-foot.hbs');
124133
},
134+
135+
/**
136+
* Accept a Pattern Lab config object from the core and use the settings to
137+
* load helpers.
138+
*
139+
* @param {object} config - the global config object from core
140+
*/
141+
usePatternLabConfig: function(config) {
142+
let helpers;
143+
144+
try {
145+
// Look for helpers in the config
146+
helpers = config.engines.handlebars.extend;
147+
148+
if (typeof helpers === 'string') {
149+
helpers = [helpers];
150+
}
151+
} catch (error) {
152+
// Look for helpers in default location
153+
const configPath = 'patternlab-handlebars-config.js';
154+
if (fs.existsSync(path.join(process.cwd(), configPath))) {
155+
helpers = [configPath];
156+
}
157+
}
158+
159+
// Load helpers if they were found
160+
if (helpers) {
161+
loadHelpers(helpers);
162+
}
163+
},
125164
};
126165

127166
module.exports = engine_handlebars;

packages/engine-handlebars/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "lib/engine_handlebars.js",
66
"dependencies": {
77
"fs-extra": "0.30.0",
8-
"handlebars": "4.0.5"
8+
"handlebars": "4.0.5",
9+
"glob": "7.0.0"
910
},
1011
"keywords": [
1112
"Pattern Lab",

packages/engine-nunjucks/README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,34 @@ Level of Support is more or less full. Partial calls and lineage hunting are sup
1717

1818
## Extending the Nunjucks instance
1919

20-
To add custom filters or make customizations to the nunjucks instance, create a file named `patternlab-nunjucks-config.js` in the root of your Pattern Lab project. `patternlab-nunjucks-config.js` should export a function with the Nunjucks environment as parameter.
20+
To add custom filters or make customizations to the nunjucks instance, add the following to `patternlab-config.json`:
2121

22+
```json
23+
{
24+
...
25+
"engines": {
26+
"nunjucks": {
27+
"extend": [
28+
"nunjucks-extensions/*.js"
29+
]
30+
}
31+
}
32+
}
2233
```
34+
35+
...or use the default file name: `patternlab-nunjucks-config.js` (in the root of your Pattern Lab project).
36+
37+
Each file providing extensions should export a function with the Nunjucks environment as parameter.
38+
39+
```js
2340
module.exports = function (env) {
2441
[YOUR CUSTOM CODE HERE]
2542
};
2643
```
2744

2845
Example: `patternlab-nunjucks-config.js` file that uses lodash and adds three custom filters.
29-
```
46+
47+
```js
3048
var _shuffle = require('lodash/shuffle'),
3149
_take = require('lodash/take');
3250

packages/engine-nunjucks/lib/engine_nunjucks.js

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,39 +22,10 @@
2222

2323
const fs = require('fs-extra');
2424
const path = require('path');
25-
const plPath = process.cwd();
26-
const plConfig = require(path.join(plPath, 'patternlab-config.json'));
2725
const nunjucks = require('nunjucks');
2826
const partialRegistry = [];
2927

30-
// Create Pattern Loader
31-
// Since Pattern Lab includes are not path based we need a custom loader for Nunjucks.
32-
function PatternLoader() {}
33-
34-
PatternLoader.prototype.getSource = function(name) {
35-
const fullPath = path.resolve(
36-
plConfig.paths.source.patterns,
37-
partialRegistry[name]
38-
);
39-
return {
40-
src: fs.readFileSync(fullPath, 'utf-8'),
41-
path: fullPath,
42-
noCache: true,
43-
};
44-
};
45-
46-
const env = new nunjucks.Environment(new PatternLoader());
47-
48-
// Load any user Defined configurations
49-
try {
50-
const nunjucksConfig = require(path.join(
51-
plPath,
52-
'patternlab-nunjucks-config.js'
53-
));
54-
if (typeof nunjucksConfig === 'function') {
55-
nunjucksConfig(env);
56-
}
57-
} catch (err) {}
28+
let env;
5829

5930
// Nunjucks Engine
6031
const engine_nunjucks = {
@@ -153,6 +124,72 @@ const engine_nunjucks = {
153124
this.spawnFile(config, '_00-head.njk');
154125
this.spawnFile(config, '_01-foot.njk');
155126
},
127+
128+
/**
129+
* Accept a Pattern Lab config object from the core and use the settings to
130+
* load helpers.
131+
*
132+
* @param {object} config - the global config object from core
133+
*/
134+
usePatternLabConfig: function(config) {
135+
// Create Pattern Loader
136+
// Since Pattern Lab includes are not path based we need a custom loader for Nunjucks.
137+
function PatternLoader() {}
138+
139+
PatternLoader.prototype.getSource = function(name) {
140+
const fullPath = path.resolve(
141+
config.paths.source.patterns,
142+
partialRegistry[name]
143+
);
144+
return {
145+
src: fs.readFileSync(fullPath, 'utf-8'),
146+
path: fullPath,
147+
noCache: true,
148+
};
149+
};
150+
151+
env = new nunjucks.Environment(new PatternLoader());
152+
153+
let extensions;
154+
155+
try {
156+
// Look for helpers in the config
157+
extensions = config.engines.nunjucks.extend;
158+
159+
if (typeof extensions === 'string') {
160+
extensions = [extensions];
161+
}
162+
} catch (error) {
163+
// No defined path(s) found, look in default location
164+
165+
const configPath = 'patternlab-nunjucks-config.js';
166+
if (fs.existsSync(path.join(process.cwd(), configPath))) {
167+
extensions = [configPath];
168+
}
169+
}
170+
171+
if (extensions) {
172+
extensions.forEach(extensionPath => {
173+
// Load any user Defined configurations
174+
const nunjucksConfigPath = path.join(process.cwd(), extensionPath);
175+
176+
try {
177+
const nunjucksConfig = require(nunjucksConfigPath);
178+
if (typeof nunjucksConfig === 'function') {
179+
nunjucksConfig(env);
180+
} else {
181+
console.error(
182+
`Failed to load Nunjucks extension: Expected ${extensionPath} to export a function.`
183+
);
184+
}
185+
} catch (err) {
186+
console.error(
187+
`Failed to load Nunjucks extension ${nunjucksConfigPath}.`
188+
);
189+
}
190+
});
191+
}
192+
},
156193
};
157194

158195
module.exports = engine_nunjucks;

0 commit comments

Comments
 (0)