| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- ;(function () {
- 'use strict';
- /*jshint laxbreak: true, browser:true */
- /*global define*/
- var esprima
- , exportFn
- , toString = Object.prototype.toString
- ;
- if (typeof module === 'object' && typeof module.exports === 'object' && typeof require === 'function') {
- // server side
- esprima = require('esprima');
- exportFn = function (redeyed) { module.exports = redeyed; };
- bootstrap(esprima, exportFn);
- } else if (typeof define === 'function' && define.amd) {
- // client side
- // amd
- define(['esprima'], function (esprima) {
- return bootstrap(esprima);
- });
- } else if (typeof window === 'object') {
- // no amd -> attach to window if it exists
- // Note that this requires 'esprima' to be defined on the window, so that script has to be loaded first
- window.redeyed = bootstrap(window.esprima);
- }
- function bootstrap(esprima, exportFn) {
- function isFunction (obj) {
- return toString.call(obj) === '[object Function]';
- }
- function isString (obj) {
- return toString.call(obj) === '[object String]';
- }
- function isNumber (obj) {
- return toString.call(obj) === '[object Number]';
- }
- function isObject (obj) {
- return toString.call(obj) === '[object Object]';
- }
- function surroundWith (before, after) {
- return function (s) { return before + s + after; };
- }
- function isNonCircular(key) {
- return key !== '_parent';
- }
- function objectizeString (value) {
- var vals = value.split(':');
- if (0 === vals.length || vals.length > 2)
- throw new Error(
- 'illegal string config: ' + value +
- '\nShould be of format "before:after"'
- );
- if (vals.length === 1 || vals[1].length === 0) {
- return vals.indexOf(':') < 0 ? { _before: vals[0] } : { _after: vals[0] };
- } else {
- return { _before: vals[0], _after: vals[1] };
- }
- }
- function objectize (node) {
- // Converts 'bef:aft' to { _before: bef, _after: aft }
- // and resolves undefined before/after from parent or root
- function resolve (value, key) {
- // resolve before/after from root or parent if it isn't present on the current node
- if (!value._parent) return undefined;
-
- // Immediate parent
- if (value._parent._default && value._parent._default[key]) return value._parent._default[key];
- // Root
- var root = value._parent._parent;
- if (!root) return undefined;
- return root._default ? root._default[key] : undefined;
- }
- function process (key) {
- var value = node[key];
- if (!value) return;
- if (isFunction(value)) return;
- // normalize all strings to objects
- if (isString(value)) {
- node[key] = value = objectizeString(value);
- }
-
- value._parent = node;
- if (isObject(value)) {
- if (!value._before && !value._after) return objectize (value);
- // resolve missing _before or _after from parent(s)
- // in case we only have either one on this node
- value._before = value._before || resolve(value, '_before');
- value._after = value._after || resolve(value, '_after');
-
- return;
- }
- throw new Error('nodes need to be either {String}, {Object} or {Function}.' + value + ' is neither.');
- }
- // Process _default ones first so children can resolve missing before/after from them
- if (node._default) process('_default');
- Object.keys(node)
- .filter(function (key) {
- return isNonCircular(key)
- && node.hasOwnProperty(key)
- && key !== '_before'
- && key !== '_after'
- && key !== '_default';
- })
- .forEach(process);
- }
- function functionize (node) {
- Object.keys(node)
- .filter(function (key) {
- return isNonCircular(key) && node.hasOwnProperty(key);
- })
- .forEach(function (key) {
- var value = node[key];
- if (isFunction(value)) return;
- if (isObject(value)) {
- if (!value._before && !value._after) return functionize(value);
- // at this point before/after were "inherited" from the parent or root
- // (see objectize)
- var before = value._before || '';
- var after = value._after || '';
- node[key] = surroundWith (before, after);
- return node[key];
- }
- });
- }
- function normalize (root) {
- objectize(root);
- functionize(root);
- }
- function mergeTokensAndComments(tokens, comments) {
- var all = {};
- function addToAllByRangeStart(t) { all[ t.range[0] ] = t; }
- tokens.forEach(addToAllByRangeStart);
- comments.forEach(addToAllByRangeStart);
- // keys are sorted automatically
- return Object.keys(all)
- .map(function (k) { return all[k]; });
- }
- function redeyed (code, config, opts) {
- opts = opts || {};
- var parser = opts.parser || esprima;
- var buildAst = !!opts.buildAst;
- var hashbang = ''
- , ast
- , tokens
- , comments
- , lastSplitEnd = 0
- , splits = []
- , transformedCode
- , all
- , info
- ;
- // Replace hashbang line with empty whitespaces to preserve token locations
- if (code[0] === '#' && code[1] === '!') {
- hashbang = code.substr(0, code.indexOf('\n') + 1);
- code = Array.apply(0, Array(hashbang.length)).join(' ') + '\n' + code.substr(hashbang.length);
- }
- if (buildAst) {
- ast = parser.parse(code, { tokens: true, comment: true, range: true, tolerant: true });
- tokens = ast.tokens;
- comments = ast.comments;
- } else {
- tokens = [];
- comments = [];
- parser.tokenize(code, { range: true, comment: true }, function (token) {
- if (token.type === 'LineComment') {
- token.type = 'Line';
- comments.push(token)
- } else if (token.type === 'BlockComment') {
- token.type = 'Block';
- comments.push(token)
- } else {
- // Optimistically upgrade 'static' to a keyword
- if (token.type === 'Identifier' && token.value === 'static') token.type = 'Keyword';
- tokens.push(token);
- }
- });
- }
- normalize(config);
- function tokenIndex(tokens, tkn, start) {
- var current
- , rangeStart = tkn.range[0];
- for (current = start; current < tokens.length; current++) {
- if (tokens[current].range[0] === rangeStart) return current;
- }
- throw new Error('Token %s not found at or after index: %d', tkn, start);
- }
- function process(surround) {
- var result
- , currentIndex
- , nextIndex
- , skip = 0
- , splitEnd
- ;
- result = surround(code.slice(start, end), info);
- if (isObject(result)) {
- splits.push(result.replacement);
- currentIndex = info.tokenIndex;
- nextIndex = tokenIndex(info.tokens, result.skipPastToken, currentIndex);
- skip = nextIndex - currentIndex;
- splitEnd = skip > 0 ? tokens[nextIndex - 1].range[1] : end;
- } else {
- splits.push(result);
- splitEnd = end;
- }
- return { skip: skip, splitEnd: splitEnd };
- }
- function addSplit (start, end, surround, info) {
- var result
- , nextIndex
- , skip = 0
- ;
- if (start >= end) return;
- if (surround) {
- result = process(surround);
- skip = result.skip;
- lastSplitEnd = result.splitEnd;
- } else {
- splits.push(code.slice(start, end));
- lastSplitEnd = end;
- }
- return skip;
- }
- all = mergeTokensAndComments(tokens, comments);
- for (var tokenIdx = 0; tokenIdx < all.length; tokenIdx++) {
- var token = all[tokenIdx]
- , surroundForType = config[token.type]
- , surround
- , start
- , end;
-
- // At least the type (e.g., 'Keyword') needs to be specified for the token to be surrounded
- if (surroundForType) {
- // root defaults are only taken into account while resolving before/after otherwise
- // a root default would apply to everything, even if no type default was specified
- surround = surroundForType
- && surroundForType.hasOwnProperty(token.value)
- && surroundForType[token.value]
- && isFunction(surroundForType[token.value])
- ? surroundForType[token.value]
- : surroundForType._default;
- start = token.range[0];
- end = token.range[1];
- addSplit(lastSplitEnd, start);
- info = { tokenIndex: tokenIdx, tokens: all, ast: ast, code: code };
- tokenIdx += addSplit(start, end, surround, info);
- }
- }
- if (lastSplitEnd < code.length) {
- addSplit(lastSplitEnd, code.length);
- }
- if (!opts.nojoin) {
- transformedCode = splits.join('');
- if (hashbang.length > 0) {
- transformedCode = hashbang + transformedCode.substr(hashbang.length);
- }
- }
- return {
- ast : ast
- , tokens : tokens
- , comments : comments
- , splits : splits
- , code : transformedCode
- };
- }
- return exportFn ? exportFn(redeyed) : redeyed;
- }
- })();
|