123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- ;(function() {
- 'use strict'
- /* global define */
- var esprima
- var exportFn
- var 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 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 (vals.length === 0 || 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 jsx = !!opts.jsx
- // tokenizer doesn't support JSX at this point (esprima@4.0.0)
- // therefore we need to generate the AST via the parser not only to
- // avoid the tokenizer from erroring but also to get JSXIdentifier tokens
- var buildAst = jsx || !!opts.buildAst
- var hashbang = ''
- var ast
- var tokens
- var comments
- var lastSplitEnd = 0
- var splits = []
- var transformedCode
- var all
- var 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, loc: true, tolerant: true, jsx: true })
- tokens = ast.tokens
- comments = ast.comments
- } else {
- tokens = []
- comments = []
- parser.tokenize(code, { range: true, loc: 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
- var 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
- var currentIndex
- var nextIndex
- var skip = 0
- var 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
- var 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]
- var surroundForType = config[token.type]
- var surround
- var start
- var 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
- }
- })()
|