Skip to content

Commit 1cee97c

Browse files
feat: Add utilities#assert [#95469256]
DOMRenderer previously had methods for asserting state. Putting those methods on the prototype makes it impossible for the minifier to do any kind of dead-code elimination. This commit adds an assert utility function used in order to check if a certain condition is met. Checking for NODE_ENV and not including the passed in error message in the thrown error should be trivial and only needs to be done once in the more generic assert function.
1 parent b2017d9 commit 1cee97c

File tree

5 files changed

+162
-85
lines changed

5 files changed

+162
-85
lines changed

dom-renderers/DOMRenderer.js

Lines changed: 33 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var PathUtils = require('../core/Path');
3030
var vendorPrefix = require('../utilities/vendorPrefix');
3131
var CallbackStore = require('../utilities/CallbackStore');
3232
var eventMap = require('./events/EventMap');
33+
var assert = require('../utilities/assert');
3334

3435
var TRANSFORM = null;
3536

@@ -93,7 +94,6 @@ function DOMRenderer (element, selector, compositor) {
9394
this._lastEv = null;
9495
}
9596

96-
9797
/**
9898
* Attaches an EventListener to the element associated with the passed in path.
9999
* Prevents the default browser action on all subsequent events if
@@ -110,11 +110,26 @@ function DOMRenderer (element, selector, compositor) {
110110
* @return {undefined} undefined
111111
*/
112112
DOMRenderer.prototype.subscribe = function subscribe(type) {
113-
this._assertTargetLoaded();
113+
assert(this._target, 'No target loaded');
114114
this._listen(type);
115115
this._target.subscribe[type] = true;
116116
};
117117

118+
/**
119+
* Unsubscribes from all events that are of the specified type.
120+
*
121+
* @method
122+
*
123+
* @param {String} type Event type to unsubscribe from.
124+
* @return {undefined} undefined
125+
*/
126+
DOMRenderer.prototype.unsubscribe = function unsubscribe(type) {
127+
assert(this._target, 'No target loaded');
128+
129+
this._listen(type);
130+
this._target.subscribe[type] = false;
131+
};
132+
118133
/**
119134
* Used to preventDefault if an event of the specified type is being emitted on
120135
* the currently loaded target.
@@ -125,7 +140,8 @@ DOMRenderer.prototype.subscribe = function subscribe(type) {
125140
* @return {undefined} undefined
126141
*/
127142
DOMRenderer.prototype.preventDefault = function preventDefault(type) {
128-
this._assertTargetLoaded();
143+
assert(this._target, 'No target loaded');
144+
129145
this._listen(type);
130146
this._target.preventDefault[type] = true;
131147
};
@@ -142,7 +158,8 @@ DOMRenderer.prototype.preventDefault = function preventDefault(type) {
142158
* @return {undefined} undefined
143159
*/
144160
DOMRenderer.prototype.allowDefault = function allowDefault(type) {
145-
this._assertTargetLoaded();
161+
assert(this._target, 'No target loaded');
162+
146163
this._listen(type);
147164
this._target.preventDefault[type] = false;
148165
};
@@ -163,7 +180,7 @@ DOMRenderer.prototype.allowDefault = function allowDefault(type) {
163180
* @return {undefined} undefined
164181
*/
165182
DOMRenderer.prototype._listen = function _listen(type) {
166-
this._assertTargetLoaded();
183+
assert(this._target, 'No target loaded');
167184

168185
if (
169186
!this._target.listeners[type] && !this._root.listeners[type]
@@ -175,19 +192,6 @@ DOMRenderer.prototype._listen = function _listen(type) {
175192
}
176193
};
177194

178-
/**
179-
* Unsubscribes from all events that are of the specified type.
180-
*
181-
* @method
182-
*
183-
* @param {String} type DOM event type (e.g. click, mouseover).
184-
* @return {undefined} undefined
185-
*/
186-
DOMRenderer.prototype.unsubscribe = function unsubscribe(type) {
187-
this._assertTargetLoaded();
188-
this._target.subscribe[type] = false;
189-
};
190-
191195
/**
192196
* Function to be added using `addEventListener` to the corresponding
193197
* DOMElement.
@@ -236,7 +240,6 @@ DOMRenderer.prototype._triggerEvent = function _triggerEvent(ev) {
236240
}
237241
};
238242

239-
240243
/**
241244
* getSizeOf gets the dom size of a particular DOM element. This is
242245
* needed for render sizing in the scene graph.
@@ -308,55 +311,6 @@ DOMRenderer.prototype.draw = function draw(renderState) {
308311
}
309312
};
310313

311-
312-
/**
313-
* Internal helper function used for ensuring that a path is currently loaded.
314-
*
315-
* @method
316-
* @private
317-
*
318-
* @return {undefined} undefined
319-
*/
320-
DOMRenderer.prototype._assertPathLoaded = function _asserPathLoaded() {
321-
if (!this._path) throw new Error('path not loaded');
322-
};
323-
324-
/**
325-
* Internal helper function used for ensuring that a parent is currently loaded.
326-
*
327-
* @method
328-
* @private
329-
*
330-
* @return {undefined} undefined
331-
*/
332-
DOMRenderer.prototype._assertParentLoaded = function _assertParentLoaded() {
333-
if (!this._parent) throw new Error('parent not loaded');
334-
};
335-
336-
/**
337-
* Internal helper function used for ensuring that children are currently
338-
* loaded.
339-
*
340-
* @method
341-
* @private
342-
*
343-
* @return {undefined} undefined
344-
*/
345-
DOMRenderer.prototype._assertChildrenLoaded = function _assertChildrenLoaded() {
346-
if (!this._children) throw new Error('children not loaded');
347-
};
348-
349-
/**
350-
* Internal helper function used for ensuring that a target is currently loaded.
351-
*
352-
* @method _assertTargetLoaded
353-
*
354-
* @return {undefined} undefined
355-
*/
356-
DOMRenderer.prototype._assertTargetLoaded = function _assertTargetLoaded() {
357-
if (!this._target) throw new Error('No target loaded');
358-
};
359-
360314
/**
361315
* Finds and sets the parent of the currently loaded element (path).
362316
*
@@ -366,7 +320,7 @@ DOMRenderer.prototype._assertTargetLoaded = function _assertTargetLoaded() {
366320
* @return {ElementCache} Parent element.
367321
*/
368322
DOMRenderer.prototype.findParent = function findParent () {
369-
this._assertPathLoaded();
323+
assert(this._path, 'No path loaded');
370324

371325
var path = this._path;
372326
var parent;
@@ -453,7 +407,7 @@ DOMRenderer.prototype.insertEl = function insertEl (tagName) {
453407

454408
this.findParent();
455409

456-
this._assertParentLoaded();
410+
assert(this._parent, 'No parent loaded');
457411

458412
if (this._parent.void)
459413
throw new Error(
@@ -475,7 +429,6 @@ DOMRenderer.prototype.insertEl = function insertEl (tagName) {
475429

476430
};
477431

478-
479432
/**
480433
* Sets a property on the currently loaded target.
481434
*
@@ -487,11 +440,10 @@ DOMRenderer.prototype.insertEl = function insertEl (tagName) {
487440
* @return {undefined} undefined
488441
*/
489442
DOMRenderer.prototype.setProperty = function setProperty (name, value) {
490-
this._assertTargetLoaded();
443+
assert(this._target, 'No target loaded');
491444
this._target.element.style[name] = value;
492445
};
493446

494-
495447
/**
496448
* Sets the size of the currently loaded target.
497449
* Removes any explicit sizing constraints when passed in `false`
@@ -508,7 +460,7 @@ DOMRenderer.prototype.setProperty = function setProperty (name, value) {
508460
* @return {undefined} undefined
509461
*/
510462
DOMRenderer.prototype.setSize = function setSize (width, height) {
511-
this._assertTargetLoaded();
463+
assert(this._target, 'No target loaded');
512464

513465
this.setWidth(width);
514466
this.setHeight(height);
@@ -528,7 +480,7 @@ DOMRenderer.prototype.setSize = function setSize (width, height) {
528480
* @return {undefined} undefined
529481
*/
530482
DOMRenderer.prototype.setWidth = function setWidth(width) {
531-
this._assertTargetLoaded();
483+
assert(this._target, 'No target loaded');
532484

533485
var contentWrapper = this._target.content;
534486

@@ -561,7 +513,7 @@ DOMRenderer.prototype.setWidth = function setWidth(width) {
561513
* @return {undefined} undefined
562514
*/
563515
DOMRenderer.prototype.setHeight = function setHeight(height) {
564-
this._assertTargetLoaded();
516+
assert(this._target, 'No target loaded');
565517

566518
var contentWrapper = this._target.content;
567519

@@ -591,7 +543,7 @@ DOMRenderer.prototype.setHeight = function setHeight(height) {
591543
* @return {undefined} undefined
592544
*/
593545
DOMRenderer.prototype.setAttribute = function setAttribute(name, value) {
594-
this._assertTargetLoaded();
546+
assert(this._target, 'No target loaded');
595547
this._target.element.setAttribute(name, value);
596548
};
597549

@@ -605,7 +557,7 @@ DOMRenderer.prototype.setAttribute = function setAttribute(name, value) {
605557
* @return {undefined} undefined
606558
*/
607559
DOMRenderer.prototype.setContent = function setContent(content) {
608-
this._assertTargetLoaded();
560+
assert(this._target, 'No target loaded');
609561

610562
if (this._target.formElement) {
611563
this._target.element.value = content;
@@ -629,7 +581,6 @@ DOMRenderer.prototype.setContent = function setContent(content) {
629581
);
630582
};
631583

632-
633584
/**
634585
* Sets the passed in transform matrix (world space). Inverts the parent's world
635586
* transform.
@@ -641,11 +592,10 @@ DOMRenderer.prototype.setContent = function setContent(content) {
641592
* @return {undefined} undefined
642593
*/
643594
DOMRenderer.prototype.setMatrix = function setMatrix (transform) {
644-
this._assertTargetLoaded();
595+
assert(this._target, 'No target loaded');
645596
this._target.element.style[TRANSFORM] = this._stringifyMatrix(transform);
646597
};
647598

648-
649599
/**
650600
* Adds a class to the classList associated with the currently loaded target.
651601
*
@@ -656,11 +606,10 @@ DOMRenderer.prototype.setMatrix = function setMatrix (transform) {
656606
* @return {undefined} undefined
657607
*/
658608
DOMRenderer.prototype.addClass = function addClass(domClass) {
659-
this._assertTargetLoaded();
609+
assert(this._target, 'No target loaded');
660610
this._target.element.classList.add(domClass);
661611
};
662612

663-
664613
/**
665614
* Removes a class from the classList associated with the currently loaded
666615
* target.
@@ -672,11 +621,10 @@ DOMRenderer.prototype.addClass = function addClass(domClass) {
672621
* @return {undefined} undefined
673622
*/
674623
DOMRenderer.prototype.removeClass = function removeClass(domClass) {
675-
this._assertTargetLoaded();
624+
assert(this._target, 'No target loaded');
676625
this._target.element.classList.remove(domClass);
677626
};
678627

679-
680628
/**
681629
* Stringifies the passed in matrix for setting the `transform` property.
682630
*

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,18 @@
4646
"tap-closer": "^1.0.0",
4747
"tape": "^4.0.0",
4848
"tap-spec": "^4.0.0",
49+
"escodegen": "^1.6.1",
50+
"esprima": "^2.2.0",
51+
"replace-method": "0.0.0",
52+
"through": "^2.3.7",
4953
"uglify-js": "^2.4.17"
5054
},
5155
"dependencies": {
5256
"glslify": "^2.0.0"
5357
},
5458
"browserify": {
5559
"transform": [
60+
["./scripts/optimize-assertions", { "stripErrors": false, "preCheck": true }],
5661
"glslify"
5762
]
5863
}

scripts/optimize-assertions.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
var replaceMethod = require('replace-method');
2+
var codegen = require('escodegen');
3+
var esprima = require('esprima');
4+
var through = require('through');
5+
6+
/**
7+
* Browserify transform used for transforming `assert()` function calls.
8+
*
9+
* @param {String} file Original filename of the current source
10+
* file.
11+
* @param {Object} options Configuration object.
12+
* @param {Boolean} [stripErrors=true] Boolean value indicating if the error
13+
* messages should be excluded from the
14+
* bundle.
15+
* @param {Boolean} [preCheck=true] Avoid `assert` function call.
16+
* @return {Stream} `through` stream.
17+
*/
18+
var optimizeAssertionsTransform = function(file, options) {
19+
options = options || {};
20+
21+
if (options.stripErrors == null)
22+
options.stripErrors = true;
23+
24+
if (options.preCheck == null)
25+
options.preCheck = true;
26+
27+
var stream = through(write, end);
28+
var source = '';
29+
30+
function write(chunk) {
31+
source += chunk;
32+
}
33+
34+
function end() {
35+
var ast;
36+
37+
try {
38+
ast = esprima.parse(source);
39+
}
40+
catch (e) {
41+
return stream.emit('error', e);
42+
}
43+
44+
var src = replaceMethod(ast);
45+
src.replace(['assert'], function(node) {
46+
if (options.stripErrors) {
47+
node.arguments.length = 1;
48+
}
49+
50+
if (options.preCheck) {
51+
node = {
52+
'type': 'IfStatement',
53+
'test': {
54+
'type': 'UnaryExpression',
55+
'operator': '!',
56+
'argument': node.arguments[0],
57+
'prefix': true
58+
},
59+
'consequent': node,
60+
'alternate': null
61+
};
62+
}
63+
64+
return node;
65+
});
66+
67+
var code;
68+
69+
try {
70+
code = codegen.generate(ast);
71+
}
72+
catch (e) {
73+
return stream.emit('error', e);
74+
}
75+
76+
stream.queue(code);
77+
stream.queue(null);
78+
}
79+
80+
return stream;
81+
};
82+
83+
module.exports = optimizeAssertionsTransform;

0 commit comments

Comments
 (0)