Skip to content

Commit c85bb16

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 fa8d794 commit c85bb16

File tree

5 files changed

+166
-90
lines changed

5 files changed

+166
-90
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;
@@ -454,7 +408,7 @@ DOMRenderer.prototype.insertEl = function insertEl (tagName) {
454408

455409
this.findParent();
456410

457-
this._assertParentLoaded();
411+
assert(this._parent, 'No parent loaded');
458412

459413
if (this._parent.void)
460414
throw new Error(
@@ -481,7 +435,6 @@ DOMRenderer.prototype.insertEl = function insertEl (tagName) {
481435
}
482436
};
483437

484-
485438
/**
486439
* Sets a property on the currently loaded target.
487440
*
@@ -493,11 +446,10 @@ DOMRenderer.prototype.insertEl = function insertEl (tagName) {
493446
* @return {undefined} undefined
494447
*/
495448
DOMRenderer.prototype.setProperty = function setProperty (name, value) {
496-
this._assertTargetLoaded();
449+
assert(this._target, 'No target loaded');
497450
this._target.element.style[name] = value;
498451
};
499452

500-
501453
/**
502454
* Sets the size of the currently loaded target.
503455
* Removes any explicit sizing constraints when passed in `false`
@@ -514,7 +466,7 @@ DOMRenderer.prototype.setProperty = function setProperty (name, value) {
514466
* @return {undefined} undefined
515467
*/
516468
DOMRenderer.prototype.setSize = function setSize (width, height) {
517-
this._assertTargetLoaded();
469+
assert(this._target, 'No target loaded');
518470

519471
this.setWidth(width);
520472
this.setHeight(height);
@@ -534,7 +486,7 @@ DOMRenderer.prototype.setSize = function setSize (width, height) {
534486
* @return {undefined} undefined
535487
*/
536488
DOMRenderer.prototype.setWidth = function setWidth(width) {
537-
this._assertTargetLoaded();
489+
assert(this._target, 'No target loaded');
538490

539491
var contentWrapper = this._target.content;
540492

@@ -567,7 +519,7 @@ DOMRenderer.prototype.setWidth = function setWidth(width) {
567519
* @return {undefined} undefined
568520
*/
569521
DOMRenderer.prototype.setHeight = function setHeight(height) {
570-
this._assertTargetLoaded();
522+
assert(this._target, 'No target loaded');
571523

572524
var contentWrapper = this._target.content;
573525

@@ -597,7 +549,7 @@ DOMRenderer.prototype.setHeight = function setHeight(height) {
597549
* @return {undefined} undefined
598550
*/
599551
DOMRenderer.prototype.setAttribute = function setAttribute(name, value) {
600-
this._assertTargetLoaded();
552+
assert(this._target, 'No target loaded');
601553
this._target.element.setAttribute(name, value);
602554
};
603555

@@ -611,7 +563,7 @@ DOMRenderer.prototype.setAttribute = function setAttribute(name, value) {
611563
* @return {undefined} undefined
612564
*/
613565
DOMRenderer.prototype.setContent = function setContent(content) {
614-
this._assertTargetLoaded();
566+
assert(this._target, 'No target loaded');
615567

616568
if (this._target.formElement) {
617569
this._target.element.value = content;
@@ -635,7 +587,6 @@ DOMRenderer.prototype.setContent = function setContent(content) {
635587
);
636588
};
637589

638-
639590
/**
640591
* Sets the passed in transform matrix (world space). Inverts the parent's world
641592
* transform.
@@ -647,11 +598,10 @@ DOMRenderer.prototype.setContent = function setContent(content) {
647598
* @return {undefined} undefined
648599
*/
649600
DOMRenderer.prototype.setMatrix = function setMatrix (transform) {
650-
this._assertTargetLoaded();
601+
assert(this._target, 'No target loaded');
651602
this._target.element.style[TRANSFORM] = this._stringifyMatrix(transform);
652603
};
653604

654-
655605
/**
656606
* Adds a class to the classList associated with the currently loaded target.
657607
*
@@ -662,11 +612,10 @@ DOMRenderer.prototype.setMatrix = function setMatrix (transform) {
662612
* @return {undefined} undefined
663613
*/
664614
DOMRenderer.prototype.addClass = function addClass(domClass) {
665-
this._assertTargetLoaded();
615+
assert(this._target, 'No target loaded');
666616
this._target.element.classList.add(domClass);
667617
};
668618

669-
670619
/**
671620
* Removes a class from the classList associated with the currently loaded
672621
* target.
@@ -678,11 +627,10 @@ DOMRenderer.prototype.addClass = function addClass(domClass) {
678627
* @return {undefined} undefined
679628
*/
680629
DOMRenderer.prototype.removeClass = function removeClass(domClass) {
681-
this._assertTargetLoaded();
630+
assert(this._target, 'No target loaded');
682631
this._target.element.classList.remove(domClass);
683632
};
684633

685-
686634
/**
687635
* Stringifies the passed in matrix for setting the `transform` property.
688636
*

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)