| !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) |
| }); |