Skip to content

Commit 1338712

Browse files
committedMar 5, 2019
Emit 'load' events on Loader and Environment instances
fixes #1153
1 parent 057e7b3 commit 1338712

13 files changed

+140
-44
lines changed
 

‎CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changelog
22
=========
33

4+
* Emit 'load' events on `Environment` instances, to allow runtime dependency
5+
tracking. Fixes [#1153](https://github.com/mozilla/nunjucks/issues/1153).
6+
47
3.1.7 (Jan 12 2019)
58
------------------
69

‎docs/api.md

+17
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,23 @@ SafeString (to be documented) if one was passed in, so the output will
353353
copy the safeness of the input, but this property is helpful in rare
354354
circumstances.
355355
{% endapi %}
356+
357+
{% api %}
358+
'load' event
359+
env.on('load', function(name, source, loader))
360+
361+
The 'load' event gets emitted whenever a Loader retrieves the source of a
362+
template. It can be listened to in order to determine template dependencies
363+
at runtime. The arguments emitted to the callback are:
364+
365+
* **name** *(String)* The template name, as passed to the loader
366+
* **source** *(Object)* The object that gets returned from Loader.getSource
367+
* **src** *(String)* The template source
368+
* **path** *(String)* The full path to the template
369+
* **noCache** *(Bool)* If `true`, the template wasn't cached.
370+
* **loader** The Loader instance that triggered the event.
371+
{% endapi %}
372+
356373
{% raw %}
357374

358375
## Template

‎docs/fr/api.md

+16
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,22 @@ de rares circonstances.
344344
{% endapi %}
345345
{% raw %}
346346

347+
{% api %}
348+
'load' événement
349+
env.on('load', function(name, source, loader))
350+
351+
Quand une instance 'Loader' charge un template, elle émet l'événement
352+
'load'. On peut utiliser cette événement pour détérminer les dépendances
353+
à l'exécution. Ses arguments sont :
354+
355+
* **name** *(String)* Le nom du template
356+
* **source** *(Object)* Le resultat de l'appel de `Loader.getSource`
357+
* **src** *(String)* Le code source du template
358+
* **path** *(String)* Le chemin d'accèss du fichier ou l'URL
359+
* **noCache** *(Bool)* Si la valeur n'est pas mise en cache.
360+
* **loader** L'instance du Loader qui a émetté l'événement
361+
{% endapi %}
362+
347363
## Template
348364

349365
Un `Template` est un objet qui gère la compilation des chaînes de template

‎nunjucks/src/compiler.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const transformer = require('./transformer');
55
const nodes = require('./nodes');
66
const {TemplateError} = require('./lib');
77
const {Frame} = require('./runtime');
8-
const Obj = require('./object');
8+
const {Obj} = require('./object');
99

1010
// These are all the same for now, but shouldn't be passed straight
1111
// through

‎nunjucks/src/environment.js

+17-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const filters = require('./filters');
88
const {FileSystemLoader, WebLoader, PrecompiledLoader} = require('./loaders');
99
const tests = require('./tests');
1010
const globals = require('./globals');
11-
const Obj = require('./object');
11+
const {Obj, EmitterObj} = require('./object');
1212
const globalRuntime = require('./runtime');
1313
const {handleError, Frame} = globalRuntime;
1414
const expressApp = require('./express-app');
@@ -37,7 +37,7 @@ const noopTmplSrc = {
3737
}
3838
};
3939

40-
class Environment extends Obj {
40+
class Environment extends EmitterObj {
4141
init(loaders, opts) {
4242
// The dev flag determines the trace that'll be shown on errors.
4343
// If set to true, returns the full trace from the error point,
@@ -82,7 +82,7 @@ class Environment extends Obj {
8282
);
8383
}
8484

85-
this.initCache();
85+
this._initLoaders();
8686

8787
this.globals = globals();
8888
this.filters = {};
@@ -95,18 +95,28 @@ class Environment extends Obj {
9595
lib._entries(tests).forEach(([name, test]) => this.addTest(name, test));
9696
}
9797

98-
initCache() {
99-
// Caching and cache busting
98+
_initLoaders() {
10099
this.loaders.forEach((loader) => {
100+
// Caching and cache busting
101101
loader.cache = {};
102102
if (typeof loader.on === 'function') {
103-
loader.on('update', (template) => {
104-
loader.cache[template] = null;
103+
loader.on('update', (name, fullname) => {
104+
loader.cache[name] = null;
105+
this.emit('update', name, fullname, loader);
106+
});
107+
loader.on('load', (name, source) => {
108+
this.emit('load', name, source, loader);
105109
});
106110
}
107111
});
108112
}
109113

114+
invalidateCache() {
115+
this.loaders.forEach((loader) => {
116+
loader.cache = {};
117+
});
118+
}
119+
110120
addExtension(name, extension) {
111121
extension.__name = name;
112122
this.extensions[name] = extension;

‎nunjucks/src/loader.js

+2-16
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,9 @@
11
'use strict';
22

33
const path = require('path');
4-
const Obj = require('./object');
5-
6-
module.exports = class Loader extends Obj {
7-
on(name, func) {
8-
this.listeners = this.listeners || {};
9-
this.listeners[name] = this.listeners[name] || [];
10-
this.listeners[name].push(func);
11-
}
12-
13-
emit(name, ...args) {
14-
if (this.listeners && this.listeners[name]) {
15-
this.listeners[name].forEach((listener) => {
16-
listener(...args);
17-
});
18-
}
19-
}
4+
const {EmitterObj} = require('./object');
205

6+
module.exports = class Loader extends EmitterObj {
217
resolve(from, to) {
228
return path.resolve(path.dirname(from), to);
239
}

‎nunjucks/src/node-loaders.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class FileSystemLoader extends Loader {
4545
watcher.on('all', (event, fullname) => {
4646
fullname = path.resolve(fullname);
4747
if (event === 'change' && fullname in this.pathsToNames) {
48-
this.emit('update', this.pathsToNames[fullname]);
48+
this.emit('update', this.pathsToNames[fullname], fullname);
4949
}
5050
});
5151
watcher.on('error', (error) => {
@@ -76,11 +76,13 @@ class FileSystemLoader extends Loader {
7676

7777
this.pathsToNames[fullpath] = name;
7878

79-
return {
79+
const source = {
8080
src: fs.readFileSync(fullpath, 'utf-8'),
8181
path: fullpath,
8282
noCache: this.noCache
8383
};
84+
this.emit('load', name, source);
85+
return source;
8486
}
8587
}
8688

‎nunjucks/src/nodes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const Obj = require('./object');
3+
const {Obj} = require('./object');
44

55
function traverseAndCheck(obj, type, results) {
66
if (obj instanceof type) {

‎nunjucks/src/object.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
// A simple class system, more documentation to come
4+
const EventEmitter = require('events');
45
const lib = require('./lib');
56

67
function parentWrap(parent, prop) {
@@ -59,4 +60,26 @@ class Obj {
5960
}
6061
}
6162

62-
module.exports = Obj;
63+
class EmitterObj extends EventEmitter {
64+
constructor(...args) {
65+
super();
66+
// Unfortunately necessary for backwards compatibility
67+
this.init(...args);
68+
}
69+
70+
init() {}
71+
72+
get typename() {
73+
return this.constructor.name;
74+
}
75+
76+
static extend(name, props) {
77+
if (typeof name === 'object') {
78+
props = name;
79+
name = 'anonymous';
80+
}
81+
return extendClass(this, name, props);
82+
}
83+
}
84+
85+
module.exports = { Obj, EmitterObj };

‎nunjucks/src/parser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
var lexer = require('./lexer');
44
var nodes = require('./nodes');
5-
var Obj = require('./object');
5+
var Obj = require('./object').Obj;
66
var lib = require('./lib');
77

88
class Parser extends Obj {

‎nunjucks/src/web-loaders.js

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class WebLoader extends Loader {
4545
path: name,
4646
noCache: !useCache
4747
};
48+
this.emit('load', name, result);
4849
if (cb) {
4950
cb(null, result);
5051
}

‎tests/api.js

+9
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,14 @@
8282
path: path.resolve(templatesPath, 'string.njk')
8383
})).to.be('FooTest3BazFizzle');
8484
});
85+
86+
it('should emit "load" event on Environment instance', function(done) {
87+
var env = new Environment(new Loader(templatesPath));
88+
env.on('load', function(name, source) {
89+
expect(name).to.equal('item.njk');
90+
done();
91+
});
92+
env.render('item.njk', {});
93+
});
8594
});
8695
}());

‎tests/loader.js

+44-15
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,6 @@
2222
}
2323

2424
describe('loader', function() {
25-
it('should have default opts for WebLoader', function() {
26-
var webLoader = new WebLoader(templatesPath);
27-
expect(webLoader).to.be.a(WebLoader);
28-
expect(webLoader.useCache).to.be(false);
29-
expect(webLoader.async).to.be(false);
30-
});
31-
32-
if (typeof FileSystemLoader !== 'undefined') {
33-
it('should have default opts for FileSystemLoader', function() {
34-
var fileSystemLoader = new FileSystemLoader(templatesPath);
35-
expect(fileSystemLoader).to.be.a(FileSystemLoader);
36-
expect(fileSystemLoader.noCache).to.be(false);
37-
});
38-
}
39-
4025
it('should allow a simple loader to be created', function() {
4126
// From Docs: http://mozilla.github.io/nunjucks/api.html#writing-a-loader
4227
// We should be able to create a loader that only exposes getSource
@@ -82,5 +67,49 @@
8267
done();
8368
});
8469
});
70+
71+
describe('WebLoader', function() {
72+
it('should have default opts for WebLoader', function() {
73+
var webLoader = new WebLoader(templatesPath);
74+
expect(webLoader).to.be.a(WebLoader);
75+
expect(webLoader.useCache).to.be(false);
76+
expect(webLoader.async).to.be(false);
77+
});
78+
79+
it('should emit a "load" event', function(done) {
80+
var loader = new WebLoader(templatesPath);
81+
82+
if (typeof window === 'undefined') {
83+
this.skip();
84+
}
85+
86+
loader.on('load', function(name, source) {
87+
expect(name).to.equal('simple-base.njk');
88+
done();
89+
});
90+
91+
loader.getSource('simple-base.njk');
92+
});
93+
});
94+
95+
if (typeof FileSystemLoader !== 'undefined') {
96+
describe('FileSystemLoader', function() {
97+
it('should have default opts', function() {
98+
var loader = new FileSystemLoader(templatesPath);
99+
expect(loader).to.be.a(FileSystemLoader);
100+
expect(loader.noCache).to.be(false);
101+
});
102+
103+
it('should emit a "load" event', function(done) {
104+
var loader = new FileSystemLoader(templatesPath);
105+
loader.on('load', function(name, source) {
106+
expect(name).to.equal('simple-base.njk');
107+
done();
108+
});
109+
110+
loader.getSource('simple-base.njk');
111+
});
112+
});
113+
}
85114
});
86115
}());

0 commit comments

Comments
 (0)