Comparing version 0.3.0 to 0.4.0
{ | ||
"name": "redeyed", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "Takes JavaScript code, along with a config and returns the original code with tokens wrapped as configured.", | ||
@@ -8,3 +8,4 @@ "author": "Thorsten Lorenz <thlorenz@gmx.de> (thlorenz.com)", | ||
"scripts": { | ||
"test": "tap test/*.js" | ||
"test": "tap test/*.js", | ||
"demo": "cd examples/browser; open index.html" | ||
}, | ||
@@ -11,0 +12,0 @@ "repository": { |
@@ -7,3 +7,3 @@ # redeyed [![Build Status](https://secure.travis-ci.org/thlorenz/redeyed.png)](http://travis-ci.org/thlorenz/redeyed) | ||
[Red Eyed Tree Frog](http://allaboutfrogs.org/info/species/redeye.html) (*Agalychnis callidryas*) | ||
[Red Eyed Tree Frog](http://allaboutfrogs.org/info/species/redeye.html) *(Agalychnis callidryas)* | ||
@@ -14,2 +14,7 @@ ## What? | ||
## Where? | ||
- server side using nodejs | ||
- in the [browser](#browser-support) | ||
## What for? | ||
@@ -117,2 +122,23 @@ | ||
## Browser Support | ||
### AMD | ||
Ensure to include [esprima](https://github.com/ariya/esprima) as one of your dependencies | ||
```js | ||
define(['redeyed'], function (redeyed) { | ||
[ .. ] | ||
}); | ||
``` | ||
### Attached to global window object | ||
The `redeyed {Function}` will be exposed globally as `window.redeyed` - big surprise! | ||
```html | ||
<script type="text/javascript" src="https://raw.github.com/ariya/esprima/master/esprima.js"></script> | ||
<script type="text/javascript" src="path/to/redeyed.js"></script> | ||
``` | ||
## redeyed in the wild | ||
@@ -122,14 +148,8 @@ | ||
the terminal | ||
- [peacock](http://thlorenz.github.com/peacock/): JavaScript syntax highlighter that generates html that is compatible | ||
with pygments styles. | ||
## Changelog | ||
## Examples | ||
### 0.3 | ||
- passing more information into {Function} config | ||
- API change: returning {Object} with code, ast, comments and tokens attached instead of just a code {String} | ||
- comments support | ||
- `npm explore redeyed; npm demo` will let you try the [browser example](https://github.com/thlorenz/redeyed/tree/master/examples/browser) | ||
### 0.2 | ||
- upgrade to Esprima 1.0.0 | ||
### 0.1 | ||
- first working version |
360
redeyed.js
@@ -0,226 +1,246 @@ | ||
;(function () { | ||
'use strict'; | ||
/*jshint laxbreak: true */ | ||
/*jshint laxbreak: true, browser:true */ | ||
/*global define*/ | ||
var esprima = require('esprima') | ||
, util = require('util') | ||
, toString = Object.prototype.toString | ||
var esprima | ||
, exportFn | ||
, toString = Object.prototype.toString | ||
; | ||
function inspect (obj) { | ||
return util.inspect(obj, false, 5, true); | ||
} | ||
if (typeof define === 'function' && define.amd) { | ||
// client side | ||
// amd | ||
define(['esprima'], function (esprima) { | ||
return bootstrap(esprima); | ||
}); | ||
function isString (obj) { | ||
return toString.call(obj) == '[object String]'; | ||
} | ||
} 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 isNumber (obj) { | ||
return toString.call(obj) == '[object Number]'; | ||
} else 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); | ||
} | ||
function isObject (obj) { | ||
return toString.call(obj) == '[object Object]'; | ||
} | ||
function bootstrap(esprima, exportFn) { | ||
function isFunction (obj) { | ||
return toString.call(obj) == '[object Function]'; | ||
} | ||
function isFunction (obj) { | ||
return toString.call(obj) === '[object Function]'; | ||
} | ||
function surroundWith (before, after) { | ||
return function (s) { return before + s + after; }; | ||
} | ||
function isString (obj) { | ||
return toString.call(obj) === '[object String]'; | ||
} | ||
function isNonCircular(key) { | ||
return key !== '_parent'; | ||
} | ||
function isNumber (obj) { | ||
return toString.call(obj) === '[object Number]'; | ||
} | ||
function objectizeString (value) { | ||
var vals = value.split(':'); | ||
function isObject (obj) { | ||
return toString.call(obj) === '[object Object]'; | ||
} | ||
if (0 === vals.length || vals.length > 2) | ||
throw new Error( | ||
'illegal string config: ' + value + | ||
'\nShould be of format "before:after"' | ||
); | ||
function surroundWith (before, after) { | ||
return function (s) { return before + s + 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 isNonCircular(key) { | ||
return key !== '_parent'; | ||
} | ||
} | ||
function objectize (node) { | ||
function objectizeString (value) { | ||
var vals = value.split(':'); | ||
// Converts 'bef:aft' to { _before: bef, _after: aft } | ||
// and resolves undefined before/after from parent or root | ||
if (0 === vals.length || vals.length > 2) | ||
throw new Error( | ||
'illegal string config: ' + value + | ||
'\nShould be of format "before:after"' | ||
); | ||
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; | ||
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 process (key) { | ||
var value = node[key]; | ||
function objectize (node) { | ||
if (!value) return; | ||
if (isFunction(value)) return; | ||
// Converts 'bef:aft' to { _before: bef, _after: aft } | ||
// and resolves undefined before/after from parent or root | ||
// 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'); | ||
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; | ||
return; | ||
} | ||
// Immediate parent | ||
if (value._parent._default && value._parent._default[key]) return value._parent._default[key]; | ||
throw new Error('nodes need to be either {String}, {Object} or {Function}.' + value + ' is neither.'); | ||
} | ||
// Root | ||
var root = value._parent._parent; | ||
if (!root) return undefined; | ||
// Process _default ones first so children can resolve missing before/after from them | ||
if (node._default) process('_default'); | ||
return root._default ? root._default[key] : undefined; | ||
} | ||
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) { | ||
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); | ||
if (!value._before && !value._after) return functionize(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; | ||
} | ||
// at this point before/after were "inherited" from the parent or root | ||
// (see objectize) | ||
var before = value._before || ''; | ||
var after = value._after || ''; | ||
throw new Error('nodes need to be either {String}, {Object} or {Function}.' + value + ' is neither.'); | ||
} | ||
return node[key] = surroundWith (before, after); | ||
} | ||
}); | ||
} | ||
// Process _default ones first so children can resolve missing before/after from them | ||
if (node._default) process('_default'); | ||
function normalize (root) { | ||
objectize(root); | ||
functionize(root); | ||
} | ||
Object.keys(node) | ||
.filter(function (key) { | ||
return isNonCircular(key) | ||
&& node.hasOwnProperty(key) | ||
&& key !== '_before' | ||
&& key !== '_after' | ||
&& key !== '_default'; | ||
}) | ||
.forEach(process); | ||
} | ||
function mergeTokensAndComments(tokens, comments) { | ||
var all = {}; | ||
function functionize (node) { | ||
Object.keys(node) | ||
.filter(function (key) { | ||
return isNonCircular(key) && node.hasOwnProperty(key); | ||
}) | ||
.forEach(function (key) { | ||
var value = node[key]; | ||
function addToAllByRangeStart(t) { all[ t.range[0] ] = t; } | ||
if (isFunction(value)) return; | ||
tokens.forEach(addToAllByRangeStart); | ||
comments.forEach(addToAllByRangeStart); | ||
if (isObject(value)) { | ||
// keys are sorted automatically | ||
return Object.keys(all) | ||
.map(function (k) { return all[k]; }); | ||
} | ||
if (!value._before && !value._after) return functionize(value); | ||
function redeyed (code, config, opts) { | ||
opts = opts || {}; | ||
// at this point before/after were "inherited" from the parent or root | ||
// (see objectize) | ||
var before = value._before || ''; | ||
var after = value._after || ''; | ||
// remove shebang | ||
code = code.replace(/^\#\!.*/, ''); | ||
return node[key] = surroundWith (before, after); | ||
} | ||
}); | ||
} | ||
var ast = esprima.parse(code, { tokens: true, comment: true, range: true, tolerant: true }) | ||
, tokens = ast.tokens | ||
, comments = ast.comments | ||
, lastSplitEnd = 0 | ||
, splits = [] | ||
, transformedCode | ||
, all | ||
; | ||
function normalize (root) { | ||
objectize(root); | ||
functionize(root); | ||
} | ||
// console.log(inspect(tokens)); | ||
function mergeTokensAndComments(tokens, comments) { | ||
var all = {}; | ||
normalize(config); | ||
function addToAllByRangeStart(t) { all[ t.range[0] ] = t; } | ||
function addSplit (start, end, surround, tokenIdx, tokens) { | ||
if (start >= end) return; | ||
if (surround) | ||
splits.push(surround(code.slice(start, end), tokenIdx, tokens)); | ||
else | ||
splits.push(code.slice(start, end)); | ||
tokens.forEach(addToAllByRangeStart); | ||
comments.forEach(addToAllByRangeStart); | ||
lastSplitEnd = end; | ||
// keys are sorted automatically | ||
return Object.keys(all) | ||
.map(function (k) { return all[k]; }); | ||
} | ||
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) { | ||
function redeyed (code, config, opts) { | ||
opts = opts || {}; | ||
// 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; | ||
// remove shebang | ||
code = code.replace(/^\#\!.*/, ''); | ||
start = token.range[0]; | ||
end = token.range[1]; | ||
var ast = esprima.parse(code, { tokens: true, comment: true, range: true, tolerant: true }) | ||
, tokens = ast.tokens | ||
, comments = ast.comments | ||
, lastSplitEnd = 0 | ||
, splits = [] | ||
, transformedCode | ||
, all | ||
; | ||
addSplit(lastSplitEnd, start); | ||
addSplit(start, end, surround, tokenIdx, all); | ||
normalize(config); | ||
function addSplit (start, end, surround, tokenIdx, tokens) { | ||
if (start >= end) return; | ||
if (surround) | ||
splits.push(surround(code.slice(start, end), tokenIdx, tokens)); | ||
else | ||
splits.push(code.slice(start, end)); | ||
lastSplitEnd = end; | ||
} | ||
} | ||
if (lastSplitEnd < code.length) { | ||
addSplit(lastSplitEnd, code.length); | ||
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); | ||
addSplit(start, end, surround, tokenIdx, all); | ||
} | ||
} | ||
if (lastSplitEnd < code.length) { | ||
addSplit(lastSplitEnd, code.length); | ||
} | ||
transformedCode = opts.nojoin ? undefined : splits.join(''); | ||
return { | ||
ast : ast | ||
, tokens : tokens | ||
, comments : comments | ||
, splits : splits | ||
, code : transformedCode | ||
}; | ||
} | ||
transformedCode = opts.nojoin ? undefined : splits.join(''); | ||
return { | ||
ast : ast | ||
, tokens : tokens | ||
, comments : comments | ||
, splits : splits | ||
, code : transformedCode | ||
}; | ||
return exportFn ? exportFn(redeyed) : redeyed; | ||
} | ||
module.exports = redeyed; | ||
})(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
45045
24
1139
152