blob: 3b123a2f49948d86049ada32ea54cb53b518abe0 [file] [log] [blame]
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.eslint=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports={
"builtin": {
"NaN": false,
"Infinity": false,
"undefined": false,
"eval": false,
"parseFloat": false,
"parseInt": false,
"isNaN": false,
"isFinite": false,
"decodeURI": false,
"decodeURIComponent": false,
"encodeURI": false,
"encodeURIComponent": false,
"Object": false,
"Function": false,
"Array": false,
"String": false,
"Boolean": false,
"Map": false,
"Number": false,
"Date": false,
"RegExp": false,
"Error": false,
"EvalError": false,
"RangeError": false,
"ReferenceError": false,
"Set": false,
"SyntaxError": false,
"TypeError": false,
"URIError": false,
"WeakMap": false,
"WeakSet": false,
"ArrayBuffer": false,
"Float32Array": false,
"Float64Array": false,
"Int16Array": false,
"Int32Array": false,
"Int8Array": false,
"Uint16Array": false,
"Uint32Array": false,
"Uint8Array": false,
"Uint8ClampedArray": false,
"Math": false,
"JSON": false,
"toString": false,
"hasOwnProperty": false,
"valueOf": false,
"propertyIsEnumerable": false,
"constructor": false,
"isPrototypeOf": false,
"toLocaleString": false
},
"browser": {
"globals": {
"addEventListener": false,
"applicationCache": false,
"atob": false,
"Audio": false,
"Blob": false,
"blur": false,
"btoa": false,
"cancelAnimationFrame": false,
"clearInterval": false,
"clearTimeout": false,
"close": false,
"closed": false,
"crypto": false,
"DataView": false,
"defaultStatus": false,
"devicePixelRatio": false,
"dispatchEvent": false,
"document": false,
"DOMParser": false,
"Element": false,
"FileReader": false,
"find": false,
"focus": false,
"FormData": false,
"frameElement": false,
"frames": false,
"getComputedStyle": false,
"getSelection": false,
"history": false,
"HTMLAnchorElement": false,
"HTMLBaseElement": false,
"HTMLBodyElement": false,
"HTMLBRElement": false,
"HTMLButtonElement": false,
"HTMLCanvasElement": false,
"HTMLDirectoryElement": false,
"HTMLDivElement": false,
"HTMLDListElement": false,
"HTMLElement": false,
"HTMLFieldSetElement": false,
"HTMLFontElement": false,
"HTMLFormElement": false,
"HTMLFrameElement": false,
"HTMLFrameSetElement": false,
"HTMLHeadElement": false,
"HTMLHeadingElement": false,
"HTMLHRElement": false,
"HTMLHtmlElement": false,
"HTMLIFrameElement": false,
"HTMLImageElement": false,
"HTMLInputElement": false,
"HTMLLabelElement": false,
"HTMLLegendElement": false,
"HTMLLIElement": false,
"HTMLLinkElement": false,
"HTMLMapElement": false,
"HTMLMenuElement": false,
"HTMLMetaElement": false,
"HTMLModElement": false,
"HTMLObjectElement": false,
"HTMLOListElement": false,
"HTMLOptGroupElement": false,
"HTMLOptionElement": false,
"HTMLParagraphElement": false,
"HTMLParamElement": false,
"HTMLPreElement": false,
"HTMLQuoteElement": false,
"HTMLScriptElement": false,
"HTMLSelectElement": false,
"HTMLStyleElement": false,
"HTMLTableCaptionElement": false,
"HTMLTableCellElement": false,
"HTMLTableColElement": false,
"HTMLTableElement": false,
"HTMLTableRowElement": false,
"HTMLTableSectionElement": false,
"HTMLTextAreaElement": false,
"HTMLTitleElement": false,
"HTMLUListElement": false,
"HTMLVideoElement": false,
"Image": false,
"indexedDB": false,
"innerHeight": false,
"innerWidth": false,
"Intl": false,
"length": false,
"localStorage": false,
"location": false,
"matchMedia": false,
"MessageChannel": false,
"MessageEvent": false,
"MessagePort": false,
"moveBy": false,
"moveTo": false,
"MutationObserver": false,
"name": false,
"navigator": false,
"Node": false,
"NodeFilter": false,
"onbeforeunload": true,
"onblur": true,
"onerror": true,
"onfocus": true,
"onload": true,
"onresize": true,
"onunload": true,
"open": false,
"openDatabase": false,
"opener": false,
"Option": false,
"outerHeight": false,
"outerWidth": false,
"pageXOffset": false,
"pageYOffset": false,
"parent": false,
"postMessage": false,
"print": false,
"removeEventListener": false,
"requestAnimationFrame": false,
"resizeBy": false,
"resizeTo": false,
"screen": false,
"screenX": false,
"screenY": false,
"scroll": false,
"scrollbars": false,
"scrollBy": false,
"scrollTo": false,
"scrollX": false,
"scrollY": false,
"self": false,
"sessionStorage": false,
"setInterval": false,
"setTimeout": false,
"SharedWorker": false,
"showModalDialog": false,
"stop": false,
"SVGAElement": false,
"SVGAltGlyphDefElement": false,
"SVGAltGlyphElement": false,
"SVGAltGlyphItemElement": false,
"SVGAngle": false,
"SVGAnimateColorElement": false,
"SVGAnimatedAngle": false,
"SVGAnimatedBoolean": false,
"SVGAnimatedEnumeration": false,
"SVGAnimatedInteger": false,
"SVGAnimatedLength": false,
"SVGAnimatedLengthList": false,
"SVGAnimatedNumber": false,
"SVGAnimatedNumberList": false,
"SVGAnimatedPreserveAspectRatio": false,
"SVGAnimatedRect": false,
"SVGAnimatedString": false,
"SVGAnimatedTransformList": false,
"SVGAnimateElement": false,
"SVGAnimateMotionElement": false,
"SVGAnimateTransformElement": false,
"SVGAnimationElement": false,
"SVGCircleElement": false,
"SVGClipPathElement": false,
"SVGColor": false,
"SVGComponentTransferFunctionElement": false,
"SVGCursorElement": false,
"SVGDefsElement": false,
"SVGDescElement": false,
"SVGDocument": false,
"SVGElement": false,
"SVGElementInstance": false,
"SVGElementInstanceList": false,
"SVGEllipseElement": false,
"SVGFEBlendElement": false,
"SVGFEColorMatrixElement": false,
"SVGFEComponentTransferElement": false,
"SVGFECompositeElement": false,
"SVGFEConvolveMatrixElement": false,
"SVGFEDiffuseLightingElement": false,
"SVGFEDisplacementMapElement": false,
"SVGFEDistantLightElement": false,
"SVGFEFloodElement": false,
"SVGFEFuncAElement": false,
"SVGFEFuncBElement": false,
"SVGFEFuncGElement": false,
"SVGFEFuncRElement": false,
"SVGFEGaussianBlurElement": false,
"SVGFEImageElement": false,
"SVGFEMergeElement": false,
"SVGFEMergeNodeElement": false,
"SVGFEMorphologyElement": false,
"SVGFEOffsetElement": false,
"SVGFEPointLightElement": false,
"SVGFESpecularLightingElement": false,
"SVGFESpotLightElement": false,
"SVGFETileElement": false,
"SVGFETurbulenceElement": false,
"SVGFilterElement": false,
"SVGFontElement": false,
"SVGFontFaceElement": false,
"SVGFontFaceFormatElement": false,
"SVGFontFaceNameElement": false,
"SVGFontFaceSrcElement": false,
"SVGFontFaceUriElement": false,
"SVGForeignObjectElement": false,
"SVGGElement": false,
"SVGGlyphElement": false,
"SVGGlyphRefElement": false,
"SVGGradientElement": false,
"SVGHKernElement": false,
"SVGImageElement": false,
"SVGLength": false,
"SVGLengthList": false,
"SVGLinearGradientElement": false,
"SVGLineElement": false,
"SVGMarkerElement": false,
"SVGMaskElement": false,
"SVGMatrix": false,
"SVGMetadataElement": false,
"SVGMissingGlyphElement": false,
"SVGMPathElement": false,
"SVGNumber": false,
"SVGNumberList": false,
"SVGPaint": false,
"SVGPathElement": false,
"SVGPathSeg": false,
"SVGPathSegArcAbs": false,
"SVGPathSegArcRel": false,
"SVGPathSegClosePath": false,
"SVGPathSegCurvetoCubicAbs": false,
"SVGPathSegCurvetoCubicRel": false,
"SVGPathSegCurvetoCubicSmoothAbs": false,
"SVGPathSegCurvetoCubicSmoothRel": false,
"SVGPathSegCurvetoQuadraticAbs": false,
"SVGPathSegCurvetoQuadraticRel": false,
"SVGPathSegCurvetoQuadraticSmoothAbs": false,
"SVGPathSegCurvetoQuadraticSmoothRel": false,
"SVGPathSegLinetoAbs": false,
"SVGPathSegLinetoHorizontalAbs": false,
"SVGPathSegLinetoHorizontalRel": false,
"SVGPathSegLinetoRel": false,
"SVGPathSegLinetoVerticalAbs": false,
"SVGPathSegLinetoVerticalRel": false,
"SVGPathSegList": false,
"SVGPathSegMovetoAbs": false,
"SVGPathSegMovetoRel": false,
"SVGPatternElement": false,
"SVGPoint": false,
"SVGPointList": false,
"SVGPolygonElement": false,
"SVGPolylineElement": false,
"SVGPreserveAspectRatio": false,
"SVGRadialGradientElement": false,
"SVGRect": false,
"SVGRectElement": false,
"SVGRenderingIntent": false,
"SVGScriptElement": false,
"SVGSetElement": false,
"SVGStopElement": false,
"SVGStringList": false,
"SVGStyleElement": false,
"SVGSVGElement": false,
"SVGSwitchElement": false,
"SVGSymbolElement": false,
"SVGTextContentElement": false,
"SVGTextElement": false,
"SVGTextPathElement": false,
"SVGTextPositioningElement": false,
"SVGTitleElement": false,
"SVGTransform": false,
"SVGTransformList": false,
"SVGTRefElement": false,
"SVGTSpanElement": false,
"SVGUnitTypes": false,
"SVGUseElement": false,
"SVGViewElement": false,
"SVGViewSpec": false,
"SVGVKernElement": false,
"top": false,
"WebSocket": false,
"window": false,
"Worker": false,
"XMLHttpRequest": false,
"XMLSerializer": false,
"XPathEvaluator": false,
"XPathExpression": false,
"XPathResult": false
}
},
"node": {
"globals": {
"__filename": false,
"__dirname": false,
"Buffer": false,
"DataView": false,
"console": false,
"exports": true,
"GLOBAL": false,
"global": false,
"module": false,
"process": false,
"require": false,
"setTimeout": false,
"clearTimeout": false,
"setInterval": false,
"clearInterval": false,
"setImmediate": false,
"clearImmediate": false
},
"rules": {
"no-catch-shadow": 0,
"no-console": 0,
"no-mixed-requires": 2,
"no-new-require": 2,
"no-path-concat": 2,
"no-process-exit": 2,
"global-strict": [0, "always"],
"handle-callback-err": [2, "err"]
}
},
"amd": {
"globals": {
"require": false,
"define": false
}
},
"mocha": {
"globals": {
"describe": false,
"it": false,
"before": false,
"after": false,
"beforeEach": false,
"afterEach": false,
"suite": false,
"test": false,
"setup": false,
"teardown": false,
"suiteSetup": false,
"suiteTeardown": false
}
},
"jasmine": {
"globals": {
"afterEach": false,
"beforeEach": false,
"describe": false,
"expect": false,
"it": false,
"jasmine": false,
"pending": false,
"spyOn": false,
"waits": false,
"waitsFor": false,
"xdescribe": false,
"xit": false
}
}
}
},{}],2:[function(require,module,exports){
module.exports={
"env": {
"browser": false,
"node": false,
"amd": false,
"mocha": false,
"jasmine": false
},
"rules": {
"no-alert": 2,
"no-array-constructor": 2,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 2,
"no-comma-dangle": 2,
"no-cond-assign": 2,
"no-console": 2,
"no-constant-condition": 2,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-keys": 2,
"no-else-return": 0,
"no-empty": 2,
"no-empty-class": 2,
"no-empty-label": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-extra-strict": 2,
"no-fallthrough": 2,
"no-floating-decimal": 0,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-lonely-if": 0,
"no-loop-func": 2,
"no-mixed-requires": [0, false],
"no-mixed-spaces-and-tabs": [2, false],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [0, {"max": 2}],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 0,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 2,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 0,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 2,
"no-self-compare": 0,
"no-sequences": 2,
"no-shadow": 2,
"no-shadow-restricted-names": 2,
"no-space-before-semi": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-underscore-dangle": 2,
"no-unreachable": 2,
"no-unused-expressions": 2,
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"no-use-before-define": 2,
"no-void": 0,
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
"no-with": 2,
"no-wrap-func": 2,
"block-scoped-var": 0,
"brace-style": [0, "1tbs"],
"camelcase": 2,
"comma-spacing": 2,
"comma-style": 0,
"complexity": [0, 11],
"consistent-return": 2,
"consistent-this": [0, "that"],
"curly": [2, "all"],
"default-case": 0,
"dot-notation": 2,
"eol-last": 2,
"eqeqeq": 2,
"func-names": 0,
"func-style": [0, "declaration"],
"global-strict": [2, "never"],
"guard-for-in": 0,
"handle-callback-err": 0,
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"max-depth": [0, 4],
"max-len": [0, 80, 4],
"max-nested-callbacks": [0, 2],
"max-params": [0, 3],
"max-statements": [0, 10],
"new-cap": 2,
"new-parens": 2,
"one-var": 0,
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "double"],
"radix": 0,
"semi": 2,
"sort-vars": 0,
"space-after-keywords": [0, "always"],
"space-before-blocks": [0, "always"],
"space-in-brackets": [0, "never"],
"space-in-parens": [0, "never"],
"space-infix-ops": 2,
"space-return-throw-case": 2,
"space-unary-word-ops": 0,
"spaced-line-comment": [0, "always"],
"strict": 2,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": 0,
"wrap-regex": 0,
"yoda": [2, "never"]
}
}
},{}],3:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!isNumber(n) || n < 0 || isNaN(n))
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
}
throw TypeError('Uncaught, unspecified "error" event.');
}
}
handler = this._events[type];
if (isUndefined(handler))
return false;
if (isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
handler.apply(this, args);
}
} else if (isObject(handler)) {
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (isObject(this._events[type]))
// If we've already got an array, just append.
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
// Check for listener leak
if (isObject(this._events[type]) && !this._events[type].warned) {
var m;
if (!isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
if (typeof console.trace === 'function') {
// not supported in IE 10
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!isFunction(listener))
throw TypeError('listener must be a function');
var fired = false;
function g() {
this.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (isFunction(listeners)) {
this.removeListener(type, listeners);
} else {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
var ret;
if (!emitter._events || !emitter._events[type])
ret = 0;
else if (isFunction(emitter._events[type]))
ret = 1;
else
ret = emitter._events[type].length;
return ret;
};
function isFunction(arg) {
return typeof arg === 'function';
}
function isNumber(arg) {
return typeof arg === 'number';
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isUndefined(arg) {
return arg === void 0;
}
},{}],4:[function(require,module,exports){
/*
Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com>
Copyright (C) 2014 Dan Tao <daniel.tao@gmail.com>
Copyright (C) 2013 Andrew Eisenberg <andrew@eisenberg.as>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint bitwise:true plusplus:true eqeq:true nomen:true*/
/*global doctrine:true, exports:true, parseTypeExpression:true, parseTop:true*/
(function (exports) {
'use strict';
var VERSION,
Regex,
CanAccessStringByIndex,
typed,
jsdoc,
isArray,
hasOwnProperty;
// Sync with package.json.
VERSION = '0.5.2-dev';
// See also tools/generate-unicode-regex.py.
Regex = {
NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'),
NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]')
};
CanAccessStringByIndex = typeof 'doctrine'[0] !== undefined;
function sliceSource(source, index, last) {
var output;
if (!CanAccessStringByIndex) {
output = source.slice(index, last).join('');
} else {
output = source.slice(index, last);
}
return output;
}
isArray = Array.isArray;
if (!isArray) {
isArray = function isArray(ary) {
return Object.prototype.toString.call(ary) === '[object Array]';
};
}
hasOwnProperty = (function () {
var func = Object.prototype.hasOwnProperty;
return function hasOwnProperty(obj, name) {
return func.call(obj, name);
};
}());
function shallowCopy(obj) {
var ret = {}, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
ret[key] = obj[key];
}
}
return ret;
}
function isLineTerminator(ch) {
return ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029';
}
function isWhiteSpace(ch) {
return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
(ch === '\u000C') || (ch === '\u00A0') ||
(ch.charCodeAt(0) >= 0x1680 &&
'\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
}
function isDecimalDigit(ch) {
return '0123456789'.indexOf(ch) >= 0;
}
function isHexDigit(ch) {
return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
}
function isOctalDigit(ch) {
return '01234567'.indexOf(ch) >= 0;
}
function isASCIIAlphanumeric(ch) {
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
}
function isIdentifierStart(ch) {
return (ch === '$') || (ch === '_') || (ch === '\\') ||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
}
function isIdentifierPart(ch) {
return (ch === '$') || (ch === '_') || (ch === '\\') ||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
((ch >= '0') && (ch <= '9')) ||
((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
}
function isTypeName(ch) {
return '><(){}[],:*|?!='.indexOf(ch) === -1 && !isWhiteSpace(ch) && !isLineTerminator(ch);
}
function isParamTitle(title) {
return title === 'param' || title === 'argument' || title === 'arg';
}
function isProperty(title) {
return title === 'property' || title === 'prop';
}
function isNameParameterRequired(title) {
return isParamTitle(title) || isProperty(title) ||
title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
}
function isAllowedName(title) {
return isNameParameterRequired(title) || title === 'const' || title === 'constant';
}
function isAllowedNested(title) {
return isProperty(title) || isParamTitle(title);
}
function isTypeParameterRequired(title) {
return isParamTitle(title) || title === 'define' || title === 'enum' ||
title === 'implements' || title === 'return' ||
title === 'this' || title === 'type' || title === 'typedef' ||
title === 'returns' || isProperty(title);
}
// Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
// This would require changes to 'parseType'
function isAllowedType(title) {
return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
title === 'public' || title === 'private' || title === 'protected';
}
function DoctrineError(message) {
this.name = 'DoctrineError';
this.message = message;
}
DoctrineError.prototype = new Error();
DoctrineError.prototype.constructor = DoctrineError;
function throwError(message) {
throw new DoctrineError(message);
}
function assert(cond, text) {
if (VERSION.slice(-3) === 'dev') {
if (!cond) {
throwError(text);
}
}
}
function trim(str) {
return str.replace(/^\s+/, '').replace(/\s+$/, '');
}
function unwrapComment(doc) {
// JSDoc comment is following form
// /**
// * .......
// */
// remove /**, */ and *
var BEFORE_STAR = 0,
STAR = 1,
AFTER_STAR = 2,
index,
len,
mode,
result,
ch;
doc = doc.replace(/^\/\*\*?/, '').replace(/\*\/$/, '');
index = 0;
len = doc.length;
mode = BEFORE_STAR;
result = '';
while (index < len) {
ch = doc[index];
switch (mode) {
case BEFORE_STAR:
if (isLineTerminator(ch)) {
result += ch;
} else if (ch === '*') {
mode = STAR;
} else if (!isWhiteSpace(ch)) {
result += ch;
mode = AFTER_STAR;
}
break;
case STAR:
if (!isWhiteSpace(ch)) {
result += ch;
}
mode = isLineTerminator(ch) ? BEFORE_STAR : AFTER_STAR;
break;
case AFTER_STAR:
result += ch;
if (isLineTerminator(ch)) {
mode = BEFORE_STAR;
}
break;
}
index += 1;
}
return result;
}
// Type Expression Parser
(function (exports) {
var Syntax,
Token,
source,
length,
index,
previous,
token,
value;
Syntax = {
NullableLiteral: 'NullableLiteral',
AllLiteral: 'AllLiteral',
NullLiteral: 'NullLiteral',
UndefinedLiteral: 'UndefinedLiteral',
VoidLiteral: 'VoidLiteral',
UnionType: 'UnionType',
ArrayType: 'ArrayType',
RecordType: 'RecordType',
FieldType: 'FieldType',
FunctionType: 'FunctionType',
ParameterType: 'ParameterType',
RestType: 'RestType',
NonNullableType: 'NonNullableType',
OptionalType: 'OptionalType',
NullableType: 'NullableType',
NameExpression: 'NameExpression',
TypeApplication: 'TypeApplication'
};
Token = {
ILLEGAL: 0, // ILLEGAL
DOT: 1, // .
DOT_LT: 2, // .<
REST: 3, // ...
LT: 4, // <
GT: 5, // >
LPAREN: 6, // (
RPAREN: 7, // )
LBRACE: 8, // {
RBRACE: 9, // }
LBRACK: 10, // [
RBRACK: 11, // ]
COMMA: 12, // ,
COLON: 13, // :
STAR: 14, // *
PIPE: 15, // |
QUESTION: 16, // ?
BANG: 17, // !
EQUAL: 18, // =
NAME: 19, // name token
STRING: 20, // string
NUMBER: 21, // number
EOF: 22
};
function Context(previous, index, token, value) {
this._previous = previous;
this._index = index;
this._token = token;
this._value = value;
}
Context.prototype.restore = function () {
previous = this._previous;
index = this._index;
token = this._token;
value = this._value;
};
Context.save = function () {
return new Context(previous, index, token, value);
};
function advance() {
var ch = source[index];
index += 1;
return ch;
}
function scanHexEscape(prefix) {
var i, len, ch, code = 0;
len = (prefix === 'u') ? 4 : 2;
for (i = 0; i < len; ++i) {
if (index < length && isHexDigit(source[index])) {
ch = advance();
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
} else {
return '';
}
}
return String.fromCharCode(code);
}
function scanString() {
var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
quote = source[index];
++index;
while (index < length) {
ch = advance();
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
ch = advance();
if (!isLineTerminator(ch)) {
switch (ch) {
case 'n':
str += '\n';
break;
case 'r':
str += '\r';
break;
case 't':
str += '\t';
break;
case 'u':
case 'x':
restore = index;
unescaped = scanHexEscape(ch);
if (unescaped) {
str += unescaped;
} else {
index = restore;
str += ch;
}
break;
case 'b':
str += '\b';
break;
case 'f':
str += '\f';
break;
case 'v':
str += '\v';
break;
default:
if (isOctalDigit(ch)) {
code = '01234567'.indexOf(ch);
// \0 is not octal escape sequence
// Deprecating unused code. TODO review removal
//if (code !== 0) {
// octal = true;
//}
if (index < length && isOctalDigit(source[index])) {
//TODO Review Removal octal = true;
code = code * 8 + '01234567'.indexOf(advance());
// 3 digits are only allowed when string starts
// with 0, 1, 2, 3
if ('0123'.indexOf(ch) >= 0 &&
index < length &&
isOctalDigit(source[index])) {
code = code * 8 + '01234567'.indexOf(advance());
}
}
str += String.fromCharCode(code);
} else {
str += ch;
}
break;
}
} else {
if (ch === '\r' && source[index] === '\n') {
++index;
}
}
} else if (isLineTerminator(ch)) {
break;
} else {
str += ch;
}
}
if (quote !== '') {
throwError('unexpected quote');
}
value = str;
return Token.STRING;
}
function scanNumber() {
var number, ch;
number = '';
if (ch !== '.') {
number = advance();
ch = source[index];
if (number === '0') {
if (ch === 'x' || ch === 'X') {
number += advance();
while (index < length) {
ch = source[index];
if (!isHexDigit(ch)) {
break;
}
number += advance();
}
if (number.length <= 2) {
// only 0x
throwError('unexpected token');
}
if (index < length) {
ch = source[index];
if (isIdentifierStart(ch)) {
throwError('unexpected token');
}
}
value = parseInt(number, 16);
return Token.NUMBER;
}
if (isOctalDigit(ch)) {
number += advance();
while (index < length) {
ch = source[index];
if (!isOctalDigit(ch)) {
break;
}
number += advance();
}
if (index < length) {
ch = source[index];
if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
throwError('unexpected token');
}
}
value = parseInt(number, 8);
return Token.NUMBER;
}
if (isDecimalDigit(ch)) {
throwError('unexpected token');
}
}
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
number += advance();
}
}
if (ch === '.') {
number += advance();
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
number += advance();
}
}
if (ch === 'e' || ch === 'E') {
number += advance();
ch = source[index];
if (ch === '+' || ch === '-') {
number += advance();
}
ch = source[index];
if (isDecimalDigit(ch)) {
number += advance();
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
number += advance();
}
} else {
throwError('unexpected token');
}
}
if (index < length) {
ch = source[index];
if (isIdentifierStart(ch)) {
throwError('unexpected token');
}
}
value = parseFloat(number);
return Token.NUMBER;
}
function scanTypeName() {
var ch, ch2;
value = advance();
while (index < length && isTypeName(source[index])) {
ch = source[index];
if (ch === '.') {
if ((index + 1) < length) {
ch2 = source[index + 1];
if (ch2 === '<') {
break;
}
}
}
value += advance();
}
return Token.NAME;
}
function next() {
var ch;
previous = index;
while (index < length && isWhiteSpace(source[index])) {
advance();
}
if (index >= length) {
token = Token.EOF;
return token;
}
ch = source[index];
switch (ch) {
case '"':
token = scanString();
return token;
case ':':
advance();
token = Token.COLON;
return token;
case ',':
advance();
token = Token.COMMA;
return token;
case '(':
advance();
token = Token.LPAREN;
return token;
case ')':
advance();
token = Token.RPAREN;
return token;
case '[':
advance();
token = Token.LBRACK;
return token;
case ']':
advance();
token = Token.RBRACK;
return token;
case '{':
advance();
token = Token.LBRACE;
return token;
case '}':
advance();
token = Token.RBRACE;
return token;
case '.':
advance();
if (index < length) {
ch = source[index];
if (ch === '<') {
advance();
token = Token.DOT_LT;
return token;
}
if (ch === '.' && index + 1 < length && source[index + 1] === '.') {
advance();
advance();
token = Token.REST;
return token;
}
if (isDecimalDigit(ch)) {
token = scanNumber();
return token;
}
}
token = Token.DOT;
return token;
case '<':
advance();
token = Token.LT;
return token;
case '>':
advance();
token = Token.GT;
return token;
case '*':
advance();
token = Token.STAR;
return token;
case '|':
advance();
token = Token.PIPE;
return token;
case '?':
advance();
token = Token.QUESTION;
return token;
case '!':
advance();
token = Token.BANG;
return token;
case '=':
advance();
token = Token.EQUAL;
return token;
default:
if (isDecimalDigit(ch)) {
token = scanNumber();
return token;
}
// type string permits following case,
//
// namespace.module.MyClass
//
// this reduced 1 token TK_NAME
if (isTypeName(ch)) {
token = scanTypeName();
return token;
}
token = Token.ILLEGAL;
return token;
}
}
function consume(target, text) {
assert(token === target, text || 'consumed token not matched');
next();
}
function expect(target) {
if (token !== target) {
throwError('unexpected token');
}
next();
}
// UnionType := '(' TypeUnionList ')'
//
// TypeUnionList :=
// <<empty>>
// | NonemptyTypeUnionList
//
// NonemptyTypeUnionList :=
// TypeExpression
// | TypeExpression '|' NonemptyTypeUnionList
function parseUnionType() {
var elements;
consume(Token.LPAREN, 'UnionType should start with (');
elements = [];
if (token !== Token.RPAREN) {
while (true) {
elements.push(parseTypeExpression());
if (token === Token.RPAREN) {
break;
}
expect(Token.PIPE);
}
}
consume(Token.RPAREN, 'UnionType should end with )');
return {
type: Syntax.UnionType,
elements: elements
};
}
// ArrayType := '[' ElementTypeList ']'
//
// ElementTypeList :=
// <<empty>>
// | TypeExpression
// | '...' TypeExpression
// | TypeExpression ',' ElementTypeList
function parseArrayType() {
var elements;
consume(Token.LBRACK, 'ArrayType should start with [');
elements = [];
while (token !== Token.RBRACK) {
if (token === Token.REST) {
consume(Token.REST);
elements.push({
type: Syntax.RestType,
expression: parseTypeExpression()
});
break;
} else {
elements.push(parseTypeExpression());
}
if (token !== Token.RBRACK) {
expect(Token.COMMA);
}
}
expect(Token.RBRACK);
return {
type: Syntax.ArrayType,
elements: elements
};
}
function parseFieldName() {
var v = value;
if (token === Token.NAME || token === Token.STRING) {
next();
return v;
}
if (token === Token.NUMBER) {
consume(Token.NUMBER);
return String(v);
}
throwError('unexpected token');
}
// FieldType :=
// FieldName
// | FieldName ':' TypeExpression
//
// FieldName :=
// NameExpression
// | StringLiteral
// | NumberLiteral
// | ReservedIdentifier
function parseFieldType() {
var key;
key = parseFieldName();
if (token === Token.COLON) {
consume(Token.COLON);
return {
type: Syntax.FieldType,
key: key,
value: parseTypeExpression()
};
}
return {
type: Syntax.FieldType,
key: key,
value: null
};
}
// RecordType := '{' FieldTypeList '}'
//
// FieldTypeList :=
// <<empty>>
// | FieldType
// | FieldType ',' FieldTypeList
function parseRecordType() {
var fields;
consume(Token.LBRACE, 'RecordType should start with {');
fields = [];
if (token === Token.COMMA) {
consume(Token.COMMA);
} else {
while (token !== Token.RBRACE) {
fields.push(parseFieldType());
if (token !== Token.RBRACE) {
expect(Token.COMMA);
}
}
}
expect(Token.RBRACE);
return {
type: Syntax.RecordType,
fields: fields
};
}
function parseNameExpression() {
var name = value;
expect(Token.NAME);
return {
type: Syntax.NameExpression,
name: name
};
}
// TypeExpressionList :=
// TopLevelTypeExpression
// | TopLevelTypeExpression ',' TypeExpressionList
function parseTypeExpressionList() {
var elements = [];
elements.push(parseTop());
while (token === Token.COMMA) {
consume(Token.COMMA);
elements.push(parseTop());
}
return elements;
}
// TypeName :=
// NameExpression
// | NameExpression TypeApplication
//
// TypeApplication :=
// '.<' TypeExpressionList '>'
// | '<' TypeExpressionList '>' // this is extension of doctrine
function parseTypeName() {
var expr, applications;
expr = parseNameExpression();
if (token === Token.DOT_LT || token === Token.LT) {
next();
applications = parseTypeExpressionList();
expect(Token.GT);
return {
type: Syntax.TypeApplication,
expression: expr,
applications: applications
};
}
return expr;
}
// ResultType :=
// <<empty>>
// | ':' void
// | ':' TypeExpression
//
// BNF is above
// but, we remove <<empty>> pattern, so token is always TypeToken::COLON
function parseResultType() {
consume(Token.COLON, 'ResultType should start with :');
if (token === Token.NAME && value === 'void') {
consume(Token.NAME);
return {
type: Syntax.VoidLiteral
};
}
return parseTypeExpression();
}
// ParametersType :=
// RestParameterType
// | NonRestParametersType
// | NonRestParametersType ',' RestParameterType
//
// RestParameterType :=
// '...'
// '...' Identifier
//
// NonRestParametersType :=
// ParameterType ',' NonRestParametersType
// | ParameterType
// | OptionalParametersType
//
// OptionalParametersType :=
// OptionalParameterType
// | OptionalParameterType, OptionalParametersType
//
// OptionalParameterType := ParameterType=
//
// ParameterType := TypeExpression | Identifier ':' TypeExpression
//
// Identifier is "new" or "this"
function parseParametersType() {
var params = [], normal = true, expr, rest = false;
while (token !== Token.RPAREN) {
if (token === Token.REST) {
// RestParameterType
consume(Token.REST);
rest = true;
}
expr = parseTypeExpression();
if (expr.type === Syntax.NameExpression && token === Token.COLON) {
// Identifier ':' TypeExpression
consume(Token.COLON);
expr = {
type: Syntax.ParameterType,
name: expr.name,
expression: parseTypeExpression()
};
}
if (token === Token.EQUAL) {
consume(Token.EQUAL);
expr = {
type: Syntax.OptionalType,
expression: expr
};
normal = false;
} else {
if (!normal) {
throwError('unexpected token');
}
}
if (rest) {
expr = {
type: Syntax.RestType,
expression: expr
};
}
params.push(expr);
if (token !== Token.RPAREN) {
expect(Token.COMMA);
}
}
return params;
}
// FunctionType := 'function' FunctionSignatureType
//
// FunctionSignatureType :=
// | TypeParameters '(' ')' ResultType
// | TypeParameters '(' ParametersType ')' ResultType
// | TypeParameters '(' 'this' ':' TypeName ')' ResultType
// | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
function parseFunctionType() {
var isNew, thisBinding, params, result, fnType;
assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
consume(Token.NAME);
// Google Closure Compiler is not implementing TypeParameters.
// So we do not. if we don't get '(', we see it as error.
expect(Token.LPAREN);
isNew = false;
params = [];
thisBinding = null;
if (token !== Token.RPAREN) {
// ParametersType or 'this'
if (token === Token.NAME &&
(value === 'this' || value === 'new')) {
// 'this' or 'new'
// 'new' is Closure Compiler extension
isNew = value === 'new';
consume(Token.NAME);
expect(Token.COLON);
thisBinding = parseTypeName();
if (token === Token.COMMA) {
consume(Token.COMMA);
params = parseParametersType();
}
} else {
params = parseParametersType();
}
}
expect(Token.RPAREN);
result = null;
if (token === Token.COLON) {
result = parseResultType();
}
fnType = {
type: Syntax.FunctionType,
params: params,
result: result
};
if (thisBinding) {
// avoid adding null 'new' and 'this' properties
fnType['this'] = thisBinding;
if (isNew) {
fnType['new'] = true;
}
}
return fnType;
}
// BasicTypeExpression :=
// '*'
// | 'null'
// | 'undefined'
// | TypeName
// | FunctionType
// | UnionType
// | RecordType
// | ArrayType
function parseBasicTypeExpression() {
var context;
switch (token) {
case Token.STAR:
consume(Token.STAR);
return {
type: Syntax.AllLiteral
};
case Token.LPAREN:
return parseUnionType();
case Token.LBRACK:
return parseArrayType();
case Token.LBRACE:
return parseRecordType();
case Token.NAME:
if (value === 'null') {
consume(Token.NAME);
return {
type: Syntax.NullLiteral
};
}
if (value === 'undefined') {
consume(Token.NAME);
return {
type: Syntax.UndefinedLiteral
};
}
context = Context.save();
if (value === 'function') {
try {
return parseFunctionType();
} catch (e) {
context.restore();
}
}
return parseTypeName();
default:
throwError('unexpected token');
}
}
// TypeExpression :=
// BasicTypeExpression
// | '?' BasicTypeExpression
// | '!' BasicTypeExpression
// | BasicTypeExpression '?'
// | BasicTypeExpression '!'
// | '?'
// | BasicTypeExpression '[]'
function parseTypeExpression() {
var expr;
if (token === Token.QUESTION) {
consume(Token.QUESTION);
if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
token === Token.RBRACK) {
return {
type: Syntax.NullableLiteral
};
}
return {
type: Syntax.NullableType,
expression: parseBasicTypeExpression(),
prefix: true
};
}
if (token === Token.BANG) {
consume(Token.BANG);
return {
type: Syntax.NonNullableType,
expression: parseBasicTypeExpression(),
prefix: true
};
}
expr = parseBasicTypeExpression();
if (token === Token.BANG) {
consume(Token.BANG);
return {
type: Syntax.NonNullableType,
expression: expr,
prefix: false
};
}
if (token === Token.QUESTION) {
consume(Token.QUESTION);
return {
type: Syntax.NullableType,
expression: expr,
prefix: false
};
}
if (token === Token.LBRACK) {
consume(Token.LBRACK);
consume(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
return {
type: Syntax.TypeApplication,
expression: {
type: Syntax.NameExpression,
name: 'Array'
},
applications: [expr]
};
}
return expr;
}
// TopLevelTypeExpression :=
// TypeExpression
// | TypeUnionList
//
// This rule is Google Closure Compiler extension, not ES4
// like,
// { number | string }
// If strict to ES4, we should write it as
// { (number|string) }
function parseTop() {
var expr, elements;
expr = parseTypeExpression();
if (token !== Token.PIPE) {
return expr;
}
elements = [ expr ];
consume(Token.PIPE);
while (true) {
elements.push(parseTypeExpression());
if (token !== Token.PIPE) {
break;
}
consume(Token.PIPE);
}
return {
type: Syntax.UnionType,
elements: elements
};
}
function parseTopParamType() {
var expr;
if (token === Token.REST) {
consume(Token.REST);
return {
type: Syntax.RestType,
expression: parseTop()
};
}
expr = parseTop();
if (token === Token.EQUAL) {
consume(Token.EQUAL);
return {
type: Syntax.OptionalType,
expression: expr
};
}
return expr;
}
function parseType(src, opt) {
var expr;
source = src;
length = source.length;
index = 0;
previous = 0;
if (!CanAccessStringByIndex) {
source = source.split('');
}
next();
expr = parseTop();
if (opt && opt.midstream) {
return {
expression: expr,
index: previous
};
}
if (token !== Token.EOF) {
throwError('not reach to EOF');
}
return expr;
}
function parseParamType(src, opt) {
var expr;
source = src;
length = source.length;
index = 0;
previous = 0;
if (!CanAccessStringByIndex) {
source = source.split('');
}
next();
expr = parseTopParamType();
if (opt && opt.midstream) {
return {
expression: expr,
index: previous
};
}
if (token !== Token.EOF) {
throwError('not reach to EOF');
}
return expr;
}
function stringifyImpl(node, compact, topLevel) {
var result, i, iz;
switch (node.type) {
case Syntax.NullableLiteral:
result = '?';
break;
case Syntax.AllLiteral:
result = '*';
break;
case Syntax.NullLiteral:
result = 'null';
break;
case Syntax.UndefinedLiteral:
result = 'undefined';
break;
case Syntax.VoidLiteral:
result = 'void';
break;
case Syntax.UnionType:
if (!topLevel) {
result = '(';
} else {
result = '';
}
for (i = 0, iz = node.elements.length; i < iz; ++i) {
result += stringifyImpl(node.elements[i], compact);
if ((i + 1) !== iz) {
result += '|';
}
}
if (!topLevel) {
result += ')';
}
break;
case Syntax.ArrayType:
result = '[';
for (i = 0, iz = node.elements.length; i < iz; ++i) {
result += stringifyImpl(node.elements[i], compact);
if ((i + 1) !== iz) {
result += compact ? ',' : ', ';
}
}
result += ']';
break;
case Syntax.RecordType:
result = '{';
for (i = 0, iz = node.fields.length; i < iz; ++i) {
result += stringifyImpl(node.fields[i], compact);
if ((i + 1) !== iz) {
result += compact ? ',' : ', ';
}
}
result += '}';
break;
case Syntax.FieldType:
if (node.value) {
result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
} else {
result = node.key;
}
break;
case Syntax.FunctionType:
result = compact ? 'function(' : 'function (';
if (node['this']) {
if (node['new']) {
result += (compact ? 'new:' : 'new: ');
} else {
result += (compact ? 'this:' : 'this: ');
}
result += stringifyImpl(node['this'], compact);
if (node.params.length !== 0) {
result += compact ? ',' : ', ';
}
}
for (i = 0, iz = node.params.length; i < iz; ++i) {
result += stringifyImpl(node.params[i], compact);
if ((i + 1) !== iz) {
result += compact ? ',' : ', ';
}
}
result += ')';
if (node.result) {
result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
}
break;
case Syntax.ParameterType:
result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
break;
case Syntax.RestType:
result = '...';
if (node.expression) {
result += stringifyImpl(node.expression, compact);
}
break;
case Syntax.NonNullableType:
if (node.prefix) {
result = '!' + stringifyImpl(node.expression, compact);
} else {
result = stringifyImpl(node.expression, compact) + '!';
}
break;
case Syntax.OptionalType:
result = stringifyImpl(node.expression, compact) + '=';
break;
case Syntax.NullableType:
if (node.prefix) {
result = '?' + stringifyImpl(node.expression, compact);
} else {
result = stringifyImpl(node.expression, compact) + '?';
}
break;
case Syntax.NameExpression:
result = node.name;
break;
case Syntax.TypeApplication:
result = stringifyImpl(node.expression, compact) + '.<';
for (i = 0, iz = node.applications.length; i < iz; ++i) {
result += stringifyImpl(node.applications[i], compact);
if ((i + 1) !== iz) {
result += compact ? ',' : ', ';
}
}
result += '>';
break;
default:
throwError('Unknown type ' + node.type);
}
return result;
}
function stringify(node, options) {
if (options == null) {
options = {};
}
return stringifyImpl(node, options.compact, options.topLevel);
}
exports.parseType = parseType;
exports.parseParamType = parseParamType;
exports.stringify = stringify;
exports.Syntax = Syntax;
}(typed = {}));
// JSDoc Tag Parser
(function (exports) {
var Rules,
index,
lineNumber,
length,
source,
recoverable,
sloppy,
strict;
function advance() {
var ch = source[index];
index += 1;
if (isLineTerminator(ch)) {
lineNumber += 1;
}
return ch;
}
function scanTitle() {
var title = '';
// waste '@'
advance();
while (index < length && isASCIIAlphanumeric(source[index])) {
title += advance();
}
return title;
}
function seekContent() {
var ch, waiting, last = index;
waiting = false;
while (last < length) {
ch = source[last];
if (isLineTerminator(ch)) {
lineNumber += 1;
waiting = true;
} else if (waiting) {
if (ch === '@') {
break;
}
if (!isWhiteSpace(ch)) {
waiting = false;
}
}
last += 1;
}
return last;
}
// type expression may have nest brace, such as,
// { { ok: string } }
//
// therefore, scanning type expression with balancing braces.
function parseType(title, last) {
var ch, brace, type, direct = false;
// search '{'
while (index < last) {
ch = source[index];
if (isWhiteSpace(ch)) {
advance();
} else if (ch === '{') {
advance();
break;
} else {
// this is direct pattern
direct = true;
break;
}
}
if (!direct) {
// type expression { is found
brace = 1;
type = '';
while (index < last) {
ch = source[index];
if (isLineTerminator(ch)) {
advance();
} else {
if (ch === '}') {
brace -= 1;
if (brace === 0) {
advance();
break;
}
} else if (ch === '{') {
brace += 1;
}
type += advance();
}
}
if (brace !== 0) {
// braces is not balanced
return throwError('Braces are not balanced');
}
try {
if (isParamTitle(title)) {
return typed.parseParamType(type);
}
return typed.parseType(type);
} catch (e1) {
// parse failed
return null;
}
} else {
return null;
}
}
function scanIdentifier(last) {
var identifier;
if (!isIdentifierStart(source[index])) {
return null;
}
identifier = advance();
while (index < last && isIdentifierPart(source[index])) {
identifier += advance();
}
return identifier;
}
function skipWhiteSpace(last) {
while (index < last && (isWhiteSpace(source[index]) || isLineTerminator(source[index]))) {
advance();
}
}
function parseName(last, allowBrackets, allowNestedParams) {
var name = '', useBrackets;
skipWhiteSpace(last);
if (index >= last) {
return null;
}
if (allowBrackets && source[index] === '[') {
useBrackets = true;
name = advance();
}
if (!isIdentifierStart(source[index])) {
return null;
}
name += scanIdentifier(last);
if (allowNestedParams) {
while (source[index] === '.') {
name += '.';
index += 1;
name += scanIdentifier(last);
}
}
if (useBrackets) {
// do we have a default value for this?
if (source[index] === '=') {
// consume the '='' symbol
name += advance();
// scan in the default value
while (index < last && source[index] !== ']') {
name += advance();
}
}
if (index >= last || source[index] !== ']') {
// we never found a closing ']'
return null;
}
// collect the last ']'
name += advance();
}
return name;
}
function skipToTag() {
while (index < length && source[index] !== '@') {
advance();
}
if (index >= length) {
return false;
}
assert(source[index] === '@');
return true;
}
function TagParser(options, title) {
this._options = options;
this._title = title;
this._tag = {
title: title,
description: null
};
if (this._options.lineNumbers) {
this._tag.lineNumber = lineNumber;
}
this._last = 0;
// space to save special information for title parsers.
this._extra = { };
}
// addError(err, ...)
TagParser.prototype.addError = function addError(errorText) {
var args = Array.prototype.slice.call(arguments, 1),
msg = errorText.replace(
/%(\d)/g,
function (whole, index) {
assert(index < args.length, 'Message reference must be in range');
return args[index];
}
);
if (!this._tag.errors) {
this._tag.errors = [];
}
if (strict) {
throwError(msg);
}
this._tag.errors.push(msg);
return recoverable;
};
TagParser.prototype.parseType = function () {
// type required titles
if (isTypeParameterRequired(this._title)) {
try {
this._tag.type = parseType(this._title, this._last);
if (!this._tag.type) {
if (!isParamTitle(this._title)) {
if (!this.addError('Missing or invalid tag type')) {
return false;
}
}
}
} catch (error) {
this._tag.type = null;
if (!this.addError(error.message)) {
return false;
}
}
} else if (isAllowedType(this._title)) {
// optional types
try {
this._tag.type = parseType(this._title, this._last);
} catch (e) {
//For optional types, lets drop the thrown error when we hit the end of the file
}
}
return true;
};
TagParser.prototype._parseNamePath = function (optional) {
var name;
name = parseName(this._last, sloppy && isParamTitle(this._title), true);
if (!name) {
if (!optional) {
if (!this.addError('Missing or invalid tag name')) {
return false;
}
}
}
this._tag.name = name;
return true;
};
TagParser.prototype.parseNamePath = function () {
return this._parseNamePath(false);
};
TagParser.prototype.parseNamePathOptional = function () {
return this._parseNamePath(true);
};
TagParser.prototype.parseName = function () {
var assign, name;
// param, property requires name
if (isAllowedName(this._title)) {
this._tag.name = parseName(this._last, sloppy && isParamTitle(this._title), isAllowedNested(this._title));
if (!this._tag.name) {
if (!isNameParameterRequired(this._title)) {
return true;
}
// it's possible the name has already been parsed but interpreted as a type
// it's also possible this is a sloppy declaration, in which case it will be
// fixed at the end
if (isParamTitle(this._title) && this._tag.type.name) {
this._extra.name = this._tag.type;
this._tag.name = this._tag.type.name;
this._tag.type = null;
} else {
if (!this.addError('Missing or invalid tag name')) {
return false;
}
}
} else {
name = this._tag.name;
if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
// extract the default value if there is one
// example: @param {string} [somebody=John Doe] description
assign = name.substring(1, name.length - 1).split('=');
if (assign[1]) {
this._tag['default'] = assign[1];
}
this._tag.name = assign[0];
// convert to an optional type
if (this._tag.type.type !== 'OptionalType') {
this._tag.type = {
type: 'OptionalType',
expression: this._tag.type
};
}
}
}
}
return true;
};
TagParser.prototype.parseDescription = function parseDescription() {
var description = trim(sliceSource(source, index, this._last));
if (description) {
if ((/^-\s+/).test(description)) {
description = description.substring(2);
}
this._tag.description = description;
}
return true;
};
TagParser.prototype.parseKind = function parseKind() {
var kind, kinds;
kinds = {
'class': true,
'constant': true,
'event': true,
'external': true,
'file': true,
'function': true,
'member': true,
'mixin': true,
'module': true,
'namespace': true,
'typedef': true
};
kind = trim(sliceSource(source, index, this._last));
this._tag.kind = kind;
if (!hasOwnProperty(kinds, kind)) {
if (!this.addError('Invalid kind name \'%0\'', kind)) {
return false;
}
}
return true;
};
TagParser.prototype.parseAccess = function parseAccess() {
var access;
access = trim(sliceSource(source, index, this._last));
this._tag.access = access;
if (access !== 'private' && access !== 'protected' && access !== 'public') {
if (!this.addError('Invalid access name \'%0\'', access)) {
return false;
}
}
return true;
};
TagParser.prototype.parseVariation = function parseVariation() {
var variation, text;
text = trim(sliceSource(source, index, this._last));
variation = parseFloat(text, 10);
this._tag.variation = variation;
if (isNaN(variation)) {
if (!this.addError('Invalid variation \'%0\'', text)) {
return false;
}
}
return true;
};
TagParser.prototype.ensureEnd = function () {
var shouldBeEmpty = trim(sliceSource(source, index, this._last));
if (shouldBeEmpty) {
if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
return false;
}
}
return true;
};
TagParser.prototype.epilogue = function epilogue() {
var description;
description = this._tag.description;
// un-fix potentially sloppy declaration
if (isParamTitle(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
this._tag.type = this._extra.name;
this._tag.name = undefined;
if (!sloppy) {
if (!this.addError('Missing or invalid tag name')) {
return false;
}
}
}
return true;
};
Rules = {
// http://usejsdoc.org/tags-access.html
'access': ['parseAccess'],
// http://usejsdoc.org/tags-alias.html
'alias': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-augments.html
'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-constructor.html
'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-constructor.html
'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-extends.html
'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-deprecated.html
'deprecated': ['parseDescription'],
// http://usejsdoc.org/tags-global.html
'global': ['ensureEnd'],
// http://usejsdoc.org/tags-inner.html
'inner': ['ensureEnd'],
// http://usejsdoc.org/tags-instance.html
'instance': ['ensureEnd'],
// http://usejsdoc.org/tags-kind.html
'kind': ['parseKind'],
// http://usejsdoc.org/tags-mixes.html
'mixes': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-mixin.html
'mixin': ['parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-member.html
'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-method.html
'method': ['parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-module.html
'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-method.html
'func': ['parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-method.html
'function': ['parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-member.html
'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-name.html
'name': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-namespace.html
'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-private.html
'private': ['parseType', 'parseDescription'],
// http://usejsdoc.org/tags-protected.html
'protected': ['parseType', 'parseDescription'],
// http://usejsdoc.org/tags-public.html
'public': ['parseType', 'parseDescription'],
// http://usejsdoc.org/tags-readonly.html
'readonly': ['ensureEnd'],
// http://usejsdoc.org/tags-requires.html
'requires': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-since.html
'since': ['parseDescription'],
// http://usejsdoc.org/tags-static.html
'static': ['ensureEnd'],
// http://usejsdoc.org/tags-summary.html
'summary': ['parseDescription'],
// http://usejsdoc.org/tags-this.html
'this': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-todo.html
'todo': ['parseDescription'],
// http://usejsdoc.org/tags-variation.html
'variation': ['parseVariation'],
// http://usejsdoc.org/tags-version.html
'version': ['parseDescription']
};
TagParser.prototype.parse = function parse() {
var i, iz, sequences, method;
// empty title
if (!this._title) {
if (!this.addError('Missing or invalid title')) {
return null;
}
}
// Seek to content last index.
this._last = seekContent(this._title);
if (hasOwnProperty(Rules, this._title)) {
sequences = Rules[this._title];
} else {
// default sequences
sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
}
for (i = 0, iz = sequences.length; i < iz; ++i) {
method = sequences[i];
if (!this[method]()) {
return null;
}
}
// Seek global index to end of this tag.
index = this._last;
return this._tag;
};
function parseTag(options) {
var title, parser;
// skip to tag
if (!skipToTag()) {
return null;
}
// scan title
title = scanTitle();
// construct tag parser
parser = new TagParser(options, title);
return parser.parse();
}
//
// Parse JSDoc
//
function scanJSDocDescription() {
var description = '', ch, atAllowed;
atAllowed = true;
while (index < length) {
ch = source[index];
if (atAllowed && ch === '@') {
break;
}
if (isLineTerminator(ch)) {
atAllowed = true;
} else if (atAllowed && !isWhiteSpace(ch)) {
atAllowed = false;
}
description += advance();
}
return trim(description);
}
function parse(comment, options) {
var tags = [], tag, description, interestingTags, i, iz;
if (options === undefined) {
options = {};
}
if (typeof options.unwrap === 'boolean' && options.unwrap) {
source = unwrapComment(comment);
} else {
source = comment;
}
// array of relevant tags
if (options.tags) {
if (isArray(options.tags)) {
interestingTags = { };
for (i = 0, iz = options.tags.length; i < iz; i++) {
if (typeof options.tags[i] === 'string') {
interestingTags[options.tags[i]] = true;
} else {
throwError('Invalid "tags" parameter: ' + options.tags);
}
}
} else {
throwError('Invalid "tags" parameter: ' + options.tags);
}
}
if (!CanAccessStringByIndex) {
source = source.split('');
}
length = source.length;
index = 0;
lineNumber = 0;
recoverable = options.recoverable;
sloppy = options.sloppy;
strict = options.strict;
description = scanJSDocDescription();
while (true) {
tag = parseTag(options);
if (!tag) {
break;
}
if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
tags.push(tag);
}
}
return {
description: description,
tags: tags
};
}
exports.parse = parse;
}(jsdoc = {}));
exports.version = VERSION;
exports.parse = jsdoc.parse;
exports.parseType = typed.parseType;
exports.parseParamType = typed.parseParamType;
exports.unwrapComment = unwrapComment;
exports.Syntax = shallowCopy(typed.Syntax);
exports.Error = DoctrineError;
exports.type = {
Syntax: exports.Syntax,
parseType: typed.parseType,
parseParamType: typed.parseParamType,
stringify: typed.stringify
};
}(typeof exports === 'undefined' ? (doctrine = {}) : exports));
/* vim: set sw=4 ts=4 et tw=80 : */
},{}],5:[function(require,module,exports){
/*
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
Copyright (C) 2013 Alex Seville <hi@alexanderseville.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Escope (<a href="http://github.com/Constellation/escope">escope</a>) is an <a
* href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>
* scope analyzer extracted from the <a
* href="http://github.com/Constellation/esmangle">esmangle project</a/>.
* <p>
* <em>escope</em> finds lexical scopes in a source program, i.e. areas of that
* program where different occurrences of the same identifier refer to the same
* variable. With each scope the contained variables are collected, and each
* identifier reference in code is linked to its corresponding variable (if
* possible).
* <p>
* <em>escope</em> works on a syntax tree of the parsed source code which has
* to adhere to the <a
* href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">
* Mozilla Parser API</a>. E.g. <a href="http://esprima.org">esprima</a> is a parser
* that produces such syntax trees.
* <p>
* The main interface is the {@link analyze} function.
* @module
*/
/*jslint bitwise:true */
/*global exports:true, define:true, require:true*/
(function (factory, global) {
'use strict';
function namespace(str, obj) {
var i, iz, names, name;
names = str.split('.');
for (i = 0, iz = names.length; i < iz; ++i) {
name = names[i];
if (obj.hasOwnProperty(name)) {
obj = obj[name];
} else {
obj = (obj[name] = {});
}
}
return obj;
}
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// and plain browser loading,
if (typeof define === 'function' && define.amd) {
define('escope', ['exports', 'estraverse'], function (exports, estraverse) {
factory(exports, global, estraverse);
});
} else if (typeof exports !== 'undefined') {
factory(exports, global, require('estraverse'));
} else {
factory(namespace('escope', global), global, global.estraverse);
}
}(function (exports, global, estraverse) {
'use strict';
var Syntax,
Map,
currentScope,
globalScope,
scopes,
options;
Syntax = estraverse.Syntax;
if (typeof global.Map !== 'undefined') {
// ES6 Map
Map = global.Map;
} else {
Map = function Map() {
this.__data = {};
};
Map.prototype.get = function MapGet(key) {
key = '$' + key;
if (this.__data.hasOwnProperty(key)) {
return this.__data[key];
}
return undefined;
};
Map.prototype.has = function MapHas(key) {
key = '$' + key;
return this.__data.hasOwnProperty(key);
};
Map.prototype.set = function MapSet(key, val) {
key = '$' + key;
this.__data[key] = val;
};
Map.prototype['delete'] = function MapDelete(key) {
key = '$' + key;
return delete this.__data[key];
};
}
function assert(cond, text) {
if (!cond) {
throw new Error(text);
}
}
function defaultOptions() {
return {
optimistic: false,
directive: false
};
}
function updateDeeply(target, override) {
var key, val;
function isHashObject(target) {
return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
}
for (key in override) {
if (override.hasOwnProperty(key)) {
val = override[key];
if (isHashObject(val)) {
if (isHashObject(target[key])) {
updateDeeply(target[key], val);
} else {
target[key] = updateDeeply({}, val);
}
} else {
target[key] = val;
}
}
}
return target;
}
/**
* A Reference represents a single occurrence of an identifier in code.
* @class Reference
*/
function Reference(ident, scope, flag, writeExpr, maybeImplicitGlobal) {
/**
* Identifier syntax node.
* @member {esprima#Identifier} Reference#identifier
*/
this.identifier = ident;
/**
* Reference to the enclosing Scope.
* @member {Scope} Reference#from
*/
this.from = scope;
/**
* Whether the reference comes from a dynamic scope (such as 'eval',
* 'with', etc.), and may be trapped by dynamic scopes.
* @member {boolean} Reference#tainted
*/
this.tainted = false;
/**
* The variable this reference is resolved with.
* @member {Variable} Reference#resolved
*/
this.resolved = null;
/**
* The read-write mode of the reference. (Value is one of {@link
* Reference.READ}, {@link Reference.RW}, {@link Reference.WRITE}).
* @member {number} Reference#flag
* @private
*/
this.flag = flag;
if (this.isWrite()) {
/**
* If reference is writeable, this is the tree being written to it.
* @member {esprima#Node} Reference#writeExpr
*/
this.writeExpr = writeExpr;
}
/**
* Whether the Reference might refer to a global variable.
* @member {boolean} Reference#__maybeImplicitGlobal
* @private
*/
this.__maybeImplicitGlobal = maybeImplicitGlobal;
}
/**
* @constant Reference.READ
* @private
*/
Reference.READ = 0x1;
/**
* @constant Reference.WRITE
* @private
*/
Reference.WRITE = 0x2;
/**
* @constant Reference.RW
* @private
*/
Reference.RW = 0x3;
/**
* Whether the reference is static.
* @method Reference#isStatic
* @return {boolean}
*/
Reference.prototype.isStatic = function isStatic() {
return !this.tainted && this.resolved && this.resolved.scope.isStatic();
};
/**
* Whether the reference is writeable.
* @method Reference#isWrite
* @return {boolean}
*/
Reference.prototype.isWrite = function isWrite() {
return this.flag & Reference.WRITE;
};
/**
* Whether the reference is readable.
* @method Reference#isRead
* @return {boolean}
*/
Reference.prototype.isRead = function isRead() {
return this.flag & Reference.READ;
};
/**
* Whether the reference is read-only.
* @method Reference#isReadOnly
* @return {boolean}
*/
Reference.prototype.isReadOnly = function isReadOnly() {
return this.flag === Reference.READ;
};
/**
* Whether the reference is write-only.
* @method Reference#isWriteOnly
* @return {boolean}
*/
Reference.prototype.isWriteOnly = function isWriteOnly() {
return this.flag === Reference.WRITE;
};
/**
* Whether the reference is read-write.
* @method Reference#isReadWrite
* @return {boolean}
*/
Reference.prototype.isReadWrite = function isReadWrite() {
return this.flag === Reference.RW;
};
/**
* A Variable represents a locally scoped identifier. These include arguments to
* functions.
* @class Variable
*/
function Variable(name, scope) {
/**
* The variable name, as given in the source code.
* @member {String} Variable#name
*/
this.name = name;
/**
* List of defining occurrences of this variable (like in 'var ...'
* statements or as parameter), as AST nodes.
* @member {esprima.Identifier[]} Variable#identifiers
*/
this.identifiers = [];
/**
* List of {@link Reference|references} of this variable (excluding parameter entries)
* in its defining scope and all nested scopes. For defining
* occurrences only see {@link Variable#defs}.
* @member {Reference[]} Variable#references
*/
this.references = [];
/**
* List of defining occurrences of this variable (like in 'var ...'
* statements or as parameter), as custom objects.
* @typedef {Object} DefEntry
* @property {String} DefEntry.type - the type of the occurrence (e.g.
* "Parameter", "Variable", ...)
* @property {esprima.Identifier} DefEntry.name - the identifier AST node of the occurrence
* @property {esprima.Node} DefEntry.node - the enclosing node of the
* identifier
* @property {esprima.Node} [DefEntry.parent] - the enclosing statement
* node of the identifier
* @member {DefEntry[]} Variable#defs
*/
this.defs = [];
this.tainted = false;
/**
* Whether this is a stack variable.
* @member {boolean} Variable#stack
*/
this.stack = true;
/**
* Reference to the enclosing Scope.
* @member {Scope} Variable#scope
*/
this.scope = scope;
}
Variable.CatchClause = 'CatchClause';
Variable.Parameter = 'Parameter';
Variable.FunctionName = 'FunctionName';
Variable.Variable = 'Variable';
Variable.ImplicitGlobalVariable = 'ImplicitGlobalVariable';
function isStrictScope(scope, block) {
var body, i, iz, stmt, expr;
// When upper scope is exists and strict, inner scope is also strict.
if (scope.upper && scope.upper.isStrict) {
return true;
}
if (scope.type === 'function') {
body = block.body;
} else if (scope.type === 'global') {
body = block;
} else {
return false;
}
if (options.directive) {
for (i = 0, iz = body.body.length; i < iz; ++i) {
stmt = body.body[i];
if (stmt.type !== 'DirectiveStatement') {
break;
}
if (stmt.raw === '"use strict"' || stmt.raw === '\'use strict\'') {
return true;
}
}
} else {
for (i = 0, iz = body.body.length; i < iz; ++i) {
stmt = body.body[i];
if (stmt.type !== Syntax.ExpressionStatement) {
break;
}
expr = stmt.expression;
if (expr.type !== Syntax.Literal || typeof expr.value !== 'string') {
break;
}
if (expr.raw != null) {
if (expr.raw === '"use strict"' || expr.raw === '\'use strict\'') {
return true;
}
} else {
if (expr.value === 'use strict') {
return true;
}
}
}
}
return false;
}
/**
* @class Scope
*/
function Scope(block, opt) {
var variable, body;
/**
* One of 'catch', 'with', 'function' or 'global'.
* @member {String} Scope#type
*/
this.type =
(block.type === Syntax.CatchClause) ? 'catch' :
(block.type === Syntax.WithStatement) ? 'with' :
(block.type === Syntax.Program) ? 'global' : 'function';
/**
* The scoped {@link Variable}s of this scope, as <code>{ Variable.name
* : Variable }</code>.
* @member {Map} Scope#set
*/
this.set = new Map();
/**
* The tainted variables of this scope, as <code>{ Variable.name :
* boolean }</code>.
* @member {Map} Scope#taints */
this.taints = new Map();
/**
* Generally, through the lexical scoping of JS you can always know
* which variable an identifier in the source code refers to. There are
* a few exceptions to this rule. With 'global' and 'with' scopes you
* can only decide at runtime which variable a reference refers to.
* Moreover, if 'eval()' is used in a scope, it might introduce new
* bindings in this or its prarent scopes.
* All those scopes are considered 'dynamic'.
* @member {boolean} Scope#dynamic
*/
this.dynamic = this.type === 'global' || this.type === 'with';
/**
* A reference to the scope-defining syntax node.
* @member {esprima.Node} Scope#block
*/
this.block = block;
/**
* The {@link Reference|references} that are not resolved with this scope.
* @member {Reference[]} Scope#through
*/
this.through = [];
/**
* The scoped {@link Variable}s of this scope. In the case of a
* 'function' scope this includes the automatic argument <em>arguments</em> as
* its first element, as well as all further formal arguments.
* @member {Variable[]} Scope#variables
*/
this.variables = [];
/**
* Any variable {@link Reference|reference} found in this scope. This
* includes occurrences of local variables as well as variables from
* parent scopes (including the global scope). For local variables
* this also includes defining occurrences (like in a 'var' statement).
* In a 'function' scope this does not include the occurrences of the
* formal parameter in the parameter list.
* @member {Reference[]} Scope#references
*/
this.references = [];
/**
* List of {@link Reference}s that are left to be resolved (i.e. which
* need to be linked to the variable they refer to). Used internally to
* resolve bindings during scope analysis. On a finalized scope
* analysis, all sopes have <em>left</em> value <strong>null</strong>.
* @member {Reference[]} Scope#left
*/
this.left = [];
/**
* For 'global' and 'function' scopes, this is a self-reference. For
* other scope types this is the <em>variableScope</em> value of the
* parent scope.
* @member {Scope} Scope#variableScope
*/
this.variableScope =
(this.type === 'global' || this.type === 'function') ? this : currentScope.variableScope;
/**
* Whether this scope is created by a FunctionExpression.
* @member {boolean} Scope#functionExpressionScope
*/
this.functionExpressionScope = false;
/**
* Whether this is a scope that contains an 'eval()' invocation.
* @member {boolean} Scope#directCallToEvalScope
*/
this.directCallToEvalScope = false;
/**
* @member {boolean} Scope#thisFound
*/
this.thisFound = false;
body = this.type === 'function' ? block.body : block;
if (opt.naming) {
this.__define(block.id, {
type: Variable.FunctionName,
name: block.id,
node: block
});
this.functionExpressionScope = true;
} else {
if (this.type === 'function') {
variable = new Variable('arguments', this);
this.taints.set('arguments', true);
this.set.set('arguments', variable);
this.variables.push(variable);
}
if (block.type === Syntax.FunctionExpression && block.id) {
new Scope(block, { naming: true });
}
}
/**
* Reference to the parent {@link Scope|scope}.
* @member {Scope} Scope#upper
*/
this.upper = currentScope;
/**
* Whether 'use strict' is in effect in this scope.
* @member {boolean} Scope#isStrict
*/
this.isStrict = isStrictScope(this, block);
/**
* List of nested {@link Scope}s.
* @member {Scope[]} Scope#childScopes
*/
this.childScopes = [];
if (currentScope) {
currentScope.childScopes.push(this);
}
// RAII
currentScope = this;
if (this.type === 'global') {
globalScope = this;
globalScope.implicit = {
set: new Map(),
variables: []
};
}
scopes.push(this);
}
Scope.prototype.__close = function __close() {
var i, iz, ref, current, node, implicit;
// Because if this is global environment, upper is null
if (!this.dynamic || options.optimistic) {
// static resolve
for (i = 0, iz = this.left.length; i < iz; ++i) {
ref = this.left[i];
if (!this.__resolve(ref)) {
this.__delegateToUpperScope(ref);
}
}
} else {
// this is "global" / "with" / "function with eval" environment
if (this.type === 'with') {
for (i = 0, iz = this.left.length; i < iz; ++i) {
ref = this.left[i];
ref.tainted = true;
this.__delegateToUpperScope(ref);
}
} else {
for (i = 0, iz = this.left.length; i < iz; ++i) {
// notify all names are through to global
ref = this.left[i];
current = this;
do {
current.through.push(ref);
current = current.upper;
} while (current);
}
}
}
if (this.type === 'global') {
implicit = [];
for (i = 0, iz = this.left.length; i < iz; ++i) {
ref = this.left[i];
if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) {
implicit.push(ref.__maybeImplicitGlobal);
}
}
// create an implicit global variable from assignment expression
for (i = 0, iz = implicit.length; i < iz; ++i) {
node = implicit[i];
this.__defineImplicit(node.left, {
type: Variable.ImplicitGlobalVariable,
name: node.left,
node: node
});
}
}
this.left = null;
currentScope = this.upper;
};
Scope.prototype.__resolve = function __resolve(ref) {
var variable, name;
name = ref.identifier.name;
if (this.set.has(name)) {
variable = this.set.get(name);
variable.references.push(ref);
variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
if (ref.tainted) {
variable.tainted = true;
this.taints.set(variable.name, true);
}
ref.resolved = variable;
return true;
}
return false;
};
Scope.prototype.__delegateToUpperScope = function __delegateToUpperScope(ref) {
if (this.upper) {
this.upper.left.push(ref);
}
this.through.push(ref);
};
Scope.prototype.__defineImplicit = function __defineImplicit(node, info) {
var name, variable;
if (node && node.type === Syntax.Identifier) {
name = node.name;
if (!this.implicit.set.has(name)) {
variable = new Variable(name, this);
variable.identifiers.push(node);
variable.defs.push(info);
this.implicit.set.set(name, variable);
this.implicit.variables.push(variable);
} else {
variable = this.implicit.set.get(name);
variable.identifiers.push(node);
variable.defs.push(info);
}
}
};
Scope.prototype.__define = function __define(node, info) {
var name, variable;
if (node && node.type === Syntax.Identifier) {
name = node.name;
if (!this.set.has(name)) {
variable = new Variable(name, this);
variable.identifiers.push(node);
variable.defs.push(info);
this.set.set(name, variable);
this.variables.push(variable);
} else {
variable = this.set.get(name);
variable.identifiers.push(node);
variable.defs.push(info);
}
}
};
Scope.prototype.__referencing = function __referencing(node, assign, writeExpr, maybeImplicitGlobal) {
var ref;
// because Array element may be null
if (node && node.type === Syntax.Identifier) {
ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal);
this.references.push(ref);
this.left.push(ref);
}
};
Scope.prototype.__detectEval = function __detectEval() {
var current;
current = this;
this.directCallToEvalScope = true;
do {
current.dynamic = true;
current = current.upper;
} while (current);
};
Scope.prototype.__detectThis = function __detectThis() {
this.thisFound = true;
};
Scope.prototype.__isClosed = function isClosed() {
return this.left === null;
};
// API Scope#resolve(name)
// returns resolved reference
Scope.prototype.resolve = function resolve(ident) {
var ref, i, iz;
assert(this.__isClosed(), 'scope should be closed');
assert(ident.type === Syntax.Identifier, 'target should be identifier');
for (i = 0, iz = this.references.length; i < iz; ++i) {
ref = this.references[i];
if (ref.identifier === ident) {
return ref;
}
}
return null;
};
// API Scope#isStatic
// returns this scope is static
Scope.prototype.isStatic = function isStatic() {
return !this.dynamic;
};
// API Scope#isArgumentsMaterialized
// return this scope has materialized arguments
Scope.prototype.isArgumentsMaterialized = function isArgumentsMaterialized() {
// TODO(Constellation)
// We can more aggressive on this condition like this.
//
// function t() {
// // arguments of t is always hidden.
// function arguments() {
// }
// }
var variable;
// This is not function scope
if (this.type !== 'function') {
return true;
}
if (!this.isStatic()) {
return true;
}
variable = this.set.get('arguments');
assert(variable, 'always have arguments variable');
return variable.tainted || variable.references.length !== 0;
};
// API Scope#isThisMaterialized
// return this scope has materialized `this` reference
Scope.prototype.isThisMaterialized = function isThisMaterialized() {
// This is not function scope
if (this.type !== 'function') {
return true;
}
if (!this.isStatic()) {
return true;
}
return this.thisFound;
};
Scope.mangledName = '__$escope$__';
Scope.prototype.attach = function attach() {
if (!this.functionExpressionScope) {
this.block[Scope.mangledName] = this;
}
};
Scope.prototype.detach = function detach() {
if (!this.functionExpressionScope) {
delete this.block[Scope.mangledName];
}
};
Scope.prototype.isUsedName = function (name) {
if (this.set.has(name)) {
return true;
}
for (var i = 0, iz = this.through.length; i < iz; ++i) {
if (this.through[i].identifier.name === name) {
return true;
}
}
return false;
};
/**
* @class ScopeManager
*/
function ScopeManager(scopes) {
this.scopes = scopes;
this.attached = false;
}
// Returns appropliate scope for this node
ScopeManager.prototype.__get = function __get(node) {
var i, iz, scope;
if (this.attached) {
return node[Scope.mangledName] || null;
}
if (Scope.isScopeRequired(node)) {
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
scope = this.scopes[i];
if (!scope.functionExpressionScope) {
if (scope.block === node) {
return scope;
}
}
}
}
return null;
};
ScopeManager.prototype.acquire = function acquire(node) {
return this.__get(node);
};
ScopeManager.prototype.release = function release(node) {
var scope = this.__get(node);
if (scope) {
scope = scope.upper;
while (scope) {
if (!scope.functionExpressionScope) {
return scope;
}
scope = scope.upper;
}
}
return null;
};
ScopeManager.prototype.attach = function attach() {
var i, iz;
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
this.scopes[i].attach();
}
this.attached = true;
};
ScopeManager.prototype.detach = function detach() {
var i, iz;
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
this.scopes[i].detach();
}
this.attached = false;
};
Scope.isScopeRequired = function isScopeRequired(node) {
return Scope.isVariableScopeRequired(node) || node.type === Syntax.WithStatement || node.type === Syntax.CatchClause;
};
Scope.isVariableScopeRequired = function isVariableScopeRequired(node) {
return node.type === Syntax.Program || node.type === Syntax.FunctionExpression || node.type === Syntax.FunctionDeclaration;
};
/**
* Main interface function. Takes an Esprima syntax tree and returns the
* analyzed scopes.
* @function analyze
* @param {esprima.Tree} tree
* @param {Object} providedOptions - Options that tailor the scope analysis
* @param {boolean} [providedOptions.optimistic=false] - the optimistic flag
* @param {boolean} [providedOptions.directive=false]- the directive flag
* @param {boolean} [providedOptions.ignoreEval=false]- whether to check 'eval()' calls
* @return {ScopeManager}
*/
function analyze(tree, providedOptions) {
var resultScopes;
options = updateDeeply(defaultOptions(), providedOptions);
resultScopes = scopes = [];
currentScope = null;
globalScope = null;
// attach scope and collect / resolve names
estraverse.traverse(tree, {
enter: function enter(node) {
var i, iz, decl;
if (Scope.isScopeRequired(node)) {
new Scope(node, {});
}
switch (node.type) {
case Syntax.AssignmentExpression:
if (node.operator === '=') {
currentScope.__referencing(node.left, Reference.WRITE, node.right, (!currentScope.isStrict && node.left.name != null) && node);
} else {
currentScope.__referencing(node.left, Reference.RW, node.right);
}
currentScope.__referencing(node.right);
break;
case Syntax.ArrayExpression:
for (i = 0, iz = node.elements.length; i < iz; ++i) {
currentScope.__referencing(node.elements[i]);
}
break;
case Syntax.BlockStatement:
break;
case Syntax.BinaryExpression:
currentScope.__referencing(node.left);
currentScope.__referencing(node.right);
break;
case Syntax.BreakStatement:
break;
case Syntax.CallExpression:
currentScope.__referencing(node.callee);
for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
currentScope.__referencing(node['arguments'][i]);
}
// check this is direct call to eval
if (!options.ignoreEval && node.callee.type === Syntax.Identifier && node.callee.name === 'eval') {
currentScope.variableScope.__detectEval();
}
break;
case Syntax.CatchClause:
currentScope.__define(node.param, {
type: Variable.CatchClause,
name: node.param,
node: node
});
break;
case Syntax.ConditionalExpression:
currentScope.__referencing(node.test);
currentScope.__referencing(node.consequent);
currentScope.__referencing(node.alternate);
break;
case Syntax.ContinueStatement:
break;
case Syntax.DirectiveStatement:
break;
case Syntax.DoWhileStatement:
currentScope.__referencing(node.test);
break;
case Syntax.DebuggerStatement:
break;
case Syntax.EmptyStatement:
break;
case Syntax.ExpressionStatement:
currentScope.__referencing(node.expression);
break;
case Syntax.ForStatement:
currentScope.__referencing(node.init);
currentScope.__referencing(node.test);
currentScope.__referencing(node.update);
break;
case Syntax.ForInStatement:
if (node.left.type === Syntax.VariableDeclaration) {
currentScope.__referencing(node.left.declarations[0].id, Reference.WRITE, null, false);
} else {
currentScope.__referencing(node.left, Reference.WRITE, null, (!currentScope.isStrict && node.left.name != null) && node);
}
currentScope.__referencing(node.right);
break;
case Syntax.FunctionDeclaration:
// FunctionDeclaration name is defined in upper scope
currentScope.upper.__define(node.id, {
type: Variable.FunctionName,
name: node.id,
node: node
});
for (i = 0, iz = node.params.length; i < iz; ++i) {
currentScope.__define(node.params[i], {
type: Variable.Parameter,
name: node.params[i],
node: node,
index: i
});
}
break;
case Syntax.FunctionExpression:
// id is defined in upper scope
for (i = 0, iz = node.params.length; i < iz; ++i) {
currentScope.__define(node.params[i], {
type: Variable.Parameter,
name: node.params[i],
node: node,
index: i
});
}
break;
case Syntax.Identifier:
break;
case Syntax.IfStatement:
currentScope.__referencing(node.test);
break;
case Syntax.Literal:
break;
case Syntax.LabeledStatement:
break;
case Syntax.LogicalExpression:
currentScope.__referencing(node.left);
currentScope.__referencing(node.right);
break;
case Syntax.MemberExpression:
currentScope.__referencing(node.object);
if (node.computed) {
currentScope.__referencing(node.property);
}
break;
case Syntax.NewExpression:
currentScope.__referencing(node.callee);
for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
currentScope.__referencing(node['arguments'][i]);
}
break;
case Syntax.ObjectExpression:
break;
case Syntax.Program:
break;
case Syntax.Property:
currentScope.__referencing(node.value);
break;
case Syntax.ReturnStatement:
currentScope.__referencing(node.argument);
break;
case Syntax.SequenceExpression:
for (i = 0, iz = node.expressions.length; i < iz; ++i) {
currentScope.__referencing(node.expressions[i]);
}
break;
case Syntax.SwitchStatement:
currentScope.__referencing(node.discriminant);
break;
case Syntax.SwitchCase:
currentScope.__referencing(node.test);
break;
case Syntax.ThisExpression:
currentScope.variableScope.__detectThis();
break;
case Syntax.ThrowStatement:
currentScope.__referencing(node.argument);
break;
case Syntax.TryStatement:
break;
case Syntax.UnaryExpression:
currentScope.__referencing(node.argument);
break;
case Syntax.UpdateExpression:
currentScope.__referencing(node.argument, Reference.RW, null);
break;
case Syntax.VariableDeclaration:
for (i = 0, iz = node.declarations.length; i < iz; ++i) {
decl = node.declarations[i];
currentScope.variableScope.__define(decl.id, {
type: Variable.Variable,
name: decl.id,
node: decl,
index: i,
parent: node
});
if (decl.init) {
// initializer is found
currentScope.__referencing(decl.id, Reference.WRITE, decl.init, false);
currentScope.__referencing(decl.init);
}
}
break;
case Syntax.VariableDeclarator:
break;
case Syntax.WhileStatement:
currentScope.__referencing(node.test);
break;
case Syntax.WithStatement:
// WithStatement object is referenced at upper scope
currentScope.upper.__referencing(node.object);
break;
}
},
leave: function leave(node) {
while (currentScope && node === currentScope.block) {
currentScope.__close();
}
}
});
assert(currentScope === null);
globalScope = null;
scopes = null;
options = null;
return new ScopeManager(resultScopes);
}
/** @name module:escope.version */
exports.version = '1.0.1';
/** @name module:escope.Reference */
exports.Reference = Reference;
/** @name module:escope.Variable */
exports.Variable = Variable;
/** @name module:escope.Scope */
exports.Scope = Scope;
/** @name module:escope.ScopeManager */
exports.ScopeManager = ScopeManager;
/** @name module:escope.analyze */
exports.analyze = analyze;
}, this));
/* vim: set sw=4 ts=4 et tw=80 : */
},{"estraverse":7}],6:[function(require,module,exports){
/*
Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com>
Copyright (C) 2013 Mathias Bynens <mathias@qiwi.be>
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint bitwise:true plusplus:true */
/*global esprima:true, define:true, exports:true, window: true,
throwErrorTolerant: true,
throwError: true, generateStatement: true, peek: true,
parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
parseFunctionDeclaration: true, parseFunctionExpression: true,
parseFunctionSourceElements: true, parseVariableIdentifier: true,
parseLeftHandSideExpression: true,
parseUnaryExpression: true,
parseStatement: true, parseSourceElement: true */
(function (root, factory) {
'use strict';
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// Rhino, and plain browser loading.
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.esprima = {}));
}
}(this, function (exports) {
'use strict';
var Token,
TokenName,
FnExprTokens,
Syntax,
PropertyKind,
Messages,
Regex,
SyntaxTreeDelegate,
source,
strict,
index,
lineNumber,
lineStart,
length,
delegate,
lookahead,
state,
extra;
Token = {
BooleanLiteral: 1,
EOF: 2,
Identifier: 3,
Keyword: 4,
NullLiteral: 5,
NumericLiteral: 6,
Punctuator: 7,
StringLiteral: 8,
RegularExpression: 9
};
TokenName = {};
TokenName[Token.BooleanLiteral] = 'Boolean';
TokenName[Token.EOF] = '<end>';
TokenName[Token.Identifier] = 'Identifier';
TokenName[Token.Keyword] = 'Keyword';
TokenName[Token.NullLiteral] = 'Null';
TokenName[Token.NumericLiteral] = 'Numeric';
TokenName[Token.Punctuator] = 'Punctuator';
TokenName[Token.StringLiteral] = 'String';
TokenName[Token.RegularExpression] = 'RegularExpression';
// A function following one of those tokens is an expression.
FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
'return', 'case', 'delete', 'throw', 'void',
// assignment operators
'=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=',
'&=', '|=', '^=', ',',
// binary/unary operators
'+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&',
'|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=',
'<=', '<', '>', '!=', '!=='];
Syntax = {
AssignmentExpression: 'AssignmentExpression',
ArrayExpression: 'ArrayExpression',
BlockStatement: 'BlockStatement',
BinaryExpression: 'BinaryExpression',
BreakStatement: 'BreakStatement',
CallExpression: 'CallExpression',
CatchClause: 'CatchClause',
ConditionalExpression: 'ConditionalExpression',
ContinueStatement: 'ContinueStatement',
DoWhileStatement: 'DoWhileStatement',
DebuggerStatement: 'DebuggerStatement',
EmptyStatement: 'EmptyStatement',
ExpressionStatement: 'ExpressionStatement',
ForStatement: 'ForStatement',
ForInStatement: 'ForInStatement',
FunctionDeclaration: 'FunctionDeclaration',
FunctionExpression: 'FunctionExpression',
Identifier: 'Identifier',
IfStatement: 'IfStatement',
Literal: 'Literal',
LabeledStatement: 'LabeledStatement',
LogicalExpression: 'LogicalExpression',
MemberExpression: 'MemberExpression',
NewExpression: 'NewExpression',
ObjectExpression: 'ObjectExpression',
Program: 'Program',
Property: 'Property',
ReturnStatement: 'ReturnStatement',
SequenceExpression: 'SequenceExpression',
SwitchStatement: 'SwitchStatement',
SwitchCase: 'SwitchCase',
ThisExpression: 'ThisExpression',
ThrowStatement: 'ThrowStatement',
TryStatement: 'TryStatement',
UnaryExpression: 'UnaryExpression',
UpdateExpression: 'UpdateExpression',
VariableDeclaration: 'VariableDeclaration',
VariableDeclarator: 'VariableDeclarator',
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement'
};
PropertyKind = {
Data: 1,
Get: 2,
Set: 4
};
// Error messages should be identical to V8.
Messages = {
UnexpectedToken: 'Unexpected token %0',
UnexpectedNumber: 'Unexpected number',
UnexpectedString: 'Unexpected string',
UnexpectedIdentifier: 'Unexpected identifier',
UnexpectedReserved: 'Unexpected reserved word',
UnexpectedEOS: 'Unexpected end of input',
NewlineAfterThrow: 'Illegal newline after throw',
InvalidRegExp: 'Invalid regular expression',
UnterminatedRegExp: 'Invalid regular expression: missing /',
InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
InvalidLHSInForIn: 'Invalid left-hand side in for-in',
MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
NoCatchOrFinally: 'Missing catch or finally after try',
UnknownLabel: 'Undefined label \'%0\'',
Redeclaration: '%0 \'%1\' has already been declared',
IllegalContinue: 'Illegal continue statement',
IllegalBreak: 'Illegal break statement',
IllegalReturn: 'Illegal return statement',
StrictModeWith: 'Strict mode code may not include a with statement',
StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
StrictVarName: 'Variable name may not be eval or arguments in strict mode',
StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
StrictDelete: 'Delete of an unqualified identifier in strict mode.',
StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
StrictReservedWord: 'Use of future reserved word in strict mode'
};
// See also tools/generate-unicode-regex.py.
Regex = {
NonAsciiIdentifierStart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]'),
NonAsciiIdentifierPart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]')
};
// Ensure the condition is true, otherwise throw an error.
// This is only to have a better contract semantic, i.e. another safety net
// to catch a logic error. The condition shall be fulfilled in normal case.
// Do NOT use this to enforce a certain condition on any user input.
function assert(condition, message) {
/* istanbul ignore if */
if (!condition) {
throw new Error('ASSERT: ' + message);
}
}
function isDecimalDigit(ch) {
return (ch >= 48 && ch <= 57); // 0..9
}
function isHexDigit(ch) {
return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
}
function isOctalDigit(ch) {
return '01234567'.indexOf(ch) >= 0;
}
// 7.2 White Space
function isWhiteSpace(ch) {
return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
(ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0);
}
// 7.3 Line Terminators
function isLineTerminator(ch) {
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029);
}
// 7.6 Identifier Names and Identifiers
function isIdentifierStart(ch) {
return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore)
(ch >= 0x41 && ch <= 0x5A) || // A..Z
(ch >= 0x61 && ch <= 0x7A) || // a..z
(ch === 0x5C) || // \ (backslash)
((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch)));
}
function isIdentifierPart(ch) {
return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore)
(ch >= 0x41 && ch <= 0x5A) || // A..Z
(ch >= 0x61 && ch <= 0x7A) || // a..z
(ch >= 0x30 && ch <= 0x39) || // 0..9
(ch === 0x5C) || // \ (backslash)
((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch)));
}
// 7.6.1.2 Future Reserved Words
function isFutureReservedWord(id) {
switch (id) {
case 'class':
case 'enum':
case 'export':
case 'extends':
case 'import':
case 'super':
return true;
default:
return false;
}
}
function isStrictModeReservedWord(id) {
switch (id) {
case 'implements':
case 'interface':
case 'package':
case 'private':
case 'protected':
case 'public':
case 'static':
case 'yield':
case 'let':
return true;
default:
return false;
}
}
function isRestrictedWord(id) {
return id === 'eval' || id === 'arguments';
}
// 7.6.1.1 Keywords
function isKeyword(id) {
if (strict && isStrictModeReservedWord(id)) {
return true;
}
// 'const' is specialized as Keyword in V8.
// 'yield' and 'let' are for compatiblity with SpiderMonkey and ES.next.
// Some others are from future reserved words.
switch (id.length) {
case 2:
return (id === 'if') || (id === 'in') || (id === 'do');
case 3:
return (id === 'var') || (id === 'for') || (id === 'new') ||
(id === 'try') || (id === 'let');
case 4:
return (id === 'this') || (id === 'else') || (id === 'case') ||
(id === 'void') || (id === 'with') || (id === 'enum');
case 5:
return (id === 'while') || (id === 'break') || (id === 'catch') ||
(id === 'throw') || (id === 'const') || (id === 'yield') ||
(id === 'class') || (id === 'super');
case 6:
return (id === 'return') || (id === 'typeof') || (id === 'delete') ||
(id === 'switch') || (id === 'export') || (id === 'import');
case 7:
return (id === 'default') || (id === 'finally') || (id === 'extends');
case 8:
return (id === 'function') || (id === 'continue') || (id === 'debugger');
case 10:
return (id === 'instanceof');
default:
return false;
}
}
// 7.4 Comments
function addComment(type, value, start, end, loc) {
var comment, attacher;
assert(typeof start === 'number', 'Comment must have valid position');
// Because the way the actual token is scanned, often the comments
// (if any) are skipped twice during the lexical analysis.
// Thus, we need to skip adding a comment if the comment array already
// handled it.
if (state.lastCommentStart >= start) {
return;
}
state.lastCommentStart = start;
comment = {
type: type,
value: value
};
if (extra.range) {
comment.range = [start, end];
}
if (extra.loc) {
comment.loc = loc;
}
extra.comments.push(comment);
if (extra.attachComment) {
extra.leadingComments.push(comment);
extra.trailingComments.push(comment);
}
}
function skipSingleLineComment(offset) {
var start, loc, ch, comment;
start = index - offset;
loc = {
start: {
line: lineNumber,
column: index - lineStart - offset
}
};
while (index < length) {
ch = source.charCodeAt(index);
++index;
if (isLineTerminator(ch)) {
if (extra.comments) {
comment = source.slice(start + offset, index - 1);
loc.end = {
line: lineNumber,
column: index - lineStart - 1
};
addComment('Line', comment, start, index - 1, loc);
}
if (ch === 13 && source.charCodeAt(index) === 10) {
++index;
}
++lineNumber;
lineStart = index;
return;
}
}
if (extra.comments) {
comment = source.slice(start + offset, index);
loc.end = {
line: lineNumber,
column: index - lineStart
};
addComment('Line', comment, start, index, loc);
}
}
function skipMultiLineComment() {
var start, loc, ch, comment;
if (extra.comments) {
start = index - 2;
loc = {
start: {
line: lineNumber,
column: index - lineStart - 2
}
};
}
while (index < length) {
ch = source.charCodeAt(index);
if (isLineTerminator(ch)) {
if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) {
++index;
}
++lineNumber;
++index;
lineStart = index;
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else if (ch === 0x2A) {
// Block comment ends with '*/'.
if (source.charCodeAt(index + 1) === 0x2F) {
++index;
++index;
if (extra.comments) {
comment = source.slice(start + 2, index - 2);
loc.end = {
line: lineNumber,
column: index - lineStart
};
addComment('Block', comment, start, index, loc);
}
return;
}
++index;
} else {
++index;
}
}
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
function skipComment() {
var ch, start;
start = (index === 0);
while (index < length) {
ch = source.charCodeAt(index);
if (isWhiteSpace(ch)) {
++index;
} else if (isLineTerminator(ch)) {
++index;
if (ch === 0x0D && source.charCodeAt(index) === 0x0A) {
++index;
}
++lineNumber;
lineStart = index;
start = true;
} else if (ch === 0x2F) { // U+002F is '/'
ch = source.charCodeAt(index + 1);
if (ch === 0x2F) {
++index;
++index;
skipSingleLineComment(2);
start = true;
} else if (ch === 0x2A) { // U+002A is '*'
++index;
++index;
skipMultiLineComment();
} else {
break;
}
} else if (start && ch === 0x2D) { // U+002D is '-'
// U+003E is '>'
if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) {
// '-->' is a single-line comment
index += 3;
skipSingleLineComment(3);
} else {
break;
}
} else if (ch === 0x3C) { // U+003C is '<'
if (source.slice(index + 1, index + 4) === '!--') {
++index; // `<`
++index; // `!`
++index; // `-`
++index; // `-`
skipSingleLineComment(4);
} else {
break;
}
} else {
break;
}
}
}
function scanHexEscape(prefix) {
var i, len, ch, code = 0;
len = (prefix === 'u') ? 4 : 2;
for (i = 0; i < len; ++i) {
if (index < length && isHexDigit(source[index])) {
ch = source[index++];
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
} else {
return '';
}
}
return String.fromCharCode(code);
}
function getEscapedIdentifier() {
var ch, id;
ch = source.charCodeAt(index++);
id = String.fromCharCode(ch);
// '\u' (U+005C, U+0075) denotes an escaped character.
if (ch === 0x5C) {
if (source.charCodeAt(index) !== 0x75) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
++index;
ch = scanHexEscape('u');
if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
id = ch;
}
while (index < length) {
ch = source.charCodeAt(index);
if (!isIdentifierPart(ch)) {
break;
}
++index;
id += String.fromCharCode(ch);
// '\u' (U+005C, U+0075) denotes an escaped character.
if (ch === 0x5C) {
id = id.substr(0, id.length - 1);
if (source.charCodeAt(index) !== 0x75) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
++index;
ch = scanHexEscape('u');
if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
id += ch;
}
}
return id;
}
function getIdentifier() {
var start, ch;
start = index++;
while (index < length) {
ch = source.charCodeAt(index);
if (ch === 0x5C) {
// Blackslash (U+005C) marks Unicode escape sequence.
index = start;
return getEscapedIdentifier();
}
if (isIdentifierPart(ch)) {
++index;
} else {
break;
}
}
return source.slice(start, index);
}
function scanIdentifier() {
var start, id, type;
start = index;
// Backslash (U+005C) starts an escaped character.
id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier();
// There is no keyword or literal with only one character.
// Thus, it must be an identifier.
if (id.length === 1) {
type = Token.Identifier;
} else if (isKeyword(id)) {
type = Token.Keyword;
} else if (id === 'null') {
type = Token.NullLiteral;
} else if (id === 'true' || id === 'false') {
type = Token.BooleanLiteral;
} else {
type = Token.Identifier;
}
return {
type: type,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// 7.7 Punctuators
function scanPunctuator() {
var start = index,
code = source.charCodeAt(index),
code2,
ch1 = source[index],
ch2,
ch3,
ch4;
switch (code) {
// Check for most common single-character punctuators.
case 0x2E: // . dot
case 0x28: // ( open bracket
case 0x29: // ) close bracket
case 0x3B: // ; semicolon
case 0x2C: // , comma
case 0x7B: // { open curly brace
case 0x7D: // } close curly brace
case 0x5B: // [
case 0x5D: // ]
case 0x3A: // :
case 0x3F: // ?
case 0x7E: // ~
++index;
if (extra.tokenize) {
if (code === 0x28) {
extra.openParenToken = extra.tokens.length;
} else if (code === 0x7B) {
extra.openCurlyToken = extra.tokens.length;
}
}
return {
type: Token.Punctuator,
value: String.fromCharCode(code),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
default:
code2 = source.charCodeAt(index + 1);
// '=' (U+003D) marks an assignment or comparison operator.
if (code2 === 0x3D) {
switch (code) {
case 0x2B: // +
case 0x2D: // -
case 0x2F: // /
case 0x3C: // <
case 0x3E: // >
case 0x5E: // ^
case 0x7C: // |
case 0x25: // %
case 0x26: // &
case 0x2A: // *
index += 2;
return {
type: Token.Punctuator,
value: String.fromCharCode(code) + String.fromCharCode(code2),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
case 0x21: // !
case 0x3D: // =
index += 2;
// !== and ===
if (source.charCodeAt(index) === 0x3D) {
++index;
}
return {
type: Token.Punctuator,
value: source.slice(start, index),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
}
}
// 4-character punctuator: >>>=
ch4 = source.substr(index, 4);
if (ch4 === '>>>=') {
index += 4;
return {
type: Token.Punctuator,
value: ch4,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// 3-character punctuators: === !== >>> <<= >>=
ch3 = ch4.substr(0, 3);
if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') {
index += 3;
return {
type: Token.Punctuator,
value: ch3,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// Other 2-character punctuators: ++ -- << >> && ||
ch2 = ch3.substr(0, 2);
if ((ch1 === ch2[1] && ('+-<>&|'.indexOf(ch1) >= 0)) || ch2 === '=>') {
index += 2;
return {
type: Token.Punctuator,
value: ch2,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// 1-character punctuators: < > = ! + - * % & | ^ /
if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
++index;
return {
type: Token.Punctuator,
value: ch1,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
// 7.8.3 Numeric Literals
function scanHexLiteral(start) {
var number = '';
while (index < length) {
if (!isHexDigit(source[index])) {
break;
}
number += source[index++];
}
if (number.length === 0) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
if (isIdentifierStart(source.charCodeAt(index))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.NumericLiteral,
value: parseInt('0x' + number, 16),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
function scanOctalLiteral(start) {
var number = '0' + source[index++];
while (index < length) {
if (!isOctalDigit(source[index])) {
break;
}
number += source[index++];
}
if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.NumericLiteral,
value: parseInt(number, 8),
octal: true,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
function scanNumericLiteral() {
var number, start, ch;
ch = source[index];
assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
'Numeric literal must start with a decimal digit or a decimal point');
start = index;
number = '';
if (ch !== '.') {
number = source[index++];
ch = source[index];
// Hex number starts with '0x'.
// Octal number starts with '0'.
if (number === '0') {
if (ch === 'x' || ch === 'X') {
++index;
return scanHexLiteral(start);
}
if (isOctalDigit(ch)) {
return scanOctalLiteral(start);
}
// decimal number starts with '0' such as '09' is illegal.
if (ch && isDecimalDigit(ch.charCodeAt(0))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
ch = source[index];
}
if (ch === '.') {
number += source[index++];
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
ch = source[index];
}
if (ch === 'e' || ch === 'E') {
number += source[index++];
ch = source[index];
if (ch === '+' || ch === '-') {
number += source[index++];
}
if (isDecimalDigit(source.charCodeAt(index))) {
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
} else {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
if (isIdentifierStart(source.charCodeAt(index))) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.NumericLiteral,
value: parseFloat(number),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// 7.8.4 String Literals
function scanStringLiteral() {
var str = '', quote, start, ch, code, unescaped, restore, octal = false, startLineNumber, startLineStart;
startLineNumber = lineNumber;
startLineStart = lineStart;
quote = source[index];
assert((quote === '\'' || quote === '"'),
'String literal must starts with a quote');
start = index;
++index;
while (index < length) {
ch = source[index++];
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
ch = source[index++];
if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
switch (ch) {
case 'u':
case 'x':
restore = index;
unescaped = scanHexEscape(ch);
if (unescaped) {
str += unescaped;
} else {
index = restore;
str += ch;
}
break;
case 'n':
str += '\n';
break;
case 'r':
str += '\r';
break;
case 't':
str += '\t';
break;
case 'b':
str += '\b';
break;
case 'f':
str += '\f';
break;
case 'v':
str += '\x0B';
break;
default:
if (isOctalDigit(ch)) {
code = '01234567'.indexOf(ch);
// \0 is not octal escape sequence
if (code !== 0) {
octal = true;
}
if (index < length && isOctalDigit(source[index])) {
octal = true;
code = code * 8 + '01234567'.indexOf(source[index++]);
// 3 digits are only allowed when string starts
// with 0, 1, 2, 3
if ('0123'.indexOf(ch) >= 0 &&
index < length &&
isOctalDigit(source[index])) {
code = code * 8 + '01234567'.indexOf(source[index++]);
}
}
str += String.fromCharCode(code);
} else {
str += ch;
}
break;
}
} else {
++lineNumber;
if (ch === '\r' && source[index] === '\n') {
++index;
}
lineStart = index;
}
} else if (isLineTerminator(ch.charCodeAt(0))) {
break;
} else {
str += ch;
}
}
if (quote !== '') {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.StringLiteral,
value: str,
octal: octal,
startLineNumber: startLineNumber,
startLineStart: startLineStart,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
function testRegExp(pattern, flags) {
var value;
try {
value = new RegExp(pattern, flags);
} catch (e) {
throwError({}, Messages.InvalidRegExp);
}
return value;
}
function scanRegExpBody() {
var ch, str, classMarker, terminated, body;
ch = source[index];
assert(ch === '/', 'Regular expression literal must start with a slash');
str = source[index++];
classMarker = false;
terminated = false;
while (index < length) {
ch = source[index++];
str += ch;
if (ch === '\\') {
ch = source[index++];
// ECMA-262 7.8.5
if (isLineTerminator(ch.charCodeAt(0))) {
throwError({}, Messages.UnterminatedRegExp);
}
str += ch;
} else if (isLineTerminator(ch.charCodeAt(0))) {
throwError({}, Messages.UnterminatedRegExp);
} else if (classMarker) {
if (ch === ']') {
classMarker = false;
}
} else {
if (ch === '/') {
terminated = true;
break;
} else if (ch === '[') {
classMarker = true;
}
}
}
if (!terminated) {
throwError({}, Messages.UnterminatedRegExp);
}
// Exclude leading and trailing slash.
body = str.substr(1, str.length - 2);
return {
value: body,
literal: str
};
}
function scanRegExpFlags() {
var ch, str, flags, restore;
str = '';
flags = '';
while (index < length) {
ch = source[index];
if (!isIdentifierPart(ch.charCodeAt(0))) {
break;
}
++index;
if (ch === '\\' && index < length) {
ch = source[index];
if (ch === 'u') {
++index;
restore = index;
ch = scanHexEscape('u');
if (ch) {
flags += ch;
for (str += '\\u'; restore < index; ++restore) {
str += source[restore];
}
} else {
index = restore;
flags += 'u';
str += '\\u';
}
throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL');
} else {
str += '\\';
throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
flags += ch;
str += ch;
}
}
return {
value: flags,
literal: str
};
}
function scanRegExp() {
var start, body, flags, pattern, value;
lookahead = null;
skipComment();
start = index;
body = scanRegExpBody();
flags = scanRegExpFlags();
value = testRegExp(body.value, flags.value);
if (extra.tokenize) {
return {
type: Token.RegularExpression,
value: value,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
return {
literal: body.literal + flags.literal,
value: value,
start: start,
end: index
};
}
function collectRegex() {
var pos, loc, regex, token;
skipComment();
pos = index;
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
regex = scanRegExp();
loc.end = {
line: lineNumber,
column: index - lineStart
};
/* istanbul ignore next */
if (!extra.tokenize) {
// Pop the previous token, which is likely '/' or '/='
if (extra.tokens.length > 0) {
token = extra.tokens[extra.tokens.length - 1];
if (token.range[0] === pos && token.type === 'Punctuator') {
if (token.value === '/' || token.value === '/=') {
extra.tokens.pop();
}
}
}
extra.tokens.push({
type: 'RegularExpression',
value: regex.literal,
range: [pos, index],
loc: loc
});
}
return regex;
}
function isIdentifierName(token) {
return token.type === Token.Identifier ||
token.type === Token.Keyword ||
token.type === Token.BooleanLiteral ||
token.type === Token.NullLiteral;
}
function advanceSlash() {
var prevToken,
checkToken;
// Using the following algorithm:
// https://github.com/mozilla/sweet.js/wiki/design
prevToken = extra.tokens[extra.tokens.length - 1];
if (!prevToken) {
// Nothing before that: it cannot be a division.
return collectRegex();
}
if (prevToken.type === 'Punctuator') {
if (prevToken.value === ']') {
return scanPunctuator();
}
if (prevToken.value === ')') {
checkToken = extra.tokens[extra.openParenToken - 1];
if (checkToken &&
checkToken.type === 'Keyword' &&
(checkToken.value === 'if' ||
checkToken.value === 'while' ||
checkToken.value === 'for' ||
checkToken.value === 'with')) {
return collectRegex();
}
return scanPunctuator();
}
if (prevToken.value === '}') {
// Dividing a function by anything makes little sense,
// but we have to check for that.
if (extra.tokens[extra.openCurlyToken - 3] &&
extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') {
// Anonymous function.
checkToken = extra.tokens[extra.openCurlyToken - 4];
if (!checkToken) {
return scanPunctuator();
}
} else if (extra.tokens[extra.openCurlyToken - 4] &&
extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') {
// Named function.
checkToken = extra.tokens[extra.openCurlyToken - 5];
if (!checkToken) {
return collectRegex();
}
} else {
return scanPunctuator();
}
// checkToken determines whether the function is
// a declaration or an expression.
if (FnExprTokens.indexOf(checkToken.value) >= 0) {
// It is an expression.
return scanPunctuator();
}
// It is a declaration.
return collectRegex();
}
return collectRegex();
}
if (prevToken.type === 'Keyword') {
return collectRegex();
}
return scanPunctuator();
}
function advance() {
var ch;
skipComment();
if (index >= length) {
return {
type: Token.EOF,
lineNumber: lineNumber,
lineStart: lineStart,
start: index,
end: index
};
}
ch = source.charCodeAt(index);
if (isIdentifierStart(ch)) {
return scanIdentifier();
}
// Very common: ( and ) and ;
if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
return scanPunctuator();
}
// String literal starts with single quote (U+0027) or double quote (U+0022).
if (ch === 0x27 || ch === 0x22) {
return scanStringLiteral();
}
// Dot (.) U+002E can also start a floating-point number, hence the need
// to check the next character.
if (ch === 0x2E) {
if (isDecimalDigit(source.charCodeAt(index + 1))) {
return scanNumericLiteral();
}
return scanPunctuator();
}
if (isDecimalDigit(ch)) {
return scanNumericLiteral();
}
// Slash (/) U+002F can also start a regex.
if (extra.tokenize && ch === 0x2F) {
return advanceSlash();
}
return scanPunctuator();
}
function collectToken() {
var loc, token, range, value;
skipComment();
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
token = advance();
loc.end = {
line: lineNumber,
column: index - lineStart
};
if (token.type !== Token.EOF) {
value = source.slice(token.start, token.end);
extra.tokens.push({
type: TokenName[token.type],
value: value,
range: [token.start, token.end],
loc: loc
});
}
return token;
}
function lex() {
var token;
token = lookahead;
index = token.end;
lineNumber = token.lineNumber;
lineStart = token.lineStart;
lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance();
index = token.end;
lineNumber = token.lineNumber;
lineStart = token.lineStart;
return token;
}
function peek() {
var pos, line, start;
pos = index;
line = lineNumber;
start = lineStart;
lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance();
index = pos;
lineNumber = line;
lineStart = start;
}
function Position(line, column) {
this.line = line;
this.column = column;
}
function SourceLocation(startLine, startColumn, line, column) {
this.start = new Position(startLine, startColumn);
this.end = new Position(line, column);
}
SyntaxTreeDelegate = {
name: 'SyntaxTree',
processComment: function (node) {
var lastChild, trailingComments;
if (node.type === Syntax.Program) {
if (node.body.length > 0) {
return;
}
}
if (extra.trailingComments.length > 0) {
if (extra.trailingComments[0].range[0] >= node.range[1]) {
trailingComments = extra.trailingComments;
extra.trailingComments = [];
} else {
extra.trailingComments.length = 0;
}
} else {
if (extra.bottomRightStack.length > 0 &&
extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments &&
extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments[0].range[0] >= node.range[1]) {
trailingComments = extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
delete extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
}
}
// Eating the stack.
while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) {
lastChild = extra.bottomRightStack.pop();
}
if (lastChild) {
if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) {
node.leadingComments = lastChild.leadingComments;
delete lastChild.leadingComments;
}
} else if (extra.leadingComments.length > 0 && extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) {
node.leadingComments = extra.leadingComments;
extra.leadingComments = [];
}
if (trailingComments) {
node.trailingComments = trailingComments;
}
extra.bottomRightStack.push(node);
},
markEnd: function (node, startToken) {
if (extra.range) {
node.range = [startToken.start, index];
}
if (extra.loc) {
node.loc = new SourceLocation(
startToken.startLineNumber === undefined ? startToken.lineNumber : startToken.startLineNumber,
startToken.start - (startToken.startLineStart === undefined ? startToken.lineStart : startToken.startLineStart),
lineNumber,
index - lineStart
);
this.postProcess(node);
}
if (extra.attachComment) {
this.processComment(node);
}
return node;
},
postProcess: function (node) {
if (extra.source) {
node.loc.source = extra.source;
}
return node;
},
createArrayExpression: function (elements) {
return {
type: Syntax.ArrayExpression,
elements: elements
};
},
createAssignmentExpression: function (operator, left, right) {
return {
type: Syntax.AssignmentExpression,
operator: operator,
left: left,
right: right
};
},
createBinaryExpression: function (operator, left, right) {
var type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression :
Syntax.BinaryExpression;
return {
type: type,
operator: operator,
left: left,
right: right
};
},
createBlockStatement: function (body) {
return {
type: Syntax.BlockStatement,
body: body
};
},
createBreakStatement: function (label) {
return {
type: Syntax.BreakStatement,
label: label
};
},
createCallExpression: function (callee, args) {
return {
type: Syntax.CallExpression,
callee: callee,
'arguments': args
};
},
createCatchClause: function (param, body) {
return {
type: Syntax.CatchClause,
param: param,
body: body
};
},
createConditionalExpression: function (test, consequent, alternate) {
return {
type: Syntax.ConditionalExpression,
test: test,
consequent: consequent,
alternate: alternate
};
},
createContinueStatement: function (label) {
return {
type: Syntax.ContinueStatement,
label: label
};
},
createDebuggerStatement: function () {
return {
type: Syntax.DebuggerStatement
};
},
createDoWhileStatement: function (body, test) {
return {
type: Syntax.DoWhileStatement,
body: body,
test: test
};
},
createEmptyStatement: function () {
return {
type: Syntax.EmptyStatement
};
},
createExpressionStatement: function (expression) {
return {
type: Syntax.ExpressionStatement,
expression: expression
};
},
createForStatement: function (init, test, update, body) {
return {
type: Syntax.ForStatement,
init: init,
test: test,
update: update,
body: body
};
},
createForInStatement: function (left, right, body) {
return {
type: Syntax.ForInStatement,
left: left,
right: right,
body: body,
each: false
};
},
createFunctionDeclaration: function (id, params, defaults, body) {
return {
type: Syntax.FunctionDeclaration,
id: id,
params: params,
defaults: defaults,
body: body,
rest: null,
generator: false,
expression: false
};
},
createFunctionExpression: function (id, params, defaults, body) {
return {
type: Syntax.FunctionExpression,
id: id,
params: params,
defaults: defaults,
body: body,
rest: null,
generator: false,
expression: false
};
},
createIdentifier: function (name) {
return {
type: Syntax.Identifier,
name: name
};
},
createIfStatement: function (test, consequent, alternate) {
return {
type: Syntax.IfStatement,
test: test,
consequent: consequent,
alternate: alternate
};
},
createLabeledStatement: function (label, body) {
return {
type: Syntax.LabeledStatement,
label: label,
body: body
};
},
createLiteral: function (token) {
return {
type: Syntax.Literal,
value: token.value,
raw: source.slice(token.start, token.end)
};
},
createMemberExpression: function (accessor, object, property) {
return {
type: Syntax.MemberExpression,
computed: accessor === '[',
object: object,
property: property
};
},
createNewExpression: function (callee, args) {
return {
type: Syntax.NewExpression,
callee: callee,
'arguments': args
};
},
createObjectExpression: function (properties) {
return {
type: Syntax.ObjectExpression,
properties: properties
};
},
createPostfixExpression: function (operator, argument) {
return {
type: Syntax.UpdateExpression,
operator: operator,
argument: argument,
prefix: false
};
},
createProgram: function (body) {
return {
type: Syntax.Program,
body: body
};
},
createProperty: function (kind, key, value) {
return {
type: Syntax.Property,
key: key,
value: value,
kind: kind
};
},
createReturnStatement: function (argument) {
return {
type: Syntax.ReturnStatement,
argument: argument
};
},
createSequenceExpression: function (expressions) {
return {
type: Syntax.SequenceExpression,
expressions: expressions
};
},
createSwitchCase: function (test, consequent) {
return {
type: Syntax.SwitchCase,
test: test,
consequent: consequent
};
},
createSwitchStatement: function (discriminant, cases) {
return {
type: Syntax.SwitchStatement,
discriminant: discriminant,
cases: cases
};
},
createThisExpression: function () {
return {
type: Syntax.ThisExpression
};
},
createThrowStatement: function (argument) {
return {
type: Syntax.ThrowStatement,
argument: argument
};
},
createTryStatement: function (block, guardedHandlers, handlers, finalizer) {
return {
type: Syntax.TryStatement,
block: block,
guardedHandlers: guardedHandlers,
handlers: handlers,
finalizer: finalizer
};
},
createUnaryExpression: function (operator, argument) {
if (operator === '++' || operator === '--') {
return {
type: Syntax.UpdateExpression,
operator: operator,
argument: argument,
prefix: true
};
}
return {
type: Syntax.UnaryExpression,
operator: operator,
argument: argument,
prefix: true
};
},
createVariableDeclaration: function (declarations, kind) {
return {
type: Syntax.VariableDeclaration,
declarations: declarations,
kind: kind
};
},
createVariableDeclarator: function (id, init) {
return {
type: Syntax.VariableDeclarator,
id: id,
init: init
};
},
createWhileStatement: function (test, body) {
return {
type: Syntax.WhileStatement,
test: test,
body: body
};
},
createWithStatement: function (object, body) {
return {
type: Syntax.WithStatement,
object: object,
body: body
};
}
};
// Return true if there is a line terminator before the next token.
function peekLineTerminator() {
var pos, line, start, found;
pos = index;
line = lineNumber;
start = lineStart;
skipComment();
found = lineNumber !== line;
index = pos;
lineNumber = line;
lineStart = start;
return found;
}
// Throw an exception
function throwError(token, messageFormat) {
var error,
args = Array.prototype.slice.call(arguments, 2),
msg = messageFormat.replace(
/%(\d)/g,
function (whole, index) {
assert(index < args.length, 'Message reference must be in range');
return args[index];
}
);
if (typeof token.lineNumber === 'number') {
error = new Error('Line ' + token.lineNumber + ': ' + msg);
error.index = token.start;
error.lineNumber = token.lineNumber;
error.column = token.start - lineStart + 1;
} else {
error = new Error('Line ' + lineNumber + ': ' + msg);
error.index = index;
error.lineNumber = lineNumber;
error.column = index - lineStart + 1;
}
error.description = msg;
throw error;
}
function throwErrorTolerant() {
try {
throwError.apply(null, arguments);
} catch (e) {
if (extra.errors) {
extra.errors.push(e);
} else {
throw e;
}
}
}
// Throw an exception because of the token.
function throwUnexpected(token) {
if (token.type === Token.EOF) {
throwError(token, Messages.UnexpectedEOS);
}
if (token.type === Token.NumericLiteral) {
throwError(token, Messages.UnexpectedNumber);
}
if (token.type === Token.StringLiteral) {
throwError(token, Messages.UnexpectedString);
}
if (token.type === Token.Identifier) {
throwError(token, Messages.UnexpectedIdentifier);
}
if (token.type === Token.Keyword) {
if (isFutureReservedWord(token.value)) {
throwError(token, Messages.UnexpectedReserved);
} else if (strict && isStrictModeReservedWord(token.value)) {
throwErrorTolerant(token, Messages.StrictReservedWord);
return;
}
throwError(token, Messages.UnexpectedToken, token.value);
}
// BooleanLiteral, NullLiteral, or Punctuator.
throwError(token, Messages.UnexpectedToken, token.value);
}
// Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
function expect(value) {
var token = lex();
if (token.type !== Token.Punctuator || token.value !== value) {
throwUnexpected(token);
}
}
// Expect the next token to match the specified keyword.
// If not, an exception will be thrown.
function expectKeyword(keyword) {
var token = lex();
if (token.type !== Token.Keyword || token.value !== keyword) {
throwUnexpected(token);
}
}
// Return true if the next token matches the specified punctuator.
function match(value) {
return lookahead.type === Token.Punctuator && lookahead.value === value;
}
// Return true if the next token matches the specified keyword
function matchKeyword(keyword) {
return lookahead.type === Token.Keyword && lookahead.value === keyword;
}
// Return true if the next token is an assignment operator
function matchAssign() {
var op;
if (lookahead.type !== Token.Punctuator) {
return false;
}
op = lookahead.value;
return op === '=' ||
op === '*=' ||
op === '/=' ||
op === '%=' ||
op === '+=' ||
op === '-=' ||
op === '<<=' ||
op === '>>=' ||
op === '>>>=' ||
op === '&=' ||
op === '^=' ||
op === '|=';
}
function consumeSemicolon() {
var line;
// Catch the very common case first: immediately a semicolon (U+003B).
if (source.charCodeAt(index) === 0x3B || match(';')) {
lex();
return;
}
line = lineNumber;
skipComment();
if (lineNumber !== line) {
return;
}
if (lookahead.type !== Token.EOF && !match('}')) {
throwUnexpected(lookahead);
}
}
// Return true if provided expression is LeftHandSideExpression
function isLeftHandSide(expr) {
return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
}
// 11.1.4 Array Initialiser
function parseArrayInitialiser() {
var elements = [], startToken;
startToken = lookahead;
expect('[');
while (!match(']')) {
if (match(',')) {
lex();
elements.push(null);
} else {
elements.push(parseAssignmentExpression());
if (!match(']')) {
expect(',');
}
}
}
lex();
return delegate.markEnd(delegate.createArrayExpression(elements), startToken);
}
// 11.1.5 Object Initialiser
function parsePropertyFunction(param, first) {
var previousStrict, body, startToken;
previousStrict = strict;
startToken = lookahead;
body = parseFunctionSourceElements();
if (first && strict && isRestrictedWord(param[0].name)) {
throwErrorTolerant(first, Messages.StrictParamName);
}
strict = previousStrict;
return delegate.markEnd(delegate.createFunctionExpression(null, param, [], body), startToken);
}
function parseObjectPropertyKey() {
var token, startToken;
startToken = lookahead;
token = lex();
// Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.
if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
if (strict && token.octal) {
throwErrorTolerant(token, Messages.StrictOctalLiteral);
}
return delegate.markEnd(delegate.createLiteral(token), startToken);
}
return delegate.markEnd(delegate.createIdentifier(token.value), startToken);
}
function parseObjectProperty() {
var token, key, id, value, param, startToken;
token = lookahead;
startToken = lookahead;
if (token.type === Token.Identifier) {
id = parseObjectPropertyKey();
// Property Assignment: Getter and Setter.
if (token.value === 'get' && !match(':')) {
key = parseObjectPropertyKey();
expect('(');
expect(')');
value = parsePropertyFunction([]);
return delegate.markEnd(delegate.createProperty('get', key, value), startToken);
}
if (token.value === 'set' && !match(':')) {
key = parseObjectPropertyKey();
expect('(');
token = lookahead;
if (token.type !== Token.Identifier) {
expect(')');
throwErrorTolerant(token, Messages.UnexpectedToken, token.value);
value = parsePropertyFunction([]);
} else {
param = [ parseVariableIdentifier() ];
expect(')');
value = parsePropertyFunction(param, token);
}
return delegate.markEnd(delegate.createProperty('set', key, value), startToken);
}
expect(':');
value = parseAssignmentExpression();
return delegate.markEnd(delegate.createProperty('init', id, value), startToken);
}
if (token.type === Token.EOF || token.type === Token.Punctuator) {
throwUnexpected(token);
} else {
key = parseObjectPropertyKey();
expect(':');
value = parseAssignmentExpression();
return delegate.markEnd(delegate.createProperty('init', key, value), startToken);
}
}
function parseObjectInitialiser() {
var properties = [], property, name, key, kind, map = {}, toString = String, startToken;
startToken = lookahead;
expect('{');
while (!match('}')) {
property = parseObjectProperty();
if (property.key.type === Syntax.Identifier) {
name = property.key.name;
} else {
name = toString(property.key.value);
}
kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
key = '$' + name;
if (Object.prototype.hasOwnProperty.call(map, key)) {
if (map[key] === PropertyKind.Data) {
if (strict && kind === PropertyKind.Data) {
throwErrorTolerant({}, Messages.StrictDuplicateProperty);
} else if (kind !== PropertyKind.Data) {
throwErrorTolerant({}, Messages.AccessorDataProperty);
}
} else {
if (kind === PropertyKind.Data) {
throwErrorTolerant({}, Messages.AccessorDataProperty);
} else if (map[key] & kind) {
throwErrorTolerant({}, Messages.AccessorGetSet);
}
}
map[key] |= kind;
} else {
map[key] = kind;
}
properties.push(property);
if (!match('}')) {
expect(',');
}
}
expect('}');
return delegate.markEnd(delegate.createObjectExpression(properties), startToken);
}
// 11.1.6 The Grouping Operator
function parseGroupExpression() {
var expr;
expect('(');
expr = parseExpression();
expect(')');
return expr;
}
// 11.1 Primary Expressions
function parsePrimaryExpression() {
var type, token, expr, startToken;
if (match('(')) {
return parseGroupExpression();
}
if (match('[')) {
return parseArrayInitialiser();
}
if (match('{')) {
return parseObjectInitialiser();
}
type = lookahead.type;
startToken = lookahead;
if (type === Token.Identifier) {
expr = delegate.createIdentifier(lex().value);
} else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
if (strict && lookahead.octal) {
throwErrorTolerant(lookahead, Messages.StrictOctalLiteral);
}
expr = delegate.createLiteral(lex());
} else if (type === Token.Keyword) {
if (matchKeyword('function')) {
return parseFunctionExpression();
}
if (matchKeyword('this')) {
lex();
expr = delegate.createThisExpression();
} else {
throwUnexpected(lex());
}
} else if (type === Token.BooleanLiteral) {
token = lex();
token.value = (token.value === 'true');
expr = delegate.createLiteral(token);
} else if (type === Token.NullLiteral) {
token = lex();
token.value = null;
expr = delegate.createLiteral(token);
} else if (match('/') || match('/=')) {
if (typeof extra.tokens !== 'undefined') {
expr = delegate.createLiteral(collectRegex());
} else {
expr = delegate.createLiteral(scanRegExp());
}
peek();
} else {
throwUnexpected(lex());
}
return delegate.markEnd(expr, startToken);
}
// 11.2 Left-Hand-Side Expressions
function parseArguments() {
var args = [];
expect('(');
if (!match(')')) {
while (index < length) {
args.push(parseAssignmentExpression());
if (match(')')) {
break;
}
expect(',');
}
}
expect(')');
return args;
}
function parseNonComputedProperty() {
var token, startToken;
startToken = lookahead;
token = lex();
if (!isIdentifierName(token)) {
throwUnexpected(token);
}
return delegate.markEnd(delegate.createIdentifier(token.value), startToken);
}
function parseNonComputedMember() {
expect('.');
return parseNonComputedProperty();
}
function parseComputedMember() {
var expr;
expect('[');
expr = parseExpression();
expect(']');
return expr;
}
function parseNewExpression() {
var callee, args, startToken;
startToken = lookahead;
expectKeyword('new');
callee = parseLeftHandSideExpression();
args = match('(') ? parseArguments() : [];
return delegate.markEnd(delegate.createNewExpression(callee, args), startToken);
}
function parseLeftHandSideExpressionAllowCall() {
var previousAllowIn, expr, args, property, startToken;
startToken = lookahead;
previousAllowIn = state.allowIn;
state.allowIn = true;
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
state.allowIn = previousAllowIn;
for (;;) {
if (match('.')) {
property = parseNonComputedMember();
expr = delegate.createMemberExpression('.', expr, property);
} else if (match('(')) {
args = parseArguments();
expr = delegate.createCallExpression(expr, args);
} else if (match('[')) {
property = parseComputedMember();
expr = delegate.createMemberExpression('[', expr, property);
} else {
break;
}
delegate.markEnd(expr, startToken);
}
return expr;
}
function parseLeftHandSideExpression() {
var previousAllowIn, expr, property, startToken;
startToken = lookahead;
previousAllowIn = state.allowIn;
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
state.allowIn = previousAllowIn;
while (match('.') || match('[')) {
if (match('[')) {
property = parseComputedMember();
expr = delegate.createMemberExpression('[', expr, property);
} else {
property = parseNonComputedMember();
expr = delegate.createMemberExpression('.', expr, property);
}
delegate.markEnd(expr, startToken);
}
return expr;
}
// 11.3 Postfix Expressions
function parsePostfixExpression() {
var expr, token, startToken = lookahead;
expr = parseLeftHandSideExpressionAllowCall();
if (lookahead.type === Token.Punctuator) {
if ((match('++') || match('--')) && !peekLineTerminator()) {
// 11.3.1, 11.3.2
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
throwErrorTolerant({}, Messages.StrictLHSPostfix);
}
if (!isLeftHandSide(expr)) {
throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
}
token = lex();
expr = delegate.markEnd(delegate.createPostfixExpression(token.value, expr), startToken);
}
}
return expr;
}
// 11.4 Unary Operators
function parseUnaryExpression() {
var token, expr, startToken;
if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
expr = parsePostfixExpression();
} else if (match('++') || match('--')) {
startToken = lookahead;
token = lex();
expr = parseUnaryExpression();
// 11.4.4, 11.4.5
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
throwErrorTolerant({}, Messages.StrictLHSPrefix);
}
if (!isLeftHandSide(expr)) {
throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
}
expr = delegate.createUnaryExpression(token.value, expr);
expr = delegate.markEnd(expr, startToken);
} else if (match('+') || match('-') || match('~') || match('!')) {
startToken = lookahead;
token = lex();
expr = parseUnaryExpression();
expr = delegate.createUnaryExpression(token.value, expr);
expr = delegate.markEnd(expr, startToken);
} else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
startToken = lookahead;
token = lex();
expr = parseUnaryExpression();
expr = delegate.createUnaryExpression(token.value, expr);
expr = delegate.markEnd(expr, startToken);
if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
throwErrorTolerant({}, Messages.StrictDelete);
}
} else {
expr = parsePostfixExpression();
}
return expr;
}
function binaryPrecedence(token, allowIn) {
var prec = 0;
if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
return 0;
}
switch (token.value) {
case '||':
prec = 1;
break;
case '&&':
prec = 2;
break;
case '|':
prec = 3;
break;
case '^':
prec = 4;
break;
case '&':
prec = 5;
break;
case '==':
case '!=':
case '===':
case '!==':
prec = 6;
break;
case '<':
case '>':
case '<=':
case '>=':
case 'instanceof':
prec = 7;
break;
case 'in':
prec = allowIn ? 7 : 0;
break;
case '<<':
case '>>':
case '>>>':
prec = 8;
break;
case '+':
case '-':
prec = 9;
break;
case '*':
case '/':
case '%':
prec = 11;
break;
default:
break;
}
return prec;
}
// 11.5 Multiplicative Operators
// 11.6 Additive Operators
// 11.7 Bitwise Shift Operators
// 11.8 Relational Operators
// 11.9 Equality Operators
// 11.10 Binary Bitwise Operators
// 11.11 Binary Logical Operators
function parseBinaryExpression() {
var marker, markers, expr, token, prec, stack, right, operator, left, i;
marker = lookahead;
left = parseUnaryExpression();
token = lookahead;
prec = binaryPrecedence(token, state.allowIn);
if (prec === 0) {
return left;
}
token.prec = prec;
lex();
markers = [marker, lookahead];
right = parseUnaryExpression();
stack = [left, token, right];
while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) {
// Reduce: make a binary expression from the three topmost entries.
while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
right = stack.pop();
operator = stack.pop().value;
left = stack.pop();
expr = delegate.createBinaryExpression(operator, left, right);
markers.pop();
marker = markers[markers.length - 1];
delegate.markEnd(expr, marker);
stack.push(expr);
}
// Shift.
token = lex();
token.prec = prec;
stack.push(token);
markers.push(lookahead);
expr = parseUnaryExpression();
stack.push(expr);
}
// Final reduce to clean-up the stack.
i = stack.length - 1;
expr = stack[i];
markers.pop();
while (i > 1) {
expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
i -= 2;
marker = markers.pop();
delegate.markEnd(expr, marker);
}
return expr;
}
// 11.12 Conditional Operator
function parseConditionalExpression() {
var expr, previousAllowIn, consequent, alternate, startToken;
startToken = lookahead;
expr = parseBinaryExpression();
if (match('?')) {
lex();
previousAllowIn = state.allowIn;
state.allowIn = true;
consequent = parseAssignmentExpression();
state.allowIn = previousAllowIn;
expect(':');
alternate = parseAssignmentExpression();
expr = delegate.createConditionalExpression(expr, consequent, alternate);
delegate.markEnd(expr, startToken);
}
return expr;
}
// 11.13 Assignment Operators
function parseAssignmentExpression() {
var token, left, right, node, startToken;
token = lookahead;
startToken = lookahead;
node = left = parseConditionalExpression();
if (matchAssign()) {
// LeftHandSideExpression
if (!isLeftHandSide(left)) {
throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
}
// 11.13.1
if (strict && left.type === Syntax.Identifier && isRestrictedWord(left.name)) {
throwErrorTolerant(token, Messages.StrictLHSAssignment);
}
token = lex();
right = parseAssignmentExpression();
node = delegate.markEnd(delegate.createAssignmentExpression(token.value, left, right), startToken);
}
return node;
}
// 11.14 Comma Operator
function parseExpression() {
var expr, startToken = lookahead;
expr = parseAssignmentExpression();
if (match(',')) {
expr = delegate.createSequenceExpression([ expr ]);
while (index < length) {
if (!match(',')) {
break;
}
lex();
expr.expressions.push(parseAssignmentExpression());
}
delegate.markEnd(expr, startToken);
}
return expr;
}
// 12.1 Block
function parseStatementList() {
var list = [],
statement;
while (index < length) {
if (match('}')) {
break;
}
statement = parseSourceElement();
if (typeof statement === 'undefined') {
break;
}
list.push(statement);
}
return list;
}
function parseBlock() {
var block, startToken;
startToken = lookahead;
expect('{');
block = parseStatementList();
expect('}');
return delegate.markEnd(delegate.createBlockStatement(block), startToken);
}
// 12.2 Variable Statement
function parseVariableIdentifier() {
var token, startToken;
startToken = lookahead;
token = lex();
if (token.type !== Token.Identifier) {
throwUnexpected(token);
}
return delegate.markEnd(delegate.createIdentifier(token.value), startToken);
}
function parseVariableDeclaration(kind) {
var init = null, id, startToken;
startToken = lookahead;
id = parseVariableIdentifier();
// 12.2.1
if (strict && isRestrictedWord(id.name)) {
throwErrorTolerant({}, Messages.StrictVarName);
}
if (kind === 'const') {
expect('=');
init = parseAssignmentExpression();
} else if (match('=')) {
lex();
init = parseAssignmentExpression();
}
return delegate.markEnd(delegate.createVariableDeclarator(id, init), startToken);
}
function parseVariableDeclarationList(kind) {
var list = [];
do {
list.push(parseVariableDeclaration(kind));
if (!match(',')) {
break;
}
lex();
} while (index < length);
return list;
}
function parseVariableStatement() {
var declarations;
expectKeyword('var');
declarations = parseVariableDeclarationList();
consumeSemicolon();
return delegate.createVariableDeclaration(declarations, 'var');
}
// kind may be `const` or `let`
// Both are experimental and not in the specification yet.
// see http://wiki.ecmascript.org/doku.php?id=harmony:const
// and http://wiki.ecmascript.org/doku.php?id=harmony:let
function parseConstLetDeclaration(kind) {
var declarations, startToken;
startToken = lookahead;
expectKeyword(kind);
declarations = parseVariableDeclarationList(kind);
consumeSemicolon();
return delegate.markEnd(delegate.createVariableDeclaration(declarations, kind), startToken);
}
// 12.3 Empty Statement
function parseEmptyStatement() {
expect(';');
return delegate.createEmptyStatement();
}
// 12.4 Expression Statement
function parseExpressionStatement() {
var expr = parseExpression();
consumeSemicolon();
return delegate.createExpressionStatement(expr);
}
// 12.5 If statement
function parseIfStatement() {
var test, consequent, alternate;
expectKeyword('if');
expect('(');
test = parseExpression();
expect(')');
consequent = parseStatement();
if (matchKeyword('else')) {
lex();
alternate = parseStatement();
} else {
alternate = null;
}
return delegate.createIfStatement(test, consequent, alternate);
}
// 12.6 Iteration Statements
function parseDoWhileStatement() {
var body, test, oldInIteration;
expectKeyword('do');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
expectKeyword('while');
expect('(');
test = parseExpression();
expect(')');
if (match(';')) {
lex();
}
return delegate.createDoWhileStatement(body, test);
}
function parseWhileStatement() {
var test, body, oldInIteration;
expectKeyword('while');
expect('(');
test = parseExpression();
expect(')');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
return delegate.createWhileStatement(test, body);
}
function parseForVariableDeclaration() {
var token, declarations, startToken;
startToken = lookahead;
token = lex();
declarations = parseVariableDeclarationList();
return delegate.markEnd(delegate.createVariableDeclaration(declarations, token.value), startToken);
}
function parseForStatement() {
var init, test, update, left, right, body, oldInIteration;
init = test = update = null;
expectKeyword('for');
expect('(');
if (match(';')) {
lex();
} else {
if (matchKeyword('var') || matchKeyword('let')) {
state.allowIn = false;
init = parseForVariableDeclaration();
state.allowIn = true;
if (init.declarations.length === 1 && matchKeyword('in')) {
lex();
left = init;
right = parseExpression();
init = null;
}
} else {
state.allowIn = false;
init = parseExpression();
state.allowIn = true;
if (matchKeyword('in')) {
// LeftHandSideExpression
if (!isLeftHandSide(init)) {
throwErrorTolerant({}, Messages.InvalidLHSInForIn);
}
lex();
left = init;
right = parseExpression();
init = null;
}
}
if (typeof left === 'undefined') {
expect(';');
}
}
if (typeof left === 'undefined') {
if (!match(';')) {
test = parseExpression();
}
expect(';');
if (!match(')')) {
update = parseExpression();
}
}
expect(')');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
return (typeof left === 'undefined') ?
delegate.createForStatement(init, test, update, body) :
delegate.createForInStatement(left, right, body);
}
// 12.7 The continue statement
function parseContinueStatement() {
var label = null, key;
expectKeyword('continue');
// Optimize the most common form: 'continue;'.
if (source.charCodeAt(index) === 0x3B) {
lex();
if (!state.inIteration) {
throwError({}, Messages.IllegalContinue);
}
return delegate.createContinueStatement(null);
}
if (peekLineTerminator()) {
if (!state.inIteration) {
throwError({}, Messages.IllegalContinue);
}
return delegate.createContinueStatement(null);
}
if (lookahead.type === Token.Identifier) {
label = parseVariableIdentifier();
key = '$' + label.name;
if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
throwError({}, Messages.UnknownLabel, label.name);
}
}
consumeSemicolon();
if (label === null && !state.inIteration) {
throwError({}, Messages.IllegalContinue);
}
return delegate.createContinueStatement(label);
}
// 12.8 The break statement
function parseBreakStatement() {
var label = null, key;
expectKeyword('break');
// Catch the very common case first: immediately a semicolon (U+003B).
if (source.charCodeAt(index) === 0x3B) {
lex();
if (!(state.inIteration || state.inSwitch)) {
throwError({}, Messages.IllegalBreak);
}
return delegate.createBreakStatement(null);
}
if (peekLineTerminator()) {
if (!(state.inIteration || state.inSwitch)) {
throwError({}, Messages.IllegalBreak);
}
return delegate.createBreakStatement(null);
}
if (lookahead.type === Token.Identifier) {
label = parseVariableIdentifier();
key = '$' + label.name;
if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
throwError({}, Messages.UnknownLabel, label.name);
}
}
consumeSemicolon();
if (label === null && !(state.inIteration || state.inSwitch)) {
throwError({}, Messages.IllegalBreak);
}
return delegate.createBreakStatement(label);
}
// 12.9 The return statement
function parseReturnStatement() {
var argument = null;
expectKeyword('return');
if (!state.inFunctionBody) {
throwErrorTolerant({}, Messages.IllegalReturn);
}
// 'return' followed by a space and an identifier is very common.
if (source.charCodeAt(index) === 0x20) {
if (isIdentifierStart(source.charCodeAt(index + 1))) {
argument = parseExpression();
consumeSemicolon();
return delegate.createReturnStatement(argument);
}
}
if (peekLineTerminator()) {
return delegate.createReturnStatement(null);
}
if (!match(';')) {
if (!match('}') && lookahead.type !== Token.EOF) {
argument = parseExpression();
}
}
consumeSemicolon();
return delegate.createReturnStatement(argument);
}
// 12.10 The with statement
function parseWithStatement() {
var object, body;
if (strict) {
// TODO(ikarienator): Should we update the test cases instead?
skipComment();
throwErrorTolerant({}, Messages.StrictModeWith);
}
expectKeyword('with');
expect('(');
object = parseExpression();
expect(')');
body = parseStatement();
return delegate.createWithStatement(object, body);
}
// 12.10 The swith statement
function parseSwitchCase() {
var test, consequent = [], statement, startToken;
startToken = lookahead;
if (matchKeyword('default')) {
lex();
test = null;
} else {
expectKeyword('case');
test = parseExpression();
}
expect(':');
while (index < length) {
if (match('}') || matchKeyword('default') || matchKeyword('case')) {
break;
}
statement = parseStatement();
consequent.push(statement);
}
return delegate.markEnd(delegate.createSwitchCase(test, consequent), startToken);
}
function parseSwitchStatement() {
var discriminant, cases, clause, oldInSwitch, defaultFound;
expectKeyword('switch');
expect('(');
discriminant = parseExpression();
expect(')');
expect('{');
cases = [];
if (match('}')) {
lex();
return delegate.createSwitchStatement(discriminant, cases);
}
oldInSwitch = state.inSwitch;
state.inSwitch = true;
defaultFound = false;
while (index < length) {
if (match('}')) {
break;
}
clause = parseSwitchCase();
if (clause.test === null) {
if (defaultFound) {
throwError({}, Messages.MultipleDefaultsInSwitch);
}
defaultFound = true;
}
cases.push(clause);
}
state.inSwitch = oldInSwitch;
expect('}');
return delegate.createSwitchStatement(discriminant, cases);
}
// 12.13 The throw statement
function parseThrowStatement() {
var argument;
expectKeyword('throw');
if (peekLineTerminator()) {
throwError({}, Messages.NewlineAfterThrow);
}
argument = parseExpression();
consumeSemicolon();
return delegate.createThrowStatement(argument);
}
// 12.14 The try statement
function parseCatchClause() {
var param, body, startToken;
startToken = lookahead;
expectKeyword('catch');
expect('(');
if (match(')')) {
throwUnexpected(lookahead);
}
param = parseVariableIdentifier();
// 12.14.1
if (strict && isRestrictedWord(param.name)) {
throwErrorTolerant({}, Messages.StrictCatchVariable);
}
expect(')');
body = parseBlock();
return delegate.markEnd(delegate.createCatchClause(param, body), startToken);
}
function parseTryStatement() {
var block, handlers = [], finalizer = null;
expectKeyword('try');
block = parseBlock();
if (matchKeyword('catch')) {
handlers.push(parseCatchClause());
}
if (matchKeyword('finally')) {
lex();
finalizer = parseBlock();
}
if (handlers.length === 0 && !finalizer) {
throwError({}, Messages.NoCatchOrFinally);
}
return delegate.createTryStatement(block, [], handlers, finalizer);
}
// 12.15 The debugger statement
function parseDebuggerStatement() {
expectKeyword('debugger');
consumeSemicolon();
return delegate.createDebuggerStatement();
}
// 12 Statements
function parseStatement() {
var type = lookahead.type,
expr,
labeledBody,
key,
startToken;
if (type === Token.EOF) {
throwUnexpected(lookahead);
}
if (type === Token.Punctuator && lookahead.value === '{') {
return parseBlock();
}
startToken = lookahead;
if (type === Token.Punctuator) {
switch (lookahead.value) {
case ';':
return delegate.markEnd(parseEmptyStatement(), startToken);
case '(':
return delegate.markEnd(parseExpressionStatement(), startToken);
default:
break;
}
}
if (type === Token.Keyword) {
switch (lookahead.value) {
case 'break':
return delegate.markEnd(parseBreakStatement(), startToken);
case 'continue':
return delegate.markEnd(parseContinueStatement(), startToken);
case 'debugger':
return delegate.markEnd(parseDebuggerStatement(), startToken);
case 'do':
return delegate.markEnd(parseDoWhileStatement(), startToken);
case 'for':
return delegate.markEnd(parseForStatement(), startToken);
case 'function':
return delegate.markEnd(parseFunctionDeclaration(), startToken);
case 'if':
return delegate.markEnd(parseIfStatement(), startToken);
case 'return':
return delegate.markEnd(parseReturnStatement(), startToken);
case 'switch':
return delegate.markEnd(parseSwitchStatement(), startToken);
case 'throw':
return delegate.markEnd(parseThrowStatement(), startToken);
case 'try':
return delegate.markEnd(parseTryStatement(), startToken);
case 'var':
return delegate.markEnd(parseVariableStatement(), startToken);
case 'while':
return delegate.markEnd(parseWhileStatement(), startToken);
case 'with':
return delegate.markEnd(parseWithStatement(), startToken);
default:
break;
}
}
expr = parseExpression();
// 12.12 Labelled Statements
if ((expr.type === Syntax.Identifier) && match(':')) {
lex();
key = '$' + expr.name;
if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
throwError({}, Messages.Redeclaration, 'Label', expr.name);
}
state.labelSet[key] = true;
labeledBody = parseStatement();
delete state.labelSet[key];
return delegate.markEnd(delegate.createLabeledStatement(expr, labeledBody), startToken);
}
consumeSemicolon();
return delegate.markEnd(delegate.createExpressionStatement(expr), startToken);
}
// 13 Function Definition
function parseFunctionSourceElements() {
var sourceElement, sourceElements = [], token, directive, firstRestricted,
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, startToken;
startToken = lookahead;
expect('{');
while (index < length) {
if (lookahead.type !== Token.StringLiteral) {
break;
}
token = lookahead;
sourceElement = parseSourceElement();
sourceElements.push(sourceElement);
if (sourceElement.expression.type !== Syntax.Literal) {
// this is not directive
break;
}
directive = source.slice(token.start + 1, token.end - 1);
if (directive === 'use strict') {
strict = true;
if (firstRestricted) {
throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
}
} else {
if (!firstRestricted && token.octal) {
firstRestricted = token;
}
}
}
oldLabelSet = state.labelSet;
oldInIteration = state.inIteration;
oldInSwitch = state.inSwitch;
oldInFunctionBody = state.inFunctionBody;
state.labelSet = {};
state.inIteration = false;
state.inSwitch = false;
state.inFunctionBody = true;
while (index < length) {
if (match('}')) {
break;
}
sourceElement = parseSourceElement();
if (typeof sourceElement === 'undefined') {
break;
}
sourceElements.push(sourceElement);
}
expect('}');
state.labelSet = oldLabelSet;
state.inIteration = oldInIteration;
state.inSwitch = oldInSwitch;
state.inFunctionBody = oldInFunctionBody;
return delegate.markEnd(delegate.createBlockStatement(sourceElements), startToken);
}
function parseParams(firstRestricted) {
var param, params = [], token, stricted, paramSet, key, message;
expect('(');
if (!match(')')) {
paramSet = {};
while (index < length) {
token = lookahead;
param = parseVariableIdentifier();
key = '$' + token.value;
if (strict) {
if (isRestrictedWord(token.value)) {
stricted = token;
message = Messages.StrictParamName;
}
if (Object.prototype.hasOwnProperty.call(paramSet, key)) {
stricted = token;
message = Messages.StrictParamDupe;
}
} else if (!firstRestricted) {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictParamName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
} else if (Object.prototype.hasOwnProperty.call(paramSet, key)) {
firstRestricted = token;
message = Messages.StrictParamDupe;
}
}
params.push(param);
paramSet[key] = true;
if (match(')')) {
break;
}
expect(',');
}
}
expect(')');
return {
params: params,
stricted: stricted,
firstRestricted: firstRestricted,
message: message
};
}
function parseFunctionDeclaration() {
var id, params = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, startToken;
startToken = lookahead;
expectKeyword('function');
token = lookahead;
id = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
throwErrorTolerant(token, Messages.StrictFunctionName);
}
} else {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictFunctionName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
}
}
tmp = parseParams(firstRestricted);
params = tmp.params;
stricted = tmp.stricted;
firstRestricted = tmp.firstRestricted;
if (tmp.message) {
message = tmp.message;
}
previousStrict = strict;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwError(firstRestricted, message);
}
if (strict && stricted) {
throwErrorTolerant(stricted, message);
}
strict = previousStrict;
return delegate.markEnd(delegate.createFunctionDeclaration(id, params, [], body), startToken);
}
function parseFunctionExpression() {
var token, id = null, stricted, firstRestricted, message, tmp, params = [], body, previousStrict, startToken;
startToken = lookahead;
expectKeyword('function');
if (!match('(')) {
token = lookahead;
id = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
throwErrorTolerant(token, Messages.StrictFunctionName);
}
} else {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictFunctionName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
}
}
}
tmp = parseParams(firstRestricted);
params = tmp.params;
stricted = tmp.stricted;
firstRestricted = tmp.firstRestricted;
if (tmp.message) {
message = tmp.message;
}
previousStrict = strict;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwError(firstRestricted, message);
}
if (strict && stricted) {
throwErrorTolerant(stricted, message);
}
strict = previousStrict;
return delegate.markEnd(delegate.createFunctionExpression(id, params, [], body), startToken);
}
// 14 Program
function parseSourceElement() {
if (lookahead.type === Token.Keyword) {
switch (lookahead.value) {
case 'const':
case 'let':
return parseConstLetDeclaration(lookahead.value);
case 'function':
return parseFunctionDeclaration();
default:
return parseStatement();
}
}
if (lookahead.type !== Token.EOF) {
return parseStatement();
}
}
function parseSourceElements() {
var sourceElement, sourceElements = [], token, directive, firstRestricted;
while (index < length) {
token = lookahead;
if (token.type !== Token.StringLiteral) {
break;
}
sourceElement = parseSourceElement();
sourceElements.push(sourceElement);
if (sourceElement.expression.type !== Syntax.Literal) {
// this is not directive
break;
}
directive = source.slice(token.start + 1, token.end - 1);
if (directive === 'use strict') {
strict = true;
if (firstRestricted) {
throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
}
} else {
if (!firstRestricted && token.octal) {
firstRestricted = token;
}
}
}
while (index < length) {
sourceElement = parseSourceElement();
/* istanbul ignore if */
if (typeof sourceElement === 'undefined') {
break;
}
sourceElements.push(sourceElement);
}
return sourceElements;
}
function parseProgram() {
var body, startToken;
skipComment();
peek();
startToken = lookahead;
strict = false;
body = parseSourceElements();
return delegate.markEnd(delegate.createProgram(body), startToken);
}
function filterTokenLocation() {
var i, entry, token, tokens = [];
for (i = 0; i < extra.tokens.length; ++i) {
entry = extra.tokens[i];
token = {
type: entry.type,
value: entry.value
};
if (extra.range) {
token.range = entry.range;
}
if (extra.loc) {
token.loc = entry.loc;
}
tokens.push(token);
}
extra.tokens = tokens;
}
function tokenize(code, options) {
var toString,
token,
tokens;
toString = String;
if (typeof code !== 'string' && !(code instanceof String)) {
code = toString(code);
}
delegate = SyntaxTreeDelegate;
source = code;
index = 0;
lineNumber = (source.length > 0) ? 1 : 0;
lineStart = 0;
length = source.length;
lookahead = null;
state = {
allowIn: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
inSwitch: false,
lastCommentStart: -1
};
extra = {};
// Options matching.
options = options || {};
// Of course we collect tokens here.
options.tokens = true;
extra.tokens = [];
extra.tokenize = true;
// The following two fields are necessary to compute the Regex tokens.
extra.openParenToken = -1;
extra.openCurlyToken = -1;
extra.range = (typeof options.range === 'boolean') && options.range;
extra.loc = (typeof options.loc === 'boolean') && options.loc;
if (typeof options.comment === 'boolean' && options.comment) {
extra.comments = [];
}
if (typeof options.tolerant === 'boolean' && options.tolerant) {
extra.errors = [];
}
try {
peek();
if (lookahead.type === Token.EOF) {
return extra.tokens;
}
token = lex();
while (lookahead.type !== Token.EOF) {
try {
token = lex();
} catch (lexError) {
token = lookahead;
if (extra.errors) {
extra.errors.push(lexError);
// We have to break on the first error
// to avoid infinite loops.
break;
} else {
throw lexError;
}
}
}
filterTokenLocation();
tokens = extra.tokens;
if (typeof extra.comments !== 'undefined') {
tokens.comments = extra.comments;
}
if (typeof extra.errors !== 'undefined') {
tokens.errors = extra.errors;
}
} catch (e) {
throw e;
} finally {
extra = {};
}
return tokens;
}
function parse(code, options) {
var program, toString;
toString = String;
if (typeof code !== 'string' && !(code instanceof String)) {
code = toString(code);
}
delegate = SyntaxTreeDelegate;
source = code;
index = 0;
lineNumber = (source.length > 0) ? 1 : 0;
lineStart = 0;
length = source.length;
lookahead = null;
state = {
allowIn: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
inSwitch: false,
lastCommentStart: -1
};
extra = {};
if (typeof options !== 'undefined') {
extra.range = (typeof options.range === 'boolean') && options.range;
extra.loc = (typeof options.loc === 'boolean') && options.loc;
extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment;
if (extra.loc && options.source !== null && options.source !== undefined) {
extra.source = toString(options.source);
}
if (typeof options.tokens === 'boolean' && options.tokens) {
extra.tokens = [];
}
if (typeof options.comment === 'boolean' && options.comment) {
extra.comments = [];
}
if (typeof options.tolerant === 'boolean' && options.tolerant) {
extra.errors = [];
}
if (extra.attachComment) {
extra.range = true;
extra.comments = [];
extra.bottomRightStack = [];
extra.trailingComments = [];
extra.leadingComments = [];
}
}
try {
program = parseProgram();
if (typeof extra.comments !== 'undefined') {
program.comments = extra.comments;
}
if (typeof extra.tokens !== 'undefined') {
filterTokenLocation();
program.tokens = extra.tokens;
}
if (typeof extra.errors !== 'undefined') {
program.errors = extra.errors;
}
} catch (e) {
throw e;
} finally {
extra = {};
}
return program;
}
// Sync with *.json manifests.
exports.version = '1.2.2';
exports.tokenize = tokenize;
exports.parse = parse;
// Deep copy.
/* istanbul ignore next */
exports.Syntax = (function () {
var name, types = {};
if (typeof Object.create === 'function') {
types = Object.create(null);
}
for (name in Syntax) {
if (Syntax.hasOwnProperty(name)) {
types[name] = Syntax[name];
}
}
if (typeof Object.freeze === 'function') {
Object.freeze(types);
}
return types;
}());
}));
/* vim: set sw=4 ts=4 et tw=80 : */
},{}],7:[function(require,module,exports){
/*
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint vars:false, bitwise:true*/
/*jshint indent:4*/
/*global exports:true, define:true*/
(function (root, factory) {
'use strict';
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// and plain browser loading,
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.estraverse = {}));
}
}(this, function (exports) {
'use strict';
var Syntax,
isArray,
VisitorOption,
VisitorKeys,
BREAK,
SKIP;
Syntax = {
AssignmentExpression: 'AssignmentExpression',
ArrayExpression: 'ArrayExpression',
ArrayPattern: 'ArrayPattern',
ArrowFunctionExpression: 'ArrowFunctionExpression',
BlockStatement: 'BlockStatement',
BinaryExpression: 'BinaryExpression',
BreakStatement: 'BreakStatement',
CallExpression: 'CallExpression',
CatchClause: 'CatchClause',
ClassBody: 'ClassBody',
ClassDeclaration: 'ClassDeclaration',
ClassExpression: 'ClassExpression',
ConditionalExpression: 'ConditionalExpression',
ContinueStatement: 'ContinueStatement',
DebuggerStatement: 'DebuggerStatement',
DirectiveStatement: 'DirectiveStatement',
DoWhileStatement: 'DoWhileStatement',
EmptyStatement: 'EmptyStatement',
ExpressionStatement: 'ExpressionStatement',
ForStatement: 'ForStatement',
ForInStatement: 'ForInStatement',
FunctionDeclaration: 'FunctionDeclaration',
FunctionExpression: 'FunctionExpression',
Identifier: 'Identifier',
IfStatement: 'IfStatement',
Literal: 'Literal',
LabeledStatement: 'LabeledStatement',
LogicalExpression: 'LogicalExpression',
MemberExpression: 'MemberExpression',
MethodDefinition: 'MethodDefinition',
NewExpression: 'NewExpression',
ObjectExpression: 'ObjectExpression',
ObjectPattern: 'ObjectPattern',
Program: 'Program',
Property: 'Property',
ReturnStatement: 'ReturnStatement',
SequenceExpression: 'SequenceExpression',
SwitchStatement: 'SwitchStatement',
SwitchCase: 'SwitchCase',
ThisExpression: 'ThisExpression',
ThrowStatement: 'ThrowStatement',
TryStatement: 'TryStatement',
UnaryExpression: 'UnaryExpression',
UpdateExpression: 'UpdateExpression',
VariableDeclaration: 'VariableDeclaration',
VariableDeclarator: 'VariableDeclarator',
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement',
YieldExpression: 'YieldExpression'
};
function ignoreJSHintError() { }
isArray = Array.isArray;
if (!isArray) {
isArray = function isArray(array) {
return Object.prototype.toString.call(array) === '[object Array]';
};
}
function deepCopy(obj) {
var ret = {}, key, val;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
val = obj[key];
if (typeof val === 'object' && val !== null) {
ret[key] = deepCopy(val);
} else {
ret[key] = val;
}
}
}
return ret;
}
function shallowCopy(obj) {
var ret = {}, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
ret[key] = obj[key];
}
}
return ret;
}
ignoreJSHintError(shallowCopy);
// based on LLVM libc++ upper_bound / lower_bound
// MIT License
function upperBound(array, func) {
var diff, len, i, current;
len = array.length;
i = 0;
while (len) {
diff = len >>> 1;
current = i + diff;
if (func(array[current])) {
len = diff;
} else {
i = current + 1;
len -= diff + 1;
}
}
return i;
}
function lowerBound(array, func) {
var diff, len, i, current;
len = array.length;
i = 0;
while (len) {
diff = len >>> 1;
current = i + diff;
if (func(array[current])) {
i = current + 1;
len -= diff + 1;
} else {
len = diff;
}
}
return i;
}
ignoreJSHintError(lowerBound);
VisitorKeys = {
AssignmentExpression: ['left', 'right'],
ArrayExpression: ['elements'],
ArrayPattern: ['elements'],
ArrowFunctionExpression: ['params', 'defaults', 'rest', 'body'],
BlockStatement: ['body'],
BinaryExpression: ['left', 'right'],
BreakStatement: ['label'],
CallExpression: ['callee', 'arguments'],
CatchClause: ['param', 'body'],
ClassBody: ['body'],
ClassDeclaration: ['id', 'body', 'superClass'],
ClassExpression: ['id', 'body', 'superClass'],
ConditionalExpression: ['test', 'consequent', 'alternate'],
ContinueStatement: ['label'],
DebuggerStatement: [],
DirectiveStatement: [],
DoWhileStatement: ['body', 'test'],
EmptyStatement: [],
ExpressionStatement: ['expression'],
ForStatement: ['init', 'test', 'update', 'body'],
ForInStatement: ['left', 'right', 'body'],
ForOfStatement: ['left', 'right', 'body'],
FunctionDeclaration: ['id', 'params', 'defaults', 'rest', 'body'],
FunctionExpression: ['id', 'params', 'defaults', 'rest', 'body'],
Identifier: [],
IfStatement: ['test', 'consequent', 'alternate'],
Literal: [],
LabeledStatement: ['label', 'body'],
LogicalExpression: ['left', 'right'],
MemberExpression: ['object', 'property'],
MethodDefinition: ['key', 'value'],
NewExpression: ['callee', 'arguments'],
ObjectExpression: ['properties'],
ObjectPattern: ['properties'],
Program: ['body'],
Property: ['key', 'value'],
ReturnStatement: ['argument'],
SequenceExpression: ['expressions'],
SwitchStatement: ['discriminant', 'cases'],
SwitchCase: ['test', 'consequent'],
ThisExpression: [],
ThrowStatement: ['argument'],
TryStatement: ['block', 'handlers', 'handler', 'guardedHandlers', 'finalizer'],
UnaryExpression: ['argument'],
UpdateExpression: ['argument'],
VariableDeclaration: ['declarations'],
VariableDeclarator: ['id', 'init'],
WhileStatement: ['test', 'body'],
WithStatement: ['object', 'body'],
YieldExpression: ['argument']
};
// unique id
BREAK = {};
SKIP = {};
VisitorOption = {
Break: BREAK,
Skip: SKIP
};
function Reference(parent, key) {
this.parent = parent;
this.key = key;
}
Reference.prototype.replace = function replace(node) {
this.parent[this.key] = node;
};
function Element(node, path, wrap, ref) {
this.node = node;
this.path = path;
this.wrap = wrap;
this.ref = ref;
}
function Controller() { }
// API:
// return property path array from root to current node
Controller.prototype.path = function path() {
var i, iz, j, jz, result, element;
function addToPath(result, path) {
if (isArray(path)) {
for (j = 0, jz = path.length; j < jz; ++j) {
result.push(path[j]);
}
} else {
result.push(path);
}
}
// root node
if (!this.__current.path) {
return null;
}
// first node is sentinel, second node is root element
result = [];
for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
element = this.__leavelist[i];
addToPath(result, element.path);
}
addToPath(result, this.__current.path);
return result;
};
// API:
// return array of parent elements
Controller.prototype.parents = function parents() {
var i, iz, result;
// first node is sentinel
result = [];
for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
result.push(this.__leavelist[i].node);
}
return result;
};
// API:
// return current node
Controller.prototype.current = function current() {
return this.__current.node;
};
Controller.prototype.__execute = function __execute(callback, element) {
var previous, result;
result = undefined;
previous = this.__current;
this.__current = element;
this.__state = null;
if (callback) {
result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
}
this.__current = previous;
return result;
};
// API:
// notify control skip / break
Controller.prototype.notify = function notify(flag) {
this.__state = flag;
};
// API:
// skip child nodes of current node
Controller.prototype.skip = function () {
this.notify(SKIP);
};
// API:
// break traversals
Controller.prototype['break'] = function () {
this.notify(BREAK);
};
Controller.prototype.__initialize = function(root, visitor) {
this.visitor = visitor;
this.root = root;
this.__worklist = [];
this.__leavelist = [];
this.__current = null;
this.__state = null;
};
Controller.prototype.traverse = function traverse(root, visitor) {
var worklist,
leavelist,
element,
node,
nodeType,
ret,
key,
current,
current2,
candidates,
candidate,
sentinel;
this.__initialize(root, visitor);
sentinel = {};
// reference
worklist = this.__worklist;
leavelist = this.__leavelist;
// initialize
worklist.push(new Element(root, null, null, null));
leavelist.push(new Element(null, null, null, null));
while (worklist.length) {
element = worklist.pop();
if (element === sentinel) {
element = leavelist.pop();
ret = this.__execute(visitor.leave, element);
if (this.__state === BREAK || ret === BREAK) {
return;
}
continue;
}
if (element.node) {
ret = this.__execute(visitor.enter, element);
if (this.__state === BREAK || ret === BREAK) {
return;
}
worklist.push(sentinel);
leavelist.push(element);
if (this.__state === SKIP || ret === SKIP) {
continue;
}
node = element.node;
nodeType = element.wrap || node.type;
candidates = VisitorKeys[nodeType];
current = candidates.length;
while ((current -= 1) >= 0) {
key = candidates[current];
candidate = node[key];
if (!candidate) {
continue;
}
if (!isArray(candidate)) {
worklist.push(new Element(candidate, key, null, null));
continue;
}
current2 = candidate.length;
while ((current2 -= 1) >= 0) {
if (!candidate[current2]) {
continue;
}
if ((nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === candidates[current]) {
element = new Element(candidate[current2], [key, current2], 'Property', null);
} else {
element = new Element(candidate[current2], [key, current2], null, null);
}
worklist.push(element);
}
}
}
}
};
Controller.prototype.replace = function replace(root, visitor) {
var worklist,
leavelist,
node,
nodeType,
target,
element,
current,
current2,
candidates,
candidate,
sentinel,
outer,
key;
this.__initialize(root, visitor);
sentinel = {};
// reference
worklist = this.__worklist;
leavelist = this.__leavelist;
// initialize
outer = {
root: root
};
element = new Element(root, null, null, new Reference(outer, 'root'));
worklist.push(element);
leavelist.push(element);
while (worklist.length) {
element = worklist.pop();
if (element === sentinel) {
element = leavelist.pop();
target = this.__execute(visitor.leave, element);
// node may be replaced with null,
// so distinguish between undefined and null in this place
if (target !== undefined && target !== BREAK && target !== SKIP) {
// replace
element.ref.replace(target);
}
if (this.__state === BREAK || target === BREAK) {
return outer.root;
}
continue;
}
target = this.__execute(visitor.enter, element);
// node may be replaced with null,
// so distinguish between undefined and null in this place
if (target !== undefined && target !== BREAK && target !== SKIP) {
// replace
element.ref.replace(target);
element.node = target;
}
if (this.__state === BREAK || target === BREAK) {
return outer.root;
}
// node may be null
node = element.node;
if (!node) {
continue;
}
worklist.push(sentinel);
leavelist.push(element);
if (this.__state === SKIP || target === SKIP) {
continue;
}
nodeType = element.wrap || node.type;
candidates = VisitorKeys[nodeType];
current = candidates.length;
while ((current -= 1) >= 0) {
key = candidates[current];
candidate = node[key];
if (!candidate) {
continue;
}
if (!isArray(candidate)) {
worklist.push(new Element(candidate, key, null, new Reference(node, key)));
continue;
}
current2 = candidate.length;
while ((current2 -= 1) >= 0) {
if (!candidate[current2]) {
continue;
}
if (nodeType === Syntax.ObjectExpression && 'properties' === candidates[current]) {
element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
} else {
element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
}
worklist.push(element);
}
}
}
return outer.root;
};
function traverse(root, visitor) {
var controller = new Controller();
return controller.traverse(root, visitor);
}
function replace(root, visitor) {
var controller = new Controller();
return controller.replace(root, visitor);
}
function extendCommentRange(comment, tokens) {
var target;
target = upperBound(tokens, function search(token) {
return token.range[0] > comment.range[0];
});
comment.extendedRange = [comment.range[0], comment.range[1]];
if (target !== tokens.length) {
comment.extendedRange[1] = tokens[target].range[0];
}
target -= 1;
if (target >= 0) {
comment.extendedRange[0] = tokens[target].range[1];
}
return comment;
}
function attachComments(tree, providedComments, tokens) {
// At first, we should calculate extended comment ranges.
var comments = [], comment, len, i, cursor;
if (!tree.range) {
throw new Error('attachComments needs range information');
}
// tokens array is empty, we attach comments to tree as 'leadingComments'
if (!tokens.length) {
if (providedComments.length) {
for (i = 0, len = providedComments.length; i < len; i += 1) {
comment = deepCopy(providedComments[i]);
comment.extendedRange = [0, tree.range[0]];
comments.push(comment);
}
tree.leadingComments = comments;
}
return tree;
}
for (i = 0, len = providedComments.length; i < len; i += 1) {
comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
}
// This is based on John Freeman's implementation.
cursor = 0;
traverse(tree, {
enter: function (node) {
var comment;
while (cursor < comments.length) {
comment = comments[cursor];
if (comment.extendedRange[1] > node.range[0]) {
break;
}
if (comment.extendedRange[1] === node.range[0]) {
if (!node.leadingComments) {
node.leadingComments = [];
}
node.leadingComments.push(comment);
comments.splice(cursor, 1);
} else {
cursor += 1;
}
}
// already out of owned node
if (cursor === comments.length) {
return VisitorOption.Break;
}
if (comments[cursor].extendedRange[0] > node.range[1]) {
return VisitorOption.Skip;
}
}
});
cursor = 0;
traverse(tree, {
leave: function (node) {
var comment;
while (cursor < comments.length) {
comment = comments[cursor];
if (node.range[1] < comment.extendedRange[0]) {
break;
}
if (node.range[1] === comment.extendedRange[0]) {
if (!node.trailingComments) {
node.trailingComments = [];
}
node.trailingComments.push(comment);
comments.splice(cursor, 1);
} else {
cursor += 1;
}
}
// already out of owned node
if (cursor === comments.length) {
return VisitorOption.Break;
}
if (comments[cursor].extendedRange[0] > node.range[1]) {
return VisitorOption.Skip;
}
}
});
return tree;
}
exports.version = '1.5.1-dev';
exports.Syntax = Syntax;
exports.traverse = traverse;
exports.replace = replace;
exports.attachComments = attachComments;
exports.VisitorKeys = VisitorKeys;
exports.VisitorOption = VisitorOption;
exports.Controller = Controller;
}));
/* vim: set sw=4 ts=4 et tw=80 : */
},{}],8:[function(require,module,exports){
'use strict';
function ToObject(val) {
if (val == null) {
throw new TypeError('Object.assign cannot be called with null or undefined');
}
return Object(val);
}
module.exports = Object.assign || function (target, source) {
var pendingException;
var from;
var keys;
var to = ToObject(target);
for (var s = 1; s < arguments.length; s++) {
from = arguments[s];
keys = Object.keys(Object(from));
for (var i = 0; i < keys.length; i++) {
try {
to[keys[i]] = from[keys[i]];
} catch (err) {
if (pendingException === undefined) {
pendingException = err;
}
}
}
}
if (pendingException) {
throw pendingException;
}
return to;
};
},{}],9:[function(require,module,exports){
/**
* @fileoverview Main ESLint object.
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var esprima = require("esprima"),
estraverse = require("estraverse"),
escope = require("escope"),
environments = require("../conf/environments.json"),
assign = require("object-assign"),
rules = require("./rules"),
util = require("./util"),
RuleContext = require("./rule-context"),
EventEmitter = require("events").EventEmitter;
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
function escapeRegExp(rx) {
return rx.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
/**
* Parses a list of "name:boolean_value" or/and "name" options divided by comma or
* whitespace.
* @param {string} string The string to parse.
* @returns {Object} Result map object of names and boolean values
*/
function parseBooleanConfig(string) {
var items = {};
// Collapse whitespace around : to make parsing easier
string = string.replace(/\s*:\s*/g, ":");
// Collapse whitespace around ,
string = string.replace(/\s*,\s*/g, ",");
string.split(/\s|,+/).forEach(function(name) {
if (!name) {
return;
}
var pos = name.indexOf(":"),
value;
if (pos !== -1) {
value = name.substring(pos + 1, name.length);
name = name.substring(0, pos);
}
items[name] = (value === "true");
});
return items;
}
/**
* Parses a JSON-like config.
* @param {string} string The string to parse.
* @returns {Object} Result map object
*/
function parseJsonConfig(string) {
var items = {};
string = string.replace(/([a-z0-9\-\/]+):/g, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/, "$1,");
try {
items = JSON.parse("{" + string + "}");
} catch(e) { }
return items;
}
/**
* Parses a config of values separated by comma.
* @param {string} string The string to parse.
* @returns {Object} Result map of values and true values
*/
function parseListConfig(string) {
var items = {};
// Collapse whitespace around ,
string = string.replace(/\s*,\s*/g, ",");
string.split(/,+/).forEach(function(name) {
name = name.trim();
if (!name) {
return;
}
items[name] = true;
});
return items;
}
/**
* @param {Scope} scope The scope object to check.
* @param {string} name The name of the variable to look up.
* @returns {Variable} The variable object if found or null if not.
*/
function getVariable(scope, name) {
var variable = null;
scope.variables.some(function(v) {
if (v.name === name) {
variable = v;
return true;
} else {
return false;
}
});
return variable;
}
/**
* Ensures that variables representing built-in properties of the Global Object,
* and any globals declared by special block comments, are present in the global
* scope.
* @param {ASTNode} program The top node of the AST.
* @param {Scope} globalScope The global scope.
* @param {Object} config The existing configuration data.
* @returns {void}
*/
function addDeclaredGlobals(program, globalScope, config) {
var declaredGlobals = {},
explicitGlobals = {},
builtin = environments.builtin;
assign(declaredGlobals, builtin);
Object.keys(config.env).forEach(function (name) {
if (config.env[name]) {
var environmentGlobals = environments[name] && environments[name].globals;
if (environmentGlobals) {
assign(declaredGlobals, environmentGlobals);
}
}
});
assign(declaredGlobals, config.globals);
assign(explicitGlobals, config.astGlobals);
Object.keys(declaredGlobals).forEach(function(name) {
var variable = getVariable(globalScope, name);
if (!variable) {
variable = new escope.Variable(name, globalScope);
variable.eslintExplicitGlobal = false;
globalScope.variables.push(variable);
}
variable.writeable = declaredGlobals[name];
});
Object.keys(explicitGlobals).forEach(function(name) {
var variable = getVariable(globalScope, name);
if (!variable) {
variable = new escope.Variable(name, globalScope);
variable.eslintExplicitGlobal = true;
globalScope.variables.push(variable);
}
variable.writeable = explicitGlobals[name];
});
}
/**
* Add data to reporting configuration to disable reporting for list of rules
* starting from start location
* @param {Object[]} reportingConfig Current reporting configuration
* @param {Object} start Position to start
* @param {string[]} rules List of rules
* @returns {void}
*/
function disableReporting(reportingConfig, start, rules) {
if (rules.length) {
rules.forEach(function(rule) {
reportingConfig.push({
start: start,
end: null,
rule: rule
});
});
} else {
reportingConfig.push({
start: start,
end: null,
rule: null
});
}
}
/**
* Add data to reporting configuration to enable reporting for list of rules
* starting from start location
* @param {Object[]} reportingConfig Current reporting configuration
* @param {Object} start Position to start
* @param {string[]} rules List of rules
* @returns {void}
*/
function enableReporting(reportingConfig, start, rules) {
var i;
if (rules.length) {
rules.forEach(function(rule) {
for (i = reportingConfig.length - 1; i >= 0; i--) {
if (!reportingConfig[i].end && reportingConfig[i].rule === rule ) {
reportingConfig[i].end = start;
break;
}
}
});
} else {
// find all previous disabled locations if they was started as list of rules
var prevStart;
for (i = reportingConfig.length - 1; i >= 0; i--) {
if (prevStart && prevStart !== reportingConfig[i].start) {
break;
}
if (!reportingConfig[i].end) {
reportingConfig[i].end = start;
prevStart = reportingConfig[i].start;
}
}
}
}
/**
* Parses comments in file to extract file-specific config of rules, globals
* and environments and merges them with global config; also code blocks
* where reporting is disabled or enabled and merges them with reporting config.
* @param {ASTNode} ast The top node of the AST.
* @param {Object} config The existing configuration data.
* @param {Object[]} reportingConfig The existing reporting configuration data.
* @returns {void}
*/
function modifyConfigsFromComments(ast, config, reportingConfig) {
var commentConfig = {
astGlobals: {},
rules: {},
env: {}
};
var commentRules = {};
ast.comments.forEach(function(comment) {
if (comment.type === "Block") {
var value = comment.value.trim();
var match = /^(eslint-\w+|eslint|globals?)(\s|$)/.exec(value);
if (match) {
value = value.substring(match.index + match[1].length);
switch (match[1]) {
case "globals":
case "global":
util.mixin(commentConfig.astGlobals, parseBooleanConfig(value));
break;
case "eslint-env":
util.mixin(commentConfig.env, parseListConfig(value));
break;
case "eslint-disable":
disableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
break;
case "eslint-enable":
enableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
break;
case "eslint":
var items = parseJsonConfig(value);
Object.keys(items).forEach(function(name) {
var ruleValue = items[name];
if (typeof ruleValue === "number" || (Array.isArray(ruleValue) && typeof ruleValue[0] === "number")) {
commentRules[name] = ruleValue;
}
});
break;
// no default
}
}
}
});
// apply environment rules before user rules
Object.keys(commentConfig.env).forEach(function (name) {
var environmentRules = environments[name] && environments[name].rules;
if (commentConfig.env[name] && environmentRules) {
util.mixin(commentConfig.rules, environmentRules);
}
});
util.mixin(commentConfig.rules, commentRules);
util.mergeConfigs(config, commentConfig);
}
/**
* Check if message of rule with ruleId should be ignored in location
* @param {Object[]} reportingConfig Collection of ignore records
* @param {string} ruleId Id of rule
* @param {Object} location Location of message
* @returns {boolean} True if message should be ignored, false otherwise
*/
function isDisabledByReportingConfig(reportingConfig, ruleId, location) {
for (var i = 0, c = reportingConfig.length; i < c; i++) {
var ignore = reportingConfig[i];
if ((!ignore.rule || ignore.rule === ruleId) &&
(location.line > ignore.start.line || (location.line === ignore.start.line && location.column >= ignore.start.column)) &&
(!ignore.end || (location.line < ignore.end.line || (location.line === ignore.end.line && location.column <= ignore.end.column)))) {
return true;
}
}
return false;
}
/**
* Process initial config to make it safe to extend by file comment config
* @param {Object} config Initial config
* @returns {Object} Processed config
*/
function prepareConfig(config) {
config.globals = config.globals || config.global || {};
delete config.global;
var copiedRules = {};
if (typeof config.rules === "object") {
Object.keys(config.rules).forEach(function(k) {
var rule = config.rules[k];
if (Array.isArray(rule)) {
copiedRules[k] = rule.slice();
} else {
copiedRules[k] = rule;
}
});
}
return {
rules: copiedRules,
globals: util.mergeConfigs({}, config.globals),
env: util.mergeConfigs({}, config.env || {}),
settings: util.mergeConfigs({}, config.settings || {})
};
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/**
* Object that is responsible for verifying JavaScript text
* @name eslint
*/
module.exports = (function() {
var api = Object.create(new EventEmitter()),
messages = [],
currentText = null,
currentTextLines = [],
currentConfig = null,
currentTokens = null,
currentScopes = null,
scopeMap = null,
currentFilename = null,
controller = null,
reportingConfig = [],
commentLocsEnter = [],
commentLocsExit = [];
/**
* Parses text into an AST. Moved out here because the try-catch prevents
* optimization of functions, so it's best to keep the try-catch as isolated
* as possible
* @param {string} text The text to parse.
* @returns {ASTNode} The AST if successful or null if not.
* @private
*/
function parse(text) {
/*
* Check for parsing errors first. If there's a parsing error, nothing
* else can happen. However, a parsing error does not throw an error
* from this method - it's just considered a fatal error message, a
* problem that ESLint identified just like any other.
*/
try {
return esprima.parse(text, {
loc: true,
range: true,
raw: true,
tokens: true,
comment: true,
attachComment: true
});
} catch (ex) {
messages.push({
fatal: true,
severity: 2,
// messages come as "Line X: Unexpected token foo", so strip off leading part
message: ex.message.substring(ex.message.indexOf(":") + 1).trim(),
line: ex.lineNumber,
column: ex.column
});
return null;
}
}
/**
* Check collection of comments to prevent double event for comment as
* leading and trailing, then emit event if passing
* @param {ASTNode[]} comments Collection of comment nodes
* @param {Object[]} locs List of locations of previous comment nodes
* @param {string} eventName Event name postfix
* @returns {void}
*/
function emitComments(comments, locs, eventName) {
if (comments.length) {
comments.forEach(function(node) {
if (locs.indexOf(node.loc) >= 0) {
locs.splice(locs.indexOf(node.loc), 1);
} else {
locs.push(node.loc);
api.emit(node.type + eventName, node);
}
});
}
}
/**
* Shortcut to check and emit enter of comment nodes
* @param {ASTNode[]} comments Collection of comment nodes
* @returns {void}
*/
function emitCommentsEnter(comments) {
emitComments(comments, commentLocsEnter, "Comment");
}
/**
* Shortcut to check and emit exit of comment nodes
* @param {ASTNode[]} comments Collection of comment nodes
* @returns {void}
*/
function emitCommentsExit(comments) {
emitComments(comments, commentLocsExit, "Comment:exit");
}
/**
* Get the severity level of a rule (0 - none, 1 - warning, 2 - error)
* Returns 0 if the rule config is not valid (an Array or a number)
* @param {Array|number} ruleConfig rule configuration
* @returns {number} 0, 1, or 2, indicating rule severity
*/
function getRuleSeverity(ruleConfig) {
if (typeof ruleConfig === "number") {
return ruleConfig;
} else if (Array.isArray(ruleConfig)) {
return ruleConfig[0];
} else {
return 0;
}
}
/**
* Get the options for a rule (not including severity), if any
* @param {Array|number} ruleConfig rule configuration
* @returns {Array} of rule options, empty Array if none
*/
function getRuleOptions(ruleConfig) {
if (Array.isArray(ruleConfig)) {
return ruleConfig.slice(1);
} else {
return [];
}
}
// set unlimited listeners (see https://github.com/eslint/eslint/issues/524)
api.setMaxListeners(0);
/**
* Resets the internal state of the object.
* @returns {void}
*/
api.reset = function() {
this.removeAllListeners();
messages = [];
currentConfig = null;
currentText = null;
currentTextLines = [];
currentTokens = null;
currentScopes = null;
scopeMap = null;
controller = null;
reportingConfig = [];
commentLocsEnter = [];
commentLocsExit = [];
};
/**
* Verifies the text against the rules specified by the second argument.
* @param {string} text The JavaScript text to verify.
* @param {Object} config An object whose keys specify the rules to use.
* @param {string=} filename The optional filename of the file being checked.
* If this is not set, the filename will default to '<input>' in the rule context.
* @param {boolean=} saveState Indicates if the state from the last run should be saved.
* Mostly useful for testing purposes.
* @returns {Object[]} The results as an array of messages or null if no messages.
*/
api.verify = function(text, config, filename, saveState) {
var ast;
// set the current parsed filename
currentFilename = filename;
if (!saveState) {
this.reset();
}
ast = parse(text.replace(/^#!([^\r\n]+[\r\n]+)/, "//$1"));
// if Esprima failed to parse the file, there's no sense in setting up rules
if (ast) {
// process initial config to make it safe to extend
config = prepareConfig(config);
// parse global comments and modify config
modifyConfigsFromComments(ast, config, reportingConfig);
// enable appropriate rules
Object.keys(config.rules).filter(function(key) {
return getRuleSeverity(config.rules[key]) > 0;
}).forEach(function(key) {
var ruleCreator = rules.get(key),
severity = getRuleSeverity(config.rules[key]),
options = getRuleOptions(config.rules[key]),
rule;
if (ruleCreator) {
try {
rule = ruleCreator(new RuleContext(key, api, severity, options, config.settings));
// add all the node types as listeners
Object.keys(rule).forEach(function(nodeType) {
api.on(nodeType, rule[nodeType]);
});
} catch(ex) {
ex.message = "Error while loading rule '" + key + "': " + ex.message;
throw ex;
}
} else {
throw new Error("Definition for rule '" + key + "' was not found.");
}
});
// save config so rules can access as necessary
currentConfig = config;
currentText = text;
controller = new estraverse.Controller();
// gather data that may be needed by the rules
currentScopes = escope.analyze(ast, { ignoreEval: true }).scopes;
/*
* Index the scopes by the start range of their block for efficient
* lookup in getScope.
*/
scopeMap = [];
currentScopes.forEach(function (scope, index) {
var range = scope.block.range[0];
// Sometimes two scopes are returned for a given node. This is
// handled later in a known way, so just don't overwrite here.
if (!scopeMap[range]) {
scopeMap[range] = index;
}
});
/*
* Split text here into array of lines so
* it's not being done repeatedly
* by individual rules.
*/
currentTextLines = currentText.split(/\r?\n/g);
// Freezing so array isn't accidentally changed by a rule.
Object.freeze(currentTextLines);
/* get all tokens from the ast and store them as a hashtable to
* improve traversal speed when wanting to find tokens for a given
* node
*/
currentTokens = [];
ast.tokens.forEach(function(token) {
currentTokens[token.range[0]] = token;
});
// augment global scope with declared global variables
addDeclaredGlobals(ast, currentScopes[0], currentConfig);
/*
* Each node has a type property. Whenever a particular type of node is found,
* an event is fired. This allows any listeners to automatically be informed
* that this type of node has been found and react accordingly.
*/
controller.traverse(ast, {
enter: function(node, parent) {
var comments = api.getComments(node);
emitCommentsEnter(comments.leading);
node.parent = parent;
api.emit(node.type, node);
emitCommentsEnter(comments.trailing);
},
leave: function(node) {
var comments = api.getComments(node);
emitCommentsExit(comments.trailing);
api.emit(node.type + ":exit", node);
emitCommentsExit(comments.leading);
}
});
}
return messages;
};
/**
* Reports a message from one of the rules.
* @param {string} ruleId The ID of the rule causing the message.
* @param {number} severity The severity level of the rule as configured.
* @param {ASTNode} node The AST node that the message relates to.
* @param {Object=} location An object containing the error line and column
* numbers. If location is not provided the node's start location will
* be used.
* @param {string} message The actual message.
* @param {Object} opts Optional template data which produces a formatted message
* with symbols being replaced by this object's values.
* @returns {void}
*/
api.report = function(ruleId, severity, node, location, message, opts) {
if (typeof location === "string") {
opts = message;
message = location;
location = node.loc.start;
}
Object.keys(opts || {}).forEach(function (key) {
var rx = new RegExp("{{" + escapeRegExp(key) + "}}", "g");
message = message.replace(rx, opts[key]);
});
if (isDisabledByReportingConfig(reportingConfig, ruleId, location)) {
return;
}
messages.push({
ruleId: ruleId,
severity: severity,
node: node,
message: message,
line: location.line,
column: location.column,
source: api.getSource(node)
});
};
/**
* Gets the source code for the given node.
* @param {ASTNode=} node The AST node to get the text for.
* @param {int=} beforeCount The number of characters before the node to retrieve.
* @param {int=} afterCount The number of characters after the node to retrieve.
* @returns {string} The text representing the AST node.
*/
api.getSource = function(node, beforeCount, afterCount) {
if (node) {
return (currentText !== null) ? currentText.slice(node.range[0] - (beforeCount || 0),
node.range[1] + (afterCount || 0)) : null;
} else {
return currentText;
}
};
/**
* Gets the entire source text split into an array of lines.
* @returns {Array} The source text as an array of lines.
*/
api.getSourceLines = function() {
return currentTextLines;
};
/**
* Gets all comments for the given node.
* @param {ASTNode} node The AST node to get the comments for.
* @returns {Object} The list of comments indexed by their position.
*/
api.getComments = function(node) {
var leadingComments = node.leadingComments || [],
trailingComments = node.trailingComments || [];
/*
* Esprima adds a "comments" array on Program nodes rather than
* leadingComments/trailingComments. Comments are only left in the
* Program node comments array if there is no executable code.
*/
if (node.type === "Program") {
if (node.body.length === 0) {
leadingComments = node.comments;
}
}
return {
leading: leadingComments,
trailing: trailingComments
};
};
/**
* Retrieves the JSDoc comment for a given node.
* @param {ASTNode} node The AST node to get the comment for.
* @returns {ASTNode} The BlockComment node containing the JSDoc for the
* given node or null if not found.
*/
api.getJSDocComment = function(node) {
var parent = node.parent,
line = node.loc.start.line;
/**
* Finds a JSDoc comment node in an array of comment nodes.
* @param {ASTNode[]} comments The array of comment nodes to search.
* @returns {ASTNode} The node if found, null if not.
* @private
*/
function findJSDocComment(comments) {
if (comments) {
for (var i = comments.length - 1; i >= 0; i--) {
if (comments[i].type === "Block" && comments[i].value.charAt(0) === "*") {
if (line - comments[i].loc.end.line <= 1) {
return comments[i];
} else {
break;
}
}
}
}
return null;
}
switch (node.type) {
case "FunctionDeclaration":
return findJSDocComment(node.leadingComments);
case "FunctionExpression":
if (parent.type !== "CallExpression" || parent.callee !== node) {
while (parent && !parent.leadingComments && parent.type !== "FunctionExpression" && parent.type !== "FunctionDeclaration") {
parent = parent.parent;
}
return parent && (parent.type !== "FunctionDeclaration") ? findJSDocComment(parent.leadingComments) : null;
}
// falls through
default:
return null;
}
};
/**
* Gets a number of tokens that precede a given node's tokens in the token stream.
* @param {ASTNode} node The AST node.
* @param {int} [beforeCount=0] The number of tokens before the node to retrieve.
* @returns {[Token]} Array of objects representing tokens.
*/
api.getTokensBefore = function(node, beforeCount) {
var beforeTokens = [], cursor = node.range[0] - 1;
while (beforeCount > 0 && cursor >= 0) {
if (currentTokens[cursor]) {
beforeTokens.unshift(currentTokens[cursor]);
--beforeCount;
}
--cursor;
}
return beforeTokens;
};
/**
* Gets the token that precedes a given node's tokens in the token stream.
* @param {ASTNode} node The AST node.
* @param {int} [skip=0] A number of tokens to skip before the given node.
* @returns {Token} An object representing the token.
*/
api.getTokenBefore = function(node, skip) {
for (var cursor = node.range[0] - 1; cursor >= 0; --cursor) {
if (currentTokens[cursor]) {
if (skip > 0) {
--skip;
} else {
return currentTokens[cursor];
}
}
}
};
/**
* Gets a number of tokens that precede a given node's tokens in the token stream.
* @param {ASTNode} node The AST node.
* @param {int} [afterCount=0] The number of tokens after the node to retrieve.
* @returns {[Token]} Array of objects representing tokens.
*/
api.getTokensAfter = function(node, afterCount) {
var afterTokens = [], cursor = node.range[1];
while (afterCount > 0 && cursor < currentTokens.length) {
if (currentTokens[cursor]) {
afterTokens.push(currentTokens[cursor]);
--afterCount;
cursor = currentTokens[cursor].range[1];
} else {
++cursor;
}
}
return afterTokens;
};
/**
* Gets the token that follows a given node's tokens in the token stream.
* @param {ASTNode} node The AST node.
* @param {int} [skip=0] A number of tokens to skip after the given node.
* @returns {Token} An object representing the token.
*/
api.getTokenAfter = function(node, skip) {
for (var cursor = node.range[1]; cursor < currentTokens.length; ++cursor) {
if (currentTokens[cursor]) {
if (skip > 0) {
--skip;
} else {
return currentTokens[cursor];
}
}
}
};
/**
* Gets all tokens that are related to the given node.
* @param {ASTNode} node The AST node.
* @param {int} [beforeCount=0] The number of tokens before the node to retrieve.
* @param {int} [afterCount=0] The number of tokens after the node to retrieve.
* @returns {[Token]} Array of objects representing tokens.
*/
api.getTokens = function(node, beforeCount, afterCount) {
var beforeTokens = api.getTokensBefore(node, beforeCount),
afterTokens = api.getTokensAfter(node, afterCount),
tokens = [],
cursor = node.range[0];
while (cursor < node.range[1]) {
if (currentTokens[cursor]) {
tokens.push(currentTokens[cursor]);
cursor = currentTokens[cursor].range[1];
} else {
++cursor;
}
}
return beforeTokens.concat(tokens, afterTokens);
};
/**
* Gets the first `count` tokens of the given node's token stream.
* @param {ASTNode} node The AST node.
* @param {int} [count=0] The number of tokens of the node to retrieve.
* @returns {[Token]} Array of objects representing tokens.
*/
api.getFirstTokens = function(node, count) {
var tokens = [], cursor = node.range[0];
while (count > 0 && cursor < node.range[1]) {
if (currentTokens[cursor]) {
tokens.push(currentTokens[cursor]);
--count;
cursor = currentTokens[cursor].range[1];
} else {
++cursor;
}
}
return tokens;
};
/**
* Gets the first token of the given node's token stream.
* @param {ASTNode} node The AST node.
* @param {int} [skip=0] A number of tokens to skip.
* @returns {Token} An object representing the token.
*/
api.getFirstToken = function(node, skip) {
for (var cursor = node.range[0]; cursor < node.range[1]; ++cursor) {
if (currentTokens[cursor]) {
if (skip > 0) {
--skip;
} else {
return currentTokens[cursor];
}
}
}
};
/**
* Gets the last `count` tokens of the given node.
* @param {ASTNode} node The AST node.
* @param {int} [count=0] The number of tokens of the node to retrieve.
* @returns {[Token]} Array of objects representing tokens.
*/
api.getLastTokens = function(node, count) {
var tokens = [], cursor = node.range[1] - 1;
while (count > 0 && cursor >= node.range[0]) {
if (currentTokens[cursor]) {
tokens.unshift(currentTokens[cursor]);
--count;
}
--cursor;
}
return tokens;
};
/**
* Gets the last token of the given node's token stream.
* @param {ASTNode} node The AST node.
* @param {int} [skip=0] A number of tokens to skip.
* @returns {Token} An object representing the token.
*/
api.getLastToken = function(node, skip) {
for (var cursor = node.range[1] - 1; cursor >= node.range[0]; --cursor) {
if (currentTokens[cursor]) {
if (skip > 0) {
--skip;
} else {
return currentTokens[cursor];
}
}
}
};
/**
* Gets nodes that are ancestors of current node.
* @returns {ASTNode[]} Array of objects representing ancestors.
*/
api.getAncestors = function() {
return controller.parents();
};
/**
* Gets the scope for the current node.
* @returns {Object} An object representing the current node's scope.
*/
api.getScope = function() {
var parents = controller.parents(),
innerBlock = null,
selectedScopeIndex;
// Don't do this for Program nodes - they have no parents
if (parents.length) {
// if current node is function declaration, add it to the list
var current = controller.current();
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression") {
parents.push(current);
}
// Ascend the current node's parents
for (var i = parents.length - 1; i >= 0; --i) {
// The first node that requires a scope is the node that will be
// our current node's innermost scope.
if (escope.Scope.isScopeRequired(parents[i])) {
innerBlock = parents[i];
break;
}
}
// Find and return the innermost scope
selectedScopeIndex = scopeMap[innerBlock.range[0]];
// Named function expressions create two nested scope objects. The
// outer scope contains only the function expression name. We return
// the inner scope.
if (innerBlock.type === "FunctionExpression" && innerBlock.id && innerBlock.id.name) {
++selectedScopeIndex;
}
return currentScopes[selectedScopeIndex];
} else {
return currentScopes[0]; // global scope
}
};
/**
* Gets the filename for the currently parsed source.
* @returns {string} The filename associated with the source being parsed.
* Defaults to "<input>" if no filename info is present.
*/
api.getFilename = function() {
if (typeof currentFilename === "string") {
return currentFilename;
} else {
return "<input>";
}
};
/**
* Defines a new linting rule.
* @param {string} ruleId A unique rule identifier
* @param {Function} ruleModule Function from context to object mapping AST node types to event handlers
* @returns {void}
*/
var defineRule = api.defineRule = function(ruleId, ruleModule) {
rules.define(ruleId, ruleModule);
};
/**
* Defines many new linting rules.
* @param {object} rules map from unique rule identifier to rule
* @returns {void}
*/
api.defineRules = function(rules) {
Object.getOwnPropertyNames(rules).forEach(function(ruleId) {
defineRule(ruleId, rules[ruleId]);
});
};
/**
* Gets the default eslint configuration.
* @returns {Object} Object mapping rule IDs to their default configurations
*/
api.defaults = function() {
return require("../conf/eslint.json");
};
return api;
}());
},{"../conf/environments.json":1,"../conf/eslint.json":2,"./rule-context":11,"./rules":12,"./util":150,"escope":5,"esprima":6,"estraverse":7,"events":3,"object-assign":8}],10:[function(require,module,exports){
module.exports = function() {
var rules = Object.create(null);
rules["block-scoped-var"] = require("./rules/block-scoped-var");
rules["brace-style"] = require("./rules/brace-style");
rules["camelcase"] = require("./rules/camelcase");
rules["comma-spacing"] = require("./rules/comma-spacing");
rules["comma-style"] = require("./rules/comma-style");
rules["complexity"] = require("./rules/complexity");
rules["consistent-return"] = require("./rules/consistent-return");
rules["consistent-this"] = require("./rules/consistent-this");
rules["curly"] = require("./rules/curly");
rules["default-case"] = require("./rules/default-case");
rules["dot-notation"] = require("./rules/dot-notation");
rules["eol-last"] = require("./rules/eol-last");
rules["eqeqeq"] = require("./rules/eqeqeq");
rules["func-names"] = require("./rules/func-names");
rules["func-style"] = require("./rules/func-style");
rules["global-strict"] = require("./rules/global-strict");
rules["guard-for-in"] = require("./rules/guard-for-in");
rules["handle-callback-err"] = require("./rules/handle-callback-err");
rules["key-spacing"] = require("./rules/key-spacing");
rules["max-depth"] = require("./rules/max-depth");
rules["max-len"] = require("./rules/max-len");
rules["max-nested-callbacks"] = require("./rules/max-nested-callbacks");
rules["max-params"] = require("./rules/max-params");
rules["max-statements"] = require("./rules/max-statements");
rules["new-cap"] = require("./rules/new-cap");
rules["new-parens"] = require("./rules/new-parens");
rules["no-alert"] = require("./rules/no-alert");
rules["no-array-constructor"] = require("./rules/no-array-constructor");
rules["no-bitwise"] = require("./rules/no-bitwise");
rules["no-caller"] = require("./rules/no-caller");
rules["no-catch-shadow"] = require("./rules/no-catch-shadow");
rules["no-comma-dangle"] = require("./rules/no-comma-dangle");
rules["no-cond-assign"] = require("./rules/no-cond-assign");
rules["no-console"] = require("./rules/no-console");
rules["no-constant-condition"] = require("./rules/no-constant-condition");
rules["no-control-regex"] = require("./rules/no-control-regex");
rules["no-debugger"] = require("./rules/no-debugger");
rules["no-delete-var"] = require("./rules/no-delete-var");
rules["no-div-regex"] = require("./rules/no-div-regex");
rules["no-dupe-keys"] = require("./rules/no-dupe-keys");
rules["no-else-return"] = require("./rules/no-else-return");
rules["no-empty-class"] = require("./rules/no-empty-class");
rules["no-empty-label"] = require("./rules/no-empty-label");
rules["no-empty"] = require("./rules/no-empty");
rules["no-eq-null"] = require("./rules/no-eq-null");
rules["no-eval"] = require("./rules/no-eval");
rules["no-ex-assign"] = require("./rules/no-ex-assign");
rules["no-extend-native"] = require("./rules/no-extend-native");
rules["no-extra-bind"] = require("./rules/no-extra-bind");
rules["no-extra-boolean-cast"] = require("./rules/no-extra-boolean-cast");
rules["no-extra-parens"] = require("./rules/no-extra-parens");
rules["no-extra-semi"] = require("./rules/no-extra-semi");
rules["no-extra-strict"] = require("./rules/no-extra-strict");
rules["no-fallthrough"] = require("./rules/no-fallthrough");
rules["no-floating-decimal"] = require("./rules/no-floating-decimal");
rules["no-func-assign"] = require("./rules/no-func-assign");
rules["no-implied-eval"] = require("./rules/no-implied-eval");
rules["no-inner-declarations"] = require("./rules/no-inner-declarations");
rules["no-invalid-regexp"] = require("./rules/no-invalid-regexp");
rules["no-iterator"] = require("./rules/no-iterator");
rules["no-label-var"] = require("./rules/no-label-var");
rules["no-labels"] = require("./rules/no-labels");
rules["no-lone-blocks"] = require("./rules/no-lone-blocks");
rules["no-lonely-if"] = require("./rules/no-lonely-if");
rules["no-loop-func"] = require("./rules/no-loop-func");
rules["no-mixed-requires"] = require("./rules/no-mixed-requires");
rules["no-mixed-spaces-and-tabs"] = require("./rules/no-mixed-spaces-and-tabs");
rules["no-multi-spaces"] = require("./rules/no-multi-spaces");
rules["no-multi-str"] = require("./rules/no-multi-str");
rules["no-multiple-empty-lines"] = require("./rules/no-multiple-empty-lines");
rules["no-native-reassign"] = require("./rules/no-native-reassign");
rules["no-negated-in-lhs"] = require("./rules/no-negated-in-lhs");
rules["no-nested-ternary"] = require("./rules/no-nested-ternary");
rules["no-new-func"] = require("./rules/no-new-func");
rules["no-new-object"] = require("./rules/no-new-object");
rules["no-new-require"] = require("./rules/no-new-require");
rules["no-new-wrappers"] = require("./rules/no-new-wrappers");
rules["no-new"] = require("./rules/no-new");
rules["no-obj-calls"] = require("./rules/no-obj-calls");
rules["no-octal-escape"] = require("./rules/no-octal-escape");
rules["no-octal"] = require("./rules/no-octal");
rules["no-path-concat"] = require("./rules/no-path-concat");
rules["no-plusplus"] = require("./rules/no-plusplus");
rules["no-process-env"] = require("./rules/no-process-env");
rules["no-process-exit"] = require("./rules/no-process-exit");
rules["no-proto"] = require("./rules/no-proto");
rules["no-redeclare"] = require("./rules/no-redeclare");
rules["no-regex-spaces"] = require("./rules/no-regex-spaces");
rules["no-reserved-keys"] = require("./rules/no-reserved-keys");
rules["no-restricted-modules"] = require("./rules/no-restricted-modules");
rules["no-return-assign"] = require("./rules/no-return-assign");
rules["no-script-url"] = require("./rules/no-script-url");
rules["no-self-compare"] = require("./rules/no-self-compare");
rules["no-sequences"] = require("./rules/no-sequences");
rules["no-shadow-restricted-names"] = require("./rules/no-shadow-restricted-names");
rules["no-shadow"] = require("./rules/no-shadow");
rules["no-space-before-semi"] = require("./rules/no-space-before-semi");
rules["no-spaced-func"] = require("./rules/no-spaced-func");
rules["no-sparse-arrays"] = require("./rules/no-sparse-arrays");
rules["no-sync"] = require("./rules/no-sync");
rules["no-ternary"] = require("./rules/no-ternary");
rules["no-trailing-spaces"] = require("./rules/no-trailing-spaces");
rules["no-undef-init"] = require("./rules/no-undef-init");
rules["no-undef"] = require("./rules/no-undef");
rules["no-undefined"] = require("./rules/no-undefined");
rules["no-underscore-dangle"] = require("./rules/no-underscore-dangle");
rules["no-unreachable"] = require("./rules/no-unreachable");
rules["no-unused-expressions"] = require("./rules/no-unused-expressions");
rules["no-unused-vars"] = require("./rules/no-unused-vars");
rules["no-use-before-define"] = require("./rules/no-use-before-define");
rules["no-void"] = require("./rules/no-void");
rules["no-warning-comments"] = require("./rules/no-warning-comments");
rules["no-with"] = require("./rules/no-with");
rules["no-wrap-func"] = require("./rules/no-wrap-func");
rules["one-var"] = require("./rules/one-var");
rules["padded-blocks"] = require("./rules/padded-blocks");
rules["quote-props"] = require("./rules/quote-props");
rules["quotes"] = require("./rules/quotes");
rules["radix"] = require("./rules/radix");
rules["semi"] = require("./rules/semi");
rules["sort-vars"] = require("./rules/sort-vars");
rules["space-after-keywords"] = require("./rules/space-after-keywords");
rules["space-before-blocks"] = require("./rules/space-before-blocks");
rules["space-in-brackets"] = require("./rules/space-in-brackets");
rules["space-in-parens"] = require("./rules/space-in-parens");
rules["space-infix-ops"] = require("./rules/space-infix-ops");
rules["space-return-throw-case"] = require("./rules/space-return-throw-case");
rules["space-unary-word-ops"] = require("./rules/space-unary-word-ops");
rules["spaced-line-comment"] = require("./rules/spaced-line-comment");
rules["strict"] = require("./rules/strict");
rules["use-isnan"] = require("./rules/use-isnan");
rules["valid-jsdoc"] = require("./rules/valid-jsdoc");
rules["valid-typeof"] = require("./rules/valid-typeof");
rules["vars-on-top"] = require("./rules/vars-on-top");
rules["wrap-iife"] = require("./rules/wrap-iife");
rules["wrap-regex"] = require("./rules/wrap-regex");
rules["yoda"] = require("./rules/yoda");
return rules;
};
},{"./rules/block-scoped-var":13,"./rules/brace-style":14,"./rules/camelcase":15,"./rules/comma-spacing":16,"./rules/comma-style":17,"./rules/complexity":18,"./rules/consistent-return":19,"./rules/consistent-this":20,"./rules/curly":21,"./rules/default-case":22,"./rules/dot-notation":23,"./rules/eol-last":24,"./rules/eqeqeq":25,"./rules/func-names":26,"./rules/func-style":27,"./rules/global-strict":28,"./rules/guard-for-in":29,"./rules/handle-callback-err":30,"./rules/key-spacing":31,"./rules/max-depth":32,"./rules/max-len":33,"./rules/max-nested-callbacks":34,"./rules/max-params":35,"./rules/max-statements":36,"./rules/new-cap":37,"./rules/new-parens":38,"./rules/no-alert":39,"./rules/no-array-constructor":40,"./rules/no-bitwise":41,"./rules/no-caller":42,"./rules/no-catch-shadow":43,"./rules/no-comma-dangle":44,"./rules/no-cond-assign":45,"./rules/no-console":46,"./rules/no-constant-condition":47,"./rules/no-control-regex":48,"./rules/no-debugger":49,"./rules/no-delete-var":50,"./rules/no-div-regex":51,"./rules/no-dupe-keys":52,"./rules/no-else-return":53,"./rules/no-empty":56,"./rules/no-empty-class":54,"./rules/no-empty-label":55,"./rules/no-eq-null":57,"./rules/no-eval":58,"./rules/no-ex-assign":59,"./rules/no-extend-native":60,"./rules/no-extra-bind":61,"./rules/no-extra-boolean-cast":62,"./rules/no-extra-parens":63,"./rules/no-extra-semi":64,"./rules/no-extra-strict":65,"./rules/no-fallthrough":66,"./rules/no-floating-decimal":67,"./rules/no-func-assign":68,"./rules/no-implied-eval":69,"./rules/no-inner-declarations":70,"./rules/no-invalid-regexp":71,"./rules/no-iterator":72,"./rules/no-label-var":73,"./rules/no-labels":74,"./rules/no-lone-blocks":75,"./rules/no-lonely-if":76,"./rules/no-loop-func":77,"./rules/no-mixed-requires":78,"./rules/no-mixed-spaces-and-tabs":79,"./rules/no-multi-spaces":80,"./rules/no-multi-str":81,"./rules/no-multiple-empty-lines":82,"./rules/no-native-reassign":83,"./rules/no-negated-in-lhs":84,"./rules/no-nested-ternary":85,"./rules/no-new":90,"./rules/no-new-func":86,"./rules/no-new-object":87,"./rules/no-new-require":88,"./rules/no-new-wrappers":89,"./rules/no-obj-calls":91,"./rules/no-octal":93,"./rules/no-octal-escape":92,"./rules/no-path-concat":94,"./rules/no-plusplus":95,"./rules/no-process-env":96,"./rules/no-process-exit":97,"./rules/no-proto":98,"./rules/no-redeclare":99,"./rules/no-regex-spaces":100,"./rules/no-reserved-keys":101,"./rules/no-restricted-modules":102,"./rules/no-return-assign":103,"./rules/no-script-url":104,"./rules/no-self-compare":105,"./rules/no-sequences":106,"./rules/no-shadow":108,"./rules/no-shadow-restricted-names":107,"./rules/no-space-before-semi":109,"./rules/no-spaced-func":110,"./rules/no-sparse-arrays":111,"./rules/no-sync":112,"./rules/no-ternary":113,"./rules/no-trailing-spaces":114,"./rules/no-undef":116,"./rules/no-undef-init":115,"./rules/no-undefined":117,"./rules/no-underscore-dangle":118,"./rules/no-unreachable":119,"./rules/no-unused-expressions":120,"./rules/no-unused-vars":121,"./rules/no-use-before-define":122,"./rules/no-void":123,"./rules/no-warning-comments":124,"./rules/no-with":125,"./rules/no-wrap-func":126,"./rules/one-var":127,"./rules/padded-blocks":128,"./rules/quote-props":129,"./rules/quotes":130,"./rules/radix":131,"./rules/semi":132,"./rules/sort-vars":133,"./rules/space-after-keywords":134,"./rules/space-before-blocks":135,"./rules/space-in-brackets":136,"./rules/space-in-parens":137,"./rules/space-infix-ops":138,"./rules/space-return-throw-case":139,"./rules/space-unary-word-ops":140,"./rules/spaced-line-comment":141,"./rules/strict":142,"./rules/use-isnan":143,"./rules/valid-jsdoc":144,"./rules/valid-typeof":145,"./rules/vars-on-top":146,"./rules/wrap-iife":147,"./rules/wrap-regex":148,"./rules/yoda":149}],11:[function(require,module,exports){
/**
* @fileoverview RuleContext utility for rules
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
var PASSTHROUGHS = [
"getSource",
"getSourceLines",
"getTokens",
"getTokensBefore",
"getTokenBefore",
"getTokensAfter",
"getTokenAfter",
"getFirstTokens",
"getFirstToken",
"getLastTokens",
"getLastToken",
"getComments",
"getAncestors",
"getScope",
"getJSDocComment",
"getFilename"
];
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/**
* Acts as an abstraction layer between rules and the main eslint object.
* @constructor
* @param {string} ruleId The ID of the rule using this object.
* @param {eslint} eslint The eslint object.
* @param {number} severity The configured severity level of the rule.
* @param {array} options The configuration information to be added to the rule.
* @param {object} settings The configuration settings passed from the config file.
*/
function RuleContext(ruleId, eslint, severity, options, settings) {
/**
* The read-only ID of the rule.
*/
Object.defineProperty(this, "id", {
value: ruleId
});
/**
* The read-only options of the rule
*/
Object.defineProperty(this, "options", {
value: options
});
/**
* The read-only settings shared between all rules
*/
Object.defineProperty(this, "settings", {
value: settings
});
// copy over passthrough methods
PASSTHROUGHS.forEach(function(name) {
this[name] = function() {
return eslint[name].apply(eslint, arguments);
};
}, this);
/**
* Passthrough to eslint.report() that automatically assigns the rule ID and severity.
* @param {ASTNode} node The AST node related to the message.
* @param {Object=} location The location of the error.
* @param {string} message The message to display to the user.
* @param {Object} opts Optional template data which produces a formatted message
* with symbols being replaced by this object's values.
* @returns {void}
*/
this.report = function(node, location, message, opts) {
eslint.report(ruleId, severity, node, location, message, opts);
};
}
RuleContext.prototype = {
constructor: RuleContext
};
module.exports = RuleContext;
},{}],12:[function(require,module,exports){
/**
* @fileoverview Defines a storage for rules.
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var loadRules = require("./load-rules");
//------------------------------------------------------------------------------
// Privates
//------------------------------------------------------------------------------
var rules = Object.create(null);
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/**
* Registers a rule module for rule id in storage.
* @param {String} ruleId Rule id (file name).
* @param {Function} ruleModule Rule handler.
* @returns {void}
*/
function define(ruleId, ruleModule) {
rules[ruleId] = ruleModule;
}
exports.define = define;
/**
* Loads and registers all rules from passed rules directory.
* @param {String} [rulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
* @returns {void}
*/
function load(rulesDir) {
var newRules = loadRules(rulesDir);
Object.keys(newRules).forEach(function(ruleId) {
define(ruleId, newRules[ruleId]);
});
}
exports.load = load;
/**
* Registers all given rules of a plugin.
* @param {Object} pluginRules A key/value map of rule definitions.
* @param {String} pluginName The name of the plugin without prefix (`eslint-plugin-`).
* @returns {void}
*/
exports.import = function (pluginRules, pluginName) {
Object.keys(pluginRules).forEach(function (ruleId) {
var qualifiedRuleId = pluginName + "/" + ruleId,
rule = pluginRules[ruleId];
define(qualifiedRuleId, rule);
});
};
/**
* Access rule handler by id (file name).
* @param {String} ruleId Rule id (file name).
* @returns {Function} Rule handler.
*/
exports.get = function(ruleId) {
return rules[ruleId];
};
/**
* Reset rules storage.
* Should be used only in tests.
* @returns {void}
*/
exports.testClear = function() {
rules = Object.create(null);
};
//------------------------------------------------------------------------------
// Initialization
//------------------------------------------------------------------------------
// loads built-in rules
load();
},{"./load-rules":10}],13:[function(require,module,exports){
/**
* @fileoverview Rule to check for "block scoped" variables by binding context
* @author Matt DuVall <http://www.mattduvall.com>
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var scopeStack = [];
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines whether an identifier is in declaration position or is a non-declaration reference.
* @param {ASTNode} id The identifier.
* @param {ASTNode} parent The identifier's parent AST node.
* @returns {Boolean} true when the identifier is in declaration position.
*/
function isDeclaration(id, parent) {
switch (parent.type) {
case "FunctionDeclaration":
case "FunctionExpression":
return parent.params.indexOf(id) > -1 || id === parent.id;
case "VariableDeclarator":
return id === parent.id;
case "CatchClause":
return id === parent.param;
default:
return false;
}
}
/**
* Determines whether an identifier is in property position.
* @param {ASTNode} id The identifier.
* @param {ASTNode} parent The identifier's parent AST node.
* @returns {Boolean} true when the identifier is in property position.
*/
function isProperty(id, parent) {
switch (parent.type) {
case "MemberExpression":
return id === parent.property && !parent.computed;
case "Property":
return id === parent.key;
default:
return false;
}
}
/**
* Pushes a new scope object on the scope stack.
* @returns {void}
*/
function pushScope() {
scopeStack.push([]);
}
/**
* Removes the topmost scope object from the scope stack.
* @returns {void}
*/
function popScope() {
scopeStack.pop();
}
/**
* Declares the given names in the topmost scope object.
* @param {[String]} names A list of names to declare.
* @returns {void}
*/
function declare(names) {
[].push.apply(scopeStack[scopeStack.length - 1], names);
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
function functionHandler(node) {
pushScope();
declare(node.params.map(function(id) {
return id.name;
}));
declare(node.id ? [node.id.name] : []);
declare(["arguments"]);
}
function variableDeclarationHandler(node) {
declare(node.declarations.map(function(decl) {
return decl.id.name;
}));
}
return {
"Program": function() {
scopeStack = [context.getScope().variables.map(function(v) {
return v.name;
})];
},
"BlockStatement": function(node) {
var statements = node.body;
pushScope();
statements.forEach(function(stmt) {
if (stmt.type === "VariableDeclaration") {
variableDeclarationHandler(stmt);
} else if (stmt.type === "FunctionDeclaration") {
declare([stmt.id.name]);
}
});
},
"BlockStatement:exit": popScope,
"CatchClause": function(node) {
pushScope();
declare([node.param.name]);
},
"CatchClause:exit": popScope,
"FunctionDeclaration": functionHandler,
"FunctionDeclaration:exit": popScope,
"FunctionExpression": functionHandler,
"FunctionExpression:exit": popScope,
"ForStatement": function(node) {
pushScope();
if (node.init && node.init.type === "VariableDeclaration") {
variableDeclarationHandler(node.init);
}
},
"ForStatement:exit": popScope,
"ForInStatement": function(node) {
pushScope();
if (node.left.type === "VariableDeclaration") {
variableDeclarationHandler(node.left);
}
},
"ForInStatement:exit": popScope,
"Identifier": function(node) {
var ancestor = context.getAncestors().pop();
if (isDeclaration(node, ancestor) || isProperty(node, ancestor)) {
return;
}
for (var i = 0, l = scopeStack.length; i < l; i++) {
if (scopeStack[i].indexOf(node.name) > -1) {
return;
}
}
context.report(node, "\"" + node.name + "\" used outside of binding context.");
}
};
};
},{}],14:[function(require,module,exports){
/**
* @fileoverview Rule to flag block statements that do not use the one true brace style
* @author Ian Christian Myers
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var style = context.options[0] || "1tbs";
var params = context.options[1] || {};
var OPEN_MESSAGE = "Opening curly brace does not appear on the same line as controlling statement.",
BODY_MESSAGE = "Statement inside of curly braces should be on next line.",
CLOSE_MESSAGE = "Closing curly brace does not appear on the same line as the subsequent block.",
CLOSE_MESSAGE_SINGLE = "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
CLOSE_MESSAGE_STROUSTRUP = "Closing curly brace appears on the same line as the subsequent block.";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Binds a list of properties to a function that verifies that the opening
* curly brace is on the same line as its controlling statement of a given
* node.
* @param {...string} The properties to check on the node.
* @returns {Function} A function that will perform the check on a node
*/
function checkBlock() {
var blockProperties = arguments;
return function(node) {
[].forEach.call(blockProperties, function(blockProp) {
var block = node[blockProp], previousToken, curlyToken, curlyTokenEnd, curlyTokensOnSameLine;
block = node[blockProp];
if (block && block.type === "BlockStatement") {
previousToken = context.getTokenBefore(block);
curlyToken = context.getFirstToken(block);
curlyTokenEnd = context.getLastToken(block);
curlyTokensOnSameLine = curlyToken.loc.start.line === curlyTokenEnd.loc.start.line;
if (previousToken.loc.start.line !== curlyToken.loc.start.line) {
context.report(node, OPEN_MESSAGE);
} else if (block.body.length && params.allowSingleLine) {
if (curlyToken.loc.start.line === block.body[0].loc.start.line && !curlyTokensOnSameLine) {
context.report(block.body[0], BODY_MESSAGE);
} else if (curlyTokenEnd.loc.start.line === block.body[block.body.length - 1].loc.start.line && !curlyTokensOnSameLine) {
context.report(block.body[block.body.length - 1], CLOSE_MESSAGE_SINGLE);
}
} else if (block.body.length && curlyToken.loc.start.line === block.body[0].loc.start.line) {
context.report(block.body[0], BODY_MESSAGE);
}
}
});
};
}
/**
* Enforces the configured brace style on IfStatements
* @param {ASTNode} node An IfStatement node.
* @returns {void}
*/
function checkIfStatement(node) {
var tokens;
checkBlock("consequent", "alternate")(node);
if (node.alternate && node.alternate.type === "BlockStatement") {
tokens = context.getTokensBefore(node.alternate, 2);
if (style === "1tbs") {
if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
context.report(node.alternate, CLOSE_MESSAGE);
}
} else if (style === "stroustrup") {
if (tokens[0].loc.start.line === tokens[1].loc.start.line) {
context.report(node.alternate, CLOSE_MESSAGE_STROUSTRUP);
}
}
}
}
/**
* Enforces the configured brace style on TryStatements
* @param {ASTNode} node A TryStatement node.
* @returns {void}
*/
function checkTryStatement(node) {
var tokens;
checkBlock("block", "finalizer")(node);
if (node.finalizer && node.finalizer.type === "BlockStatement") {
tokens = context.getTokensBefore(node.finalizer, 2);
if (style === "1tbs") {
if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
context.report(node.finalizer, CLOSE_MESSAGE);
}
} else if (style === "stroustrup") {
if (tokens[0].loc.start.line === tokens[1].loc.start.line) {
context.report(node.finalizer, CLOSE_MESSAGE_STROUSTRUP);
}
}
}
}
/**
* Enforces the configured brace style on CatchClauses
* @param {ASTNode} node A CatchClause node.
* @returns {void}
*/
function checkCatchClause(node) {
var previousToken = context.getTokenBefore(node),
firstToken = context.getFirstToken(node);
checkBlock("body")(node);
if (node.body && node.body.type === "BlockStatement") {
if (style === "1tbs") {
if (previousToken.loc.start.line !== firstToken.loc.start.line) {
context.report(node, CLOSE_MESSAGE);
}
} else if (style === "stroustrup") {
if (previousToken.loc.start.line === firstToken.loc.start.line) {
context.report(node, CLOSE_MESSAGE_STROUSTRUP);
}
}
}
}
/**
* Enforces the configured brace style on SwitchStatements
* @param {ASTNode} node A SwitchStatement node.
* @returns {void}
*/
function checkSwitchStatement(node) {
var tokens;
if (node.cases && node.cases.length) {
tokens = context.getTokensBefore(node.cases[0], 2);
if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
context.report(node, OPEN_MESSAGE);
}
} else {
tokens = context.getLastTokens(node, 3);
if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
context.report(node, OPEN_MESSAGE);
}
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"FunctionDeclaration": checkBlock("body"),
"FunctionExpression": checkBlock("body"),
"IfStatement": checkIfStatement,
"TryStatement": checkTryStatement,
"CatchClause": checkCatchClause,
"DoWhileStatement": checkBlock("body"),
"WhileStatement": checkBlock("body"),
"WithStatement": checkBlock("body"),
"ForStatement": checkBlock("body"),
"ForInStatement": checkBlock("body"),
"SwitchStatement": checkSwitchStatement
};
};
},{}],15:[function(require,module,exports){
/**
* @fileoverview Rule to flag non-camelcased identifiers
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Checks if a string contains an underscore and isn't all upper-case
* @param {String} name The string to check.
* @returns {boolean} if the string is underscored
* @private
*/
function isUnderscored(name) {
// if there's an underscore, it might be A_CONSTANT, which is okay
return name.indexOf("_") > -1 && name !== name.toUpperCase();
}
/**
* Reports an AST node as a rule violation.
* @param {ASTNode} node The node to report.
* @returns {void}
* @private
*/
function report(node) {
context.report(node, "Identifier '{{name}}' is not in camel case.", { name: node.name });
}
return {
"Identifier": function(node) {
// Leading and trailing underscores are commonly used to flag private/protected identifiers, strip them
var name = node.name.replace(/^_+|_+$/g, ""),
effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
// MemberExpressions get special rules
if (node.parent.type === "MemberExpression") {
// Always report underscored object names
if (node.parent.object.type === "Identifier" &&
node.parent.object.name === node.name &&
isUnderscored(name)) {
report(node);
// Report AssignmentExpressions only if they are the left side of the assignment
} else if (effectiveParent.type === "AssignmentExpression" &&
isUnderscored(name) &&
(effectiveParent.right.type !== "MemberExpression" ||
effectiveParent.left.type === "MemberExpression" &&
effectiveParent.left.property.name === node.name)) {
report(node);
}
// Report anything that is underscored that isn't a CallExpression
} else if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
report(node);
}
}
};
};
},{}],16:[function(require,module,exports){
/**
* @fileoverview Comma spacing - validates spacing before and after comma
* @author Vignesh Anand aka vegetableman.
* @copyright 2014 Vignesh Anand. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var options = {
before: context.options[0] ? !!context.options[0].before : false,
after: context.options[0] ? !!context.options[0].after : true
};
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines whether two adjacent tokens have whitespace between them.
* @param {Object} left - The left token object.
* @param {Object} right - The right token object.
* @returns {boolean} Whether or not there is space between the tokens.
*/
function isSpaced(left, right) {
return left.range[1] < right.range[0];
}
/**
* Run report.
* @param {ASTNode} node The binary expression node to check.
* @param {string} msg The error msg to show.
* @private
* @returns {void}
*/
function report(node, msg) {
context.report(node, msg);
}
/**
* Show space required message.
* @param {string} dir The location of spacing.
* @private
* @returns {string} The spacing error msg.
*/
function getSpaceReqMsg(dir) {
return "A space is required " + dir + " ','.";
}
/**
* Show no space message.
* @param {string} dir The location of spacing
* @private
* @returns {string} The spacing error msg.
*/
function getNoSpaceMsg(dir) {
return "There should be no space " + dir + " ','.";
}
/**
* Validates the spacing before and after commas.
* @param {ASTNode} node The binary expression node to check.
* @param {string} property The property of the node.
* @private
* @returns {void}
*/
function validateCommaSpacing(node, property) {
var items = node[property];
if (items && items.length > 1) {
items.forEach(function(item, index) {
var tokenBefore = context.getTokenBefore(item),
itemBefore = items[index - 1],
tokenEndLine;
if (tokenBefore && tokenBefore.value === ",") {
tokenEndLine = tokenBefore.loc.end.line;
// single line
if (tokenEndLine === itemBefore.loc.end.line &&
tokenEndLine === item.loc.start.line) {
if (options.before && options.after) {
if (!isSpaced(itemBefore, tokenBefore)) {
report(item, getSpaceReqMsg("before"));
}
if (!isSpaced(tokenBefore, item)) {
report(item, getSpaceReqMsg("after"));
}
} else if (options.before) {
if (!isSpaced(itemBefore, tokenBefore)) {
report(item, getSpaceReqMsg("before"));
}
if (isSpaced(tokenBefore, item)) {
report(item, getNoSpaceMsg("after"));
}
} else if (options.after) {
if (!isSpaced(tokenBefore, item)) {
report(item, getSpaceReqMsg("after"));
}
if (isSpaced(itemBefore, tokenBefore)) {
report(item, getNoSpaceMsg("before"));
}
} else {
if (isSpaced(itemBefore, tokenBefore)) {
report(item, getNoSpaceMsg("before"));
}
if (isSpaced(tokenBefore, item)) {
report(item, getNoSpaceMsg("after"));
}
}
}
}
});
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"VariableDeclaration": function(node) {
validateCommaSpacing(node, "declarations");
},
"ObjectExpression": function(node) {
validateCommaSpacing(node, "properties");
},
"ArrayExpression": function(node) {
validateCommaSpacing(node, "elements");
},
"SequenceExpression": function(node) {
validateCommaSpacing(node, "expressions");
},
"FunctionExpression": function(node) {
validateCommaSpacing(node, "params");
},
"FunctionDeclaration": function(node) {
validateCommaSpacing(node, "params");
}
};
};
},{}],17:[function(require,module,exports){
/**
* @fileoverview Comma style - enforces comma styles of two types: last and first
* @author Vignesh Anand aka vegetableman
* @copyright 2014 Vignesh Anand. All rights reserved.
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var style = context.options[0] || "last";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Checks the comma placement with regards to a declaration/property/element
* @param {ASTNode} node The binary expression node to check
* @private
* @returns {void}
*/
function validateComma(node) {
var items = node.declarations || node.properties || node.elements;
if (items.length > 1) {
items.forEach(function(item, index) {
var tokenBefore = context.getTokenBefore(item),
itemBefore = items[index - 1];
if (tokenBefore.value === ",") {
// if single line
if (tokenBefore.loc.end.line === item.loc.start.line &&
tokenBefore.loc.end.line === itemBefore.loc.end.line) {
return;
}
// lone comma
else if (tokenBefore.loc.end.line !== item.loc.start.line &&
tokenBefore.loc.end.line !== itemBefore.loc.end.line) {
context.report(item, {
line: tokenBefore.loc.end.line,
column: tokenBefore.loc.start.column
}, "Bad line breaking before and after ','.");
}
else if (style === "first" &&
tokenBefore.loc.start.line !== item.loc.start.line) {
context.report(item, "',' should be placed first.");
}
else if (style === "last" &&
tokenBefore.loc.end.line === item.loc.start.line) {
context.report(item, {
line: itemBefore.loc.end.line,
column: itemBefore.loc.end.column
}, "',' should be placed last.");
}
}
});
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"VariableDeclaration": validateComma,
"ObjectExpression": validateComma,
"ArrayExpression": validateComma
};
};
},{}],18:[function(require,module,exports){
/**
* @fileoverview Counts the cyclomatic complexity of each function of the script. See http://en.wikipedia.org/wiki/Cyclomatic_complexity.
* Counts the number of if, conditional, for, whilte, try, switch/case,
* @author Patrick Brosset
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var THRESHOLD = context.options[0];
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
// Using a stack to store complexity (handling nested functions)
var fns = [];
// When parsing a new function, store it in our function stack
function startFunction() {
fns.push(1);
}
function endFunction(node) {
var complexity = fns.pop(), name = "anonymous";
if (node.id) {
name = node.id.name;
}
if (complexity > THRESHOLD) {
context.report(node, "Function '{{name}}' has a complexity of {{complexity}}.", { name: name, complexity: complexity });
}
}
function increaseComplexity() {
if (fns.length) {
fns[fns.length - 1] ++;
}
}
function increaseSwitchComplexity(node) {
// Avoiding `default`
if (node.test) {
increaseComplexity(node);
}
}
function increaseLogicalComplexity(node) {
// Avoiding &&
if (node.operator === "||") {
increaseComplexity(node);
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"FunctionDeclaration": startFunction,
"FunctionExpression": startFunction,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction,
"CatchClause": increaseComplexity,
"ConditionalExpression": increaseComplexity,
"LogicalExpression": increaseLogicalComplexity,
"ForStatement": increaseComplexity,
"ForInStatement": increaseComplexity,
"IfStatement": increaseComplexity,
"SwitchCase": increaseSwitchComplexity,
"WhileStatement": increaseComplexity,
"DoWhileStatement": increaseComplexity
};
};
},{}],19:[function(require,module,exports){
/**
* @fileoverview Rule to flag consistent return values
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var functions = [];
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
function enterFunction() {
functions.push({});
}
function exitFunction() {
functions.pop();
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"FunctionDeclaration": enterFunction,
"FunctionExpression": enterFunction,
"FunctionDeclaration:exit": exitFunction,
"FunctionExpression:exit": exitFunction,
"ReturnStatement": function(node) {
var returnInfo = functions[functions.length - 1],
returnTypeDefined = "type" in returnInfo;
if (returnTypeDefined) {
if (returnInfo.type !== !!node.argument) {
context.report(node, "Expected " + (returnInfo.type ? "a" : "no") + " return value.");
}
} else {
returnInfo.type = !!node.argument;
}
}
};
};
},{}],20:[function(require,module,exports){
/**
* @fileoverview Rule to enforce consistent naming of "this" context variables
* @author Raphael Pigulla
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"VariableDeclaration": function(node) {
var alias = context.options[0];
node.declarations.forEach(function(declaration) {
if (declaration.id.name === alias &&
!(declaration.init && declaration.init.type === "ThisExpression")) {
context.report(
node,
"Designated 'this' alias '{{alias}}' is not assigned " +
"to the current execution context.",
{ alias: declaration.id.name }
);
}
if (declaration.init &&
declaration.init.type === "ThisExpression" &&
declaration.id.name !== alias) {
context.report(
node,
"Unexpected alias '{{alias}}' for 'this'.",
{ alias: declaration.id.name }
);
}
});
}
};
};
},{}],21:[function(require,module,exports){
/**
* @fileoverview Rule to flag statements without curly braces
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var multiOnly = (context.options[0] === "multi");
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Checks the body of a node to see if it's a block statement. Depending on
* the rule options, reports the appropriate problems.
* @param {ASTNode} node The node to report if there's a problem.
* @param {ASTNode} body The body node to check for blocks.
* @param {string} name The name to report if there's a problem.
* @param {string} suffix Additional string to add to the end of a report.
* @returns {void}
*/
function checkBody(node, body, name, suffix) {
var hasBlock = (body.type === "BlockStatement");
if (multiOnly) {
if (hasBlock && body.body.length === 1) {
context.report(node, "Unnecessary { after '{{name}}'{{suffix}}.",
{
name: name,
suffix: (suffix ? " " + suffix : "")
}
);
}
} else {
if (!hasBlock) {
context.report(node, "Expected { after '{{name}}'{{suffix}}.",
{
name: name,
suffix: (suffix ? " " + suffix : "")
}
);
}
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"IfStatement": function(node) {
checkBody(node, node.consequent, "if", "condition");
if (node.alternate && node.alternate.type !== "IfStatement") {
checkBody(node, node.alternate, "else");
}
},
"WhileStatement": function(node) {
checkBody(node, node.body, "while", "condition");
},
"DoWhileStatement": function (node) {
checkBody(node, node.body, "do");
},
"ForStatement": function(node) {
checkBody(node, node.body, "for", "condition");
}
};
};
},{}],22:[function(require,module,exports){
/**
* @fileoverview require default case in switch statements
* @author Aliaksei Shytkin
*/
"use strict";
var COMMENT_VALUE = "no default";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Shortcut to get last element of array
* @param {*[]} collection Array
* @returns {*} Last element
*/
function last(collection) {
return collection[collection.length - 1];
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"SwitchStatement": function(node) {
if (!node.cases.length) {
// skip check of empty switch because there is no easy way
// to extract comments inside it now
return;
}
var hasDefault = node.cases.some(function(v) {
return v.test === null;
});
if (!hasDefault) {
var comment;
var comments;
var lastCase = last(node.cases);
comments = context.getComments(lastCase).trailing;
if (comments.length) {
comment = last(comments);
}
if (!comment || comment.value.trim() !== COMMENT_VALUE) {
context.report(node, "Expected a default case.");
}
}
}
};
};
},{}],23:[function(require,module,exports){
/**
* @fileoverview Rule to warn about using dot notation instead of square bracket notation when possible.
* @author Josh Perez
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
var validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
var keywords = [
"this",
"function",
"if",
"return",
"var",
"else",
"for",
"new",
"arguments",
"in",
"typeof",
"while",
"case",
"break",
"try",
"catch",
"delete",
"throw",
"switch",
"continue",
"default",
"instanceof",
"do",
"void",
"finally",
"with",
"debugger",
"eval",
"implements",
"interface",
"package",
"private",
"protected",
"public",
"static",
"yield",
"let",
"class",
"enum",
"export",
"extends",
"import",
"super",
"true",
"false",
"null",
"abstract",
"boolean",
"byte",
"char",
"const",
"double",
"final",
"float",
"goto",
"int",
"long",
"native",
"short",
"synchronized",
"throws",
"transient",
"volatile"
];
function canBeWrittenInDotNotation(node) {
return node.computed === true &&
node.property.type === "Literal" &&
validIdentifier.test(node.property.value) &&
keywords.indexOf("" + node.property.value) === -1;
}
module.exports = function(context) {
return {
"MemberExpression": function(node) {
if (canBeWrittenInDotNotation(node)) {
context.report(node, "['" + node.property.value + "'] is better written in dot notation.");
}
}
};
};
},{}],24:[function(require,module,exports){
/**
* @fileoverview Require file to end with single newline.
* @author Nodeca Team <https://github.com/nodeca>
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"Program": function checkBadEOF(node) {
// Get the whole source code, not for node only.
var src = context.getSource(), location = {column: 1};
if (src.length === 0) {
return;
}
if (src[src.length - 1] !== "\n") {
// file is not newline-terminated
location.line = src.split(/\n/g).length;
context.report(node, location, "Newline required at end of file but not found.");
} else if (/\n\s*\n$/.test(src)) {
// last line is empty
location.line = src.split(/\n/g).length - 1;
context.report(node, location, "Unexpected blank line at end of file.");
}
}
};
};
},{}],25:[function(require,module,exports){
/**
* @fileoverview Rule to flag statements that use != and == instead of !== and ===
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
/**
* Checks if either operand of a binary expression is a typeof operation
* @param {ASTNode} node The node to check
* @returns {boolean} if one of the operands is typeof
* @private
*/
function isTypeOf(node) {
return [node.left, node.right].some(function(node) {
return node.type === "UnaryExpression" && node.operator === "typeof";
});
}
/**
* Checks if operands are literals of the same type (via typeof)
* @param {ASTNode} node The node to check
* @returns {boolean} if operands are of same type
* @private
*/
function areLiteralsAndSameType(node) {
return node.left.type === "Literal" && node.right.type === "Literal" &&
typeof node.left.value === typeof node.right.value;
}
/**
* Checks if one of the operands is a literal null
* @param {ASTNode} node The node to check
* @returns {boolean} if operands are null
* @private
*/
function isNullCheck(node) {
return (node.right.type === "Literal" && node.right.value === null) ||
(node.left.type === "Literal" && node.left.value === null);
}
/**
* Gets the location (line and column) of the binary expression's operator
* @param {ASTNode} node The binary expression node to check
* @param {String} operator The operator to find
* @returns {Object} { line, column } location of operator
* @private
*/
function getOperatorLocation(node) {
var opToken = context.getTokens(node)[context.getTokens(node.left).length];
return {line: opToken.loc.start.line, column: opToken.loc.start.column};
}
return {
"BinaryExpression": function(node) {
if (node.operator !== "==" && node.operator !== "!=") {
return;
}
if (context.options[0] === "smart" && (isTypeOf(node) ||
areLiteralsAndSameType(node)) || isNullCheck(node)) {
return;
}
if (context.options[0] === "allow-null" && isNullCheck(node)) {
return;
}
context.report(
node, getOperatorLocation(node),
"Expected '{{op}}=' and instead saw '{{op}}'.",
{op: node.operator}
);
}
};
};
},{}],26:[function(require,module,exports){
/**
* @fileoverview Rule to warn when a function expression does not have a name.
* @author Kyle T. Nunery
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"FunctionExpression": function(node) {
var name = node.id && node.id.name;
if (!name) {
context.report(node, "Missing function expression name.");
}
}
};
};
},{}],27:[function(require,module,exports){
/**
* @fileoverview Rule to enforce a particular function style
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var style = context.options[0],
enforceDeclarations = (style === "declaration");
return {
"FunctionDeclaration": function(node) {
if (!enforceDeclarations) {
context.report(node, "Expected a function expression.");
}
},
"FunctionExpression": function() {
var parent = context.getAncestors().pop();
if (enforceDeclarations && parent.type === "VariableDeclarator") {
context.report(parent, "Expected a function declaration.");
}
}
};
};
},{}],28:[function(require,module,exports){
/**
* @fileoverview Rule to flag or require global strict mode.
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var mode = context.options[0];
if (mode === "always") {
return {
"Program": function(node) {
if (node.body.length > 0) {
var statement = node.body[0];
if (!(statement.type === "ExpressionStatement" && statement.expression.value === "use strict")) {
context.report(node, "Use the global form of \"use strict\".");
}
}
}
};
} else { // mode = "never"
return {
"ExpressionStatement": function(node) {
var parent = context.getAncestors().pop();
if (node.expression.value === "use strict" && parent.type === "Program") {
context.report(node, "Use the function form of \"use strict\".");
}
}
};
}
};
},{}],29:[function(require,module,exports){
/**
* @fileoverview Rule to flag for-in loops without if statements inside
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"ForInStatement": function(node) {
/*
* If the for-in statement has {}, then the real body is the body
* of the BlockStatement. Otherwise, just use body as provided.
*/
var body = node.body.type === "BlockStatement" ? node.body.body[0] : node.body;
if (body && body.type !== "IfStatement") {
context.report(node, "The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.");
}
}
};
};
},{}],30:[function(require,module,exports){
/**
* @fileoverview Ensure handling of errors when we know they exist.
* @author Jamund Ferguson
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var errorArgument = context.options[0] || "err";
var callbacks = [];
var scopes = 0;
/**
* Checks if the given argument should be interpreted as a regexp pattern.
* @param {string} stringToCheck The string which should be checked.
* @returns {boolean} Whether or not the string should be interpreted as a pattern.
*/
function isPattern(stringToCheck) {
var firstChar = stringToCheck[0];
return firstChar === "^";
}
/**
* Checks if the given name matches the configured error argument.
* @param {string} name The name which should be compared.
* @returns {boolean} Whether or not the given name matches the configured error variable name.
*/
function matchesConfiguredErrorName(name) {
if (isPattern(errorArgument)) {
var regexp = new RegExp(errorArgument);
return regexp.test(name);
}
return name === errorArgument;
}
/**
* Check the arguments to see if we need to start tracking the error object.
* @param {ASTNode} node The AST node to check.
* @returns {void}
*/
function startFunction(node) {
// keep track of nested scopes
scopes++;
// check if the first argument matches our argument name
var firstArg = node.params && node.params[0];
if (firstArg && matchesConfiguredErrorName(firstArg.name)) {
callbacks.push({handled: false, depth: scopes, errorVariableName: firstArg.name});
}
}
/**
* At the end of a function check to see if the error was handled.
* @param {ASTNode} node The AST node to check.
* @returns {void}
*/
function endFunction(node) {
var callback = callbacks[callbacks.length - 1] || {};
// check if a callback is ending, if so pop it off the stack
if (callback.depth === scopes) {
callbacks.pop();
// check if there were no handled errors since the last callback
if (!callback.handled) {
context.report(node, "Expected error to be handled.");
}
}
// less nested functions
scopes--;
}
/**
* Check to see if we're handling the error object properly.
* @param {ASTNode} node The AST node to check.
* @returns {void}
*/
function checkForError(node) {
if (callbacks.length > 0) {
var callback = callbacks[callbacks.length - 1] || {};
// make sure the node's name matches our error argument name
var isAboutError = node.name === callback.errorVariableName;
// we don't consider these use cases as "handling" the error
var doNotCount = ["FunctionDeclaration", "FunctionExpression", "CatchClause"];
// make sure this identifier isn't used as part of one of them
var isHandled = doNotCount.indexOf(node.parent.type) === -1;
if (isAboutError && isHandled) {
// record that this callback handled its error
callback.handled = true;
}
}
}
return {
"FunctionDeclaration": startFunction,
"FunctionExpression": startFunction,
"Identifier": checkForError,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction
};
};
},{}],31:[function(require,module,exports){
/**
* @fileoverview Rule to specify spacing of object literal keys and values
* @author Brandon Mills
* @copyright 2014 Brandon Mills. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Gets an object literal property's key as the identifier name or string value.
* @param {ASTNode} property Property node whose key to retrieve.
* @returns {string} The property's key.
*/
function getKey(property) {
return property.key.name || property.key.value;
}
/**
* Gets the number of characters in a key, including quotes around string keys.
* @param {ASTNode} property Property of on object literal.
* @returns {int} Width of the key, including string quotes where present.
*/
function getKeyWidth(property) {
var key = property.key;
return (key.type === "Identifier" ? key.name : key.raw).length;
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
var messages = {
key: "{{error}} space after key \"{{key}}\".",
value: "{{error}} space before value for key \"{{key}}\"."
};
module.exports = function(context) {
/**
* OPTIONS
* "key-spacing": [2, {
* beforeColon: false,
* afterColon: true,
* align: "colon" // Optional, or "value"
* }
*/
var options = context.options[0] || {},
align = options.align,
beforeColon = +!!options.beforeColon, // Defaults to false
afterColon = +!(options.afterColon === false); // Defaults to true
/**
* Gets the spacing around the colon in an object literal property
* @param {ASTNode} property Property node from an object literal
* @returns {Object} Spacing before and after the property's colon
*/
function getPropertySpacing(property) {
var whitespace = /^(\s*):(\s*)/.exec(context.getSource().slice(
property.key.range[1], property.value.range[0]
));
if (whitespace) {
return {
beforeColon: whitespace[1].length,
afterColon: whitespace[2].length
};
}
}
/**
* Reports an appropriately-formatted error if spacing is incorrect on one
* side of the colon.
* @param {ASTNode} property Key-value pair in an object literal.
* @param {string} side Side being verified - either "key" or "value".
* @param {int} diff Difference between actual and expected spacing.
* @returns {void}
*/
function report(property, side, diff) {
if (diff) {
context.report(property[side], messages[side], {
error: diff > 0 ? "Extra" : "Missing",
key: getKey(property)
});
}
}
if (align) { // Verify vertical alignment
return {
"ObjectExpression": function(node) {
var properties = node.properties,
length = properties.length,
widths = properties.map(getKeyWidth), // Width of keys, including quotes
targetWidth = Math.max.apply(null, widths),
i, property, spacing, width;
// Conditionally include one space before or after colon
targetWidth += (align === "colon" ? beforeColon : afterColon);
for (i = 0; i < length; i++) {
property = properties[i];
spacing = getPropertySpacing(property);
if (!spacing) {
continue; // Object literal getters/setters lack a colon
}
width = widths[i];
if (align === "value") {
report(property, "key", spacing.beforeColon - beforeColon);
report(property, "value", (width + spacing.afterColon) - targetWidth);
} else { // align = "colon"
report(property, "key", (width + spacing.beforeColon) - targetWidth);
report(property, "value", spacing.afterColon - afterColon);
}
}
}
};
} else { // Strictly obey beforeColon and afterColon in each property
return {
"Property": function (node) {
var spacing = getPropertySpacing(node);
if (spacing) { // Object literal getters/setters lack colon spacing
report(node, "key", spacing.beforeColon - beforeColon);
report(node, "value", spacing.afterColon - afterColon);
}
}
};
}
};
},{}],32:[function(require,module,exports){
/**
* @fileoverview A rule to set the maximum depth block can be nested in a function.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
var functionStack = [],
maxDepth = context.options[0] || 4;
function startFunction() {
functionStack.push(0);
}
function endFunction() {
functionStack.pop();
}
function pushBlock(node) {
var len = ++functionStack[functionStack.length - 1];
if (len > maxDepth) {
context.report(node, "Blocks are nested too deeply ({{depth}}).",
{ depth: len });
}
}
function popBlock() {
functionStack[functionStack.length - 1]--;
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"Program": startFunction,
"FunctionDeclaration": startFunction,
"FunctionExpression": startFunction,
"IfStatement": pushBlock,
"SwitchStatement": pushBlock,
"TryStatement": pushBlock,
"DoWhileStatement": pushBlock,
"WhileStatement": pushBlock,
"WithStatement": pushBlock,
"ForStatement": pushBlock,
"ForInStatement": pushBlock,
"IfStatement:exit": popBlock,
"SwitchStatement:exit": popBlock,
"TryStatement:exit": popBlock,
"DoWhileStatement:exit": popBlock,
"WhileStatement:exit": popBlock,
"WithStatement:exit": popBlock,
"ForStatement:exit": popBlock,
"ForInStatement:exit": popBlock,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction,
"Program:exit": endFunction
};
};
},{}],33:[function(require,module,exports){
/**
* @fileoverview Rule to check for max length on a line.
* @author Matt DuVall <http://www.mattduvall.com>
* @copyright 2013 Matt DuVall. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
/**
* Creates a string that is made up of repeating a given string a certain
* number of times. This uses exponentiation of squares to achieve significant
* performance gains over the more traditional implementation of such
* functionality.
* @param {string} str The string to repeat.
* @param {int} num The number of times to repeat the string.
* @returns {string} The created string.
* @private
*/
function stringRepeat(str, num) {
var result = "";
for (num |= 0; num > 0; num >>>= 1, str += str) {
if (num & 1) {
result += str;
}
}
return result;
}
var tabWidth = context.options[1];
var maxLength = context.options[0],
tabString = stringRepeat(" ", tabWidth);
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
function checkProgramForMaxLength(node) {
var lines = context.getSourceLines();
// Replace the tabs
// Split (honors line-ending)
// Iterate
lines.forEach(function(line, i) {
if (line.replace(/\t/g, tabString).length > maxLength) {
context.report(node, { line: i + 1, col: 1 }, "Line " + (i + 1) + " exceeds the maximum line length of " + maxLength + ".");
}
});
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"Program": checkProgramForMaxLength
};
};
},{}],34:[function(require,module,exports){
/**
* @fileoverview Rule to enforce a maximum number of nested callbacks.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Constants
//--------------------------------------------------------------------------
var THRESHOLD = context.options[0];
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
var callbackStack = [];
return {
"FunctionExpression": function (node) {
var parent = context.getAncestors().pop();
if (parent.type === "CallExpression") {
callbackStack.push(node);
}
if (callbackStack.length > THRESHOLD) {
var opts = {num: callbackStack.length, max: THRESHOLD};
context.report(node, "Too many nested callbacks ({{num}}). Maximum allowed is {{max}}.", opts);
}
},
"FunctionExpression:exit": function() {
callbackStack.pop();
}
};
};
},{}],35:[function(require,module,exports){
/**
* @fileoverview Rule to flag when a function has too many parameters
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var numParams = context.options[0] || 3;
return {
"FunctionDeclaration": function(node) {
if (node.params.length > numParams) {
context.report(node, "This function has too many parameters ({{count}}). Maximum allowed is {{max}}.", {
count: node.params.length,
max: numParams
});
}
},
"FunctionExpression": function(node) {
if (node.params.length > numParams) {
context.report(node, "This function has too many parameters ({{count}}). Maximum allowed is {{max}}.", {
count: node.params.length,
max: numParams
});
}
}
};
};
},{}],36:[function(require,module,exports){
/**
* @fileoverview A rule to set the maximum number of statements in a function.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
var functionStack = [],
maxStatements = context.options[0] || 10;
function startFunction() {
functionStack.push(0);
}
function endFunction(node) {
var count = functionStack.pop();
if (count > maxStatements) {
context.report(node, "This function has too many statements ({{count}}). Maximum allowed is {{max}}.",
{ count: count, max: maxStatements });
}
}
function countStatements(node) {
functionStack[functionStack.length - 1] += node.body.length;
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"FunctionDeclaration": startFunction,
"FunctionExpression": startFunction,
"BlockStatement": countStatements,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction
};
};
},{}],37:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of constructors without capital letters
* @author Nicholas C. Zakas
* @copyright 2013-2014 Nicholas C. Zakas. All rights reserved.
*/
"use strict";
var CAPS_ALLOWED = [
"Number",
"String",
"Boolean",
"Date",
"Array",
"Symbol",
"RegExp"
];
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var config = context.options[0] || {};
config.newIsCap = config.newIsCap === false ? false : true;
config.capIsNew = config.capIsNew === false ? false : true;
var listeners = {};
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Get exact callee name from expression
* @param {ASTNode} node CallExpression or NewExpression node
* @returns {String} name
*/
function extractNameFromExpression(node) {
var name = "",
property;
if (node.callee.type === "MemberExpression") {
property = node.callee.property;
if (property.type === "Literal" && (typeof property.value === "string")) {
name = property.value;
} else if (property.type === "Identifier" && !node.callee.computed) {
name = property.name;
}
} else {
name = node.callee.name;
}
return name;
}
/**
* Returns the capitalization state of the string -
* Whether the first character is uppercase, lowercase, or non-alphabetic
* @param {String} str String
* @returns {String} capitalization state: "non-alpha", "lower", or "upper"
*/
function getCap(str) {
var firstChar = str.charAt(0);
var firstCharLower = firstChar.toLowerCase();
var firstCharUpper = firstChar.toUpperCase();
if (firstCharLower === firstCharUpper) {
// char has no uppercase variant, so it's non-alphabetic
return "non-alpha";
} else if (firstChar === firstCharLower) {
return "lower";
} else {
return "upper";
}
}
/**
* Check if capitalization is allowed for a CallExpression
* @param {ASTNode} node CallExpression node
* @param {String} calleeName Capitalized callee name from a CallExpression
* @returns {Boolean} Returns true if the callee may be capitalized
*/
function isCapAllowed(node, calleeName) {
if (CAPS_ALLOWED.indexOf(calleeName) >= 0) {
return true;
}
if (calleeName === "UTC" && node.callee.type === "MemberExpression") {
// allow if callee is Date.UTC
return node.callee.object.type === "Identifier" &&
node.callee.object.name === "Date";
}
return false;
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
if (config.newIsCap) {
listeners.NewExpression = function(node) {
var constructorName = extractNameFromExpression(node);
if (constructorName && getCap(constructorName) === "lower") {
context.report(node, "A constructor name should not start with a lowercase letter.");
}
};
}
if (config.capIsNew) {
listeners.CallExpression = function(node) {
var calleeName = extractNameFromExpression(node);
if (calleeName && getCap(calleeName) === "upper" && !isCapAllowed(node, calleeName)) {
context.report(node, "A function with a name starting with an uppercase letter should only be used as a constructor.");
}
};
}
return listeners;
};
},{}],38:[function(require,module,exports){
/**
* @fileoverview Rule to flag when using constructor without parentheses
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"NewExpression": function(node) {
var tokens = context.getTokens(node);
var prenticesTokens = tokens.filter(function(token) {
return token.value === "(" || token.value === ")";
});
if (prenticesTokens.length < 2) {
context.report(node, "Missing '()' invoking a constructor");
}
}
};
};
},{}],39:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of alert, confirm, prompt
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
function matchProhibited(name) {
return name.match(/^(alert|confirm|prompt)$/);
}
function report(context, node, result) {
context.report(node, "Unexpected {{name}}.", { name: result[1] });
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"CallExpression": function(node) {
var result;
// without window.
if (node.callee.type === "Identifier") {
result = matchProhibited(node.callee.name);
if (result) {
report(context, node, result);
}
} else if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier") {
result = matchProhibited(node.callee.property.name);
if (result && node.callee.object.name === "window") {
report(context, node, result);
}
}
}
};
};
},{}],40:[function(require,module,exports){
/**
* @fileoverview Disallow construction of dense arrays using the Array constructor
* @author Matt DuVall <http://www.mattduvall.com/>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function check(node) {
if (
node.arguments.length !== 1 &&
node.callee.type === "Identifier" &&
node.callee.name === "Array"
) {
context.report(node, "The array literal notation [] is preferrable.");
}
}
return {
"CallExpression": check,
"NewExpression": check
};
};
},{}],41:[function(require,module,exports){
/**
* @fileoverview Rule to flag bitwise identifiers
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function report(node) {
context.report(node, "Unexpected use of '{{operator}}'.", { operator: node.operator });
}
return {
"BinaryExpression": function(node) {
// warn for ^ | & ~ << >> >>>
if (node.operator.match(/^(?:[\^&\|~]|<<|>>>?)$/)) {
report(node);
}
},
"UnaryExpression": function(node) {
// warn for ~
if (node.operator === "~") {
report(node);
}
}
};
};
},{}],42:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of arguments.callee and arguments.caller.
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"MemberExpression": function(node) {
var objectName = node.object.name,
propertyName = node.property.name;
if (objectName === "arguments" && !node.computed && propertyName && propertyName.match(/^calle[er]$/)) {
context.report(node, "Avoid arguments.{{property}}.", { property: propertyName });
}
}
};
};
},{}],43:[function(require,module,exports){
/**
* @fileoverview Rule to flag variable leak in CatchClauses in IE 8 and earlier
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
function paramIsShadowing(scope, name) {
var found = scope.variables.some(function(variable) {
return variable.name === name;
});
if (found) {
return true;
}
if (scope.upper) {
return paramIsShadowing(scope.upper, name);
}
return false;
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"CatchClause": function(node) {
var scope = context.getScope();
if (paramIsShadowing(scope, node.param.name)) {
context.report(node, "Value of '{{name}}' may be overwritten in IE 8 and earlier.",
{ name: node.param.name });
}
}
};
};
},{}],44:[function(require,module,exports){
/**
* @fileoverview Rule to flag trailing commas in object literals.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//-------------------------------------------------------------------------
// Helpers
//-------------------------------------------------------------------------
function checkForTrailingComma(node) {
var secondToLastToken = context.getLastTokens(node, 2)[0];
var items = node.properties || node.elements,
lastItem = items[items.length - 1];
// The last token in an object/array literal will always be a closing
// curly, so we check the second to last token for a comma.
if (secondToLastToken.value === "," && items.length && lastItem) {
context.report(lastItem, secondToLastToken.loc.start, "Trailing comma.");
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"ObjectExpression": checkForTrailingComma,
"ArrayExpression": checkForTrailingComma
};
};
},{}],45:[function(require,module,exports){
/**
* @fileoverview Rule to flag assignment in a conditional expression
* @author Stephen Murray <spmurrayzzz>
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
function isParenthesised(node) {
var previousToken = context.getTokenBefore(node),
nextToken = context.getTokenAfter(node);
return previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
function isParenthesisedTwice(node) {
var previousToken = context.getTokenBefore(node, 1),
nextToken = context.getTokenAfter(node, 1);
return isParenthesised(node) &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
function testForAssign(node) {
if (node.test && (node.test.type === "AssignmentExpression") && !isParenthesisedTwice(node.test)) {
context.report(node, "Expected a conditional expression and instead saw an assignment.");
}
}
return {
"IfStatement": testForAssign,
"WhileStatement": testForAssign,
"DoWhileStatement": testForAssign,
"ForStatement": testForAssign
};
};
},{}],46:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of console object
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"MemberExpression": function(node) {
if (node.object.name === "console") {
context.report(node, "Unexpected console statement.");
}
}
};
};
},{}],47:[function(require,module,exports){
/**
* @fileoverview Rule to flag use constant conditions
* @author Christian Schulz <http://rndm.de>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Checks if a node has a constant truthiness value.
* @param {ASTNode} node The AST node to check.
* @returns {Bool} true when node's truthiness is constant
* @private
*/
function isConstant(node) {
switch (node.type) {
case "Literal":
case "FunctionExpression":
case "ObjectExpression":
case "ArrayExpression":
return true;
case "UnaryExpression":
return isConstant(node.argument);
case "BinaryExpression":
case "LogicalExpression":
return isConstant(node.left) && isConstant(node.right);
case "AssignmentExpression":
return isConstant(node.right);
case "SequenceExpression":
return isConstant(node.expressions[node.expressions.length - 1]);
// no default
}
return false;
}
/**
* Reports when the given node contains a constant condition.
* @param {ASTNode} node The AST node to check.
* @returns {void}
* @private
*/
function checkConstantCondition(node) {
if (node.test && isConstant(node.test)) {
context.report(node, "Unexpected constant condition.");
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"ConditionalExpression": checkConstantCondition,
"IfStatement": checkConstantCondition,
"WhileStatement": checkConstantCondition,
"DoWhileStatement": checkConstantCondition,
"ForStatement": checkConstantCondition
};
};
},{}],48:[function(require,module,exports){
/**
* @fileoverview Rule to forbid control charactes from regular expressions.
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function getRegExp(node) {
if (node.value instanceof RegExp) {
return node.value;
} else if (typeof node.value === "string") {
var parent = context.getAncestors().pop();
if ((parent.type === "NewExpression" || parent.type === "CallExpression") &&
parent.callee.type === "Identifier" && parent.callee.name === "RegExp") {
// there could be an invalid regular expression string
try {
return new RegExp(node.value);
} catch (ex) {
return null;
}
}
} else {
return null;
}
}
return {
"Literal": function(node) {
var computedValue,
regex = getRegExp(node);
if (regex) {
computedValue = regex.toString();
if (/[\x00-\x1f]/.test(computedValue)) {
context.report(node, "Unexpected control character in regular expression.");
}
}
}
};
};
},{}],49:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of a debugger statement
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"DebuggerStatement": function(node) {
context.report(node, "Unexpected 'debugger' statement.");
}
};
};
},{}],50:[function(require,module,exports){
/**
* @fileoverview Rule to flag when deleting variables
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"UnaryExpression": function(node) {
if (node.operator === "delete" && node.argument.type === "Identifier") {
context.report(node, "Variables should not be deleted.");
}
}
};
};
},{}],51:[function(require,module,exports){
/**
* @fileoverview Rule to check for ambiguous div operator in regexes
* @author Matt DuVall <http://www.mattduvall.com>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
var token = context.getFirstTokens(node, 1)[0],
nodeType = token.type,
source;
if (nodeType === "RegularExpression") {
source = context.getFirstTokens(node, 1)[0].value;
if (source[1] === "=") {
context.report(node, "A regular expression literal can be confused with '/='.");
}
}
}
};
};
},{}],52:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of duplicate keys in an object.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"ObjectExpression": function(node) {
// Object that will be a map of properties--safe because we will
// prefix all of the keys.
var nodeProps = {};
node.properties.forEach(function(property) {
var keyName = property.key.name || property.key.value;
var key = property.kind + "-" + keyName;
if (nodeProps[key]) {
context.report(node, "Duplicate key '{{key}}'.", { key: keyName });
} else {
nodeProps[key] = true;
}
});
}
};
};
},{}],53:[function(require,module,exports){
/**
* @fileoverview Rule to flag `else` after a `return` in `if`
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
function checkForReturnStatement(node, alternate) {
if (node.type === "ReturnStatement") {
context.report(alternate, "Unexpected 'else' after 'return'.");
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"IfStatement": function(node) {
// Don't bother finding a ReturnStatement, if there's no `else`
// or if the alternate is also an if (indicating an else if).
if (node.alternate && node.consequent && node.alternate.type !== "IfStatement") {
// If we have a BlockStatement, check each consequent body node.
if (node.consequent.type === "BlockStatement") {
node.consequent.body.forEach(function (bodyNode) {
checkForReturnStatement(bodyNode, node.alternate);
});
// If not a block statement, make sure the consequent isn't a
// ReturnStatement
} else {
checkForReturnStatement(node.consequent, node.alternate);
}
}
}
};
};
},{}],54:[function(require,module,exports){
/**
* @fileoverview Rule to flag the use of empty character classes in regular expressions
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
var tokens = context.getTokens(node);
tokens.forEach(function (token) {
/*
plain-English description of the following regexp:
0. `^` fix the match at the beginning of the string
1. `\/`: the `/` that begins the regexp
2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
2.1. `\\.`: an escape sequence
2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
3. `\/` the `/` that ends the regexp
4. `[gimy]*`: optional regexp flags
5. `$`: fix the match at the end of the string
*/
if (token.type === "RegularExpression" &&
!/^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimy]*$/.test(token.value)) {
context.report(node, "Empty class.");
}
});
}
};
};
},{}],55:[function(require,module,exports){
/**
* @fileoverview Rule to flag when label is not used for a loop or switch
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"LabeledStatement": function(node) {
if (node.body.type !== "ForStatement" && node.body.type !== "WhileStatement" && node.body.type !== "DoWhileStatement" && node.body.type !== "SwitchStatement" && node.body.type !== "ForInStatement") {
context.report(node, "Unexpected label {{l}}", {l: node.label.name});
}
}
};
};
},{}],56:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of an empty block statement
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"BlockStatement": function(node) {
var ancestors = context.getAncestors(),
parent = ancestors[ancestors.length - 1],
parentType = parent.type,
isFinallyBlock = (parentType === "TryStatement") && (parent.finalizer === node);
if (/FunctionExpression|FunctionDeclaration|CatchClause/.test(parentType) ||
(isFinallyBlock && !parent.handlers.length)) {
return;
}
if (node.body.length === 0) {
context.report(node, "Empty block statement.");
}
},
"SwitchStatement": function(node) {
if (typeof node.cases === "undefined" || node.cases.length === 0) {
context.report(node, "Empty switch statement.");
}
}
};
};
},{}],57:[function(require,module,exports){
/**
* @fileoverview Rule to flag comparisons to null without a type-checking
* operator.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"BinaryExpression": function(node) {
var badOperator = node.operator === "==" || node.operator === "!=";
if (node.right.type === "Literal" && node.right.raw === "null" && badOperator ||
node.left.type === "Literal" && node.left.raw === "null" && badOperator) {
context.report(node, "Use ‘===’ to compare with ‘null’.");
}
}
};
};
},{}],58:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of eval() statement
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var IMPLIED_EVAL = /set(?:Timeout|Interval)/;
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines if a node represents a call to setTimeout/setInterval with
* a string argument.
* @param {ASTNode} node The node to check.
* @returns {boolean} True if the node matches, false if not.
* @private
*/
function isImpliedEval(node) {
var isMemberExpression = (node.callee.type === "MemberExpression"),
isIdentifier = (node.callee.type === "Identifier"),
isSetMethod = (isIdentifier && IMPLIED_EVAL.test(node.callee.name)) ||
(isMemberExpression && node.callee.object.name === "window" &&
IMPLIED_EVAL.test(node.callee.property.name)),
hasStringArgument = node.arguments.length && (typeof node.arguments[0].value === "string");
return isSetMethod && hasStringArgument;
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"CallExpression": function(node) {
if (node.callee.name === "eval") {
context.report(node, "eval can be harmful.");
} else if (isImpliedEval(node)) {
if (node.arguments.length && (typeof node.arguments[0].value === "string")) {
context.report(node, "Implied eval can be harmful. Pass a function instead of a string.");
}
}
}
};
};
},{}],59:[function(require,module,exports){
/**
* @fileoverview Rule to flag assignment of the exception parameter
* @author Stephen Murray <spmurrayzzz>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var inCatch = false,
exceptionName = null;
return {
"CatchClause": function(node) {
inCatch = true;
exceptionName = node.param.name;
},
"CatchClause:exit": function() {
inCatch = false;
exceptionName = null;
},
"AssignmentExpression": function(node) {
if (inCatch) {
if (node.left.name === exceptionName) {
context.report(node, "Do not assign to the exception parameter.");
}
}
}
};
};
},{}],60:[function(require,module,exports){
/**
* @fileoverview Rule to flag adding properties to native object's prototypes.
* @author David Nelson
*/
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var BUILTINS = [
"Object", "Function", "Array", "String", "Boolean", "Number", "Date",
"RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
"SyntaxError", "TypeError", "URIError"
];
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
// handle the Array.prototype.extra style case
"AssignmentExpression": function(node) {
var lhs = node.left, affectsProto;
if (lhs.type !== "MemberExpression" || lhs.object.type !== "MemberExpression") {
return;
}
affectsProto = lhs.object.computed ?
lhs.object.property.type === "Literal" && lhs.object.property.value === "prototype" :
lhs.object.property.name === "prototype";
if (!affectsProto) {
return;
}
BUILTINS.forEach(function(builtin) {
if (lhs.object.object.name === builtin) {
context.report(node, builtin + " prototype is read only, properties should not be added.");
}
});
},
// handle the Object.defineProperty(Array.prototype) case
"CallExpression": function(node) {
var callee = node.callee,
subject,
object;
// only worry about Object.defineProperty
if (callee.type === "MemberExpression" &&
callee.object.name === "Object" &&
callee.property.name === "defineProperty") {
// verify the object being added to is a native prototype
subject = node.arguments[0];
object = subject.object;
if (object &&
object.type === "Identifier" &&
(BUILTINS.indexOf(object.name) > -1) &&
subject.property.name === "prototype") {
context.report(node, object.name + " prototype is read only, properties should not be added.");
}
}
}
};
};
},{}],61:[function(require,module,exports){
/**
* @fileoverview Rule to flag unnecessary bind calls
* @author Bence Dányi <bence@danyi.me>
* @copyright 2014 Bence Dányi. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var scope = [{
depth: -1,
found: 0
}];
/**
* Get the topmost scope
* @returns {Object} The topmost scope
*/
function getTopScope() {
return scope[scope.length - 1];
}
/**
* Increment the depth of the top scope
* @returns {void}
*/
function incrementScopeDepth() {
var top = getTopScope();
top.depth++;
}
/**
* Decrement the depth of the top scope
* @returns {void}
*/
function decrementScopeDepth() {
var top = getTopScope();
top.depth--;
}
return {
"CallExpression": function(node) {
if (node.arguments.length === 1 &&
node.callee.type === "MemberExpression" &&
node.callee.property.name === "bind" &&
node.callee.object.type === "FunctionExpression") {
scope.push({
call: node,
depth: -1,
found: 0
});
}
},
"CallExpression:exit": function(node) {
var top = getTopScope();
if (top.call === node && top.found === 0) {
context.report(node, "The function binding is unnecessary.");
scope.pop();
}
},
"FunctionExpression": incrementScopeDepth,
"FunctionExpression:exit": decrementScopeDepth,
"FunctionDeclaration": incrementScopeDepth,
"FunctionDeclaration:exit": decrementScopeDepth,
"ThisExpression": function() {
var top = getTopScope();
if (top.depth === 0) {
top.found++;
}
}
};
};
},{}],62:[function(require,module,exports){
/**
* @fileoverview Rule to flag unnecessary double negation in Boolean contexts
* @author Brandon Mills
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"UnaryExpression": function (node) {
var ancestors = context.getAncestors(),
parent = ancestors.pop(),
grandparent = ancestors.pop();
// Exit early if it's guaranteed not to match
if (node.operator !== "!" ||
parent.type !== "UnaryExpression" ||
parent.operator !== "!") {
return;
}
// if (<bool>) ...
if (grandparent.type === "IfStatement") {
context.report(node, "Redundant double negation in an if statement condition.");
// do ... while (<bool>)
} else if (grandparent.type === "DoWhileStatement") {
context.report(node, "Redundant double negation in a do while loop condition.");
// while (<bool>) ...
} else if (grandparent.type === "WhileStatement") {
context.report(node, "Redundant double negation in a while loop condition.");
// <bool> ? ... : ...
} else if ((grandparent.type === "ConditionalExpression" &&
parent === grandparent.test)) {
context.report(node, "Redundant double negation in a ternary condition.");
// for (...; <bool>; ...) ...
} else if ((grandparent.type === "ForStatement" &&
parent === grandparent.test)) {
context.report(node, "Redundant double negation in a for loop condition.");
// !<bool>
} else if ((grandparent.type === "UnaryExpression" &&
grandparent.operator === "!")) {
context.report(node, "Redundant multiple negation.");
// Boolean(<bool>)
} else if ((grandparent.type === "CallExpression" &&
grandparent.callee.type === "Identifier" &&
grandparent.callee.name === "Boolean")) {
context.report(node, "Redundant double negation in call to Boolean().");
// new Boolean(<bool>)
} else if ((grandparent.type === "NewExpression" &&
grandparent.callee.type === "Identifier" &&
grandparent.callee.name === "Boolean")) {
context.report(node, "Redundant double negation in Boolean constructor call.");
}
}
};
};
},{}],63:[function(require,module,exports){
/**
* @fileoverview Disallow parenthesesisng higher precedence subexpressions.
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
function isParenthesised(node) {
var previousToken = context.getTokenBefore(node),
nextToken = context.getTokenAfter(node);
return previousToken && nextToken &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
function isParenthesisedTwice(node) {
var previousToken = context.getTokenBefore(node, 1),
nextToken = context.getTokenAfter(node, 1);
return isParenthesised(node) && previousToken && nextToken &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
function precedence(node) {
switch (node.type) {
case "SequenceExpression":
return 0;
case "AssignmentExpression":
return 1;
case "ConditionalExpression":
return 3;
case "LogicalExpression":
switch (node.operator) {
case "||":
return 4;
case "&&":
return 5;
// no default
}
/* falls through */
case "BinaryExpression":
switch (node.operator) {
case "|":
return 6;
case "^":
return 7;
case "&":
return 8;
case "==":
case "!=":
case "===":
case "!==":
return 9;
case "<":
case "<=":
case ">":
case ">=":
case "in":
case "instanceof":
return 10;
case "<<":
case ">>":
case ">>>":
return 11;
case "+":
case "-":
return 12;
case "*":
case "/":
case "%":
return 13;
// no default
}
/* falls through */
case "UnaryExpression":
return 14;
case "UpdateExpression":
return 15;
case "CallExpression":
// IIFE is allowed to have parens in any position (#655)
if (node.callee.type === "FunctionExpression") {
return -1;
}
return 16;
case "NewExpression":
return 17;
// no default
}
return 18;
}
function report(node) {
context.report(node, "Gratuitous parentheses around expression.");
}
function dryUnaryUpdate(node) {
if (isParenthesised(node.argument) && precedence(node.argument) >= precedence(node)) {
report(node.argument);
}
}
function dryCallNew(node) {
if (isParenthesised(node.callee) && precedence(node.callee) >= precedence(node) &&
!(node.type === "CallExpression" && node.callee.type === "FunctionExpression")) {
report(node.callee);
}
if (node.arguments.length === 1) {
if (isParenthesisedTwice(node.arguments[0]) && precedence(node.arguments[0]) >= precedence({type: "AssignmentExpression"})) {
report(node.arguments[0]);
}
} else {
[].forEach.call(node.arguments, function(arg) {
if (isParenthesised(arg) && precedence(arg) >= precedence({type: "AssignmentExpression"})) {
report(arg);
}
});
}
}
function dryBinaryLogical(node) {
var prec = precedence(node);
if (isParenthesised(node.left) && precedence(node.left) >= prec) {
report(node.left);
}
if (isParenthesised(node.right) && precedence(node.right) > prec) {
report(node.right);
}
}
return {
"ArrayExpression": function(node) {
[].forEach.call(node.elements, function(e) {
if (e && isParenthesised(e) && precedence(e) >= precedence({type: "AssignmentExpression"})) {
report(e);
}
});
},
"AssignmentExpression": function(node) {
if (isParenthesised(node.right) && precedence(node.right) >= precedence(node)) {
report(node.right);
}
},
"BinaryExpression": dryBinaryLogical,
"CallExpression": dryCallNew,
"ConditionalExpression": function(node) {
if (isParenthesised(node.test) && precedence(node.test) >= precedence({type: "LogicalExpression", operator: "||"})) {
report(node.test);
}
if (isParenthesised(node.consequent) && precedence(node.consequent) >= precedence({type: "AssignmentExpression"})) {
report(node.consequent);
}
if (isParenthesised(node.alternate) && precedence(node.alternate) >= precedence({type: "AssignmentExpression"})) {
report(node.alternate);
}
},
"DoWhileStatement": function(node) {
if (isParenthesisedTwice(node.test)) {
report(node.test);
}
},
"ExpressionStatement": function(node) {
var firstToken;
if (isParenthesised(node.expression)) {
firstToken = context.getFirstToken(node.expression);
if (firstToken.value !== "function" && firstToken.value !== "{") {
report(node.expression);
}
}
},
"ForInStatement": function(node) {
if (isParenthesised(node.right)) {
report(node.right);
}
},
"ForStatement": function(node) {
if (node.init && isParenthesised(node.init)) {
report(node.init);
}
if (node.test && isParenthesised(node.test)) {
report(node.test);
}
if (node.update && isParenthesised(node.update)) {
report(node.update);
}
},
"IfStatement": function(node) {
if (isParenthesisedTwice(node.test)) {
report(node.test);
}
},
"LogicalExpression": dryBinaryLogical,
"MemberExpression": function(node) {
if (
isParenthesised(node.object) &&
precedence(node.object) >= precedence(node) &&
(
node.computed ||
!(
node.object.type === "Literal" &&
typeof node.object.value === "number" &&
/^[0-9]+$/.test(context.getFirstToken(node.object).value)
)
)
) {
report(node.object);
}
},
"NewExpression": dryCallNew,
"ObjectExpression": function(node) {
[].forEach.call(node.properties, function(e) {
var v = e.value;
if (v && isParenthesised(v) && precedence(v) >= precedence({type: "AssignmentExpression"})) {
report(v);
}
});
},
"ReturnStatement": function(node) {
if (node.argument && isParenthesised(node.argument)) {
report(node.argument);
}
},
"SequenceExpression": function(node) {
[].forEach.call(node.expressions, function(e) {
if (isParenthesised(e) && precedence(e) >= precedence(node)) {
report(e);
}
});
},
"SwitchCase": function(node) {
if (node.test && isParenthesised(node.test)) {
report(node.test);
}
},
"SwitchStatement": function(node) {
if (isParenthesisedTwice(node.discriminant)) {
report(node.discriminant);
}
},
"ThrowStatement": function(node) {
if (isParenthesised(node.argument)) {
report(node.argument);
}
},
"UnaryExpression": dryUnaryUpdate,
"UpdateExpression": dryUnaryUpdate,
"VariableDeclarator": function(node) {
if (node.init && isParenthesised(node.init) && precedence(node.init) >= precedence({type: "AssignmentExpression"})) {
report(node.init);
}
},
"WhileStatement": function(node) {
if (isParenthesisedTwice(node.test)) {
report(node.test);
}
},
"WithStatement": function(node) {
if (isParenthesisedTwice(node.object)) {
report(node.object);
}
}
};
};
},{}],64:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of unnecessary semicolons
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"EmptyStatement": function(node) {
context.report(node, "Unnecessary semicolon.");
}
};
};
},{}],65:[function(require,module,exports){
/**
* @fileoverview Rule to flag unnecessary strict directives.
* @author Ian Christian Myers
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
function directives(block) {
var ds = [], body = block.body, e, i, l;
for (i = 0, l = body.length; i < l; ++i) {
e = body[i];
if (
e.type === "ExpressionStatement" &&
e.expression.type === "Literal" &&
typeof e.expression.value === "string"
) {
ds.push(e.expression);
} else {
break;
}
}
return ds;
}
function isStrict(directive) {
return directive.value === "use strict";
}
function checkForUnnecessaryUseStrict(node) {
var useStrictDirectives, scope, upper;
useStrictDirectives = directives(node).filter(isStrict);
switch (useStrictDirectives.length) {
case 0:
break;
case 1:
scope = context.getScope();
upper = scope.upper;
if (upper && upper.functionExpressionScope) {
upper = upper.upper;
}
if (upper && upper.isStrict) {
context.report(useStrictDirectives[0], "Unnecessary 'use strict'.");
}
break;
default:
context.report(useStrictDirectives[1], "Multiple 'use strict' directives.");
}
}
return {
"Program": checkForUnnecessaryUseStrict,
"FunctionExpression": function(node) {
checkForUnnecessaryUseStrict(node.body);
},
"FunctionDeclaration": function(node) {
checkForUnnecessaryUseStrict(node.body);
}
};
};
},{}],66:[function(require,module,exports){
/**
* @fileoverview Rule to flag fall-through cases in switch statements.
* @author Matt DuVall <http://mattduvall.com/>
*/
"use strict";
var FALLTHROUGH_COMMENT = /falls\sthrough/;
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var switches = [];
return {
"SwitchCase": function(node) {
var consequent = node.consequent,
switchData = switches[switches.length - 1],
i,
comments,
comment;
/*
* Some developers wrap case bodies in blocks, so if there is just one
* node and it's a block statement, check inside.
*/
if (consequent.length === 1 && consequent[0].type === "BlockStatement") {
consequent = consequent[0];
}
// checking on previous case
if (!switchData.lastCaseClosed) {
// a fall through comment will be the last trailing comment of the last case
comments = context.getComments(switchData.lastCase).trailing;
comment = comments[comments.length - 1];
// check for comment
if (!comment || !FALLTHROUGH_COMMENT.test(comment.value)) {
context.report(switchData.lastCase,
"Expected a \"break\" statement before \"{{code}}\".",
{ code: node.test ? "case" : "default" });
}
}
// now dealing with the current case
switchData.lastCaseClosed = false;
switchData.lastCase = node;
// try to verify using statements - go backwards as a fast path for the search
if (consequent.length) {
for (i = consequent.length - 1; i >= 0; i--) {
if (/(?:Break|Continue|Return|Throw)Statement/.test(consequent[i].type)) {
switchData.lastCaseClosed = true;
break;
}
}
} else {
// the case statement has no statements, so it must logically fall through
switchData.lastCaseClosed = true;
}
/*
* Any warnings are triggered when the next SwitchCase occurs.
* There is no need to warn on the last SwitchCase, since it can't
* fall through to anything.
*/
},
"SwitchStatement": function(node) {
switches.push({
node: node,
lastCaseClosed: true,
lastCase: null
});
},
"SwitchStatement:exit": function() {
switches.pop();
}
};
};
},{}],67:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of a leading/trailing decimal point in a numeric literal
* @author James Allardice
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
if (typeof node.value === "number") {
if (node.raw.indexOf(".") === 0) {
context.report(node, "A leading decimal point can be confused with a dot.");
}
if (node.raw.indexOf(".") === node.raw.length - 1) {
context.report(node, "A trailing decimal point can be confused with a dot.");
}
}
}
};
};
},{}],68:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of function declaration identifiers as variables.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/*
* Walk the scope chain looking for either a FunctionDeclaration or a
* VariableDeclaration with the same name as left-hand side of the
* AssignmentExpression. If we find the FunctionDeclaration first, then we
* warn, because a FunctionDeclaration is trying to become a Variable or a
* FunctionExpression. If we find a VariableDeclaration first, then we have
* a legitimate shadow variable.
*/
function checkIfIdentifierIsFunction(scope, name) {
var variable,
def,
i,
j;
// Loop over all of the identifiers available in scope.
for (i = 0; i < scope.variables.length; i++) {
variable = scope.variables[i];
// For each identifier, see if it was defined in _this_ scope.
for (j = 0; j < variable.defs.length; j++) {
def = variable.defs[j];
// Identifier is a function and was declared in this scope
if (def.name.name === name && def.type === "FunctionName") {
return true;
}
// Identifier is a variable and was declared in this scope. This
// is a legitimate shadow variable.
if (def.name.name === name) {
return false;
}
}
}
// Check the upper scope.
if (scope.upper) {
return checkIfIdentifierIsFunction(scope.upper, name);
}
// We've reached the global scope and haven't found anything.
return false;
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"AssignmentExpression": function(node) {
var scope = context.getScope(),
name = node.left.name;
if (checkIfIdentifierIsFunction(scope, name)) {
context.report(node, "'{{name}}' is a function.", { name: name });
}
}
};
};
},{}],69:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of implied eval via setTimeout and setInterval
* @author James Allardice
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"CallExpression": function(node) {
if (node.callee.type === "Identifier") {
var callee = node.callee.name;
if (callee === "setTimeout" || callee === "setInterval") {
var argument = node.arguments[0];
if (argument && argument.type === "Literal" && typeof argument.value === "string") {
context.report(node, "Implied eval. Consider passing a function instead of a string.");
}
}
}
}
};
};
},{}],70:[function(require,module,exports){
/**
* @fileoverview Rule to enforce declarations in program or function body root.
* @author Brandon Mills
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
/**
* Find the nearest Program or Function ancestor node.
* @returns {Object} Ancestor's type and distance from node.
*/
function nearestBody() {
var ancestors = context.getAncestors(),
ancestor = ancestors.pop(),
generation = 1;
while (ancestor && ["Program", "FunctionDeclaration",
"FunctionExpression"].indexOf(ancestor.type) < 0) {
generation += 1;
ancestor = ancestors.pop();
}
return {
// Type of containing ancestor
type: ancestor.type,
// Separation between ancestor and node
distance: generation
};
}
/**
* Ensure that a given node is at a program or function body's root.
* @param {ASTNode} node Declaration node to check.
* @returns {void}
*/
function check(node) {
var body = nearestBody(node),
valid = ((body.type === "Program" && body.distance === 1) ||
body.distance === 2);
if (!valid) {
context.report(node, "Move {{type}} declaration to {{body}} root.",
{
type: (node.type === "FunctionDeclaration" ?
"function" : "variable"),
body: (body.type === "Program" ?
"program" : "function body")
}
);
}
}
return {
"FunctionDeclaration": check,
"VariableDeclaration": function(node) {
if (context.options[0] === "both") {
check(node);
}
}
};
};
},{}],71:[function(require,module,exports){
/**
* @fileoverview Validate strings passed to the RegExp constructor
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
function isString(node) {
return node && node.type === "Literal" && typeof node.value === "string";
}
function check(node) {
if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0])) {
try {
if (isString(node.arguments[1])) {
void new RegExp(node.arguments[0].value, node.arguments[1].value);
} else {
void new RegExp(node.arguments[0].value);
}
} catch(e) {
context.report(node, e.message);
}
}
}
return {
"CallExpression": check,
"NewExpression": check
};
};
},{}],72:[function(require,module,exports){
/**
* @fileoverview Rule to flag usage of __iterator__ property
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"MemberExpression": function(node) {
if (node.property &&
(node.property.type === "Identifier" && node.property.name === "__iterator__" && !node.computed) ||
(node.property.type === "Literal" && node.property.value === "__iterator__")) {
context.report(node, "Reserved name '__iterator__'.");
}
}
};
};
},{}],73:[function(require,module,exports){
/**
* @fileoverview Rule to flag labels that are the same as an identifier
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
function findIdentifier(scope, identifier) {
var found = false;
scope.variables.forEach(function(variable) {
if (variable.name === identifier) {
found = true;
}
});
scope.references.forEach(function(reference) {
if (reference.identifier.name === identifier) {
found = true;
}
});
// If we have not found the identifier in this scope, check the parent
// scope.
if (scope.upper && !found) {
return findIdentifier(scope.upper, identifier);
}
return found;
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"LabeledStatement": function(node) {
// Fetch the innermost scope.
var scope = context.getScope();
// Recursively find the identifier walking up the scope, starting
// with the innermost scope.
if (findIdentifier(scope, node.label.name)) {
context.report(node, "Found identifier with same name as label.");
}
}
};
};
},{}],74:[function(require,module,exports){
/**
* @fileoverview Disallow Labeled Statements
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"LabeledStatement": function(node) {
context.report(node, "Unexpected labeled statement.");
},
"BreakStatement": function(node) {
if (node.label) {
context.report(node, "Unexpected label in break statement.");
}
},
"ContinueStatement": function(node) {
if (node.label) {
context.report(node, "Unexpected label in continue statement.");
}
}
};
};
},{}],75:[function(require,module,exports){
/**
* @fileoverview Rule to flag blocks with no reason to exist
* @author Brandon Mills
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"BlockStatement": function (node) {
// Check for any occurrence of BlockStatement > BlockStatement or
// Program > BlockStatement
var parent = context.getAncestors().pop();
if (parent.type === "BlockStatement" || parent.type === "Program") {
context.report(node, "Block is nested inside another block.");
}
}
};
};
},{}],76:[function(require,module,exports){
/**
* @fileoverview Rule to disallow if as the only statmenet in an else block
* @author Brandon Mills
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"IfStatement": function(node) {
var ancestors = context.getAncestors(),
parent = ancestors.pop(),
grandparent = ancestors.pop();
if (parent && parent.type === "BlockStatement" &&
parent.body.length === 1 && grandparent &&
grandparent.type === "IfStatement" &&
parent === grandparent.alternate) {
context.report(node, "Unexpected if as the only statement in an else block.");
}
}
};
};
},{}],77:[function(require,module,exports){
/**
* @fileoverview Rule to flag creation of function inside a loop
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function checkForLoops(node) {
var ancestors = context.getAncestors();
if (ancestors.some(function(ancestor) {
return ancestor.type === "ForStatement" || ancestor.type === "WhileStatement" || ancestor.type === "DoWhileStatement";
})) {
context.report(node, "Don't make functions within a loop");
}
}
return {
"FunctionExpression": checkForLoops,
"FunctionDeclaration": checkForLoops
};
};
},{}],78:[function(require,module,exports){
/**
* @fileoverview Rule to enforce grouped require statements for Node.JS
* @author Raphael Pigulla
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
/**
* Returns the list of built-in modules.
*
* @returns {string[]} An array of built-in Node.js modules.
*/
function getBuiltinModules() {
// This list is generated using `require("repl")._builtinLibs.concat('repl').sort()`
// This particular list was generated using node v0.11.9
return [
"assert", "buffer", "child_process", "cluster", "crypto",
"dgram", "dns", "domain", "events", "fs", "http", "https",
"net", "os", "path", "punycode", "querystring", "readline",
"repl", "smalloc", "stream", "string_decoder", "tls", "tty",
"url", "util", "vm", "zlib"
];
}
var BUILTIN_MODULES = getBuiltinModules();
var DECL_REQUIRE = "require",
DECL_UNINITIALIZED = "uninitialized",
DECL_OTHER = "other";
var REQ_CORE = "core",
REQ_FILE = "file",
REQ_MODULE = "module",
REQ_COMPUTED = "computed";
/**
* Determines the type of a declaration statement.
* @param {ASTNode} initExpression The init node of the VariableDeclarator.
* @returns {string} The type of declaration represented by the expression.
*/
function getDeclarationType(initExpression) {
if (!initExpression) {
// "var x;"
return DECL_UNINITIALIZED;
}
if (initExpression.type === "CallExpression" &&
initExpression.callee.type === "Identifier" &&
initExpression.callee.name === "require"
) {
// "var x = require('util');"
return DECL_REQUIRE;
} else if (initExpression.type === "MemberExpression") {
// "var x = require('glob').Glob;"
return getDeclarationType(initExpression.object);
}
// "var x = 42;"
return DECL_OTHER;
}
/**
* Determines the type of module that is loaded via require.
* @param {ASTNode} initExpression The init node of the VariableDeclarator.
* @returns {string} The module type.
*/
function inferModuleType(initExpression) {
if (initExpression.type === "MemberExpression") {
// "var x = require('glob').Glob;"
return inferModuleType(initExpression.object);
} else if (initExpression["arguments"].length === 0) {
// "var x = require();"
return REQ_COMPUTED;
}
var arg = initExpression["arguments"][0];
if (arg.type !== "Literal" || typeof arg.value !== "string") {
// "var x = require(42);"
return REQ_COMPUTED;
}
if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
// "var fs = require('fs');"
return REQ_CORE;
} else if (/^\.{0,2}\//.test(arg.value)) {
// "var utils = require('./utils');"
return REQ_FILE;
} else {
// "var async = require('async');"
return REQ_MODULE;
}
}
/**
* Check if the list of variable declarations is mixed, i.e. whether it
* contains both require and other declarations.
* @param {ASTNode} declarations The list of VariableDeclarators.
* @returns {boolean} True if the declarations are mixed, false if not.
*/
function isMixed(declarations) {
var contains = {};
declarations.forEach(function (declaration) {
var type = getDeclarationType(declaration.init);
contains[type] = true;
});
return !!(
contains[DECL_REQUIRE] &&
(contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
);
}
/**
* Check if all require declarations in the given list are of the same
* type.
* @param {ASTNode} declarations The list of VariableDeclarators.
* @returns {boolean} True if the declarations are grouped, false if not.
*/
function isGrouped(declarations) {
var found = {};
declarations.forEach(function (declaration) {
if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
found[inferModuleType(declaration.init)] = true;
}
});
return Object.keys(found).length <= 1;
}
return {
"VariableDeclaration": function(node) {
var grouping = !!context.options[0];
if (isMixed(node.declarations)) {
context.report(
node,
"Do not mix 'require' and other declarations."
);
} else if (grouping && !isGrouped(node.declarations)) {
context.report(
node,
"Do not mix core, module, file and computed requires."
);
}
}
};
};
},{}],79:[function(require,module,exports){
/**
* @fileoverview Disallow mixed spaces and tabs for indentation
* @author Jary Niebur
* @copyright 2014 Nicholas C. Zakas. All rights reserved.
* @copyright 2014 Jary Niebur. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var smartTabs = context.options[0];
var COMMENT_START = /^\s*\/\*/,
MAYBE_COMMENT = /^\s*\*/;
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"Program": function(node) {
/*
* At least one space followed by a tab
* or the reverse before non-tab/-space
* characters begin.
*/
var regex = /^(?=[\t ]*(\t | \t))/,
match,
lines = context.getSourceLines();
if (smartTabs) {
/*
* At least one space followed by a tab
* before non-tab/-space characters begin.
*/
regex = /^(?=[\t ]* \t)/;
}
lines.forEach(function(line, i) {
match = regex.exec(line);
if (match) {
if (!MAYBE_COMMENT.test(line) && !COMMENT_START.test(lines[i - 1])) {
context.report(node, { line: i + 1, column: match.index + 1 }, "Mixed spaces and tabs.");
}
}
});
}
};
};
},{}],80:[function(require,module,exports){
/**
* @fileoverview Disallow use of multiple spaces.
* @author Vignesh Anand aka vegetableman.
* @copyright 2014 Vignesh Anand. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var OPERATORS = [
"*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in",
"instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=",
"+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
"?", ":", ","
], errOps = [];
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Reports an AST node as a rule violation.
* @param {ASTNode} node The node to report.
* @returns {void}
* @private
*/
function report(node) {
context.report(node, "Multiple spaces found around '" + errOps.shift() + "'.");
}
/**
* Checks whether the operator is in same line as two adjacent tokens.
* @param {ASTNode} left The token left to the operator.
* @param {ASTNode} right The token right to the operator.
* @param {ASTNode} operator The operator.
* @returns {boolean} Whether the operator is in same line as two adjacent tokens.
* @private
*/
function isSameLine(left, right, operator) {
return operator.loc.end.line === left.loc.end.line &&
operator.loc.end.line === right.loc.start.line;
}
/**
* Check whether there are multiple spaces around the operator.
* @param {ASTNode} left The token left to the operator.
* @param {ASTNode} right The token right to the operator.
* @param {ASTNode} operator The operator.
* @returns {boolean} Whether there are multiple spaces.
* @private
*/
function isMultiSpaced(left, right, operator) {
return operator.range[0] - left.range[1] > 1 ||
right.range[0] - operator.range[1] > 1;
}
/**
* Get tokens and validate the spacing.
* @param {ASTNode} left The token left to the operator.
* @param {ASTNode} right The token right to the operator.
* @returns {boolean} Whether multiple spaces were found.
* @private
*/
function validateSpacing(left, right) {
var tokens = context.getTokens({range: [left.range[1], right.range[0]]}, 1, 1),
operator;
for (var i = 1, l = tokens.length - 1; i < l; ++i) {
left = tokens[i - 1];
operator = tokens[i];
right = tokens[i + 1];
if (operator && operator.type === "Punctuator" && OPERATORS.indexOf(operator.value) >= 0 &&
isSameLine(left, right, operator) && isMultiSpaced(left, right, operator)) {
errOps.push(operator.value);
return true;
}
}
return false;
}
/**
* Report if there are multiple spaces on the expression.
* @param {ASTNode} node The node to check.
* @returns {void}
* @private
*/
function checkExpression(node) {
if (validateSpacing(node.left, node.right)) {
report(node);
}
}
/**
* Report if there are multiple spaces around conditional ternary operators.
* @param {ASTNode} node The node to check.
* @returns {void}
* @private
*/
function checkConditional(node) {
if (validateSpacing(node.test, node.consequent)) {
report(node);
}
if (validateSpacing(node.consequent, node.alternate)) {
report(node);
}
}
/**
* Report if there are multiple spaces around equal operator in variable declaration.
* @param {ASTNode} node The node to check.
* @returns {void}
* @private
*/
function checkVar(node) {
if (node.init && validateSpacing(node.id, node.init)) {
report(node);
}
}
/**
* Report if there are multiple spaces around list of items in objects, arrays,
* function parameters, sequences and declarations.
* @param {ASTNode} node The node to check.
* @param {string} property The property of node.
* @returns {void}
* @private
*/
function checkList(node, property) {
var items = node[property];
for (var i = 0, l = items.length; i < l; i++) {
var left = items[i - 1],
right = items[i],
operator = context.getTokenBefore(right);
if (operator && operator.type === "Punctuator" && operator.value === ",") {
if (isSameLine(left, right, operator) && isMultiSpaced(left, right, operator)) {
errOps.push(operator.value);
report(right);
}
}
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"AssignmentExpression": checkExpression,
"BinaryExpression": checkExpression,
"LogicalExpression": checkExpression,
"ConditionalExpression": checkConditional,
"VariableDeclarator": checkVar,
"ArrayExpression": function(node) {
checkList(node, "elements");
},
"ObjectExpression": function(node) {
checkList(node, "properties");
},
"SequenceExpression": function(node) {
checkList(node, "expressions");
},
"FunctionExpression": function(node) {
checkList(node, "params");
},
"FunctionDeclaration": function(node) {
checkList(node, "params");
},
"VariableDeclaration": function(node) {
checkList(node, "declarations");
}
};
};
},{}],81:[function(require,module,exports){
/**
* @fileoverview Rule to flag when using multiline strings
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
var lineBreak = /\n/;
if (lineBreak.test(node.raw)) {
context.report(node, "Multiline support is limited to browsers supporting ES5 only.");
}
}
};
};
},{}],82:[function(require,module,exports){
/**
* @fileoverview Disallows multiple blank lines.
* implementation adapted from the no-trailing-spaces rule.
* @author Greg Cochard
* @copyright 2014 Greg Cochard. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
// Use options.max or 2 as default
var numLines = 2;
if (context.options.length) {
numLines = context.options[0].max;
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"Program": function checkBlankLines(node) {
var lines = context.getSourceLines(),
currentLocation = -1,
lastLocation,
blankCounter = 0,
location,
trimmedLines = lines.map(function(str) {
return str.trim();
});
// Aggregate and count blank lines
do {
lastLocation = currentLocation;
currentLocation = trimmedLines.indexOf("",currentLocation + 1);
if (lastLocation === currentLocation - 1) {
blankCounter++;
} else {
if (blankCounter >= numLines) {
location = {
line: lastLocation + 1,
column: lines[lastLocation].length
};
context.report(node, location, "Multiple blank lines not allowed.");
}
// Finally, reset the blank counter
blankCounter = 0;
}
} while (currentLocation !== -1);
}
};
};
},{}],83:[function(require,module,exports){
/**
* @fileoverview Rule to flag when re-assigning native objects
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var nativeObjects = ["Array", "Boolean", "Date", "decodeURI",
"decodeURIComponent", "encodeURI", "encodeURIComponent",
"Error", "eval", "EvalError", "Function", "isFinite",
"isNaN", "JSON", "Math", "Number", "Object", "parseInt",
"parseFloat", "RangeError", "ReferenceError", "RegExp",
"String", "SyntaxError", "TypeError", "URIError",
"Map", "NaN", "Set", "WeakMap", "Infinity", "undefined"];
return {
"AssignmentExpression": function(node) {
if (nativeObjects.indexOf(node.left.name) >= 0) {
context.report(node, node.left.name + " is a read-only native object.");
}
},
"VariableDeclarator": function(node) {
if (nativeObjects.indexOf(node.id.name) >= 0) {
context.report(node, "Redefinition of '{{nativeObject}}'.", { nativeObject: node.id.name });
}
}
};
};
},{}],84:[function(require,module,exports){
/**
* @fileoverview A rule to disallow negated left operands of the `in` operator
* @author Michael Ficarra
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"BinaryExpression": function(node) {
if (node.operator === "in" && node.left.type === "UnaryExpression" && node.left.operator === "!") {
context.report(node, "The `in` expression's left operand is negated");
}
}
};
};
},{}],85:[function(require,module,exports){
/**
* @fileoverview Rule to flag nested ternary expressions
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"ConditionalExpression": function(node) {
if (node.alternate.type === "ConditionalExpression" ||
node.consequent.type === "ConditionalExpression") {
context.report(node, "Do not nest ternary expressions");
}
}
};
};
},{}],86:[function(require,module,exports){
/**
* @fileoverview Rule to flag when using new Function
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"NewExpression": function(node) {
if (node.callee.name === "Function") {
context.report(node, "The Function constructor is eval.");
}
}
};
};
},{}],87:[function(require,module,exports){
/**
* @fileoverview A rule to disallow calls to the Object constructor
* @author Matt DuVall <http://www.mattduvall.com/>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"NewExpression": function(node) {
if (node.callee.name === "Object") {
context.report(node, "The object literal notation {} is preferrable.");
}
}
};
};
},{}],88:[function(require,module,exports){
/**
* @fileoverview Rule to disallow use of new operator with the `require` function
* @author Wil Moore III
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"NewExpression": function(node) {
if (node.callee.type === "Identifier" && node.callee.name === "require") {
context.report(node, "Unexpected use of new with require.");
}
}
};
};
},{}],89:[function(require,module,exports){
/**
* @fileoverview Rule to flag when using constructor for wrapper objects
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"NewExpression": function(node) {
var wrapperObjects = ["String", "Number", "Boolean", "Math", "JSON"];
if (wrapperObjects.indexOf(node.callee.name) > -1) {
context.report(node, "Do not use {{fn}} as a constructor.", { fn: node.callee.name });
}
}
};
};
},{}],90:[function(require,module,exports){
/**
* @fileoverview Rule to flag statements with function invocation preceded by
* "new" and not part of assignment
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"ExpressionStatement": function(node) {
if (node.expression.type === "NewExpression") {
context.report(node, "Do not use 'new' for side effects.");
}
}
};
};
},{}],91:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of an object property of the global object (Math and JSON) as a function
* @author James Allardice
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"CallExpression": function(node) {
if (node.callee.type === "Identifier") {
var name = node.callee.name;
if (name === "Math" || name === "JSON") {
context.report(node, "'{{name}}' is not a function.", { name: name });
}
}
}
};
};
},{}],92:[function(require,module,exports){
/**
* @fileoverview Rule to flag octal escape sequences in string literals.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
if (typeof node.value !== "string") {
return;
}
var match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-7])/),
octalDigit;
if (match) {
octalDigit = match[2];
context.report(node, "Don't use octal: '\\{{octalDigit}}'. Use '\\u....' instead.",
{ octalDigit: octalDigit });
}
}
};
};
},{}],93:[function(require,module,exports){
/**
* @fileoverview Rule to flag when initializing octal literal
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
if (typeof node.value === "number" && /^0[0-7]/.test(node.raw)) {
context.report(node, "Octal literals should not be used.");
}
}
};
};
},{}],94:[function(require,module,exports){
/**
* @fileoverview Disallow string concatenation when using __dirname and __filename
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var MATCHER = /^__(?:dir|file)name$/;
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"BinaryExpression": function(node) {
var left = node.left,
right = node.right;
if (node.operator === "+" &&
((left.type === "Identifier" && MATCHER.test(left.name)) ||
(right.type === "Identifier" && MATCHER.test(right.name)))
) {
context.report(node, "Use path.join() or path.resolve() instead of + to create paths.");
}
}
};
};
},{}],95:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of unary increment and decrement operators.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"UpdateExpression": function(node) {
context.report(node, "Unary operator '" + node.operator + "' used.");
}
};
};
},{}],96:[function(require,module,exports){
/**
* @fileoverview Disallow the use of process.env()
* @author Vignesh Anand
* @copyright 2014 Vignesh Anand. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"MemberExpression": function(node) {
var objectName = node.object.name,
propertyName = node.property.name;
if (objectName === "process" && !node.computed && propertyName && propertyName === "env") {
context.report(node, "Unexpected use of process.env.");
}
}
};
};
},{}],97:[function(require,module,exports){
/**
* @fileoverview Disallow the use of process.exit()
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"CallExpression": function(node) {
var callee = node.callee;
if (callee.type === "MemberExpression" && callee.object.name === "process" &&
callee.property.name === "exit"
) {
context.report(node, "Don't use process.exit(); throw an error instead.");
}
}
};
};
},{}],98:[function(require,module,exports){
/**
* @fileoverview Rule to flag usage of __proto__ property
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"MemberExpression": function(node) {
if (node.property &&
(node.property.type === "Identifier" && node.property.name === "__proto__" && !node.computed) ||
(node.property.type === "Literal" && node.property.value === "__proto__")) {
context.report(node, "The '__proto__' property is deprecated.");
}
}
};
};
},{}],99:[function(require,module,exports){
/**
* @fileoverview Rule to flag when the same variable is declared more then once.
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function findVariables() {
var scope = context.getScope();
scope.variables.forEach(function(variable) {
if (variable.identifiers && variable.identifiers.length > 1) {
variable.identifiers.sort(function(a, b) {
return a.range[1] - b.range[1];
});
for (var i = 1, l = variable.identifiers.length; i < l; i++) {
context.report(variable.identifiers[i], "{{a}} is already defined", {a: variable.name});
}
}
});
}
return {
"Program": findVariables,
"FunctionExpression": findVariables,
"FunctionDeclaration": findVariables
};
};
},{}],100:[function(require,module,exports){
/**
* @fileoverview Rule to count multiple spaces in regular expressions
* @author Matt DuVall <http://www.mattduvall.com/>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
var token = context.getFirstTokens(node, 1)[0],
nodeType = token.type,
nodeValue = token.value,
multipleSpacesRegex = /( {2,})+?/,
regexResults;
if (nodeType === "RegularExpression") {
regexResults = multipleSpacesRegex.exec(nodeValue);
if (regexResults !== null) {
context.report(node, "Spaces are hard to count. Use {" + regexResults[0].length + "}.");
}
}
}
};
};
},{}],101:[function(require,module,exports){
/**
* @fileoverview Rule to disallow reserved words being used as keys
* @author Emil Bay
* @copyright 2014 Emil Bay. All rights reserved.
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var MESSAGE = "Reserved word '{{key}}' used as key.";
var reservedWords = [
"abstract",
"boolean", "break", "byte",
"case", "catch", "char", "class", "const", "continue",
"debugger", "default", "delete", "do", "double",
"else", "enum", "export", "extends",
"final", "finally", "float", "for", "function",
"goto",
"if", "implements", "import", "in", "instanceof", "int", "interface",
"long",
"native", "new",
"package", "private", "protected", "public",
"return",
"short", "static", "super", "switch", "synchronized",
"this", "throw", "throws", "transient", "try", "typeof",
"var", "void", "volatile",
"while", "with"
];
return {
"ObjectExpression": function(node) {
node.properties.forEach(function(property) {
if (property.key.type === "Identifier") {
var keyName = property.key.name;
if (reservedWords.indexOf("" + keyName) !== -1) {
context.report(node, MESSAGE, { key: keyName });
}
}
});
}
};
};
},{}],102:[function(require,module,exports){
/**
* @fileoverview Restrict usage of specified node modules.
* @author Christian Schulz
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function (context) {
// trim restricted module names
var restrictedModules = context.options;
// if no modules are restricted we don't need to check the CallExpressions
if (restrictedModules.length === 0) {
return {};
}
/**
* Function to check if a node is a string literal.
* @param {ASTNode} node The node to check.
* @returns {boolean} If the node is a string literal.
*/
function isString(node) {
return node && node.type === "Literal" && typeof node.value === "string";
}
/**
* Function to check if a node is a require call.
* @param {ASTNode} node The node to check.
* @returns {boolean} If the node is a require call.
*/
function isRequireCall(node) {
return node.callee.type === "Identifier" && node.callee.name === "require";
}
/**
* Function to check if a node has an argument that is an restricted module and return its name.
* @param {ASTNode} node The node to check
* @returns {undefined|String} restricted module name or undefined if node argument isn't restricted.
*/
function getRestrictedModuleName(node) {
var moduleName;
// node has arguments and first argument is string
if (node.arguments.length && isString(node.arguments[0])) {
var argumentValue = node.arguments[0].value.trim();
// check if argument value is in restricted modules array
if (restrictedModules.indexOf(argumentValue) !== -1) {
moduleName = argumentValue;
}
}
return moduleName;
}
return {
"CallExpression": function (node) {
if (isRequireCall(node)) {
var restrictedModuleName = getRestrictedModuleName(node);
if (restrictedModuleName) {
context.report(node, "'{{moduleName}}' module is restricted from being used.", {
moduleName: restrictedModuleName
});
}
}
}
};
};
},{}],103:[function(require,module,exports){
/**
* @fileoverview Rule to flag when return statement contains assignment
* @author Ilya Volodin
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"ReturnStatement": function(node) {
if (node.argument && node.argument.type === "AssignmentExpression") {
context.report(node, "Return statement should not contain assignment.");
}
}
};
};
},{}],104:[function(require,module,exports){
/**
* @fileoverview Rule to flag when using javascript: urls
* @author Ilya Volodin
*/
/*jshint scripturl: true */
/*eslint no-script-url: 0*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"Literal": function(node) {
var value;
if (node.value && typeof(node.value) === "string") {
value = node.value.toLowerCase();
if (value.indexOf("javascript:") === 0) {
context.report(node, "Script URL is a form of eval.");
}
}
}
};
};
},{}],105:[function(require,module,exports){
/**
* @fileoverview Rule to flag comparison where left part is the same as the right
* part.
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"BinaryExpression": function(node) {
var operators = ["===", "==", "!==", "!=", ">", "<", ">=", "<="];
if (operators.indexOf(node.operator) > -1 &&
(node.left.type === "Identifier" && node.right.type === "Identifier" && node.left.name === node.right.name ||
node.left.type === "Literal" && node.right.type === "Literal" && node.left.value === node.right.value)) {
context.report(node, "Comparing to itself is potentially pointless.");
}
}
};
};
},{}],106:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of comma operator
* @author Brandon Mills
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
/**
* Parts of the grammar that are required to have parens.
*/
var parenthesized = {
"DoWhileStatement": "test",
"IfStatement": "test",
"SwitchStatement": "discriminant",
"WhileStatement": "test",
"WithStatement": "object"
// Omitting CallExpression - commas are parsed as argument separators
// Omitting NewExpression - commas are parsed as argument separators
// Omitting ForInStatement - parts aren't individually parenthesised
// Omitting ForStatement - parts aren't individually parenthesised
};
/**
* Determines whether a node is required by the grammar to be wrapped in
* parens, e.g. the test of an if statement.
* @param {ASTNode} node - The AST node
* @returns {boolean} True if parens around node belong to parent node.
*/
function requiresExtraParens(node) {
return node.parent && parenthesized[node.parent.type] != null &&
node === node.parent[parenthesized[node.parent.type]];
}
/**
* Check if a node is wrapped in parens.
* @param {ASTNode} node - The AST node
* @returns {boolean} True if the node has a paren on each side.
*/
function isParenthesised(node) {
var previousToken = context.getTokenBefore(node),
nextToken = context.getTokenAfter(node);
return previousToken && nextToken &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
/**
* Check if a node is wrapped in two levels of parens.
* @param {ASTNode} node - The AST node
* @returns {boolean} True if two parens surround the node on each side.
*/
function isParenthesisedTwice(node) {
var previousToken = context.getTokenBefore(node, 1),
nextToken = context.getTokenAfter(node, 1);
return isParenthesised(node) && previousToken && nextToken &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
return {
"SequenceExpression": function(node) {
// Always allow sequences in for statement update
if (node.parent.type === "ForStatement" &&
(node === node.parent.init || node === node.parent.update)) {
return;
}
// Wrapping a sequence in extra parens indicates intent
if (requiresExtraParens(node)) {
if (isParenthesisedTwice(node)) {
return;
}
} else {
if (isParenthesised(node)) {
return;
}
}
context.report(node, "Unexpected use of comma operator.");
}
};
};
},{}],107:[function(require,module,exports){
/**
* @fileoverview Disallow shadowing of NaN, undefined, and Infinity (ES5 section 15.1.1)
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var RESTRICTED = ["undefined", "NaN", "Infinity", "arguments", "eval"];
function checkForViolation(id) {
if (RESTRICTED.indexOf(id.name) > -1) {
context.report(id, "Shadowing of global property \"" + id.name + "\".");
}
}
return {
"VariableDeclarator": function(node) {
checkForViolation(node.id);
},
"FunctionExpression": function(node) {
if (node.id) {
checkForViolation(node.id);
}
[].map.call(node.params, checkForViolation);
},
"FunctionDeclaration": function(node) {
checkForViolation(node.id);
[].map.call(node.params, checkForViolation);
},
"CatchClause": function(node) {
checkForViolation(node.param);
}
};
};
},{}],108:[function(require,module,exports){
/**
* @fileoverview Rule to flag on declaring variables already declared in the outer scope
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
/**
* Checks if a variable is contained in the list of given scope variables.
* @param {Object} variable The variable to check.
* @param {Array} scopeVars The scope variables to look for.
* @returns {boolean} Whether or not the variable is contains in the list of scope variables.
*/
function isContainedInScopeVars(variable, scopeVars) {
return scopeVars.some(function (scopeVar) {
if (scopeVar.identifiers.length > 0) {
return variable.name === scopeVar.name;
}
return false;
});
}
/**
* Checks if the given variables are shadowed in the given scope.
* @param {Array} variables The variables to look for
* @param {Object} scope The scope to be checked.
* @returns {void}
*/
function checkShadowsInScope(variables, scope) {
variables.forEach(function (variable) {
if (isContainedInScopeVars(variable, scope.variables)) {
context.report(variable.identifiers[0], "{{a}} is already declared in the upper scope.", {a: variable.name});
}
});
}
/**
* Filters all variables of a list which already occur in another list.
* @param {Array} variableListA List of variables which should be filtered.
* @param {Array} variableListB List of variables which should no occur in variableListA.
* @returns {Array} Filtered list of variables.
*/
function filterVariableList(variableListA, variableListB) {
return variableListA.filter(function (variableA) {
return !variableListB.some(function (variableB) {
return variableA.name === variableB.name;
});
});
}
/**
* Checks the given node for shadowed variables.
* @param {ASTNode} node The AST node of a FunctionDeclaration or FunctionExpression.
* @returns {void}
*/
function checkForShadows(node) {
var scope = context.getScope(),
args = node.params,
variables = filterVariableList(scope.variables, args);
// iterate through the array of variables and find duplicates with the upper scope
var upper = scope.upper;
while (upper) {
checkShadowsInScope(variables, upper);
upper = upper.upper;
}
}
return {
"FunctionDeclaration": checkForShadows,
"FunctionExpression": checkForShadows
};
};
},{}],109:[function(require,module,exports){
/**
* @fileoverview Rule to require variables declared without whitespace before the lines semicolon
* @author Jonathan Kingston
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var semicolonWhitespace = /\s;$/;
return {
"VariableDeclaration": function(node) {
var source = context.getSource(node);
if (semicolonWhitespace.test(source)) {
context.report(node, "Variable declared with trailing whitespace before semicolon");
}
},
"ExpressionStatement": function(node) {
var source = context.getSource(node);
if (semicolonWhitespace.test(source)) {
context.report(node, "Expression called with trailing whitespace before semicolon");
}
}
};
};
},{}],110:[function(require,module,exports){
/**
* @fileoverview Rule to check that spaced function application
* @author Matt DuVall <http://www.mattduvall.com>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function detectOpenSpaces(node) {
var lastCalleeToken = context.getLastToken(node.callee);
var tokens = context.getTokens(node);
var i = tokens.indexOf(lastCalleeToken), l = tokens.length;
while (i < l && tokens[i].value !== "(") {
++i;
}
if (i >= l) {
return;
}
// look for a space between the callee and the open paren
if (tokens[i - 1].range[1] !== tokens[i].range[0]) {
context.report(node, "Unexpected space between function name and paren.");
}
}
return {
"CallExpression": detectOpenSpaces,
"NewExpression": detectOpenSpaces
};
};
},{}],111:[function(require,module,exports){
/**
* @fileoverview Disallow sparse arrays
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"ArrayExpression": function(node) {
var emptySpot = node.elements.indexOf(null) > -1;
if (emptySpot) {
context.report(node, "Unexpected comma in middle of array.");
}
}
};
};
},{}],112:[function(require,module,exports){
/**
* @fileoverview Rule to check for properties whose identifier ends with the string Sync
* @author Matt DuVall<http://mattduvall.com/>
*/
/*jshint node:true*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"MemberExpression": function(node) {
var propertyName = node.property.name,
syncRegex = /.*Sync$/;
if (syncRegex.exec(propertyName) !== null) {
context.report(node, "Unexpected sync method: '" + propertyName + "'.");
}
}
};
};
},{}],113:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of ternary operators.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"ConditionalExpression": function(node) {
context.report(node, "Ternary operator used.");
}
};
};
},{}],114:[function(require,module,exports){
/**
* @fileoverview Disallow trailing spaces at the end of lines.
* @author Nodeca Team <https://github.com/nodeca>
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var TRAILER = "[ \t\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+$";
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"Program": function checkTrailingSpaces(node) {
// Let's hack. Since Esprima does not return whitespace nodes,
// fetch the source code and do black magic via regexps.
var src = context.getSource(),
re = new RegExp(TRAILER, "mg"),
match, lines, location;
while ((match = re.exec(src)) !== null) {
lines = src.slice(0, re.lastIndex).split(/\r?\n/g);
location = {
line: lines.length,
column: lines[lines.length - 1].length - match[0].length + 1
};
// Passing node is a bit dirty, because message data will contain
// big text in `source`. But... who cares :) ?
// One more kludge will not make worse the bloody wizardry of this plugin.
context.report(node, location, "Trailing spaces not allowed.");
}
}
};
};
},{}],115:[function(require,module,exports){
/**
* @fileoverview Rule to flag when initializing to undefined
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"VariableDeclarator": function(node) {
var name = node.id.name;
var init = node.init && node.init.name;
if (init === "undefined") {
context.report(node, "It's not necessary to initialize '{{name}}' to undefined.", { name: name });
}
}
};
};
},{}],116:[function(require,module,exports){
/**
* @fileoverview Rule to flag references to undeclared variables.
* @author Mark Macdonald
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
function isImplicitGlobal(variable) {
return variable.defs.every(function(def) {
return def.type === "ImplicitGlobalVariable";
});
}
/**
* Gets the declared variable, defined in `scope`, that `ref` refers to.
* @param {Scope} scope The scope in which to search.
* @param {Reference} ref The reference to find in the scope.
* @returns {Variable} The variable, or null if ref refers to an undeclared variable.
*/
function getDeclaredGlobalVariable(scope, ref) {
var declaredGlobal = null;
scope.variables.some(function(variable) {
if (variable.name === ref.identifier.name) {
// If it's an implicit global, it must have a `writeable` field (indicating it was declared)
if (!isImplicitGlobal(variable) || {}.hasOwnProperty.call(variable, "writeable")) {
declaredGlobal = variable;
return true;
}
}
return false;
});
return declaredGlobal;
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"Program": function(/*node*/) {
var globalScope = context.getScope();
globalScope.through.forEach(function(ref) {
var variable = getDeclaredGlobalVariable(globalScope, ref),
name = ref.identifier.name;
if (!variable) {
context.report(ref.identifier, "'{{name}}' is not defined.", { name: name });
} else if (ref.isWrite() && variable.writeable === false) {
context.report(ref.identifier, "'{{name}}' is read only.", { name: name });
}
});
}
};
};
},{}],117:[function(require,module,exports){
/**
* @fileoverview Rule to flag references to the undefined variable.
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
return {
"Identifier": function(node) {
if (node.name === "undefined") {
var parent = context.getAncestors().pop();
if (!parent || parent.type !== "MemberExpression" || node !== parent.property || parent.computed) {
context.report(node, "Unexpected use of undefined.");
}
}
}
};
};
},{}],118:[function(require,module,exports){
/**
* @fileoverview Rule to flag trailing underscores in variable declarations.
* @author Matt DuVall <http://www.mattduvall.com>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//-------------------------------------------------------------------------
// Helpers
//-------------------------------------------------------------------------
function hasTrailingUnderscore(identifier) {
var len = identifier.length;
return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_");
}
function isSpecialCaseIdentifierForMemberExpression(identifier) {
return identifier === "__proto__";
}
function isSpecialCaseIdentifierInVariableExpression(identifier) {
// Checks for the underscore library usage here
return identifier === "_";
}
function checkForTrailingUnderscoreInFunctionDeclaration(node) {
var identifier = node.id.name;
if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier)) {
context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
}
}
function checkForTrailingUnderscoreInVariableExpression(node) {
var identifier = node.id.name;
if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
!isSpecialCaseIdentifierInVariableExpression(identifier)) {
context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
}
}
function checkForTrailingUnderscoreInMemberExpression(node) {
var identifier = node.property.name;
if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
!isSpecialCaseIdentifierForMemberExpression(identifier)) {
context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"FunctionDeclaration": checkForTrailingUnderscoreInFunctionDeclaration,
"VariableDeclarator": checkForTrailingUnderscoreInVariableExpression,
"MemberExpression": checkForTrailingUnderscoreInMemberExpression
};
};
},{}],119:[function(require,module,exports){
/**
* @fileoverview Checks for unreachable code due to return, throws, break, and continue.
* @author Joel Feenstra
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
function report(context, node, unreachableType) {
var keyword;
switch (unreachableType) {
case "BreakStatement":
keyword = "break";
break;
case "ContinueStatement":
keyword = "continue";
break;
case "ReturnStatement":
keyword = "return";
break;
case "ThrowStatement":
keyword = "throw";
break;
default:
return;
}
context.report(node, "Found unexpected statement after a {{type}}.", { type: keyword });
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
/**
* Checks if a node is an exception for no-unreachable because of variable/function hoisting
* @param {ASTNode} node The AST node to check.
* @returns {boolean} if the node doesn't trigger unreachable
* @private
*/
function isUnreachableAllowed(node) {
return node.type === "FunctionDeclaration" ||
node.type === "VariableDeclaration" &&
node.declarations.every(function(declaration) {
return declaration.type === "VariableDeclarator" && declaration.init === null;
});
}
/*
* Verifies that the given node is the last node or followed exclusively by
* hoisted declarations
* @param {ASTNode} node Node that should be the last node
* @returns {void}
* @private
*/
function checkNode(node) {
var parent = context.getAncestors().pop();
var field, i, sibling;
switch (parent.type) {
case "SwitchCase":
field = "consequent";
break;
case "Program":
case "BlockStatement":
field = "body";
break;
default:
return;
}
for (i = parent[field].length - 1; i >= 0; i--) {
sibling = parent[field][i];
if (sibling === node) {
return; // Found the last reachable statement, all done
}
if (!isUnreachableAllowed(sibling)) {
report(context, sibling, node.type);
}
}
}
return {
"ReturnStatement": checkNode,
"ThrowStatement": checkNode,
"ContinueStatement": checkNode,
"BreakStatement": checkNode
};
};
},{}],120:[function(require,module,exports){
/**
* @fileoverview Flag expressions in statement position that do not side effect
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
/**
* @param {ASTNode} node - any node
* @returns {Boolean} whether the given node structurally represents a directive
*/
function looksLikeDirective(node) {
return node.type === "ExpressionStatement" &&
node.expression.type === "Literal" && typeof node.expression.value === "string";
}
/**
* @param {Function} predicate - ([a] -> Boolean) the function used to make the determination
* @param {a[]} list - the input list
* @returns {a[]} the leading sequence of members in the given list that pass the given predicate
*/
function takeWhile(predicate, list) {
for (var i = 0, l = list.length; i < l; ++i) {
if (!predicate(list[i])) {
break;
}
}
return [].slice.call(list, 0, i);
}
/**
* @param {ASTNode} node - a Program or BlockStatement node
* @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
*/
function directives(node) {
return takeWhile(looksLikeDirective, node.body);
}
/**
* @param {ASTNode} node - any node
* @param {ASTNode[]} ancestors - the given node's ancestors
* @returns {Boolean} whether the given node is considered a directive in its current position
*/
function isDirective(node, ancestors) {
var parent = ancestors[ancestors.length - 1],
grandparent = ancestors[ancestors.length - 2];
return (parent.type === "Program" || parent.type === "BlockStatement" && (grandparent.type === "FunctionExpression" || grandparent.type === "FunctionDeclaration")) &&
directives(parent).indexOf(node) >= 0;
}
return {
"ExpressionStatement": function(node) {
var type = node.expression.type,
ancestors = context.getAncestors();
if (
!/^(?:Assignment|Call|New|Update)Expression$/.test(type) &&
(type !== "UnaryExpression" || ["delete", "void"].indexOf(node.expression.operator) < 0) &&
!isDirective(node, ancestors)
) {
context.report(node, "Expected an assignment or function call and instead saw an expression.");
}
}
};
};
},{}],121:[function(require,module,exports){
/**
* @fileoverview Rule to flag declared but unused variables
* @author Ilya Volodin
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var config = {
vars: "all",
args: "after-used"
};
if (context.options[0]) {
if (typeof(context.options[0]) === "string") {
config.vars = context.options[0];
} else {
config.vars = context.options[0].vars || config.vars;
config.args = context.options[0].args || config.args;
}
}
var MESSAGE = "{{name}} is defined but never used";
/**
* @param {Reference} ref - an escope Reference
* @returns {Boolean} whether the given reference represents a read operation
*/
function isReadRef(ref) {
return ref.isRead();
}
/**
* @param {Scope} scope - an escope Scope object
* @returns {Variable[]} most of the local variables with no read references
*/
function unusedLocals(scope) {
var unused = [];
var variables = scope.variables;
if (scope.type !== "global") {
for (var i = 0, l = variables.length; i < l; ++i) {
// skip function expression names
if (scope.functionExpressionScope) {
continue;
}
// skip implicit "arguments" variable
if (scope.type === "function" && variables[i].name === "arguments" && variables[i].identifiers.length === 0) {
continue;
}
var type = variables[i].defs[0].type;
// skip catch variables
if (type === "CatchClause") {
continue;
}
// if "args" option is "none", skip any parameter
if (config.args === "none" && type === "Parameter") {
continue;
}
// if "args" option is "after-used", skip all but the last parameter
if (config.args === "after-used" && type === "Parameter" && variables[i].defs[0].index < variables[i].defs[0].node.params.length - 1) {
continue;
}
if (variables[i].references.filter(isReadRef).length === 0) {
unused.push(variables[i]);
}
}
}
return [].concat.apply(unused, [].map.call(scope.childScopes, unusedLocals));
}
return {
"Program": function(programNode) {
var globalScope = context.getScope();
var unused = unusedLocals(globalScope);
var i, l;
// determine unused globals
if (config.vars === "all") {
var unresolvedRefs = globalScope.through.filter(isReadRef).map(function(ref) {
return ref.identifier.name;
});
for (i = 0, l = globalScope.variables.length; i < l; ++i) {
if (unresolvedRefs.indexOf(globalScope.variables[i].name) < 0) {
unused.push(globalScope.variables[i]);
}
}
}
for (i = 0, l = unused.length; i < l; ++i) {
if (unused[i].eslintExplicitGlobal) {
context.report(programNode, MESSAGE, unused[i]);
} else if (unused[i].defs.length > 0) {
context.report(unused[i].identifiers[0], MESSAGE, unused[i]);
}
}
}
};
};
},{}],122:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of variables before they are defined
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
var NO_FUNC = "nofunc";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
function findDeclaration(name, scope) {
// try searching in the current scope first
for (var i = 0, l = scope.variables.length; i < l; i++) {
if (scope.variables[i].name === name) {
return scope.variables[i];
}
}
// check if there's upper scope and call recursivly till we find the variable
if (scope.upper) {
return findDeclaration(name, scope.upper);
}
}
function findVariables() {
var scope = context.getScope();
var typeOption = context.options[0];
function checkLocationAndReport(reference, declaration) {
if (typeOption !== NO_FUNC || declaration.defs[0].type !== "FunctionName") {
if (declaration.identifiers[0].range[1] > reference.identifier.range[1]) {
context.report(reference.identifier, "{{a}} was used before it was defined", {a: reference.identifier.name});
}
}
}
scope.references.forEach(function(reference) {
// if the reference is resolved check for declaration location
// if not, it could be function invocation, try to find manually
if (reference.resolved && reference.resolved.identifiers.length > 0) {
checkLocationAndReport(reference, reference.resolved);
} else {
var declaration = findDeclaration(reference.identifier.name, scope);
// if there're no identifiers, this is a global environment variable
if (declaration && declaration.identifiers.length !== 0) {
checkLocationAndReport(reference, declaration);
}
}
});
}
return {
"Program": findVariables,
"FunctionExpression": findVariables,
"FunctionDeclaration": findVariables
};
};
},{}],123:[function(require,module,exports){
/**
* @fileoverview Rule to disallow use of void operator.
* @author Mike Sidorov
* @copyright 2014 Mike Sidorov. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"UnaryExpression": function(node) {
if (node.operator === "void") {
context.report(node, "Expected 'undefined' and instead saw 'void'.");
}
}
};
};
},{}],124:[function(require,module,exports){
/**
* @fileoverview Rule that warns about used warning comments
* @author Alexander Schmidt <https://github.com/lxanders>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function (context) {
"use strict";
var configuration = context.options[0] || {},
warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
location = configuration.location || "start";
/**
* Prepares a specified comment for being checked.
* @param {String} comment The comment to prepare.
* @returns {String} The specified comment prepared for being checked.
*/
function prepareCommentForChecking(comment) {
var commentToCheck;
commentToCheck = comment.toLowerCase();
commentToCheck = commentToCheck.trim();
return commentToCheck;
}
/**
* Checks if the specified comment starts with a specified term.
* @param {String} commentToCheck The comment to check.
* @param {String} lowerCaseTerm The term to search for.
* @returns {Boolean} True if the comment started with the specified term, else false.
*/
function commentStartsWithTerm(commentToCheck, lowerCaseTerm) {
return commentToCheck.indexOf(lowerCaseTerm) === 0;
}
/**
* Checks if the specified comment contains a specified term at any location.
* @param {String} commentToCheck The comment to check.
* @param {String} lowerCaseTerm The term to search for.
* @returns {Boolean} True if the term was contained in the comment, else false.
*/
function commentContainsTerm(commentToCheck, lowerCaseTerm) {
return commentToCheck.indexOf(lowerCaseTerm) !== -1;
}
/**
* Checks the specified comment for matches of the configured warning terms and returns the matches.
* @param {String} comment The comment which is checked.
* @returns {Array} All matched warning terms for this comment.
*/
function commentContainsWarningTerm(comment) {
var matches = [];
warningTerms.forEach(function (term) {
var lowerCaseTerm = term.toLowerCase(),
commentToCheck = prepareCommentForChecking(comment);
if (location === "start") {
if (commentStartsWithTerm(commentToCheck, lowerCaseTerm)) {
matches.push(term);
}
} else if (location === "anywhere") {
if (commentContainsTerm(commentToCheck, lowerCaseTerm)) {
matches.push(term);
}
}
});
return matches;
}
/**
* Checks the specified node for matching warning comments and reports them.
* @param {ASTNode} node The AST node being checked.
* @returns {void} undefined.
*/
function checkComment(node) {
var matches = commentContainsWarningTerm(node.value);
matches.forEach(function (matchedTerm) {
context.report(node, "Unexpected " + matchedTerm + " comment.");
});
}
return {
"BlockComment": checkComment,
"LineComment": checkComment
};
};
},{}],125:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of with statement
* @author Nicholas C. Zakas
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"WithStatement": function(node) {
context.report(node, "Unexpected use of 'with' statement.");
}
};
};
},{}],126:[function(require,module,exports){
/**
* @fileoverview Rule to flag wrapping none-iffe in parens
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"FunctionExpression": function(node) {
var ancestors = context.getAncestors(),
previousToken, nextToken;
if (!/CallExpression|NewExpression/.test(ancestors.pop().type)) {
previousToken = context.getTokenBefore(node);
nextToken = context.getTokenAfter(node);
if (previousToken.value === "(" && nextToken.value === ")") {
context.report(node, "Wrapping non-IIFE function literals in parens is unnecessary.");
}
}
}
};
};
},{}],127:[function(require,module,exports){
/**
* @fileoverview A rule to ensure the use of a single variable declaration.
* @author Ian Christian Myers
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
var functionStack = [];
function startFunction() {
functionStack.push(false);
}
function endFunction() {
functionStack.pop();
}
function checkDeclarations(node) {
if (functionStack[functionStack.length - 1]) {
context.report(node, "Combine this with the previous 'var' statement.");
} else {
functionStack[functionStack.length - 1] = true;
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"Program": startFunction,
"FunctionDeclaration": startFunction,
"FunctionExpression": startFunction,
"VariableDeclaration": checkDeclarations,
"Program:exit": endFunction,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction
};
};
},{}],128:[function(require,module,exports){
/**
* @fileoverview A rule to ensure blank lines within blocks.
* @author Mathias Schreck <https://github.com/lo1tuma>
* @copyright 2014 Mathias Schreck. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function (context) {
var requirePadding = context.options[0] !== "never";
/**
* Checks if the given non empty block node has a blank line before its first child node.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {boolean} Whether or not the block starts with a blank line.
*/
function isNonEmptyBlockTopPadded(node) {
var blockStart = node.loc.start.line,
first = node.body[0],
firstLine = first.loc.start.line,
expectedFirstLine = blockStart + 2,
leadingComments = context.getComments(first).leading;
if (leadingComments.length > 0) {
firstLine = leadingComments[0].loc.start.line;
}
return expectedFirstLine <= firstLine;
}
/**
* Checks if the given non empty block node has a blank line after its last child node.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {boolean} Whether or not the block ends with a blank line.
*/
function isNonEmptyBlockBottomPadded(node) {
var blockEnd = node.loc.end.line,
last = node.body[node.body.length - 1],
lastLine = last.loc.end.line,
expectedLastLine = blockEnd - 2,
trailingComments = context.getComments(last).trailing;
if (trailingComments.length > 0) {
lastLine = trailingComments[trailingComments.length - 1].loc.end.line;
}
return lastLine <= expectedLastLine;
}
/**
* Checks if the given non empty block node starts AND ends with a blank line.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {boolean} Whether or not the block starts and ends with a blank line.
*/
function isNonEmptyBlockPadded(node) {
return isNonEmptyBlockTopPadded(node) && isNonEmptyBlockBottomPadded(node);
}
/**
* Checks if the given non empty block node starts OR ends with a blank line.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {boolean} Whether or not the block starts and ends with a blank line.
*/
function hasNonEmptyBlockExtraPadding(node) {
return isNonEmptyBlockTopPadded(node) || isNonEmptyBlockBottomPadded(node);
}
/**
* Checks the given BlockStatement node to be padded if the block is not empty.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {void} undefined.
*/
function checkPadding(node) {
if (node.body.length > 0) {
if (requirePadding) {
if (!isNonEmptyBlockPadded(node)) {
context.report(node, "Block must be padded by blank lines.");
}
} else {
if (hasNonEmptyBlockExtraPadding(node)) {
context.report(node, "Block must not be padded by blank lines.");
}
}
}
}
return {
"BlockStatement": checkPadding
};
};
},{}],129:[function(require,module,exports){
/**
* @fileoverview Rule to flag non-quoted property names in object literals.
* @author Mathias Bynens <http://mathiasbynens.be/>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
var esprima = require("esprima");
module.exports = function(context) {
"use strict";
var MODE = context.options[0];
switch (MODE) {
case "as-needed":
return {
Property: function(node) {
var key = node.key;
// Ensure that any quoted property names required quoting
if (key.type === "Literal" && typeof key.value === "string") {
try {
var tokens = esprima.tokenize(key.value);
if (tokens.length !== 1) {
return;
}
var t = tokens[0];
} catch(e) {
return;
}
if (t.type === "Identifier" || t.type === "Null" || t.type === "Boolean" || t.type === "Numeric" && "" + +t.value === t.value) {
context.report(node, "Unnecessarily quoted property `{{name}}` found.", key);
}
}
}
};
default:
return {
Property: function(node) {
var key = node.key;
// Ensure all property names are quoted
if (key.type !== "Literal" || typeof key.value !== "string") {
context.report(node, "Unquoted property `{{name}}` found.", key);
}
}
};
}
};
},{"esprima":6}],130:[function(require,module,exports){
/**
* @fileoverview A rule to choose between single and double quote marks
* @author Matt DuVall <http://www.mattduvall.com/>, Brandon Payton
*/
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
var QUOTE_SETTINGS = {
"double": {
quote: "\"",
alternateQuote: "'",
description: "doublequote"
},
"single": {
quote: "'",
alternateQuote: "\"",
description: "singlequote"
}
};
var AVOID_ESCAPE = "avoid-escape";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
/**
* Validate that a string passed in is surrounded by the specified character
* @param {string} val The text to check.
* @param {string} character The character to see if it's surrounded by.
* @returns {boolean} True if the text is surrounded by the character, false if not.
*/
function isSurroundedBy(val, character) {
return val[0] === character && val[val.length - 1] === character;
}
return {
"Literal": function(node) {
var val = node.value,
rawVal = node.raw,
quoteOption = context.options[0],
settings = QUOTE_SETTINGS[quoteOption],
avoidEscape = context.options[1] === AVOID_ESCAPE,
isValid;
if (settings && typeof val === "string") {
isValid = isSurroundedBy(rawVal, settings.quote);
if (!isValid && avoidEscape) {
isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
}
if (!isValid) {
context.report(node, "Strings must use " + settings.description + ".");
}
}
}
};
};
},{}],131:[function(require,module,exports){
/**
* @fileoverview Rule to flag use of parseInt without a radix argument
* @author James Allardice
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"CallExpression": function(node) {
var radix;
if (node.callee.name === "parseInt") {
if (node.arguments.length < 2) {
context.report(node, "Missing radix parameter.");
} else {
radix = node.arguments[1];
// don't allow non-numeric literals or undefined
if ((radix.type === "Literal" && typeof radix.value !== "number") ||
(radix.type === "Identifier" && radix.name === "undefined")
) {
context.report(node, "Invalid radix parameter.");
}
}
}
}
};
};
},{}],132:[function(require,module,exports){
/**
* @fileoverview Rule to flag missing semicolons.
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var OPT_OUT_PATTERN = /[\[\(\/\+\-]/;
var always = context.options[0] !== "never";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Checks a node to see if it's followed by a semicolon.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function checkForSemicolon(node) {
var lastToken = context.getLastToken(node),
nextToken = context.getTokenAfter(node);
if (always) {
if (lastToken.type !== "Punctuator" || lastToken.value !== ";") {
context.report(node, lastToken.loc.end, "Missing semicolon.");
}
} else {
if (lastToken.type === "Punctuator" && lastToken.value === ";") {
if (!nextToken || !(OPT_OUT_PATTERN.test(nextToken.value))) {
context.report(node, node.loc.end, "Extra semicolon.");
}
}
}
}
/**
* Checks to see if there's a semicolon after a variable declaration.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function checkForSemicolonForVariableDeclaration(node) {
var ancestors = context.getAncestors(),
parentIndex = ancestors.length - 1,
parent = ancestors[parentIndex];
if ((parent.type !== "ForStatement" || parent.init !== node) &&
(parent.type !== "ForInStatement" || parent.left !== node)
) {
checkForSemicolon(node);
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"VariableDeclaration": checkForSemicolonForVariableDeclaration,
"ExpressionStatement": checkForSemicolon,
"ReturnStatement": checkForSemicolon,
"DebuggerStatement": checkForSemicolon,
"BreakStatement": checkForSemicolon,
"ContinueStatement": checkForSemicolon,
"EmptyStatement": function (node) {
var nextToken;
if (!always) {
nextToken = context.getTokenAfter(node) || context.getLastToken(node);
if (!(OPT_OUT_PATTERN.test(nextToken.value))) {
context.report(node, "Extra semicolon.");
}
}
}
};
};
},{}],133:[function(require,module,exports){
/**
* @fileoverview Rule to require sorting of variables within a single Variable Declaration block
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var configuration = context.options[0] || {},
ignoreCase = configuration.ignoreCase || false;
return {
"VariableDeclaration": function(node) {
node.declarations.reduce(function(memo, decl) {
var lastVariableName = memo.id.name,
currenVariableName = decl.id.name;
if (ignoreCase) {
lastVariableName = lastVariableName.toLowerCase();
currenVariableName = currenVariableName.toLowerCase();
}
if (currenVariableName < lastVariableName) {
context.report(decl, "Variables within the same declaration block should be sorted alphabetically");
return memo;
} else {
return decl;
}
}, node.declarations[0]);
}
};
};
},{}],134:[function(require,module,exports){
/**
* @fileoverview Rule to enforce the number of spaces after certain keywords
* @author Nick Fisher
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
// unless the first option is `"never"`, then a space is required
var requiresSpace = context.options[0] !== "never",
config = context.options[1] || { checkFunctionKeyword: false };
/**
* Check if the separation of two adjacent tokens meets the spacing rules, and report a problem if not.
*
* @param {ASTNode} node The node to which the potential problem belongs.
* @param {Token} left The first token.
* @param {Token} right The second token
* @returns {void}
*/
function checkTokens(node, left, right) {
var hasSpace = left.range[1] < right.range[0],
value = left.value;
if (hasSpace !== requiresSpace) {
context.report(node, "Keyword \"{{value}}\" must {{not}}be followed by whitespace.", {
value: value,
not: requiresSpace ? "" : "not "
});
}
}
/**
* Check if the given node (`if`, `for`, `while`, etc), has the correct spacing after it.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function check(node) {
var tokens = context.getFirstTokens(node, 2);
checkTokens(node, tokens[0], tokens[1]);
}
return {
"IfStatement": function (node) {
check(node);
// check the `else`
if (node.alternate && node.alternate.type !== "IfStatement") {
checkTokens(node.alternate, context.getTokenBefore(node.alternate), context.getFirstToken(node.alternate));
}
},
"ForStatement": check,
"ForOfStatement": check,
"ForInStatement": check,
"WhileStatement": check,
"DoWhileStatement": function (node) {
check(node);
// check the `while`
var whileTokens = context.getTokensBefore(node.test, 2);
checkTokens(node, whileTokens[0], whileTokens[1]);
},
"SwitchStatement": check,
"TryStatement": function (node) {
check(node);
// check the `finally`
if (node.finalizer) {
checkTokens(node.finalizer, context.getTokenBefore(node.finalizer), context.getFirstToken(node.finalizer));
}
},
"CatchStatement": check,
"WithStatement": check,
"FunctionExpression": function (node) {
if (config.checkFunctionKeyword) {
check(node);
}
}
};
};
},{}],135:[function(require,module,exports){
/**
* @fileoverview A rule to ensure whitespace before blocks.
* @author Mathias Schreck <https://github.com/lo1tuma>
* @copyright 2014 Mathias Schreck. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function (context) {
var requireSpace = context.options[0] !== "never";
/**
* Determines whether two adjacent tokens are have whitespace between them.
* @param {Object} left - The left token object.
* @param {Object} right - The right token object.
* @returns {boolean} Whether or not there is space between the tokens.
*/
function isSpaced(left, right) {
return left.range[1] < right.range[0];
}
/**
* Determines whether two adjacent tokens are on the same line.
* @param {Object} left - The left token object.
* @param {Object} right - The right token object.
* @returns {boolean} Whether or not the tokens are on the same line.
*/
function isSameLine(left, right) {
return left.loc.start.line === right.loc.start.line;
}
/**
* Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {void} undefined.
*/
function checkPrecedingSpace(node) {
var precedingToken = context.getTokenBefore(node),
hasSpace;
if (precedingToken && isSameLine(precedingToken, node)) {
hasSpace = isSpaced(precedingToken, node);
if (requireSpace) {
if (!hasSpace) {
context.report(node, "Missing space before opening brace.");
}
} else {
if (hasSpace) {
context.report(node, "Unexpected space before opening brace.");
}
}
}
}
return {
"BlockStatement": checkPrecedingSpace
};
};
},{}],136:[function(require,module,exports){
/**
* @fileoverview Disallows or enforces spaces inside of brackets.
* @author Ian Christian Myers
* @copyright 2014 Brandyn Bennett. All rights reserved.
* @copyright 2014 Michael Ficarra. No rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var options = {
spaced: context.options[0] === "always",
singleElementException: context.options[1] != null && !!context.options[1].singleValue,
objectsInArraysException: context.options[1] != null && !!context.options[1].objectsInArrays,
arraysInArraysException: context.options[1] != null && !!context.options[1].arraysInArrays,
propertyName: context.options[1] == null || context.options[1].propertyName == null || !!context.options[1].propertyName
};
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines whether two adjacent tokens are have whitespace between them.
* @param {Object} left - The left token object.
* @param {Object} right - The right token object.
* @returns {boolean} Whether or not there is space between the tokens.
*/
function isSpaced(left, right) {
return left.range[1] < right.range[0];
}
/**
* Determines whether two adjacent tokens are on the same line.
* @param {Object} left - The left token object.
* @param {Object} right - The right token object.
* @returns {boolean} Whether or not the tokens are on the same line.
*/
function isSameLine(left, right) {
return left.loc.start.line === right.loc.start.line;
}
/**
* Reports that there shouldn't be a space after the first token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Object[]} tokens - The tokens to be checked for spacing.
* @returns {void}
*/
function reportNoBeginningSpace(node, tokens) {
context.report(node, tokens[0].loc.start,
"There should be no space after '" + tokens[0].value + "'");
}
/**
* Reports that there shouldn't be a space before the last token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Object[]} tokens - The tokens to be checked for spacing.
* @returns {void}
*/
function reportNoEndingSpace(node, tokens) {
context.report(node, tokens[tokens.length - 1].loc.start,
"There should be no space before '" + tokens[tokens.length - 1].value + "'");
}
/**
* Reports that there should be a space after the first token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Object[]} tokens - The tokens to be checked for spacing.
* @returns {void}
*/
function reportRequiredBeginningSpace(node, tokens) {
context.report(node, tokens[0].loc.start,
"A space is required after '" + tokens[0].value + "'");
}
/**
* Reports that there should be a space before the last token
* @param {ASTNode} node - The node to report in the event of an error.
* @param {Object[]} tokens - The tokens to be checked for spacing.
* @returns {void}
*/
function reportRequiredEndingSpace(node, tokens) {
context.report(node, tokens[tokens.length - 1].loc.start,
"A space is required before '" + tokens[tokens.length - 1].value + "'");
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
MemberExpression: options.propertyName ? function checkMember(node) {
if (node.computed) {
var tokens = context.getTokens(node.property, 1, 1);
var tokenA = tokens[0], tokenB = tokens[1],
tokenC = tokens[tokens.length - 2], tokenD = tokens[tokens.length - 1];
if (isSameLine(tokenA, tokenB) || isSameLine(tokenC, tokenD)) {
if (options.spaced) {
if (!isSpaced(tokenA, tokenB) && isSameLine(tokenA, tokenB)) {
reportRequiredBeginningSpace(node, tokens);
}
if (!isSpaced(tokenC, tokenD) && isSameLine(tokenC, tokenD)) {
reportRequiredEndingSpace(node, tokens);
}
} else {
if (isSpaced(tokenA, tokenB)) {
reportNoBeginningSpace(node, tokens);
}
if (isSpaced(tokenC, tokenD)) {
reportNoEndingSpace(node, tokens);
}
}
}
}
} : function() {},
ArrayExpression: function(node) {
if (node.elements.length === 0) {
return;
}
var tokens = context.getTokens(node);
var tokenA = tokens[0], tokenB = tokens[1],
tokenC = tokens[tokens.length - 2], tokenD = tokens[tokens.length - 1];
var openingBracketMustBeSpaced =
options.objectsInArraysException && tokenB.value === "{" ||
options.arraysInArraysException && tokenB.value === "[" ||
options.singleElementException && node.elements.length === 1
? !options.spaced : options.spaced;
var closingBracketMustBeSpaced =
options.objectsInArraysException && tokenC.value === "}" ||
options.arraysInArraysException && tokenC.value === "]" ||
options.singleElementException && node.elements.length === 1
? !options.spaced : options.spaced;
if (isSameLine(tokenA, tokenB) || isSameLine(tokenC, tokenD)) {
if (openingBracketMustBeSpaced && !isSpaced(tokenA, tokenB)) {
reportRequiredBeginningSpace(node, tokens);
} else if (!openingBracketMustBeSpaced && isSpaced(tokenA, tokenB)) {
reportNoBeginningSpace(node, tokens);
}
if (closingBracketMustBeSpaced && !isSpaced(tokenC, tokenD)) {
reportRequiredEndingSpace(node, tokens);
} else if (!closingBracketMustBeSpaced && isSpaced(tokenC, tokenD)) {
reportNoEndingSpace(node, tokens);
}
}
},
ObjectExpression: function(node) {
if (node.properties.length === 0) {
return;
}
var tokens = context.getTokens(node);
var tokenA = tokens[0], tokenB = tokens[1],
tokenC = tokens[tokens.length - 2], tokenD = tokens[tokens.length - 1];
if (isSameLine(tokenA, tokenB) || isSameLine(tokenC, tokenD)) {
if (options.spaced) {
if (!isSpaced(tokenA, tokenB)) {
reportRequiredBeginningSpace(node, tokens);
}
if (!isSpaced(tokenC, tokenD)) {
reportRequiredEndingSpace(node, tokens);
}
} else {
if (isSpaced(tokenA, tokenB)) {
reportNoBeginningSpace(node, tokens);
}
if (isSpaced(tokenC, tokenD)) {
reportNoEndingSpace(node, tokens);
}
}
}
}
};
};
},{}],137:[function(require,module,exports){
/**
* @fileoverview Disallows or enforces spaces inside of parentheses.
* @author Jonathan Rajavuori
* @copyright 2014 Jonathan Rajavuori. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var RE, MESSAGE;
if (context.options[0] === "always") {
RE = /\([^ \)]|[^ \(]\)/mg;
MESSAGE = "There must be a space inside this paren.";
} else {
RE = /\( | \)/mg;
MESSAGE = "There should be no spaces inside this paren.";
}
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
var skipRanges = [];
/**
* Adds the range of a node to the set to be skipped when checking parens
* @param {ASTNode} node The node to skip
* @returns {void}
* @private
*/
function addSkipRange(node) {
skipRanges.push(node.range);
}
/**
* Sorts the skipRanges array. Must be called before shouldSkip
* @returns {void}
* @private
*/
function sortSkipRanges() {
skipRanges.sort(function (a, b) {
return a[0] - b[0];
});
}
/**
* Checks if a certain position in the source should be skipped
* @param {Number} pos The 0-based index in the source
* @returns {boolean} whether the position should be skipped
* @private
*/
function shouldSkip(pos) {
var i, len, range;
for (i = 0, len = skipRanges.length; i < len; i += 1) {
range = skipRanges[i];
if (pos < range[0]) {
break;
} else if (pos < range[1]) {
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"Program:exit": function checkParenSpaces(node) {
var match,
nextLine,
column,
line = 1,
source = context.getSource(),
pos = 0;
sortSkipRanges();
while ((match = RE.exec(source)) !== null) {
if (source.charAt(match.index) !== "(") {
// Matched a closing paren pattern
match.index += 1;
}
if (!shouldSkip(match.index)) {
while ((nextLine = source.indexOf("\n", pos)) !== -1 && nextLine < match.index) {
pos = nextLine + 1;
line += 1;
}
column = match.index - pos;
context.report(node, { line: line, column: column }, MESSAGE);
}
}
},
// These nodes can contain parentheses that this rule doesn't care about
LineComment: addSkipRange,
BlockComment: addSkipRange,
Literal: addSkipRange
};
};
},{}],138:[function(require,module,exports){
/**
* @fileoverview Require spaces around infix operators
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var OPERATORS = [
"*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in",
"instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=",
"+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
"?", ":", ","
];
function isSpaced(left, right) {
var op, tokens = context.getTokens({range: [left.range[1], right.range[0]]}, 1, 1);
for (var i = 1, l = tokens.length - 1; i < l; ++i) {
op = tokens[i];
if (
op.type === "Punctuator" &&
OPERATORS.indexOf(op.value) >= 0 &&
(tokens[i - 1].range[1] >= op.range[0] || op.range[1] >= tokens[i + 1].range[0])
) {
return false;
}
}
return true;
}
function isRightSpaced(left, right) {
var op, tokens = context.getTokens({range: [left.range[1], right.range[0]]}, 1, 1);
for (var i = 1, l = tokens.length - 1; i < l; ++i) {
op = tokens[i];
if (
op.type === "Punctuator" &&
OPERATORS.indexOf(op.value) >= 0 &&
op.range[1] >= tokens[i + 1].range[0]
) {
return false;
}
}
return true;
}
function report(node) {
context.report(node, "Infix operators must be spaced.");
}
function checkBinary(node) {
if (!isSpaced(node.left, node.right)) {
report(node);
}
}
function checkSequence(node) {
for (var i = 0, l = node.expressions.length - 1; i < l; ++i) {
if (!isRightSpaced(node.expressions[i], node.expressions[i + 1])) {
report(node);
break;
}
}
}
function checkConditional(node) {
if (!isSpaced(node.test, node.consequent) || !isSpaced(node.consequent, node.alternate)) {
report(node);
}
}
function checkVar(node) {
if (node.init && !isSpaced(node.id, node.init)) {
report(node);
}
}
return {
"AssignmentExpression": checkBinary,
"BinaryExpression": checkBinary,
"LogicalExpression": checkBinary,
"ConditionalExpression": checkConditional,
"SequenceExpression": checkSequence,
"VariableDeclarator": checkVar
};
};
},{}],139:[function(require,module,exports){
/**
* @fileoverview Require spaces following return, throw, and case
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
function check(node) {
var tokens = context.getFirstTokens(node, 2),
value = tokens[0].value;
if (tokens[0].range[1] >= tokens[1].range[0]) {
context.report(node, "Keyword \"" + value + "\" must be followed by whitespace.");
}
}
return {
"ReturnStatement": function(node) {
if (node.argument) {
check(node);
}
},
"SwitchCase": function(node) {
if (node.test) {
check(node);
}
},
"ThrowStatement": check
};
};
},{}],140:[function(require,module,exports){
/**
* @fileoverview Require spaces following unary word operators
* @author Michael Ficarra
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
function check(node) {
var tokens;
tokens = context.getFirstTokens(node, 2);
if (tokens[0].range[1] >= tokens[1].range[0]) {
switch (tokens[0].value) {
case "delete":
case "new":
case "typeof":
case "void":
context.report(node, "Unary word operator \"" + tokens[0].value + "\" must be followed by whitespace.");
break;
// no default
}
}
}
return {
"NewExpression": check,
"UnaryExpression": check
};
};
},{}],141:[function(require,module,exports){
/**
* @fileoverview Enforces or disallows a space beginning a single-line comment.
* @author Greg Cochard
* @copyright 2014 Greg Cochard. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
// Unless the first option is never, require a space
var requireSpace = context.options[0] !== "never";
// Default to match anything, so all will fail if there are no exceptions
var exceptionMatcher = new RegExp(" ");
// Grab the exceptions array and build a RegExp matcher for it
var hasExceptions = context.options.length === 2;
var unescapedExceptions = hasExceptions ? context.options[1].exceptions : [];
var exceptions;
if (unescapedExceptions.length) {
exceptions = unescapedExceptions.map(function(s) {
return s.replace(/([.*+?${}()|\^\[\]\/\\])/g, "\\$1");
});
exceptionMatcher = new RegExp("(^(" + exceptions.join(")+$)|(^(") + ")+$)");
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"LineComment": function checkCommentForSpace(node) {
if (node.loc.start.line === 1 && /^#!/.test(context.getSourceLines()[0])) {
/*
* HACK: return if we are on the first line and
* encounter a shebang at the beginning.
* It seems the parser will return this as a
* comment and filter out the # so we must fetch
* the actual source line. Is this being caused
* by esprima or eslint? Something else?
*/
return;
}
if (requireSpace) {
// Space expected and not found
if (node.value.indexOf(" ") !== 0) {
/*
* Do two tests; one for space starting the line,
* and one for a comment comprised only of exceptions
*/
if (hasExceptions && !exceptionMatcher.test(node.value)) {
context.report(node, "Expected exception block or space after // in comment.");
} else if (!hasExceptions) {
context.report(node, "Expected space after // in comment.");
}
}
} else {
if (node.value.indexOf(" ") === 0) {
context.report(node, "Unexpected space after // in comment.");
}
}
}
};
};
},{}],142:[function(require,module,exports){
/**
* @fileoverview Rule to ensure code is running in strict mode.
* @author Nicholas C. Zakas
* @copyright 2013-2014 Nicholas C. Zakas. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var scopes = [];
/**
* Determines if a given node is "use strict".
* @param {ASTNode} node The node to check.
* @returns {boolean} True if the node is a strict pragma, false if not.
* @void
*/
function isStrictPragma(node) {
return (node && node.type === "ExpressionStatement" &&
node.expression.value === "use strict");
}
/**
* When you enter a scope, push the strict value from the previous scope
* onto the stack.
* @param {ASTNode} node The AST node being checked.
* @returns {void}
* @private
*/
function enterScope(node) {
var isStrict = false,
isProgram = (node.type === "Program"),
isParentGlobal = scopes.length === 1,
isParentStrict = scopes.length ? scopes[scopes.length - 1] : false;
// look for the "use strict" pragma
if (isProgram) {
isStrict = isStrictPragma(node.body[0]) || isParentStrict;
} else {
isStrict = isStrictPragma(node.body.body[0]) || isParentStrict;
}
scopes.push(isStrict);
// never warn if the parent is strict or the function is strict
if (!isParentStrict && !isStrict && isParentGlobal) {
context.report(node, "Missing \"use strict\" statement.");
}
}
/**
* When you exit a scope, pop off the top scope and see if it's true or
* false.
* @returns {void}
* @private
*/
function exitScope() {
scopes.pop();
}
return {
"Program": enterScope,
"FunctionDeclaration": enterScope,
"FunctionExpression": enterScope,
"Program:exit": exitScope,
"FunctionDeclaration:exit": exitScope,
"FunctionExpression:exit": exitScope
};
};
},{}],143:[function(require,module,exports){
/**
* @fileoverview Rule to flag comparisons to the value NaN
* @author James Allardice
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"BinaryExpression": function(node) {
if (!/^[|&^]$/.test(node.operator) && node.left.name === "NaN" || node.right.name === "NaN") {
context.report(node, "Use the isNaN function to compare with NaN.");
}
}
};
};
},{}],144:[function(require,module,exports){
/**
* @fileoverview Validates JSDoc comments are syntactically correct
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var doctrine = require("doctrine");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var options = context.options[0] || {},
prefer = options.prefer || {},
// these both default to true, so you have to explicitly make them false
requireReturn = options.requireReturn === false ? false : true,
requireParamDescription = options.requireParamDescription === false ? false : true;
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
// Using a stack to store if a function returns or not (handling nested functions)
var fns = [];
/**
* When parsing a new function, store it in our function stack.
* @returns {void}
* @private
*/
function startFunction() {
fns.push({returnPresent: false});
}
/**
* Indicate that return has been found in the current function.
* @param {ASTNode} node The return node.
* @returns {void}
* @private
*/
function addReturn(node) {
var functionState = fns[fns.length - 1];
if (functionState && node.argument !== null) {
functionState.returnPresent = true;
}
}
/**
* Validate the JSDoc node and output warnings if anything is wrong.
* @param {ASTNode} node The AST node to check.
* @returns {void}
* @private
*/
function checkJSDoc(node) {
var jsdocNode = context.getJSDocComment(node),
functionData = fns.pop(),
hasReturns = false,
hasConstructor = false,
params = Object.create(null),
jsdoc;
// make sure only to validate JSDoc comments
if (jsdocNode) {
try {
jsdoc = doctrine.parse(jsdocNode.value, {
strict: true,
unwrap: true,
sloppy: true
});
} catch (ex) {
if (/braces/i.test(ex.message)) {
context.report(jsdocNode, "JSDoc type missing brace.");
} else {
context.report(jsdocNode, "JSDoc syntax error.");
}
return;
}
jsdoc.tags.forEach(function(tag) {
switch (tag.title) {
case "param":
if (!tag.type) {
context.report(jsdocNode, "Missing JSDoc parameter type for '{{name}}'.", { name: tag.name });
}
if (!tag.description && requireParamDescription) {
context.report(jsdocNode, "Missing JSDoc parameter description for '{{name}}'.", { name: tag.name });
}
if (params[tag.name]) {
context.report(jsdocNode, "Duplicate JSDoc parameter '{{name}}'.", { name: tag.name });
} else if (tag.name.indexOf(".") === - 1) {
params[tag.name] = 1;
}
break;
case "return":
case "returns":
hasReturns = true;
if (!requireReturn && !functionData.returnPresent && tag.type.name !== "void" && tag.type.name !== "undefined") {
context.report(jsdocNode, "Unexpected @" + tag.title + " tag; function has no return statement.");
} else {
if (!tag.type) {
context.report(jsdocNode, "Missing JSDoc return type.");
}
if (tag.type.name !== "void" && !tag.description) {
context.report(jsdocNode, "Missing JSDoc return description.");
}
}
break;
case "constructor":
hasConstructor = true;
break;
// no default
}
// check tag preferences
if (prefer.hasOwnProperty(tag.title)) {
context.report(jsdocNode, "Use @{{name}} instead.", { name: prefer[tag.title] });
}
});
// check for functions missing @returns
if (!hasReturns && !hasConstructor) {
if (requireReturn || functionData.returnPresent) {
context.report(jsdocNode, "Missing JSDoc @returns for function.");
}
}
// check the parameters
var jsdocParams = Object.keys(params);
node.params.forEach(function(param, i) {
var name = param.name;
if (jsdocParams[i] && (name !== jsdocParams[i])) {
context.report(jsdocNode, "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.", {
name: name,
jsdocName: jsdocParams[i]
});
} else if (!params[name]) {
context.report(jsdocNode, "Missing JSDoc for parameter '{{name}}'.", {
name: name
});
}
});
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"FunctionExpression": startFunction,
"FunctionDeclaration": startFunction,
"FunctionExpression:exit": checkJSDoc,
"FunctionDeclaration:exit": checkJSDoc,
"ReturnStatement": addReturn
};
};
},{"doctrine":4}],145:[function(require,module,exports){
/**
* @fileoverview Ensures that the results of typeof are compared against a valid string
* @author Ian Christian Myers
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function"],
OPERATORS = ["==", "===", "!=", "!=="];
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"UnaryExpression": function (node) {
var parent, sibling;
if (node.operator === "typeof") {
parent = context.getAncestors().pop();
if (parent.type === "BinaryExpression" && OPERATORS.indexOf(parent.operator) !== -1) {
sibling = parent.left === node ? parent.right : parent.left;
if (sibling.type === "Literal" && VALID_TYPES.indexOf(sibling.value) === -1) {
context.report(sibling, "Invalid typeof comparison value");
}
}
}
}
};
};
},{}],146:[function(require,module,exports){
/**
* @fileoverview Rule to enforce var declarations are only at the top of a function.
* @author Danny Fritz
* @author Gyandeep Singh
* @copyright 2014 Danny Fritz. All rights reserved.
* @copyright 2014 Gyandeep Singh. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function (context) {
var errorMessage = "All \"var\" declarations must be at the top of the function scope.";
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* @param {ASTNode} node - any node
* @returns {Boolean} whether the given node structurally represents a directive
*/
function looksLikeDirective(node) {
return node.type === "ExpressionStatement" &&
node.expression.type === "Literal" && typeof node.expression.value === "string";
}
/**
* Checks whether this variable is on top of the block body
* @param {ASTNode} node - The node to check
* @param {ASTNode[]} statements - collection of ASTNodes for the parent node block
* @returns {Boolean} True if var is on top otherwise false
*/
function isVarOnTop (node, statements) {
var i = 0, l = statements.length;
// skip over directives
for (; i < l; ++i) {
if (!looksLikeDirective(statements[i])) {
break;
}
}
for (; i < l; ++i) {
if (statements[i].type !== "VariableDeclaration") {
return false;
}
if (statements[i] === node) {
return true;
}
}
}
/**
* Checks whether variable is on top at the global level
* @param {ASTNode} node - The node to check
* @param {ASTNode} parent - Parent of the node
* @returns {void}
*/
function globalVarCheck (node, parent) {
if (!isVarOnTop(node, parent.body)) {
context.report(node, errorMessage);
}
}
/**
* Checks whether variable is on top at functional block scope level
* @param {ASTNode} node - The node to check
* @param {ASTNode} parent - Parent of the node
* @param {ASTNode} grandParent - Parent of the node's parent
* @returns {void}
*/
function blockScopeVarCheck (node, parent, grandParent) {
if (!((grandParent.type === "FunctionDeclaration"
|| grandParent.type === "FunctionExpression")
&& parent.type === "BlockStatement"
&& isVarOnTop(node, parent.body))) {
context.report(node, errorMessage);
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
"VariableDeclaration": function (node) {
var ancestors = context.getAncestors();
var parent = ancestors.pop();
var grandParent = ancestors.pop();
if (node.kind === "var") {// check variable is `var` type and not `let` or `const`
if (parent.type === "Program") {// That means its a global variable
globalVarCheck(node, parent);
} else {
blockScopeVarCheck(node, parent, grandParent);
}
}
}
};
};
},{}],147:[function(require,module,exports){
/**
* @fileoverview Rule to flag when IIFE is not wrapped in parens
* @author Ilya Volodin
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
var style = context.options[0] || "outside";
function wrapped(node) {
var previousToken = context.getTokenBefore(node),
nextToken = context.getTokenAfter(node);
return previousToken && previousToken.value === "(" &&
nextToken && nextToken.value === ")";
}
return {
"CallExpression": function(node) {
if (node.callee.type === "FunctionExpression") {
var callExpressionWrapped = wrapped(node),
functionExpressionWrapped = wrapped(node.callee);
if (!callExpressionWrapped && !functionExpressionWrapped) {
context.report(node, "Wrap an immediate function invocation in parentheses.");
} else if (style === "inside" && !functionExpressionWrapped) {
context.report(node, "Wrap only the function expression in parens.");
} else if (style === "outside" && !callExpressionWrapped) {
context.report(node, "Move the invocation into the parens that contain the function.");
}
}
}
};
};
},{}],148:[function(require,module,exports){
/**
* @fileoverview Rule to flag when regex literals are not wrapped in parens
* @author Matt DuVall <http://www.mattduvall.com>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
"use strict";
return {
"Literal": function(node) {
var token = context.getFirstToken(node),
nodeType = token.type,
source,
grandparent,
ancestors;
if (nodeType === "RegularExpression") {
source = context.getTokenBefore(node);
ancestors = context.getAncestors();
grandparent = ancestors[ancestors.length - 1];
if (grandparent.type === "MemberExpression" && grandparent.object === node &&
(!source || source.value !== "(")) {
context.report(node, "Wrap the regexp literal in parens to disambiguate the slash.");
}
}
}
};
};
},{}],149:[function(require,module,exports){
/**
* @fileoverview Rule to require or disallow yoda comparisons
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function (context) {
// Default to "never" (!always) if no option
var always = (context.options[0] === "always");
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines whether an operator is a comparison operator.
* @param {String} operator The operator to check.
* @returns {Boolean} Whether or not it is a comparison operator.
*/
function isComparisonOperator(operator) {
return (/^(==|===|!=|!==|<|>|<=|>=)$/).test(operator);
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"BinaryExpression": function (node) {
if (always) {
// Comparisons must always be yoda-style: if ("blue" === color)
if (node.right.type === "Literal" && isComparisonOperator(node.operator)) {
context.report(node, "Expected literal to be on the left side of " + node.operator + ".");
}
} else {
// Comparisons must never be yoda-style (default)
if (node.left.type === "Literal" && isComparisonOperator(node.operator)) {
context.report(node, "Expected literal to be on the right side of " + node.operator + ".");
}
}
}
};
};
},{}],150:[function(require,module,exports){
/**
* @fileoverview Common utilities.
*/
"use strict";
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/**
* Merges two objects together and assigns the result to the initial object. Can be used for shallow cloning.
* @param {Object} target of the cloning operation
* @param {Object} source object
* @returns {void}
*/
exports.mixin = function(target, source) {
Object.keys(source).forEach(function(key) {
target[key] = source[key];
});
};
/**
* Merges two config objects. This will not only add missing keys, but will also modify values to match.
* @param {Object} base config object
* @param {Object} custom config object. Overrides in this config object will take priority over base.
* @returns {Object} merged config object.
*/
exports.mergeConfigs = function mergeConfigs(base, custom) {
Object.keys(custom).forEach(function (key) {
var property = custom[key];
if (key === "plugins") {
if (!base[key]) {
base[key] = [];
}
property.forEach(function (plugin) {
// skip duplicates
if (base[key].indexOf(plugin) === -1) {
base[key].push(plugin);
}
});
return;
}
if (Array.isArray(base[key]) && !Array.isArray(property) && typeof property === "number") {
// assume that we are just overriding first attribute
base[key][0] = custom[key];
return;
}
if (typeof property === "object" && !Array.isArray(property)) {
// base[key] might not exist, so be careful with recursion here
base[key] = mergeConfigs(base[key] || {}, custom[key]);
} else {
base[key] = custom[key];
}
});
return base;
};
},{}]},{},[9])(9)
});