grunt-zuckrig-closure
Version:
Reduce a verbose syntax for Google Closure Compiler to be more Pythonic/Rubistic.
205 lines (178 loc) • 6.34 kB
JavaScript
var ANNO, ConstructorHook,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
require('sugar');
ANNO = {
CTOR: '@constructor',
EXTENDS: '@extends'
};
ConstructorHook = (function() {
function ConstructorHook(_extractor, _builder) {
this._extractor = _extractor;
this._builder = _builder;
this._super_class = null;
this._required = [];
this._produce_pos = -1;
this._to_inject = {};
this._class_name = null;
}
/**
Injects a constructor annotation. If the annotation is provided does nothing.
Also injects goog.require and @extends for a super class, if it's necessary.
*/
ConstructorHook.prototype.fix = function(tokens) {
var i, k, tok, v, _i, _len, _ref;
for (i = _i = 0, _len = tokens.length; _i < _len; i = ++_i) {
tok = tokens[i];
this._try_find_super_class(tok, i, tokens);
this._find_constructor(tok, i, tokens);
this._find_produce_position(tok, i, tokens);
this._find_required(tok, i, tokens);
this._find_class_name(tok, i, tokens);
}
_ref = this._to_inject;
for (k in _ref) {
v = _ref[k];
this._inject_annotations(k, v, tokens);
}
this._inject_class_provide(tokens);
return this._inject_require_super(tokens);
};
ConstructorHook.prototype._inject_annotations = function(i, comment, tokens) {
var begin, end, extends_pos;
if (comment.type === 'Block') {
if (comment.value.indexOf(ANNO.CTOR) === -1) {
extends_pos = comment.value.indexOf(" " + ANNO.EXTENDS);
if (extends_pos === -1) {
comment.value += " " + ANNO.CTOR + "\n ";
if (this._super_class != null) {
return comment.value += "" + (this._build_extends_anno()) + " ";
}
} else {
begin = comment.value.substring(0, extends_pos);
end = comment.value.substr(extends_pos);
return comment.value = [begin, " " + ANNO.CTOR + "\n ", end].join('');
}
}
} else {
return this._inject_new_block(i, this._build_ctor_token(comment.loc), tokens);
}
};
ConstructorHook.prototype._build_ctor_token = function(loc) {
var k, tok, v;
tok = {
type: 'NewBlock',
value: "*\n " + ANNO.CTOR + "\n",
loc: {}
};
if (this._super_class != null) {
tok.value += " " + (this._build_extends_anno());
}
for (k in loc) {
v = loc[k];
tok.loc[k] = Object.clone(v, true);
}
if (tok.loc.start != null) {
tok.loc.start.column = 0;
tok.loc.start.line--;
}
return tok;
};
ConstructorHook.prototype._build_extends_anno = function() {
return " " + ANNO.EXTENDS + " {" + this._super_class + "}\n";
};
ConstructorHook.prototype._inject_new_block = function(i, tok, tokens) {
return tokens.splice(i, 0, tok);
};
ConstructorHook.prototype._is_constructor = function(token, next_sibling) {
return token.type === 'Keyword' && token.value === 'function' && next_sibling && next_sibling.type === 'Identifier' && next_sibling.value !== 'ctor';
};
ConstructorHook.prototype._find_constructor = function(tok, i, tokens) {
var next;
next = tokens[i + 1];
if (!this._is_constructor(tok, next)) {
return;
}
return this._to_inject[i] = tokens[i - 1];
};
ConstructorHook.prototype._is_extending = function(tok, next) {
return tok.type === 'Identifier' && tok.value === '__extends' && next.value === '(';
};
ConstructorHook.prototype._try_find_super_class = function(tok, i, tokens) {
var next;
next = tokens[i + 1];
if (this._is_extending(tok, next)) {
this._super_class = this._extractor.parse_super_class(i, tokens);
}
};
ConstructorHook.prototype._find_produce_position = function(tok, i, tokens) {
if (this._produce_pos !== -1) {
return;
}
if (tok.value === 'goog' && tokens[i + 1].value === '.' && tokens[i + 2].value === 'provide') {
return this._produce_pos = i + 7;
}
};
ConstructorHook.prototype._inject_require_super = function(tokens) {
var _ref;
if ((this._super_class != null) && this._produce_pos !== -1 && (_ref = this._super_class, __indexOf.call(this._required, _ref) < 0)) {
return this._insert_tokens(this._build_super_require(tokens[this._produce_pos].loc), tokens, this._produce_pos);
}
};
ConstructorHook.prototype._insert_tokens = function(to_insert, tokens, i) {
var tok, _i, _len, _results;
_results = [];
for (_i = 0, _len = to_insert.length; _i < _len; _i++) {
tok = to_insert[_i];
tokens.splice(i, 0, tok);
_results.push(i++);
}
return _results;
};
ConstructorHook.prototype._build_super_require = function(orig) {
var k, loc, v;
loc = {};
for (k in orig) {
v = orig[k];
loc[k] = Object.clone(v, true);
}
loc.start.line++;
loc.end.line = loc.start.line;
return this._builder.build_goog_call('require', "'" + this._super_class + "'", loc);
};
ConstructorHook.prototype._build_fake_location = function() {
return {
start: {
line: 0,
column: 0
},
end: {
line: 0,
column: 0
}
};
};
ConstructorHook.prototype._replace_escaped_chars = function(val) {
return val.replace(/\\|'/g, '');
};
ConstructorHook.prototype._find_required = function(tok, i, tokens) {
if (tok.value === 'goog' && tokens[i + 1].value === '.' && tokens[i + 2].value === 'require') {
return this._required.push(this._replace_escaped_chars(tokens[i + 4].value));
}
};
ConstructorHook.prototype._find_class_name = function(tok, i, tokens) {
if (this._class_name != null) {
return;
}
return this._class_name = this._extractor.parse_class_from_def(i, tokens);
};
ConstructorHook.prototype._inject_class_provide = function(tokens) {
var provide;
if (this._produce_pos === -1 && (this._class_name != null)) {
provide = this._builder.build_goog_call('provide', "'" + this._class_name + "'", this._build_fake_location());
this._insert_tokens(provide, tokens, 0);
return this._produce_pos = provide.length;
}
};
return ConstructorHook;
})();
module.exports = ConstructorHook;