Skip to content

Commit b0cf524

Browse files
committed
Proposed solution to avoid require cache providing unpredictable module instances/versions when using remote templates in various scenarios. #6
1 parent 2b66db7 commit b0cf524

File tree

10 files changed

+210
-189
lines changed

10 files changed

+210
-189
lines changed

lib/generator.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ const registerHelpers = config => new Promise((resolve, reject) => {
204204
walker.on('file', async (root, stats, next) => {
205205
try {
206206
const file_path = path.resolve(config.templates, path.resolve(root, stats.name));
207-
require(file_path);
207+
// If it's a module constructor, inject dependencies to ensure consistent usage in remote templates in other projects or plain directories.
208+
const mod = require(file_path);
209+
if(typeof mod == "function") mod(_, Handlebars);
208210
next();
209211
} catch (e) {
210212
reject(e);

templates/express/.helpers/all.js

Lines changed: 128 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,129 @@
1-
const _ = require('lodash');
2-
const Handlebars = require('handlebars');
3-
4-
/**
5-
* Compares two values.
6-
*/
7-
Handlebars.registerHelper('equal', (lvalue, rvalue, options) => {
8-
if (arguments.length < 3)
9-
throw new Error('Handlebars Helper equal needs 2 parameters');
10-
if (lvalue!=rvalue) {
11-
return options.inverse(this);
12-
}
13-
14-
return options.fn(this);
15-
});
16-
17-
/**
18-
* Checks if a string ends with a provided value.
19-
*/
20-
Handlebars.registerHelper('endsWith', (lvalue, rvalue, options) => {
21-
if (arguments.length < 3)
22-
throw new Error('Handlebars Helper equal needs 2 parameters');
23-
if (lvalue.lastIndexOf(rvalue) !== lvalue.length-1 || lvalue.length-1 < 0) {
24-
return options.inverse(this);
25-
}
26-
return options.fn(this);
27-
});
28-
29-
/**
30-
* Checks if a method is a valid HTTP method.
31-
*/
32-
Handlebars.registerHelper('validMethod', (method, options) => {
33-
const authorized_methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND'];
34-
35-
if (arguments.length < 3)
36-
throw new Error('Handlebars Helper validMethod needs 1 parameter');
37-
if (authorized_methods.indexOf(method.toUpperCase()) === -1) {
38-
return options.inverse(this);
39-
}
40-
41-
return options.fn(this);
42-
});
43-
44-
/**
45-
* Checks if a collection of responses contains no error responses.
46-
*/
47-
Handlebars.registerHelper('ifNoErrorResponses', (responses, options) => {
48-
const codes = responses ? Object.keys(responses) : [];
49-
if (codes.find(code => Number(code) >= 400)) return options.inverse(this);
50-
51-
return options.fn(this);
52-
});
53-
54-
/**
55-
* Checks if a collection of responses contains no success responses.
56-
*/
57-
Handlebars.registerHelper('ifNoSuccessResponses', (responses, options) => {
58-
const codes = responses ? Object.keys(responses) : [];
59-
if (codes.find(code => Number(code) >= 200 && Number(code) < 300)) return options.inverse(this);
60-
61-
return options.fn(this);
62-
});
63-
64-
/**
65-
* Checks if a string matches a RegExp.
66-
*/
67-
Handlebars.registerHelper('match', (lvalue, rvalue, options) => {
68-
if (arguments.length < 3)
69-
throw new Error('Handlebars Helper match needs 2 parameters');
70-
if (!lvalue.match(rvalue)) {
71-
return options.inverse(this);
72-
}
73-
74-
return options.fn(this);
75-
});
76-
77-
/**
78-
* Provides different ways to compare two values (i.e. equal, greater than, different, etc.)
79-
*/
80-
Handlebars.registerHelper('compare', (lvalue, rvalue, options) => {
81-
if (arguments.length < 3) throw new Error('Handlebars Helper "compare" needs 2 parameters');
82-
83-
const operator = options.hash.operator || '==';
84-
const operators = {
85-
'==': (l,r) => { return l == r; },
86-
'===': (l,r) => { return l === r; },
87-
'!=': (l,r) => { return l != r; },
88-
'<': (l,r) => { return l < r; },
89-
'>': (l,r) => { return l > r; },
90-
'<=': (l,r) => { return l <= r; },
91-
'>=': (l,r) => { return l >= r; },
92-
typeof: (l,r) => { return typeof l == r; }
93-
};
94-
95-
if (!operators[operator]) throw new Error(`Handlebars Helper 'compare' doesn't know the operator ${operator}`);
96-
97-
const result = operators[operator](lvalue,rvalue);
98-
99-
if (result) {
1+
// Module constructor provides dependency injection from the generator instead of relying on require's cache here to ensure
2+
// the same instance of Handlebars gets the helpers installed and Lodash is definitiely available
3+
// regardless of where remote templates reside: in another Node project or a plain directory, which may have different or no modules available.
4+
module.exports = (_, Handlebars) =>{
5+
6+
/**
7+
* Compares two values.
8+
*/
9+
Handlebars.registerHelper('equal', (lvalue, rvalue, options) => {
10+
if (arguments.length < 3)
11+
throw new Error('Handlebars Helper equal needs 2 parameters');
12+
if (lvalue!=rvalue) {
13+
return options.inverse(this);
14+
}
15+
16+
return options.fn(this);
17+
});
18+
19+
/**
20+
* Checks if a string ends with a provided value.
21+
*/
22+
Handlebars.registerHelper('endsWith', (lvalue, rvalue, options) => {
23+
if (arguments.length < 3)
24+
throw new Error('Handlebars Helper equal needs 2 parameters');
25+
if (lvalue.lastIndexOf(rvalue) !== lvalue.length-1 || lvalue.length-1 < 0) {
26+
return options.inverse(this);
27+
}
28+
return options.fn(this);
29+
});
30+
31+
/**
32+
* Checks if a method is a valid HTTP method.
33+
*/
34+
Handlebars.registerHelper('validMethod', (method, options) => {
35+
const authorized_methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND'];
36+
37+
if (arguments.length < 3)
38+
throw new Error('Handlebars Helper validMethod needs 1 parameter');
39+
if (authorized_methods.indexOf(method.toUpperCase()) === -1) {
40+
return options.inverse(this);
41+
}
42+
10043
return options.fn(this);
101-
}
102-
103-
return options.inverse(this);
104-
});
105-
106-
/**
107-
* Capitalizes a string.
108-
*/
109-
Handlebars.registerHelper('capitalize', (str) => {
110-
return _.capitalize(str);
111-
});
112-
113-
/**
114-
* Converts a string to its camel-cased version.
115-
*/
116-
Handlebars.registerHelper('camelCase', (str) => {
117-
return _.camelCase(str);
118-
});
119-
120-
/**
121-
* Converts a multi-line string to a single line.
122-
*/
123-
Handlebars.registerHelper('inline', (str) => {
124-
return str ? str.replace(/\n/g, '') : '';
125-
});
44+
});
45+
46+
/**
47+
* Checks if a collection of responses contains no error responses.
48+
*/
49+
Handlebars.registerHelper('ifNoErrorResponses', (responses, options) => {
50+
const codes = responses ? Object.keys(responses) : [];
51+
if (codes.find(code => Number(code) >= 400)) return options.inverse(this);
52+
53+
return options.fn(this);
54+
});
55+
56+
/**
57+
* Checks if a collection of responses contains no success responses.
58+
*/
59+
Handlebars.registerHelper('ifNoSuccessResponses', (responses, options) => {
60+
const codes = responses ? Object.keys(responses) : [];
61+
if (codes.find(code => Number(code) >= 200 && Number(code) < 300)) return options.inverse(this);
62+
63+
return options.fn(this);
64+
});
65+
66+
/**
67+
* Checks if a string matches a RegExp.
68+
*/
69+
Handlebars.registerHelper('match', (lvalue, rvalue, options) => {
70+
if (arguments.length < 3)
71+
throw new Error('Handlebars Helper match needs 2 parameters');
72+
if (!lvalue.match(rvalue)) {
73+
return options.inverse(this);
74+
}
75+
76+
return options.fn(this);
77+
});
78+
79+
/**
80+
* Provides different ways to compare two values (i.e. equal, greater than, different, etc.)
81+
*/
82+
Handlebars.registerHelper('compare', (lvalue, rvalue, options) => {
83+
if (arguments.length < 3) throw new Error('Handlebars Helper "compare" needs 2 parameters');
84+
85+
const operator = options.hash.operator || '==';
86+
const operators = {
87+
'==': (l,r) => { return l == r; },
88+
'===': (l,r) => { return l === r; },
89+
'!=': (l,r) => { return l != r; },
90+
'<': (l,r) => { return l < r; },
91+
'>': (l,r) => { return l > r; },
92+
'<=': (l,r) => { return l <= r; },
93+
'>=': (l,r) => { return l >= r; },
94+
typeof: (l,r) => { return typeof l == r; }
95+
};
96+
97+
if (!operators[operator]) throw new Error(`Handlebars Helper 'compare' doesn't know the operator ${operator}`);
98+
99+
const result = operators[operator](lvalue,rvalue);
100+
101+
if (result) {
102+
return options.fn(this);
103+
}
104+
105+
return options.inverse(this);
106+
});
107+
108+
/**
109+
* Capitalizes a string.
110+
*/
111+
Handlebars.registerHelper('capitalize', (str) => {
112+
return _.capitalize(str);
113+
});
114+
115+
/**
116+
* Converts a string to its camel-cased version.
117+
*/
118+
Handlebars.registerHelper('camelCase', (str) => {
119+
return _.camelCase(str);
120+
});
121+
122+
/**
123+
* Converts a multi-line string to a single line.
124+
*/
125+
Handlebars.registerHelper('inline', (str) => {
126+
return str ? str.replace(/\n/g, '') : '';
127+
});
128+
129+
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
Handlebars.registerHelper('acceptedValues', items => {
4-
if (!items) return '<em>Any</em>';
3+
Handlebars.registerHelper('acceptedValues', items =>{
4+
if(!items) return '<em>Any</em>';
55

6-
return items.map(i => `<code>${i}</code>`).join(', ');
7-
});
6+
return items.map(i => `<code>${i}</code>`).join(', ');
7+
});
8+
9+
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
Handlebars.registerHelper('buildPath', (propName, path, key) => {
4-
if (!path) return propName;
5-
return `${path}.${propName}`;
6-
});
3+
Handlebars.registerHelper('buildPath', (propName, path, key) => {
4+
if (!path) return propName;
5+
return `${path}.${propName}`;
6+
});
7+
8+
}

templates/markdown/.helpers/equal.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
Handlebars.registerHelper('equal', (lvalue, rvalue, options) => {
4-
if (arguments.length < 3)
5-
throw new Error('Handlebars Helper equal needs 2 parameters');
6-
if (lvalue!==rvalue) {
7-
return options.inverse(this);
8-
}
3+
Handlebars.registerHelper('equal', (lvalue, rvalue, options) => {
4+
if (arguments.length < 3)
5+
throw new Error('Handlebars Helper equal needs 2 parameters');
6+
if (lvalue!==rvalue) {
7+
return options.inverse(this);
8+
}
9+
10+
return options.fn(this);
11+
});
912

10-
return options.fn(this);
11-
});
13+
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
Handlebars.registerHelper('isRequired', (obj, key) => {
4-
if (!obj || !obj.required) return false;
5-
return !!(obj.required.includes(key));
6-
});
3+
Handlebars.registerHelper('isRequired', (obj, key) => {
4+
if (!obj || !obj.required) return false;
5+
return !!(obj.required.includes(key));
6+
});
7+
8+
}
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
Handlebars.registerHelper('stringify', json => {
4-
try {
5-
return JSON.stringify(json || '', null, 2);
6-
} catch (e) {
7-
return '';
8-
}
9-
});
3+
Handlebars.registerHelper('stringify', json => {
4+
try {
5+
return JSON.stringify(json || '', null, 2);
6+
} catch (e) {
7+
return '';
8+
}
9+
});
10+
11+
}

templates/markdown/.helpers/tree.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
Handlebars.registerHelper('tree', path => {
4-
if (!path) return '';
5-
const filteredPaths = path.split('.').filter(Boolean);
6-
if (!filteredPaths.length) return;
7-
const dottedPath = filteredPaths.join('.');
8-
9-
return `${dottedPath}.`;
10-
});
3+
Handlebars.registerHelper('tree', path => {
4+
if (!path) return '';
5+
const filteredPaths = path.split('.').filter(Boolean);
6+
if (!filteredPaths.length) return;
7+
const dottedPath = filteredPaths.join('.');
8+
9+
return `${dottedPath}.`;
10+
});
11+
12+
}
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
const Handlebars = require('handlebars');
1+
module.exports = (_, Handlebars) =>{
22

3-
/**
4-
* Uppercases a string.
5-
*/
6-
Handlebars.registerHelper('uppercase', (str) => {
7-
return str.toUpperCase();
8-
});
3+
/**
4+
* Uppercases a string.
5+
*/
6+
Handlebars.registerHelper('uppercase', (str) => {
7+
return str.toUpperCase();
8+
});
9+
10+
}

0 commit comments

Comments
 (0)