1
0
Fork 0

Bump jshint to 2.9.2

This commit is contained in:
Alan Plum 2016-05-02 14:11:47 +02:00
parent f81c946fd7
commit 2bd0f59d9d
No known key found for this signature in database
GPG Key ID: 8ED72A9A323B6EFD
58 changed files with 12323 additions and 4857 deletions

View File

@ -5,7 +5,6 @@
"esnext": true,
"forin": true,
"freeze": true,
"globalstrict": true,
"immed": true,
"indent": 2,
"laxbreak": true,

View File

@ -37,9 +37,20 @@
////////////////////////////////////////////////////////////////////////////////
Object.defineProperty(Object.prototype, '_shallowCopy', {
set(value) {
if (this === Object.prototype) {
throw new TypeError(`Can not override Object.prototype._shallowCopy!`);
}
Object.defineProperty(this, '_shallowCopy', {
configurable: true,
enumerable: true,
writable: true,
value
});
},
get() {
var self = this;
return this.propertyKeys.reduce(function (previous, key) {
return Object.prototype.propertyKeys.reduce(function (previous, key) {
previous[key] = self[key];
return previous;
}, {});
@ -55,7 +66,18 @@ Object.defineProperty(Object.prototype, 'propertyKeys', {
return Object.keys(this).filter(function (key) {
return (key.charAt(0) !== '_' && key.charAt(0) !== '$');
});
}
},
set(value) {
if (this === Object.prototype) {
throw new TypeError(`Can not override Object.prototype.propertyKeys!`);
}
Object.defineProperty(this, 'propertyKeys', {
configurable: true,
enumerable: true,
writable: true,
value
});
},
});

1090
js/node/node_modules/jshint/CHANGELOG.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
# JSHint, A Static Code Analysis Tool for JavaScript
\[ [Use it online](http://jshint.com/) • [About](http://jshint.com/about/) •
\[ [Use it online](http://jshint.com/) •
[Docs](http://jshint.com/docs/) • [FAQ](http://jshint.com/docs/faq) •
[Install](http://jshint.com/install/) • [Hack](http://jshint.com/hack/) •
[Install](http://jshint.com/install/) •
[Contribute](http://jshint.com/contribute/) •
[Blog](http://jshint.com/blog/) • [Twitter](https://twitter.com/jshint/) \]
[![NPM version](https://img.shields.io/npm/v/jshint.svg?style=flat)](https://www.npmjs.com/package/jshint)
@ -12,10 +13,33 @@
[![devDependency Status](https://img.shields.io/david/dev/jshint/jshint.svg?style=flat)](https://david-dm.org/jshint/jshint#info=devDependencies)
[![Coverage Status](https://img.shields.io/coveralls/jshint/jshint.svg?style=flat)](https://coveralls.io/r/jshint/jshint?branch=master)
JSHint is a community-driven tool to detect errors and potential problems
in JavaScript code. It is very flexible so you can easily adjust it to your
particular coding guidelines and the environment you expect your code to
execute in.
JSHint is a community-driven tool to detect errors and potential problems in
JavaScript code and to enforce your team's coding conventions. It is very
flexible so you can easily adjust it to your particular coding guidelines and
the environment you expect your code to execute in. JSHint is open source and
will always stay this way.
## Our goal
The goal of this project is to help JavaScript developers write complex programs
without worrying about typos and language gotchas.
Any code base eventually becomes huge at some point, and simple mistakes—that
would not show themselves when written—can become show stoppers and waste
hours of debugging. And this is when static code analysis tools come into play
and help developers to spot such problems. JSHint scans a program written in
JavaScript and reports about commonly made mistakes and potential bugs. The
potential problem could be a syntax error, a bug due to implicit type
conversion, a leaking variable or something else.
Only 15% of all programs linted on [jshint.com](http://jshint.com) pass the
JSHint checks. In all other cases, JSHint finds some red flags that could've
been bugs or potential problems.
Please note, that while static code analysis tools can spot many different kind
of mistakes, it can't detect if your program is correct, fast or has memory
leaks. You should always combine tools like JSHint with unit and functional
tests as well as with code reviews.
## Reporting a bug
@ -31,25 +55,56 @@ JSHint including but not limited to:
Before reporting a bug look around to see if there are any open or closed tickets
that cover your issue. And remember the wisdom: pull request > bug report > tweet.
## Issue Priority
## Who uses JSHint?
- *P1:* Something is throwing exceptions; broken JSHint backward compatibility.
- *P2:* Something is not being parsed correctly.
- *P3:* Features that the core team will work on once P2s and P1s are done.
- *P4:* Patches welcome; The request is good, but low priority.
Engineers from these companies and projects use JSHint:
* [Mozilla](https://www.mozilla.org/)
* [Wikipedia](https://wikipedia.org/)
* [Facebook](https://facebook.com/)
* [Twitter](https://twitter.com/)
* [Bootstrap](http://getbootstrap.com/)
* [Disqus](https://disqus.com/)
* [Medium](https://medium.com/)
* [Yahoo!](https://yahoo.com/)
* [SmugMug](http://smugmug.com/)
* [jQuery](http://jquery.com/)
* [PDF.js](http://mozilla.github.io/pdf.js)
* [Coursera](http://coursera.com/)
* [Adobe Brackets](http://brackets.io/)
* [Apache Cordova](http://cordova.io/)
* [RedHat](http://redhat.com/)
* [SoundCloud](http://soundcloud.com/)
* [Nodejitsu](http://nodejitsu.com/)
* [Yelp](https://yelp.com/)
* [Voxer](http://voxer.com/)
* [EnyoJS](http://enyojs.com/)
* [QuickenLoans](http://quickenloans.com/)
* [oDesk](http://www.odesk.com/)
* [Cloud9](http://c9.io/)
* [CodeClimate](https://codeclimate.com/)
* [Pandoo TEK](http://pandootek.com/)
* [Zendesk](http://zendesk.com/)
* [Apache CouchDB](http://couchdb.apache.org/)
And many more!
## License
JSHint is distributed under the MIT License. One file and one file only
(src/stable/jshint.js) is distributed under the slightly modified MIT License.
Most files are published using [the standard MIT Expat
license](https://www.gnu.org/licenses/license-list.html#Expat). One file,
however, is provided under a slightly modified version of that license. The
so-called [JSON license](https://www.gnu.org/licenses/license-list.html#JSON)
is a non-free license, and unfortunately, we can't change it due to historical
reasons. This license is included as an in-line within the file it concerns.
## The JSHint Team
JSHint is maintained by [Rick Waldron](https://github.com/rwaldron/), [Caitlin
Potter](https://github.com/caitp/), [Mike
Sherov](https://github.com/mikesherov/), and [Mike
Pennisi](https://github.com/jugglinmike/).
JSHint is currently maintained by [Rick Waldron](https://github.com/rwaldron/),
[Caitlin Potter](https://github.com/caitp/), [Mike
Sherov](https://github.com/mikesherov/), [Mike
Pennisi](https://github.com/jugglinmike/), and [Luke
Page](https://github.com/lukeapage).
## Thank you!

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,4 +3,6 @@ node_js:
- 0.10
- 0.11
sudo: false
script: npm run coveralls

View File

@ -1,18 +1,18 @@
#htmlparser2
# htmlparser2
[![NPM version](http://img.shields.io/npm/v/htmlparser2.svg?style=flat)](https://npmjs.org/package/htmlparser2)
[![Downloads](https://img.shields.io/npm/dm/htmlparser2.svg?style=flat)](https://npmjs.org/package/htmlparser2)
[![Build Status](http://img.shields.io/travis/fb55/htmlparser2/master.svg?style=flat)](http://travis-ci.org/fb55/htmlparser2)
[![Coverage](http://img.shields.io/coveralls/fb55/htmlparser2.svg?style=flat)](https://coveralls.io/r/fb55/htmlparser2)
A forgiving HTML/XML/RSS parser written in JS for NodeJS. The parser can handle streams (chunked data) and supports custom handlers for writing custom DOMs/output.
A forgiving HTML/XML/RSS parser. The parser can handle streams and provides a callback interface.
##Installing
## Installation
npm install htmlparser2
A live demo of htmlparser2 is available at http://demos.forbeslindesay.co.uk/htmlparser2/
A live demo of htmlparser2 is available [here](http://demos.forbeslindesay.co.uk/htmlparser2/).
##Usage
## Usage
```javascript
var htmlparser = require("htmlparser2");
@ -30,7 +30,7 @@ var parser = new htmlparser.Parser({
console.log("That's it?!");
}
}
});
}, {decodeEntities: true});
parser.write("Xyz <script type='text/javascript'>var foo = '<<bar>>';</ script>");
parser.end();
```
@ -44,14 +44,16 @@ JS! Hooray!
That's it?!
```
Read more about the parser in the [wiki](https://github.com/fb55/htmlparser2/wiki/Parser-options).
## Documentation
##Get a DOM
Read more about the parser and its options in the [wiki](https://github.com/fb55/htmlparser2/wiki/Parser-options).
## Get a DOM
The `DomHandler` (known as `DefaultHandler` in the original `htmlparser` module) produces a DOM (document object model) that can be manipulated using the [`DomUtils`](https://github.com/fb55/DomUtils) helper.
The `DomHandler`, while still bundled with this module, was moved to its [own module](https://github.com/fb55/domhandler). Have a look at it for further information.
##Parsing RSS/RDF/Atom Feeds
## Parsing RSS/RDF/Atom Feeds
```javascript
new htmlparser.FeedHandler(function(<error> error, <object> feed){
@ -59,7 +61,9 @@ new htmlparser.FeedHandler(function(<error> error, <object> feed){
});
```
##Performance
Note: While the provided feed handler works for most feeds, you might want to use [danmactough/node-feedparser](https://github.com/danmactough/node-feedparser), which is much better tested and actively maintained.
## Performance
After having some artificial benchmarks for some time, __@AndreasMadsen__ published his [`htmlparser-benchmark`](https://github.com/AndreasMadsen/htmlparser-benchmark), which benchmarks HTML parses based on real-world websites.
@ -78,9 +82,10 @@ parse5 : 22.0439 ms/file ± 15.3743
sax : 49.6513 ms/file ± 26.6032
```
##How is this different from [node-htmlparser](https://github.com/tautologistics/node-htmlparser)?
## How does this module differ from [node-htmlparser](https://github.com/tautologistics/node-htmlparser)?
This is a fork of the `htmlparser` module. The main difference is that this is intended to be used only with node (it runs on other platforms using [browserify](https://github.com/substack/node-browserify)). `htmlparser2` was rewritten multiple times and, while it maintains an API that's compatible with `htmlparser` in most cases, the projects don't share any code anymore.
The parser now provides a callback interface close to [sax.js](https://github.com/isaacs/sax-js) (originally targeted at [readabilitySAX](https://github.com/fb55/readabilitysax)). As a result, old handlers won't work anymore.
The `DefaultHandler` and the `RssHandler` were renamed to clarify their purpose (to `DomHandler` and `FeedHandler`). The old names are still available when requiring `htmlparser2`, so your code should work as expected.
The `DefaultHandler` and the `RssHandler` were renamed to clarify their purpose (to `DomHandler` and `FeedHandler`). The old names are still available when requiring `htmlparser2`, your code should work as expected.

View File

@ -3,7 +3,7 @@ var Tokenizer = require("./Tokenizer.js");
/*
Options:
xmlMode: Special behavior for script/style tags (true by default)
xmlMode: Disables the special behavior for script/style tags (false by default)
lowerCaseAttributeNames: call .toLowerCase for each attribute name (true if xmlMode is `false`)
lowerCaseTags: call .toLowerCase for each tag name (true if xmlMode is `false`)
*/
@ -36,7 +36,7 @@ var formTags = {
var openImpliesClose = {
tr : { tr:true, th:true, td:true },
th : { th:true },
td : { thead:true, td:true },
td : { thead:true, th:true, td:true },
body : { head:true, link:true, script:true },
li : { li:true },
p : { p:true },
@ -87,7 +87,7 @@ var voidElements = {
use: true,
stop: true,
polyline: true,
polygone: true
polygon: true
};
var re_nameEnd = /\s|\//;

View File

@ -0,0 +1,19 @@
Copyright Node.js contributors. All rights reserved.
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.

View File

@ -21,8 +21,12 @@
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
function isArray(arg) {
if (Array.isArray) {
return Array.isArray(arg);
}
return objectToString(arg) === '[object Array]';
}
exports.isArray = isArray;
@ -62,7 +66,7 @@ function isUndefined(arg) {
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
return objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
@ -72,13 +76,12 @@ function isObject(arg) {
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
return objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) &&
(objectToString(e) === '[object Error]' || e instanceof Error);
return (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;
@ -97,11 +100,8 @@ function isPrimitive(arg) {
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return Buffer.isBuffer(arg);
}
exports.isBuffer = isBuffer;
exports.isBuffer = Buffer.isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "core-util-is",
"version": "1.0.1",
"version": "1.0.2",
"description": "The `util.is*` functions introduced in Node v0.12.",
"main": "lib/util.js",
"repository": {
@ -27,20 +27,27 @@
"bugs": {
"url": "https://github.com/isaacs/core-util-is/issues"
},
"readme": "# core-util-is\n\nThe `util.is*` functions introduced in Node v0.12.\n",
"readmeFilename": "README.md",
"homepage": "https://github.com/isaacs/core-util-is",
"_id": "core-util-is@1.0.1",
"dist": {
"shasum": "6b07085aef9a3ccac6ee53bf9d3df0c1521a5538",
"tarball": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
"scripts": {
"test": "tap test.js"
},
"devDependencies": {
"tap": "^2.3.0"
},
"gitHead": "a177da234df5638b363ddc15fa324619a38577c8",
"homepage": "https://github.com/isaacs/core-util-is#readme",
"_id": "core-util-is@1.0.2",
"_shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7",
"_from": "core-util-is@>=1.0.0 <1.1.0",
"_npmVersion": "1.3.23",
"_npmVersion": "3.3.2",
"_nodeVersion": "4.0.0",
"_npmUser": {
"name": "isaacs",
"email": "i@izs.me"
},
"dist": {
"shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7",
"tarball": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
},
"maintainers": [
{
"name": "isaacs",
@ -48,7 +55,6 @@
}
],
"directories": {},
"_shasum": "6b07085aef9a3ccac6ee53bf9d3df0c1521a5538",
"_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz",
"scripts": {}
"_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -0,0 +1,68 @@
var assert = require('tap');
var t = require('./lib/util');
assert.equal(t.isArray([]), true);
assert.equal(t.isArray({}), false);
assert.equal(t.isBoolean(null), false);
assert.equal(t.isBoolean(true), true);
assert.equal(t.isBoolean(false), true);
assert.equal(t.isNull(null), true);
assert.equal(t.isNull(undefined), false);
assert.equal(t.isNull(false), false);
assert.equal(t.isNull(), false);
assert.equal(t.isNullOrUndefined(null), true);
assert.equal(t.isNullOrUndefined(undefined), true);
assert.equal(t.isNullOrUndefined(false), false);
assert.equal(t.isNullOrUndefined(), true);
assert.equal(t.isNumber(null), false);
assert.equal(t.isNumber('1'), false);
assert.equal(t.isNumber(1), true);
assert.equal(t.isString(null), false);
assert.equal(t.isString('1'), true);
assert.equal(t.isString(1), false);
assert.equal(t.isSymbol(null), false);
assert.equal(t.isSymbol('1'), false);
assert.equal(t.isSymbol(1), false);
assert.equal(t.isSymbol(Symbol()), true);
assert.equal(t.isUndefined(null), false);
assert.equal(t.isUndefined(undefined), true);
assert.equal(t.isUndefined(false), false);
assert.equal(t.isUndefined(), true);
assert.equal(t.isRegExp(null), false);
assert.equal(t.isRegExp('1'), false);
assert.equal(t.isRegExp(new RegExp()), true);
assert.equal(t.isObject({}), true);
assert.equal(t.isObject([]), true);
assert.equal(t.isObject(new RegExp()), true);
assert.equal(t.isObject(new Date()), true);
assert.equal(t.isDate(null), false);
assert.equal(t.isDate('1'), false);
assert.equal(t.isDate(new Date()), true);
assert.equal(t.isError(null), false);
assert.equal(t.isError({ err: true }), false);
assert.equal(t.isError(new Error()), true);
assert.equal(t.isFunction(null), false);
assert.equal(t.isFunction({ }), false);
assert.equal(t.isFunction(function() {}), true);
assert.equal(t.isPrimitive(null), true);
assert.equal(t.isPrimitive(''), true);
assert.equal(t.isPrimitive(0), true);
assert.equal(t.isPrimitive(new Date()), false);
assert.equal(t.isBuffer(null), false);
assert.equal(t.isBuffer({}), false);
assert.equal(t.isBuffer(new Buffer(0)), true);

View File

@ -1,106 +0,0 @@
// 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.
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) && objectToString(e) === '[object Error]';
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return arg instanceof Buffer;
}
exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}

View File

@ -27,25 +27,9 @@
"bugs": {
"url": "https://github.com/isaacs/inherits/issues"
},
"homepage": "https://github.com/isaacs/inherits#readme",
"_id": "inherits@2.0.1",
"dist": {
"shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
"tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"_from": "inherits@>=2.0.1 <2.1.0",
"_npmVersion": "1.3.8",
"_npmUser": {
"name": "isaacs",
"email": "i@izs.me"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"directories": {},
"_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
"_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"homepage": "https://github.com/isaacs/inherits"
"_from": "inherits@>=2.0.1 <2.1.0"
}

View File

@ -1,6 +1,6 @@
{
"name": "readable-stream",
"version": "1.1.13",
"version": "1.1.14",
"description": "Streams3, a user-land copy of the stream library from Node.js v0.11.x",
"main": "readable.js",
"dependencies": {
@ -33,23 +33,27 @@
"url": "http://blog.izs.me/"
},
"license": "MIT",
"gitHead": "3b672fd7ae92acf5b4ffdbabf74b372a0a56b051",
"gitHead": "52550840cb1d6e8a98ef9a909a4bea360bc6f7da",
"bugs": {
"url": "https://github.com/isaacs/readable-stream/issues"
},
"homepage": "https://github.com/isaacs/readable-stream",
"_id": "readable-stream@1.1.13",
"_shasum": "f6eef764f514c89e2b9e23146a75ba106756d23e",
"_id": "readable-stream@1.1.14",
"_shasum": "7cf4c54ef648e3813084c636dd2079e166c081d9",
"_from": "readable-stream@>=1.1.0 <1.2.0",
"_npmVersion": "1.4.23",
"_npmVersion": "3.8.3",
"_nodeVersion": "5.10.1",
"_npmUser": {
"name": "rvagg",
"email": "rod@vagg.org"
"name": "cwmma",
"email": "calvin.metcalf@gmail.com"
},
"dist": {
"shasum": "7cf4c54ef648e3813084c636dd2079e166c081d9",
"tarball": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
"email": "isaacs@npmjs.com"
},
{
"name": "tootallnate",
@ -58,13 +62,18 @@
{
"name": "rvagg",
"email": "rod@vagg.org"
},
{
"name": "cwmma",
"email": "calvin.metcalf@gmail.com"
}
],
"dist": {
"shasum": "f6eef764f514c89e2b9e23146a75ba106756d23e",
"tarball": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz"
"_npmOperationalInternal": {
"host": "packages-16-east.internal.npmjs.com",
"tmp": "tmp/readable-stream-1.1.14.tgz_1460563293219_0.5682175166439265"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
"readme": "ERROR: No README data found!"
"_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/isaacs/readable-stream#readme"
}

View File

@ -5,3 +5,6 @@ exports.Writable = require('./lib/_stream_writable.js');
exports.Duplex = require('./lib/_stream_duplex.js');
exports.Transform = require('./lib/_stream_transform.js');
exports.PassThrough = require('./lib/_stream_passthrough.js');
if (!process.browser && process.env.READABLE_STREAM === 'disable') {
module.exports = require('stream');
}

View File

@ -1,7 +1,7 @@
{
"name": "htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser",
"version": "3.8.2",
"version": "3.8.3",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
@ -71,13 +71,13 @@
"it": true
}
},
"gitHead": "748d3da71dc664afb8357aabfe6c4a6f74644a0e",
"homepage": "https://github.com/fb55/htmlparser2",
"_id": "htmlparser2@3.8.2",
"_shasum": "0d6bc3471d01e9766fc2c274cbac1d55b36c009c",
"gitHead": "44e48f58526de05d2639199f4baaaef235521f6b",
"homepage": "https://github.com/fb55/htmlparser2#readme",
"_id": "htmlparser2@3.8.3",
"_shasum": "996c28b191516a8be86501a7d79757e5c70c1068",
"_from": "htmlparser2@>=3.8.0 <3.9.0",
"_npmVersion": "2.1.5",
"_nodeVersion": "0.10.32",
"_npmVersion": "2.11.1",
"_nodeVersion": "2.2.1",
"_npmUser": {
"name": "feedic",
"email": "me@feedic.com"
@ -89,9 +89,9 @@
}
],
"dist": {
"shasum": "0d6bc3471d01e9766fc2c274cbac1d55b36c009c",
"tarball": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz"
"shasum": "996c28b191516a8be86501a7d79757e5c70c1068",
"tarball": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz"
},
"_resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz",
"_resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -1,7 +1,7 @@
{
"name": "Implicit close tags",
"options": {},
"html": "<ol><li class=test><div><table style=width:100%><tr><td colspan=2><h3>Heading</h3><tr><td><div>Div</div><td><div>Div2</div></table></div><li><div><h3>Heading 2</h3></div></li></ol><p>Para<h4>Heading 4</h4>",
"html": "<ol><li class=test><div><table style=width:100%><tr><th>TH<td colspan=2><h3>Heading</h3><tr><td><div>Div</div><td><div>Div2</div></table></div><li><div><h3>Heading 2</h3></div></li></ol><p>Para<h4>Heading 4</h4>",
"expected": [
{ "event": "opentagname", "data": [ "ol" ] },
{ "event": "opentag", "data": [ "ol", {} ] },
@ -15,6 +15,10 @@
{ "event": "opentag", "data": [ "table", { "style": "width:100%" } ] },
{ "event": "opentagname", "data": [ "tr" ] },
{ "event": "opentag", "data": [ "tr", {} ] },
{ "event": "opentagname", "data": [ "th" ] },
{ "event": "opentag", "data": [ "th", {} ] },
{ "event": "text", "data": [ "TH" ] },
{ "event": "closetag", "data": [ "th" ] },
{ "event": "opentagname", "data": [ "td" ] },
{ "event": "attribute", "data": [ "colspan", "2" ] },
{ "event": "opentag", "data": [ "td", { "colspan": "2" } ] },

View File

@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,216 @@
# minimatch
A minimal matching utility.
[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch)
This is the matching library used internally by npm.
It works by converting glob expressions into JavaScript `RegExp`
objects.
## Usage
```javascript
var minimatch = require("minimatch")
minimatch("bar.foo", "*.foo") // true!
minimatch("bar.foo", "*.bar") // false!
minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
```
## Features
Supports these glob features:
* Brace Expansion
* Extended glob matching
* "Globstar" `**` matching
See:
* `man sh`
* `man bash`
* `man 3 fnmatch`
* `man 5 gitignore`
## Minimatch Class
Create a minimatch object by instanting the `minimatch.Minimatch` class.
```javascript
var Minimatch = require("minimatch").Minimatch
var mm = new Minimatch(pattern, options)
```
### Properties
* `pattern` The original pattern the minimatch object represents.
* `options` The options supplied to the constructor.
* `set` A 2-dimensional array of regexp or string expressions.
Each row in the
array corresponds to a brace-expanded pattern. Each item in the row
corresponds to a single path-part. For example, the pattern
`{a,b/c}/d` would expand to a set of patterns like:
[ [ a, d ]
, [ b, c, d ] ]
If a portion of the pattern doesn't have any "magic" in it
(that is, it's something like `"foo"` rather than `fo*o?`), then it
will be left as a string rather than converted to a regular
expression.
* `regexp` Created by the `makeRe` method. A single regular expression
expressing the entire pattern. This is useful in cases where you wish
to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
* `negate` True if the pattern is negated.
* `comment` True if the pattern is a comment.
* `empty` True if the pattern is `""`.
### Methods
* `makeRe` Generate the `regexp` member if necessary, and return it.
Will return `false` if the pattern is invalid.
* `match(fname)` Return true if the filename matches the pattern, or
false otherwise.
* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
filename, and match it against a single row in the `regExpSet`. This
method is mainly for internal use, but is exposed so that it can be
used by a glob-walker that needs to avoid excessive filesystem calls.
All other methods are internal, and will be called as necessary.
## Functions
The top-level exported function has a `cache` property, which is an LRU
cache set to store 100 items. So, calling these methods repeatedly
with the same pattern and options will use the same Minimatch object,
saving the cost of parsing it multiple times.
### minimatch(path, pattern, options)
Main export. Tests a path against the pattern using the options.
```javascript
var isJS = minimatch(file, "*.js", { matchBase: true })
```
### minimatch.filter(pattern, options)
Returns a function that tests its
supplied argument, suitable for use with `Array.filter`. Example:
```javascript
var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
```
### minimatch.match(list, pattern, options)
Match against the list of
files, in the style of fnmatch or glob. If nothing is matched, and
options.nonull is set, then return a list containing the pattern itself.
```javascript
var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
```
### minimatch.makeRe(pattern, options)
Make a regular expression object from the pattern.
## Options
All options are `false` by default.
### debug
Dump a ton of stuff to stderr.
### nobrace
Do not expand `{a,b}` and `{1..3}` brace sets.
### noglobstar
Disable `**` matching against multiple folder names.
### dot
Allow patterns to match filenames starting with a period, even if
the pattern does not explicitly have a period in that spot.
Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
is set.
### noext
Disable "extglob" style patterns like `+(a|b)`.
### nocase
Perform a case-insensitive match.
### nonull
When a match is not found by `minimatch.match`, return a list containing
the pattern itself if this option is set. When not set, an empty list
is returned if there are no matches.
### matchBase
If set, then patterns without slashes will be matched
against the basename of the path if it contains slashes. For example,
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
### nocomment
Suppress the behavior of treating `#` at the start of a pattern as a
comment.
### nonegate
Suppress the behavior of treating a leading `!` character as negation.
### flipNegate
Returns from negate expressions the same as if they were not negated.
(Ie, true on a hit, false on a miss.)
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between minimatch and other
implementations, and are intentional.
If the pattern starts with a `!` character, then it is negated. Set the
`nonegate` flag to suppress this behavior, and treat leading `!`
characters normally. This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
characters at the start of a pattern will negate the pattern multiple
times.
If a pattern starts with `#`, then it is treated as a comment, and
will not match anything. Use `\#` to match a literal `#` at the
start of a line, or set the `nocomment` flag to suppress this behavior.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
If an escaped pattern has no matches, and the `nonull` flag is set,
then minimatch.match returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,912 @@
module.exports = minimatch
minimatch.Minimatch = Minimatch
var path = { sep: '/' }
try {
path = require('path')
} catch (er) {}
var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
var expand = require('brace-expansion')
// any single thing other than /
// don't need to escape / when using new RegExp()
var qmark = '[^/]'
// * => any number of characters
var star = qmark + '*?'
// ** when dots are allowed. Anything goes, except .. and .
// not (^ or / followed by one or two dots followed by $ or /),
// followed by anything, any number of times.
var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
// not a ^ or / followed by a dot,
// followed by anything, any number of times.
var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
// characters that need to be escaped in RegExp.
var reSpecials = charSet('().*{}+?[]^$\\!')
// "abc" -> { a:true, b:true, c:true }
function charSet (s) {
return s.split('').reduce(function (set, c) {
set[c] = true
return set
}, {})
}
// normalizes slashes.
var slashSplit = /\/+/
minimatch.filter = filter
function filter (pattern, options) {
options = options || {}
return function (p, i, list) {
return minimatch(p, pattern, options)
}
}
function ext (a, b) {
a = a || {}
b = b || {}
var t = {}
Object.keys(b).forEach(function (k) {
t[k] = b[k]
})
Object.keys(a).forEach(function (k) {
t[k] = a[k]
})
return t
}
minimatch.defaults = function (def) {
if (!def || !Object.keys(def).length) return minimatch
var orig = minimatch
var m = function minimatch (p, pattern, options) {
return orig.minimatch(p, pattern, ext(def, options))
}
m.Minimatch = function Minimatch (pattern, options) {
return new orig.Minimatch(pattern, ext(def, options))
}
return m
}
Minimatch.defaults = function (def) {
if (!def || !Object.keys(def).length) return Minimatch
return minimatch.defaults(def).Minimatch
}
function minimatch (p, pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('glob pattern string required')
}
if (!options) options = {}
// shortcut: comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
return false
}
// "" only matches ""
if (pattern.trim() === '') return p === ''
return new Minimatch(pattern, options).match(p)
}
function Minimatch (pattern, options) {
if (!(this instanceof Minimatch)) {
return new Minimatch(pattern, options)
}
if (typeof pattern !== 'string') {
throw new TypeError('glob pattern string required')
}
if (!options) options = {}
pattern = pattern.trim()
// windows support: need to use /, not \
if (path.sep !== '/') {
pattern = pattern.split(path.sep).join('/')
}
this.options = options
this.set = []
this.pattern = pattern
this.regexp = null
this.negate = false
this.comment = false
this.empty = false
// make the set of regexps etc.
this.make()
}
Minimatch.prototype.debug = function () {}
Minimatch.prototype.make = make
function make () {
// don't do it more than once.
if (this._made) return
var pattern = this.pattern
var options = this.options
// empty patterns and comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
this.comment = true
return
}
if (!pattern) {
this.empty = true
return
}
// step 1: figure out negation, etc.
this.parseNegate()
// step 2: expand braces
var set = this.globSet = this.braceExpand()
if (options.debug) this.debug = console.error
this.debug(this.pattern, set)
// step 3: now we have a set, so turn each one into a series of path-portion
// matching patterns.
// These will be regexps, except in the case of "**", which is
// set to the GLOBSTAR object for globstar behavior,
// and will not contain any / characters
set = this.globParts = set.map(function (s) {
return s.split(slashSplit)
})
this.debug(this.pattern, set)
// glob --> regexps
set = set.map(function (s, si, set) {
return s.map(this.parse, this)
}, this)
this.debug(this.pattern, set)
// filter out everything that didn't compile properly.
set = set.filter(function (s) {
return s.indexOf(false) === -1
})
this.debug(this.pattern, set)
this.set = set
}
Minimatch.prototype.parseNegate = parseNegate
function parseNegate () {
var pattern = this.pattern
var negate = false
var options = this.options
var negateOffset = 0
if (options.nonegate) return
for (var i = 0, l = pattern.length
; i < l && pattern.charAt(i) === '!'
; i++) {
negate = !negate
negateOffset++
}
if (negateOffset) this.pattern = pattern.substr(negateOffset)
this.negate = negate
}
// Brace expansion:
// a{b,c}d -> abd acd
// a{b,}c -> abc ac
// a{0..3}d -> a0d a1d a2d a3d
// a{b,c{d,e}f}g -> abg acdfg acefg
// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
//
// Invalid sets are not expanded.
// a{2..}b -> a{2..}b
// a{b}c -> a{b}c
minimatch.braceExpand = function (pattern, options) {
return braceExpand(pattern, options)
}
Minimatch.prototype.braceExpand = braceExpand
function braceExpand (pattern, options) {
if (!options) {
if (this instanceof Minimatch) {
options = this.options
} else {
options = {}
}
}
pattern = typeof pattern === 'undefined'
? this.pattern : pattern
if (typeof pattern === 'undefined') {
throw new Error('undefined pattern')
}
if (options.nobrace ||
!pattern.match(/\{.*\}/)) {
// shortcut. no need to expand.
return [pattern]
}
return expand(pattern)
}
// parse a component of the expanded set.
// At this point, no pattern may contain "/" in it
// so we're going to return a 2d array, where each entry is the full
// pattern, split on '/', and then turned into a regular expression.
// A regexp is made at the end which joins each array with an
// escaped /, and another full one which joins each regexp with |.
//
// Following the lead of Bash 4.1, note that "**" only has special meaning
// when it is the *only* thing in a path portion. Otherwise, any series
// of * is equivalent to a single *. Globstar behavior is enabled by
// default, and can be disabled by setting options.noglobstar.
Minimatch.prototype.parse = parse
var SUBPARSE = {}
function parse (pattern, isSub) {
var options = this.options
// shortcuts
if (!options.noglobstar && pattern === '**') return GLOBSTAR
if (pattern === '') return ''
var re = ''
var hasMagic = !!options.nocase
var escaping = false
// ? => one single character
var patternListStack = []
var negativeLists = []
var plType
var stateChar
var inClass = false
var reClassStart = -1
var classStart = -1
// . and .. never match anything that doesn't start with .,
// even when options.dot is set.
var patternStart = pattern.charAt(0) === '.' ? '' // anything
// not (start or / followed by . or .. followed by / or end)
: options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
: '(?!\\.)'
var self = this
function clearStateChar () {
if (stateChar) {
// we had some state-tracking character
// that wasn't consumed by this pass.
switch (stateChar) {
case '*':
re += star
hasMagic = true
break
case '?':
re += qmark
hasMagic = true
break
default:
re += '\\' + stateChar
break
}
self.debug('clearStateChar %j %j', stateChar, re)
stateChar = false
}
}
for (var i = 0, len = pattern.length, c
; (i < len) && (c = pattern.charAt(i))
; i++) {
this.debug('%s\t%s %s %j', pattern, i, re, c)
// skip over any that are escaped.
if (escaping && reSpecials[c]) {
re += '\\' + c
escaping = false
continue
}
switch (c) {
case '/':
// completely not allowed, even escaped.
// Should already be path-split by now.
return false
case '\\':
clearStateChar()
escaping = true
continue
// the various stateChar values
// for the "extglob" stuff.
case '?':
case '*':
case '+':
case '@':
case '!':
this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
// all of those are literals inside a class, except that
// the glob [!a] means [^a] in regexp
if (inClass) {
this.debug(' in class')
if (c === '!' && i === classStart + 1) c = '^'
re += c
continue
}
// if we already have a stateChar, then it means
// that there was something like ** or +? in there.
// Handle the stateChar, then proceed with this one.
self.debug('call clearStateChar %j', stateChar)
clearStateChar()
stateChar = c
// if extglob is disabled, then +(asdf|foo) isn't a thing.
// just clear the statechar *now*, rather than even diving into
// the patternList stuff.
if (options.noext) clearStateChar()
continue
case '(':
if (inClass) {
re += '('
continue
}
if (!stateChar) {
re += '\\('
continue
}
plType = stateChar
patternListStack.push({
type: plType,
start: i - 1,
reStart: re.length
})
// negation is (?:(?!js)[^/]*)
re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
this.debug('plType %j %j', stateChar, re)
stateChar = false
continue
case ')':
if (inClass || !patternListStack.length) {
re += '\\)'
continue
}
clearStateChar()
hasMagic = true
re += ')'
var pl = patternListStack.pop()
plType = pl.type
// negation is (?:(?!js)[^/]*)
// The others are (?:<pattern>)<type>
switch (plType) {
case '!':
negativeLists.push(pl)
re += ')[^/]*?)'
pl.reEnd = re.length
break
case '?':
case '+':
case '*':
re += plType
break
case '@': break // the default anyway
}
continue
case '|':
if (inClass || !patternListStack.length || escaping) {
re += '\\|'
escaping = false
continue
}
clearStateChar()
re += '|'
continue
// these are mostly the same in regexp and glob
case '[':
// swallow any state-tracking char before the [
clearStateChar()
if (inClass) {
re += '\\' + c
continue
}
inClass = true
classStart = i
reClassStart = re.length
re += c
continue
case ']':
// a right bracket shall lose its special
// meaning and represent itself in
// a bracket expression if it occurs
// first in the list. -- POSIX.2 2.8.3.2
if (i === classStart + 1 || !inClass) {
re += '\\' + c
escaping = false
continue
}
// handle the case where we left a class open.
// "[z-a]" is valid, equivalent to "\[z-a\]"
if (inClass) {
// split where the last [ was, make sure we don't have
// an invalid re. if so, re-walk the contents of the
// would-be class to re-translate any characters that
// were passed through as-is
// TODO: It would probably be faster to determine this
// without a try/catch and a new RegExp, but it's tricky
// to do safely. For now, this is safe and works.
var cs = pattern.substring(classStart + 1, i)
try {
RegExp('[' + cs + ']')
} catch (er) {
// not a valid class!
var sp = this.parse(cs, SUBPARSE)
re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
hasMagic = hasMagic || sp[1]
inClass = false
continue
}
}
// finish up the class.
hasMagic = true
inClass = false
re += c
continue
default:
// swallow any state char that wasn't consumed
clearStateChar()
if (escaping) {
// no need
escaping = false
} else if (reSpecials[c]
&& !(c === '^' && inClass)) {
re += '\\'
}
re += c
} // switch
} // for
// handle the case where we left a class open.
// "[abc" is valid, equivalent to "\[abc"
if (inClass) {
// split where the last [ was, and escape it
// this is a huge pita. We now have to re-walk
// the contents of the would-be class to re-translate
// any characters that were passed through as-is
cs = pattern.substr(classStart + 1)
sp = this.parse(cs, SUBPARSE)
re = re.substr(0, reClassStart) + '\\[' + sp[0]
hasMagic = hasMagic || sp[1]
}
// handle the case where we had a +( thing at the *end*
// of the pattern.
// each pattern list stack adds 3 chars, and we need to go through
// and escape any | chars that were passed through as-is for the regexp.
// Go through and escape them, taking care not to double-escape any
// | chars that were already escaped.
for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
var tail = re.slice(pl.reStart + 3)
// maybe some even number of \, then maybe 1 \, followed by a |
tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) {
if (!$2) {
// the | isn't already escaped, so escape it.
$2 = '\\'
}
// need to escape all those slashes *again*, without escaping the
// one that we need for escaping the | character. As it works out,
// escaping an even number of slashes can be done by simply repeating
// it exactly after itself. That's why this trick works.
//
// I am sorry that you have to see this.
return $1 + $1 + $2 + '|'
})
this.debug('tail=%j\n %s', tail, tail)
var t = pl.type === '*' ? star
: pl.type === '?' ? qmark
: '\\' + pl.type
hasMagic = true
re = re.slice(0, pl.reStart) + t + '\\(' + tail
}
// handle trailing things that only matter at the very end.
clearStateChar()
if (escaping) {
// trailing \\
re += '\\\\'
}
// only need to apply the nodot start if the re starts with
// something that could conceivably capture a dot
var addPatternStart = false
switch (re.charAt(0)) {
case '.':
case '[':
case '(': addPatternStart = true
}
// Hack to work around lack of negative lookbehind in JS
// A pattern like: *.!(x).!(y|z) needs to ensure that a name
// like 'a.xyz.yz' doesn't match. So, the first negative
// lookahead, has to look ALL the way ahead, to the end of
// the pattern.
for (var n = negativeLists.length - 1; n > -1; n--) {
var nl = negativeLists[n]
var nlBefore = re.slice(0, nl.reStart)
var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)
var nlAfter = re.slice(nl.reEnd)
nlLast += nlAfter
// Handle nested stuff like *(*.js|!(*.json)), where open parens
// mean that we should *not* include the ) in the bit that is considered
// "after" the negated section.
var openParensBefore = nlBefore.split('(').length - 1
var cleanAfter = nlAfter
for (i = 0; i < openParensBefore; i++) {
cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
}
nlAfter = cleanAfter
var dollar = ''
if (nlAfter === '' && isSub !== SUBPARSE) {
dollar = '$'
}
var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast
re = newRe
}
// if the re is not "" at this point, then we need to make sure
// it doesn't match against an empty path part.
// Otherwise a/* will match a/, which it should not.
if (re !== '' && hasMagic) {
re = '(?=.)' + re
}
if (addPatternStart) {
re = patternStart + re
}
// parsing just a piece of a larger pattern.
if (isSub === SUBPARSE) {
return [re, hasMagic]
}
// skip the regexp for non-magical patterns
// unescape anything in it, though, so that it'll be
// an exact match against a file etc.
if (!hasMagic) {
return globUnescape(pattern)
}
var flags = options.nocase ? 'i' : ''
var regExp = new RegExp('^' + re + '$', flags)
regExp._glob = pattern
regExp._src = re
return regExp
}
minimatch.makeRe = function (pattern, options) {
return new Minimatch(pattern, options || {}).makeRe()
}
Minimatch.prototype.makeRe = makeRe
function makeRe () {
if (this.regexp || this.regexp === false) return this.regexp
// at this point, this.set is a 2d array of partial
// pattern strings, or "**".
//
// It's better to use .match(). This function shouldn't
// be used, really, but it's pretty convenient sometimes,
// when you just want to work with a regex.
var set = this.set
if (!set.length) {
this.regexp = false
return this.regexp
}
var options = this.options
var twoStar = options.noglobstar ? star
: options.dot ? twoStarDot
: twoStarNoDot
var flags = options.nocase ? 'i' : ''
var re = set.map(function (pattern) {
return pattern.map(function (p) {
return (p === GLOBSTAR) ? twoStar
: (typeof p === 'string') ? regExpEscape(p)
: p._src
}).join('\\\/')
}).join('|')
// must match entire pattern
// ending in a * or ** will make it less strict.
re = '^(?:' + re + ')$'
// can match anything, as long as it's not this.
if (this.negate) re = '^(?!' + re + ').*$'
try {
this.regexp = new RegExp(re, flags)
} catch (ex) {
this.regexp = false
}
return this.regexp
}
minimatch.match = function (list, pattern, options) {
options = options || {}
var mm = new Minimatch(pattern, options)
list = list.filter(function (f) {
return mm.match(f)
})
if (mm.options.nonull && !list.length) {
list.push(pattern)
}
return list
}
Minimatch.prototype.match = match
function match (f, partial) {
this.debug('match', f, this.pattern)
// short-circuit in the case of busted things.
// comments, etc.
if (this.comment) return false
if (this.empty) return f === ''
if (f === '/' && partial) return true
var options = this.options
// windows: need to use /, not \
if (path.sep !== '/') {
f = f.split(path.sep).join('/')
}
// treat the test path as a set of pathparts.
f = f.split(slashSplit)
this.debug(this.pattern, 'split', f)
// just ONE of the pattern sets in this.set needs to match
// in order for it to be valid. If negating, then just one
// match means that we have failed.
// Either way, return on the first hit.
var set = this.set
this.debug(this.pattern, 'set', set)
// Find the basename of the path by looking for the last non-empty segment
var filename
var i
for (i = f.length - 1; i >= 0; i--) {
filename = f[i]
if (filename) break
}
for (i = 0; i < set.length; i++) {
var pattern = set[i]
var file = f
if (options.matchBase && pattern.length === 1) {
file = [filename]
}
var hit = this.matchOne(file, pattern, partial)
if (hit) {
if (options.flipNegate) return true
return !this.negate
}
}
// didn't get any hits. this is success if it's a negative
// pattern, failure otherwise.
if (options.flipNegate) return false
return this.negate
}
// set partial to true to test if, for example,
// "/a/b" matches the start of "/*/b/*/d"
// Partial means, if you run out of file before you run
// out of pattern, then that's fine, as long as all
// the parts match.
Minimatch.prototype.matchOne = function (file, pattern, partial) {
var options = this.options
this.debug('matchOne',
{ 'this': this, file: file, pattern: pattern })
this.debug('matchOne', file.length, pattern.length)
for (var fi = 0,
pi = 0,
fl = file.length,
pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++) {
this.debug('matchOne loop')
var p = pattern[pi]
var f = file[fi]
this.debug(pattern, p, f)
// should be impossible.
// some invalid regexp stuff in the set.
if (p === false) return false
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f])
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi
var pr = pi + 1
if (pr === pl) {
this.debug('** at the end')
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' || file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.')) return false
}
return true
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr]
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee)
// found a match.
return true
} else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' || swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr)
break
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue')
fr++
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
// If there's more *pattern* left, then
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
if (fr === fl) return true
}
return false
}
// something other than **
// non-magic patterns just have to match exactly
// patterns with magic have been turned into regexps.
var hit
if (typeof p === 'string') {
if (options.nocase) {
hit = f.toLowerCase() === p.toLowerCase()
} else {
hit = f === p
}
this.debug('string match', p, f, hit)
} else {
hit = f.match(p)
this.debug('pattern match', p, f, hit)
}
if (!hit) return false
}
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time.
// an exact hit!
return true
} else if (fi === fl) {
// ran out of file, but still had pattern left.
// this is ok if we're doing the match as part of
// a glob fs traversal.
return partial
} else if (pi === pl) {
// ran out of pattern, still have file left.
// this is only acceptable if we're on the very last
// empty segment of a file with a trailing slash.
// a/* should match a/b/
var emptyFileEnd = (fi === fl - 1) && (file[fi] === '')
return emptyFileEnd
}
// should be unreachable.
throw new Error('wtf?')
}
// replace stuff like \* with *
function globUnescape (s) {
return s.replace(/\\(.)/g, '$1')
}
function regExpEscape (s) {
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
}

View File

@ -0,0 +1,3 @@
test
.gitignore
.travis.yml

View File

@ -0,0 +1,122 @@
# brace-expansion
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)
## Example
```js
var expand = require('brace-expansion');
expand('file-{a,b,c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('-v{,,}')
// => ['-v', '-v', '-v']
expand('file{0..2}.jpg')
// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
expand('file-{a..c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('file{2..0}.jpg')
// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
expand('file{0..4..2}.jpg')
// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
expand('file-{a..e..2}.jpg')
// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
expand('file{00..10..5}.jpg')
// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
expand('{{A..C},{a..c}}')
// => ['A', 'B', 'C', 'a', 'b', 'c']
expand('ppp{,config,oe{,conf}}')
// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
```
## API
```js
var expand = require('brace-expansion');
```
### var expanded = expand(str)
Return an array of all possible and valid expansions of `str`. If none are
found, `[str]` is returned.
Valid expansions are:
```js
/^(.*,)+(.+)?$/
// {a,b,...}
```
A comma seperated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
A numeric sequence from `x` to `y` inclusive, with optional increment.
If `x` or `y` start with a leading `0`, all the numbers will be padded
to have equal length. Negative numbers and backwards iteration work too.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
An alphabetic sequence from `x` to `y` inclusive, with optional increment.
`x` and `y` must be exactly one character, and if given, `incr` must be a
number.
For compatibility reasons, the string `${` is not eligible for brace expansion.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install brace-expansion
```
## Contributors
- [Julian Gruber](https://github.com/juliangruber)
- [Isaac Z. Schlueter](https://github.com/isaacs)
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
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.

View File

@ -0,0 +1,8 @@
var expand = require('./');
console.log(expand('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html'));
console.log(expand('http://www.numericals.com/file{1..100..10}.txt'));
console.log(expand('http://www.letters.com/file{a..z..2}.txt'));
console.log(expand('mkdir /usr/local/src/bash/{old,new,dist,bugs}'));
console.log(expand('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}'));

View File

@ -0,0 +1,191 @@
var concatMap = require('concat-map');
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
return expand(escapeBraces(str), true).map(unescapeBraces);
}
function identity(e) {
return e;
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str, isTop) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m || /\$$/.test(m.pre)) return [str];
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = /^(.*,)+(.+)?$/.test(m.body);
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0], false).map(embrace);
if (n.length === 1) {
var post = m.post.length
? expand(m.post, false)
: [''];
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post, false)
: [''];
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = concatMap(n, function(el) { return expand(el, false) });
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
var expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion)
expansions.push(expansion);
}
}
return expansions;
}

View File

@ -0,0 +1,5 @@
test
.gitignore
.travis.yml
Makefile
example.js

View File

@ -0,0 +1,21 @@
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
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.

View File

@ -0,0 +1,91 @@
# balanced-match
Match balanced string pairs, like `{` and `}` or `<b>` and `</b>`. Supports regular expressions as well!
[![build status](https://secure.travis-ci.org/juliangruber/balanced-match.svg)](http://travis-ci.org/juliangruber/balanced-match)
[![downloads](https://img.shields.io/npm/dm/balanced-match.svg)](https://www.npmjs.org/package/balanced-match)
[![testling badge](https://ci.testling.com/juliangruber/balanced-match.png)](https://ci.testling.com/juliangruber/balanced-match)
## Example
Get the first matching pair of braces:
```js
var balanced = require('balanced-match');
console.log(balanced('{', '}', 'pre{in{nested}}post'));
console.log(balanced('{', '}', 'pre{first}between{second}post'));
console.log(balanced(/\s+\{\s+/, /\s+\}\s+/, 'pre { in{nest} } post'));
```
The matches are:
```bash
$ node example.js
{ start: 3, end: 14, pre: 'pre', body: 'in{nested}', post: 'post' }
{ start: 3,
end: 9,
pre: 'pre',
body: 'first',
post: 'between{second}post' }
{ start: 3, end: 17, pre: 'pre', body: 'in{nest}', post: 'post' }
```
## API
### var m = balanced(a, b, str)
For the first non-nested matching pair of `a` and `b` in `str`, return an
object with those keys:
* **start** the index of the first match of `a`
* **end** the index of the matching `b`
* **pre** the preamble, `a` and `b` not included
* **body** the match, `a` and `b` not included
* **post** the postscript, `a` and `b` not included
If there's no match, `undefined` will be returned.
If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `['{', 'a', '']`.
### var r = balanced.range(a, b, str)
For the first non-nested matching pair of `a` and `b` in `str`, return an
array with indexes: `[ <a index>, <b index> ]`.
If there's no match, `undefined` will be returned.
If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `[ 1, 3 ]`.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install balanced-match
```
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
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.

View File

@ -0,0 +1,58 @@
module.exports = balanced;
function balanced(a, b, str) {
if (a instanceof RegExp) a = maybeMatch(a, str);
if (b instanceof RegExp) b = maybeMatch(b, str);
var r = range(a, b, str);
return r && {
start: r[0],
end: r[1],
pre: str.slice(0, r[0]),
body: str.slice(r[0] + a.length, r[1]),
post: str.slice(r[1] + b.length)
};
}
function maybeMatch(reg, str) {
var m = str.match(reg);
return m ? m[0] : null;
}
balanced.range = range;
function range(a, b, str) {
var begs, beg, left, right, result;
var ai = str.indexOf(a);
var bi = str.indexOf(b, ai + 1);
var i = ai;
if (ai >= 0 && bi > 0) {
begs = [];
left = str.length;
while (i < str.length && i >= 0 && ! result) {
if (i == ai) {
begs.push(i);
ai = str.indexOf(a, i + 1);
} else if (begs.length == 1) {
result = [ begs.pop(), bi ];
} else {
beg = begs.pop();
if (beg < left) {
left = beg;
right = bi;
}
bi = str.indexOf(b, i + 1);
}
i = ai < bi && ai >= 0 ? ai : bi;
}
if (begs.length) {
result = [ left, right ];
}
}
return result;
}

View File

@ -0,0 +1,76 @@
{
"name": "balanced-match",
"description": "Match balanced character pairs, like \"{\" and \"}\"",
"version": "0.4.1",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/balanced-match.git"
},
"homepage": "https://github.com/juliangruber/balanced-match",
"main": "index.js",
"scripts": {
"test": "make test"
},
"dependencies": {},
"devDependencies": {
"tape": "~4.5.0"
},
"keywords": [
"match",
"regexp",
"test",
"balanced",
"parse"
],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"gitHead": "7004b289baaaab6a832f4901735e29d37cc2a863",
"bugs": {
"url": "https://github.com/juliangruber/balanced-match/issues"
},
"_id": "balanced-match@0.4.1",
"_shasum": "19053e2e0748eadb379da6c09d455cf5e1039335",
"_from": "balanced-match@>=0.4.1 <0.5.0",
"_npmVersion": "3.8.6",
"_nodeVersion": "6.0.0",
"_npmUser": {
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
"dist": {
"shasum": "19053e2e0748eadb379da6c09d455cf5e1039335",
"tarball": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
},
"maintainers": [
{
"name": "juliangruber",
"email": "julian@juliangruber.com"
}
],
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/balanced-match-0.4.1.tgz_1462129663650_0.39764496590942144"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz"
}

View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.4
- 0.6

View File

@ -0,0 +1,18 @@
This software is released under the MIT license:
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.

View File

@ -0,0 +1,62 @@
concat-map
==========
Concatenative mapdashery.
[![browser support](http://ci.testling.com/substack/node-concat-map.png)](http://ci.testling.com/substack/node-concat-map)
[![build status](https://secure.travis-ci.org/substack/node-concat-map.png)](http://travis-ci.org/substack/node-concat-map)
example
=======
``` js
var concatMap = require('concat-map');
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ys = concatMap(xs, function (x) {
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
console.dir(ys);
```
***
```
[ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]
```
methods
=======
``` js
var concatMap = require('concat-map')
```
concatMap(xs, fn)
-----------------
Return an array of concatenated elements by calling `fn(x, i)` for each element
`x` and each index `i` in the array `xs`.
When `fn(x, i)` returns an array, its result will be concatenated with the
result array. If `fn(x, i)` returns anything else, that value will be pushed
onto the end of the result array.
install
=======
With [npm](http://npmjs.org) do:
```
npm install concat-map
```
license
=======
MIT
notes
=====
This module was written while sitting high above the ground in a tree.

View File

@ -0,0 +1,6 @@
var concatMap = require('../');
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ys = concatMap(xs, function (x) {
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
console.dir(ys);

View File

@ -0,0 +1,13 @@
module.exports = function (xs, fn) {
var res = [];
for (var i = 0; i < xs.length; i++) {
var x = fn(xs[i], i);
if (isArray(x)) res.push.apply(res, x);
else res.push(x);
}
return res;
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};

View File

@ -0,0 +1,83 @@
{
"name": "concat-map",
"description": "concatenative mapdashery",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "git://github.com/substack/node-concat-map.git"
},
"main": "index.js",
"keywords": [
"concat",
"concatMap",
"map",
"functional",
"higher-order"
],
"directories": {
"example": "example",
"test": "test"
},
"scripts": {
"test": "tape test/*.js"
},
"devDependencies": {
"tape": "~2.4.0"
},
"license": "MIT",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"testling": {
"files": "test/*.js",
"browsers": {
"ie": [
6,
7,
8,
9
],
"ff": [
3.5,
10,
15
],
"chrome": [
10,
22
],
"safari": [
5.1
],
"opera": [
12
]
}
},
"bugs": {
"url": "https://github.com/substack/node-concat-map/issues"
},
"homepage": "https://github.com/substack/node-concat-map",
"_id": "concat-map@0.0.1",
"dist": {
"shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b",
"tarball": "http://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
},
"_from": "concat-map@0.0.1",
"_npmVersion": "1.3.21",
"_npmUser": {
"name": "substack",
"email": "mail@substack.net"
},
"maintainers": [
{
"name": "substack",
"email": "mail@substack.net"
}
],
"_shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b",
"_resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -0,0 +1,39 @@
var concatMap = require('../');
var test = require('tape');
test('empty or not', function (t) {
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ixes = [];
var ys = concatMap(xs, function (x, ix) {
ixes.push(ix);
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]);
t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]);
t.end();
});
test('always something', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function (x) {
return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ];
});
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
t.end();
});
test('scalars', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function (x) {
return x === 'b' ? [ 'B', 'B', 'B' ] : x;
});
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
t.end();
});
test('undefs', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function () {});
t.same(ys, [ undefined, undefined, undefined, undefined ]);
t.end();
});

View File

@ -0,0 +1,78 @@
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
"version": "1.1.4",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"homepage": "https://github.com/juliangruber/brace-expansion",
"main": "index.js",
"scripts": {
"test": "tape test/*.js",
"gentest": "bash test/generate.sh"
},
"dependencies": {
"balanced-match": "^0.4.1",
"concat-map": "0.0.1"
},
"devDependencies": {
"tape": "4.5.1"
},
"keywords": [],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"gitHead": "1660b75d0bf03b022e7888b576cd5a4080692c1d",
"bugs": {
"url": "https://github.com/juliangruber/brace-expansion/issues"
},
"_id": "brace-expansion@1.1.4",
"_shasum": "464a204c77f482c085c2a36c456bbfbafb67a127",
"_from": "brace-expansion@>=1.0.0 <2.0.0",
"_npmVersion": "3.8.6",
"_nodeVersion": "6.0.0",
"_npmUser": {
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
"dist": {
"shasum": "464a204c77f482c085c2a36c456bbfbafb67a127",
"tarball": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.4.tgz"
},
"maintainers": [
{
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
{
"name": "isaacs",
"email": "isaacs@npmjs.com"
}
],
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/brace-expansion-1.1.4.tgz_1462130058897_0.14984136167913675"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.4.tgz"
}

View File

@ -0,0 +1,63 @@
{
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me"
},
"name": "minimatch",
"description": "a glob matcher in javascript",
"version": "2.0.10",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/minimatch.git"
},
"main": "minimatch.js",
"scripts": {
"posttest": "standard minimatch.js test/*.js",
"test": "tap test/*.js",
"prepublish": "browserify -o browser.js -e minimatch.js -s minimatch --bare"
},
"engines": {
"node": "*"
},
"dependencies": {
"brace-expansion": "^1.0.0"
},
"devDependencies": {
"browserify": "^9.0.3",
"standard": "^3.7.2",
"tap": "^1.2.0"
},
"license": "ISC",
"files": [
"minimatch.js",
"browser.js"
],
"gitHead": "6afb85f0c324b321f76a38df81891e562693e257",
"bugs": {
"url": "https://github.com/isaacs/minimatch/issues"
},
"homepage": "https://github.com/isaacs/minimatch#readme",
"_id": "minimatch@2.0.10",
"_shasum": "8d087c39c6b38c001b97fca7ce6d0e1e80afbac7",
"_from": "minimatch@>=2.0.0 <2.1.0",
"_npmVersion": "3.1.0",
"_nodeVersion": "2.2.1",
"_npmUser": {
"name": "isaacs",
"email": "isaacs@npmjs.com"
},
"dist": {
"shasum": "8d087c39c6b38c001b97fca7ce6d0e1e80afbac7",
"tarball": "http://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"directories": {},
"_resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -20,9 +20,9 @@ function getStdin(cb) {
}
if (process.argv.indexOf('-h') !== -1 || process.argv.indexOf('--help') !== -1) {
console.log('strip-json-comments <input file> > <output file>');
console.log('strip-json-comments input-file > output-file');
console.log('or');
console.log('cat <input file> | strip-json-comments > <output file>');
console.log('strip-json-comments < input-file > output-file');
return;
}

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
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.

View File

@ -1,6 +1,6 @@
{
"name": "strip-json-comments",
"version": "1.0.2",
"version": "1.0.4",
"description": "Strip comments from JSON. Lets you use comments in your JSON files!",
"keywords": [
"json",
@ -25,7 +25,7 @@
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "http://sindresorhus.com"
"url": "sindresorhus.com"
},
"files": [
"cli.js",
@ -40,7 +40,7 @@
"url": "git+https://github.com/sindresorhus/strip-json-comments.git"
},
"scripts": {
"test": "mocha"
"test": "mocha --ui tdd"
},
"devDependencies": {
"mocha": "*"
@ -48,31 +48,31 @@
"engines": {
"node": ">=0.8.0"
},
"gitHead": "142dd671c71f90fb7fdba440184b1bb64543acb3",
"gitHead": "f58348696368583cc5bb18525fe31eacc9bd00e1",
"bugs": {
"url": "https://github.com/sindresorhus/strip-json-comments/issues"
},
"homepage": "https://github.com/sindresorhus/strip-json-comments",
"_id": "strip-json-comments@1.0.2",
"_shasum": "5a48ab96023dbac1b7b8d0ffabf6f63f1677be9f",
"_id": "strip-json-comments@1.0.4",
"_shasum": "1e15fbcac97d3ee99bf2d73b4c656b082bbafb91",
"_from": "strip-json-comments@>=1.0.0 <1.1.0",
"_npmVersion": "2.1.2",
"_nodeVersion": "0.10.32",
"_npmVersion": "2.11.2",
"_nodeVersion": "0.12.5",
"_npmUser": {
"name": "sindresorhus",
"email": "sindresorhus@gmail.com"
},
"dist": {
"shasum": "1e15fbcac97d3ee99bf2d73b4c656b082bbafb91",
"tarball": "http://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz"
},
"maintainers": [
{
"name": "sindresorhus",
"email": "sindresorhus@gmail.com"
}
],
"dist": {
"shasum": "5a48ab96023dbac1b7b8d0ffabf6f63f1677be9f",
"tarball": "http://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz",
"_resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -13,10 +13,11 @@ This is now possible:
It will remove single-line comments `//` and multi-line comments `/**/`.
Also available as a [gulp](https://github.com/sindresorhus/gulp-strip-json-comments)/[grunt](https://github.com/sindresorhus/grunt-strip-json-comments)/[broccoli](https://github.com/sindresorhus/broccoli-strip-json-comments) plugin and a [require hook](https://github.com/uTest/autostrip-json-comments).
Also available as a [gulp](https://github.com/sindresorhus/gulp-strip-json-comments)/[grunt](https://github.com/sindresorhus/grunt-strip-json-comments)/[broccoli](https://github.com/sindresorhus/broccoli-strip-json-comments) plugin.
-
*There's already [json-comments](https://npmjs.org/package/json-comments), but it's only for Node.js and uses a naive regex to strip comments which fails on simple cases like `{"a":"//"}`. This module however parses out the comments.*
*There's also [`json-comments`](https://npmjs.org/package/json-comments), but it's only for Node.js, inefficient, bloated as it also minifies, and comes with a `require` hook, which is :(*
## Install
@ -69,6 +70,11 @@ strip-json-comments < input-file > output-file
```
## Related
- [`strip-css-comments`](https://github.com/sindresorhus/strip-css-comments)
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View File

@ -8,6 +8,9 @@
(function () {
'use strict';
var singleComment = 1;
var multiComment = 2;
function stripJsonComments(str) {
var currentChar;
var nextChar;
@ -19,8 +22,11 @@
currentChar = str[i];
nextChar = str[i + 1];
if (!insideComment && str[i - 1] !== '\\' && currentChar === '"') {
insideString = !insideString;
if (!insideComment && currentChar === '"') {
var escaped = str[i - 1] === '\\' && str[i - 2] !== '\\';
if (!insideComment && !escaped && currentChar === '"') {
insideString = !insideString;
}
}
if (insideString) {
@ -29,21 +35,21 @@
}
if (!insideComment && currentChar + nextChar === '//') {
insideComment = 'single';
insideComment = singleComment;
i++;
} else if (insideComment === 'single' && currentChar + nextChar === '\r\n') {
} else if (insideComment === singleComment && currentChar + nextChar === '\r\n') {
insideComment = false;
i++;
ret += currentChar;
ret += nextChar;
continue;
} else if (insideComment === 'single' && currentChar === '\n') {
} else if (insideComment === singleComment && currentChar === '\n') {
insideComment = false;
} else if (!insideComment && currentChar + nextChar === '/*') {
insideComment = 'multi';
insideComment = multiComment;
i++;
continue;
} else if (insideComment === 'multi' && currentChar + nextChar === '*/') {
} else if (insideComment === multiComment && currentChar + nextChar === '*/') {
insideComment = false;
i++;
continue;

View File

@ -1,6 +1,6 @@
{
"name": "jshint",
"version": "2.8.0",
"version": "2.9.2",
"homepage": "http://jshint.com/",
"description": "Static analysis tool for JavaScript",
"author": {
@ -10,7 +10,7 @@
},
"repository": {
"type": "git",
"url": "https://github.com/jshint/jshint.git"
"url": "git+https://github.com/jshint/jshint.git"
},
"bugs": {
"url": "https://github.com/jshint/jshint/issues"
@ -19,11 +19,19 @@
"jshint": "./bin/jshint"
},
"scripts": {
"browser-test-server": "node tests/helpers/browser/server",
"build": "node bin/build",
"coverage": "istanbul -- cover ./node_modules/.bin/nodeunit tests/unit",
"data": "node scripts/generate-identifier-data",
"pretest": "jshint src && jscs src",
"test": "nodeunit tests tests/regression tests/unit"
"changelog": "conventional-changelog -p jshint -i CHANGELOG.md -w",
"github-release": "conventional-github-releaser -p jshint",
"test-browser": "node tests/browser",
"test-cli": "nodeunit tests/cli.js",
"test-node": "npm run test-unit && npm run test-cli && npm run test-regression",
"test-regression": "nodeunit tests/regression",
"test-unit": "nodeunit tests/unit",
"test": "npm run test-node && npm run test-browser"
},
"main": "./src/jshint.js",
"dependencies": {
@ -38,12 +46,16 @@
},
"devDependencies": {
"browserify": "9.x",
"conventional-changelog": "0.4.x",
"conventional-github-releaser": "0.4.x",
"coveralls": "2.11.x",
"istanbul": "0.3.x",
"jscs": "1.11.x",
"jshint": "2.6.x",
"mock-stdin": "0.3.x",
"nodeunit": "0.9.x",
"phantom": "~0.7.2",
"phantomjs": "1.9.13",
"regenerate": "1.2.x",
"sinon": "1.12.x",
"unicode-6.3.0": "0.1.x"
@ -56,12 +68,12 @@
"dist",
"src"
],
"gitHead": "e6611af2d180bd2317d5762e85807a481de99ccb",
"_id": "jshint@2.8.0",
"_shasum": "1d09a3bd913c4cadfa81bf18d582bd85bffe0d44",
"_from": "jshint@2.8.0",
"_npmVersion": "2.6.0",
"_nodeVersion": "0.12.0",
"gitHead": "b554ffe61eabf6e3e3a2876a3a377271da308811",
"_id": "jshint@2.9.2",
"_shasum": "0b12d75f8eafb0823b7bf8efbb265b3262401619",
"_from": "jshint@latest",
"_npmVersion": "3.3.12",
"_nodeVersion": "5.3.0",
"_npmUser": {
"name": "jugglinmike",
"email": "mike@mikepennisi.com"
@ -81,9 +93,13 @@
}
],
"dist": {
"shasum": "1d09a3bd913c4cadfa81bf18d582bd85bffe0d44",
"tarball": "http://registry.npmjs.org/jshint/-/jshint-2.8.0.tgz"
"shasum": "0b12d75f8eafb0823b7bf8efbb265b3262401619",
"tarball": "https://registry.npmjs.org/jshint/-/jshint-2.9.2.tgz"
},
"_npmOperationalInternal": {
"host": "packages-16-east.internal.npmjs.com",
"tmp": "tmp/jshint-2.9.2.tgz_1461106429823_0.9580952741671354"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/jshint/-/jshint-2.8.0.tgz"
"_resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.2.tgz"
}

View File

@ -15,6 +15,13 @@ var defReporter = require("./reporters/default").reporter;
var OPTIONS = {
"config": ["c", "Custom configuration file", "string", false ],
"reporter": ["reporter", "Custom reporter (<PATH>|jslint|checkstyle|unix)", "string", undefined ],
"prereq": [
"prereq",
"Comma-separate list of prerequisite (paths). E.g. files which include" +
"definitions of global variabls used throughout your project",
"string",
null
],
"exclude": ["exclude",
"Exclude files matching the given filename pattern (same as .jshintignore)", "string", null],
"exclude-path": ["exclude-path", "Pass in a custom jshintignore file path", "string", null],
@ -74,18 +81,18 @@ function deprecated(text, alt) {
function findConfig(file) {
var dir = path.dirname(path.resolve(file));
var envs = getHomeDir();
if (!envs)
return home;
var home = path.normalize(path.join(envs, ".jshintrc"));
var proj = findFile(".jshintrc", dir);
var home;
if (proj)
return proj;
if (shjs.test("-e", home))
return home;
else if (envs) {
home = path.normalize(path.join(envs, ".jshintrc"));
if (shjs.test("-e", home))
return home;
}
return null;
}
@ -198,7 +205,8 @@ function loadIgnores(params) {
}
var lines = (file ? shjs.cat(file) : "").split("\n");
lines.unshift(params.exclude || "");
var exclude = params.exclude || "";
lines.unshift.apply(lines, exclude.split(","));
return lines
.filter(function(line) {
@ -272,7 +280,7 @@ function extract(code, when) {
// in between the last </script> tag and this <script> tag to preserve
// location information.
inscript = true;
js.push.apply(js, code.slice(index, parser.endIndex).match(/\n\r|\n|\r/g));
js.push.apply(js, code.slice(index, parser.endIndex).match(/\r\n|\n|\r/g));
startOffset = null;
}
@ -289,7 +297,7 @@ function extract(code, when) {
if (!inscript)
return;
var lines = data.split(/\n\r|\n|\r/);
var lines = data.split(/\r\n|\n|\r/);
if (!startOffset) {
lines.some(function(line) {
@ -354,7 +362,7 @@ function extractOffsets(code, when) {
// location information.
inscript = true;
var fragment = code.slice(index, parser.endIndex);
var n = (fragment.match(/\n\r|\n|\r/g) || []).length;
var n = (fragment.match(/\r\n|\n|\r/g) || []).length;
lineCounter += n;
startOffset = null;
}
@ -372,7 +380,7 @@ function extractOffsets(code, when) {
if (!inscript)
return;
var lines = data.split(/\n\r|\n|\r/);
var lines = data.split(/\r\n|\n|\r/);
if (!startOffset) {
lines.some(function(line) {
@ -596,6 +604,12 @@ var exports = {
var results = [];
var data = [];
function mergeCLIPrereq(config) {
if (opts.prereq) {
config.prereq = (config.prereq || []).concat(opts.prereq.split(/\s*,\s*/));
}
}
if (opts.useStdin) {
cli.withStdin(function(code) {
var config = opts.config;
@ -610,12 +624,13 @@ var exports = {
}
if (filename && !config) {
config = loadNpmConfig(filename) ||
exports.loadConfig(findConfig(filename));
config = exports.getConfig(filename);
}
config = config || {};
mergeCLIPrereq(config);
lint(extract(code, opts.extract), results, config, data, filename);
(opts.reporter || defReporter)(results, data, { verbose: opts.verbose });
cb(results.length === 0);
@ -627,6 +642,7 @@ var exports = {
files.forEach(function(file) {
var config = opts.config || exports.getConfig(file);
var code;
var errors = [];
try {
code = shjs.cat(file);
@ -635,19 +651,22 @@ var exports = {
exports.exit(1);
}
lint(extract(code, opts.extract), results, config, data, file);
mergeCLIPrereq(config);
if (results.length) {
lint(extract(code, opts.extract), errors, config, data, file);
if (errors.length) {
var offsets = extractOffsets(code, opts.extract);
if (offsets && offsets.length) {
results.forEach(function(errorInfo) {
errors.forEach(function(errorInfo) {
var line = errorInfo.error.line;
if (line >= 0 && line < offsets.length) {
var offset = +offsets[line];
errorInfo.error.character += offset;
if (line >= 0 && line < offsets.length && offsets[line]) {
errorInfo.error.character += offsets[line];
}
});
}
results = results.concat(errors);
}
});
@ -743,6 +762,7 @@ var exports = {
verbose: options.verbose,
extract: options.extract,
filename: options.filename,
prereq: options.prereq,
useStdin: { "-": true, "/dev/stdin": true }[args[args.length - 1]]
}, done));
}

File diff suppressed because it is too large Load Diff

View File

@ -342,7 +342,7 @@ Lexer.prototype = {
}
// 2-character punctuators: <= >= == != ++ -- << >> && ||
// += -= *= %= &= |= ^= (but not /=, see below)
// += -= *= %= &= |= ^= /=
if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) {
return {
type: Token.Punctuator,
@ -350,7 +350,7 @@ Lexer.prototype = {
};
}
if ("<>=!+-*%&|^".indexOf(ch1) >= 0) {
if ("<>=!+-*%&|^/".indexOf(ch1) >= 0) {
if (ch2 === "=") {
return {
type: Token.Punctuator,
@ -364,22 +364,6 @@ Lexer.prototype = {
};
}
// Special case: /=.
if (ch1 === "/") {
if (ch2 === "=") {
return {
type: Token.Punctuator,
value: "/="
};
}
return {
type: Token.Punctuator,
value: "/"
};
}
return null;
},
@ -399,6 +383,7 @@ Lexer.prototype = {
var rest = this.input.substr(2);
var startLine = this.line;
var startChar = this.char;
var self = this;
// Create a comment token object and make sure it
// has all the data JSHint needs to work with special
@ -417,6 +402,11 @@ Lexer.prototype = {
body = body.replace(/\n/g, " ");
if (label === "/*" && reg.fallsThrough.test(body)) {
isSpecial = true;
commentType = "falls through";
}
special.forEach(function(str) {
if (isSpecial) {
return;
@ -453,6 +443,26 @@ Lexer.prototype = {
commentType = "globals";
break;
default:
var options = body.split(":").map(function(v) {
return v.replace(/^\s+/, "").replace(/\s+$/, "");
});
if (options.length === 2) {
switch (options[0]) {
case "ignore":
switch (options[1]) {
case "start":
self.ignoringLinterErrors = true;
isSpecial = false;
break;
case "end":
self.ignoringLinterErrors = false;
isSpecial = false;
break;
}
}
}
commentType = str;
}
});
@ -767,12 +777,12 @@ Lexer.prototype = {
isAllowedDigit = isOctalDigit;
base = 8;
if (!state.option.esnext) {
if (!state.inES6(true)) {
this.trigger("warning", {
code: "W119",
line: this.line,
character: this.char,
data: [ "Octal integer literal" ]
data: [ "Octal integer literal", "6" ]
});
}
@ -785,12 +795,12 @@ Lexer.prototype = {
isAllowedDigit = isBinaryDigit;
base = 2;
if (!state.option.esnext) {
if (!state.inES6(true)) {
this.trigger("warning", {
code: "W119",
line: this.line,
character: this.char,
data: [ "Binary integer literal" ]
data: [ "Binary integer literal", "6" ]
});
}
@ -1032,10 +1042,15 @@ Lexer.prototype = {
var startChar = this.char;
var depth = this.templateStarts.length;
if (!state.option.esnext) {
// Only lex template strings in ESNext mode.
return null;
} else if (this.peek() === "`") {
if (this.peek() === "`") {
if (!state.inES6(true)) {
this.trigger("warning", {
code: "W119",
line: this.line,
character: this.char,
data: ["template literal syntax", "6"]
});
}
// Template must start with a backtick.
tokenType = Token.TemplateHead;
this.templateStarts.push({ line: this.line, char: this.char });
@ -1434,14 +1449,9 @@ Lexer.prototype = {
this.from = this.char;
// Move to the next non-space character.
var start;
if (/\s/.test(this.peek())) {
start = this.char;
while (/\s/.test(this.peek())) {
this.from += 1;
this.skip();
}
while (/\s/.test(this.peek())) {
this.from += 1;
this.skip();
}
// Methods that work with multi-line structures and move the
@ -1506,7 +1516,7 @@ Lexer.prototype = {
// If we are ignoring linter errors, replace the input with empty string
// if it doesn't already at least start or end a multi-line comment
if (state.ignoreLinterErrors === true) {
if (this.ignoringLinterErrors === true) {
if (!startsWith("/*", "//") && !(this.inComment && endsWith("*/"))) {
this.input = "";
}
@ -1527,7 +1537,8 @@ Lexer.prototype = {
// If there is a limit on line length, warn when lines get too
// long.
if (state.option.maxlen && state.option.maxlen < this.input.length) {
if (!this.ignoringLinterErrors && state.option.maxlen &&
state.option.maxlen < this.input.length) {
var inComment = this.inComment ||
startsWith.call(inputTrimmed, "//") ||
startsWith.call(inputTrimmed, "/*");
@ -1616,7 +1627,8 @@ Lexer.prototype = {
}
if (type === "(identifier)") {
if (value === "return" || value === "case" || value === "typeof") {
if (value === "return" || value === "case" ||
value === "typeof" || value === "instanceof") {
this.prereg = true;
}
@ -1630,6 +1642,10 @@ Lexer.prototype = {
}
}
if (type === "(template)" || type === "(template middle)") {
this.prereg = true;
}
if (!obj) {
obj = Object.create(state.syntax[type]);
}
@ -1757,14 +1773,14 @@ Lexer.prototype = {
return create("(no subst template)", token.value, null, token);
case Token.Identifier:
this.trigger("Identifier", {
this.triggerAsync("Identifier", {
line: this.line,
char: this.char,
from: this.form,
from: this.from,
name: token.value,
raw_name: token.text,
isProperty: state.tokens.curr.id === "."
});
}, checks, function() { return true; });
/* falls through */
case Token.Keyword:

View File

@ -20,7 +20,7 @@ var errors = {
E010: "'with' is not allowed in strict mode.",
// Constants
E011: "const '{a}' has already been declared.",
E011: "'{a}' has already been declared.",
E012: "const '{a}' is initialized to 'undefined'.",
E013: "Attempting to override '{a}' which is a constant.",
@ -62,15 +62,19 @@ var errors = {
E044: null,
E045: "Invalid for each loop.",
E046: "A yield statement shall be within a generator function (with syntax: `function*`)",
E047: null, // Vacant
E047: null,
E048: "{a} declaration not directly within block.",
E049: "A {a} cannot be named '{b}'.",
E050: "Mozilla requires the yield expression to be parenthesized here.",
E051: "Regular parameters cannot come after default parameters.",
E051: null,
E052: "Unclosed template literal.",
E053: "Export declaration must be in global scope.",
E054: "Class properties must be methods. Expected '(' but instead saw '{a}'.",
E055: "The '{a}' option cannot be set after any executable code."
E055: "The '{a}' option cannot be set after any executable code.",
E056: "'{a}' was used before it was declared, which is illegal for '{b}' variables.",
E057: "Invalid meta property: '{a}.{b}'.",
E058: "Missing semicolon.",
E059: "Incompatible values for the '{a}' and '{b}' linting options."
};
var warnings = {
@ -94,7 +98,8 @@ var warnings = {
W018: "Confusing use of '{a}'.",
W019: "Use the isNaN function to compare with NaN.",
W020: "Read only.",
W021: "'{a}' is a function.",
W021: "Reassignment of '{a}', which is is a {b}. " +
"Use 'var' or 'let' to declare bindings that may change.",
W022: "Do not assign to the exception parameter.",
W023: "Expected an identifier in an assignment and instead saw a function invocation.",
W024: "Expected an identifier and instead saw '{a}' (a reserved word).",
@ -168,7 +173,7 @@ var warnings = {
W089: "The body of a for in should be wrapped in an if statement to filter " +
"unwanted properties from the prototype.",
W090: "'{a}' is not a statement label.",
W091: "'{a}' is out of scope.",
W091: null,
W093: "Did you mean to return a conditional instead of an assignment?",
W094: "Unexpected comma.",
W095: "Expected a string and instead saw {a}.",
@ -180,7 +185,7 @@ var warnings = {
W101: "Line is too long.",
W102: null,
W103: "The '{a}' property is deprecated.",
W104: "'{a}' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).",
W104: "'{a}' is available in ES{b} (use 'esversion: {b}') or Mozilla JS extensions (use moz).",
W105: "Unexpected {a} in '{b}'.",
W106: "Identifier '{a}' is not in camel case.",
W107: "Script URL.",
@ -194,13 +199,13 @@ var warnings = {
W116: "Expected '{a}' and instead saw '{b}'.",
W117: "'{a}' is not defined.",
W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).",
W119: "'{a}' is only available in ES6 (use esnext option).",
W119: "'{a}' is only available in ES{b} (use 'esversion: {b}').",
W120: "You might be leaking a variable ({a}) here.",
W121: "Extending prototype of native object: '{a}'.",
W122: "Invalid typeof value '{a}'",
W123: "'{a}' is already defined in outer scope.",
W124: "A generator function shall contain a yield statement.",
W125: "This line contains non-breaking spaces: http://jshint.com/doc/options/#nonbsp",
W125: "This line contains non-breaking spaces: http://jshint.com/docs/options/#nonbsp",
W126: "Unnecessary grouping operator.",
W127: "Unexpected use of a comma operator.",
W128: "Empty array elements require elision=true.",
@ -210,7 +215,11 @@ var warnings = {
W131: "Invalid parameter after rest parameter.",
W132: "`var` declarations are forbidden. Use `let` or `const` instead.",
W133: "Invalid for-{a} loop left-hand-side: {b}.",
W134: "The '{a}' option is only available when linting ECMAScript {b} code."
W134: "The '{a}' option is only available when linting ECMAScript {b} code.",
W135: "{a} may not be supported by non-browser environments.",
W136: "'{a}' must be in function scope.",
W137: "Empty destructuring.",
W138: "Regular parameters should not come after default parameters."
};
var info = {

View File

@ -91,6 +91,8 @@ exports.bool = {
* specification. Use this option if you need your program to be executable
* in older browserssuch as Internet Explorer 6/7/8/9and other legacy
* JavaScript environments.
*
* @deprecated Use `esversion: 3` instead.
*/
es3 : true,
@ -98,6 +100,8 @@ exports.bool = {
* This option enables syntax first defined in [the ECMAScript 5.1
* specification](http://es5.github.io/). This includes allowing reserved
* keywords as object properties.
*
* @deprecated Use `esversion: 5` instead.
*/
es5 : true,
@ -258,21 +262,6 @@ exports.bool = {
*/
singleGroups: false,
/**
* This option requires all functions to run in ECMAScript 5's strict mode.
* [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode)
* is a way to opt in to a restricted variant of JavaScript. Strict mode
* eliminates some JavaScript pitfalls that didn't cause errors by changing
* them to produce errors. It also fixes mistakes that made it difficult
* for the JavaScript engines to perform certain optimizations.
*
* *Note:* This option enables strict mode for function scope only. It
* *prohibits* the global scoped strict mode because it might break
* third-party widgets on your page. If you really want to use global
* strict mode, see the *globalstrict* option.
*/
strict : true,
/**
* When set to true, the use of VariableStatements are forbidden.
* For example:
@ -375,6 +364,8 @@ exports.bool = {
* recommended.
*
* For more info about strict mode see the `strict` option.
*
* @deprecated Use `strict: "global"`.
*/
globalstrict: true,
@ -550,13 +541,14 @@ exports.bool = {
/**
* This option tells JSHint that your code uses ECMAScript 6 specific
* syntax. Note that these features are not finalized yet and not all
* browsers implement them.
* syntax. Note that not all browsers implement these features.
*
* More info:
*
* * [Draft Specification for ES.next (ECMA-262 Ed.
* 6)](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts)
* * [Specification for ECMAScript
* 6](http://www.ecma-international.org/ecma-262/6.0/index.html)
*
* @deprecated Use `esversion: 6` instead.
*/
esnext : true,
@ -757,7 +749,18 @@ exports.val = {
*/
maxerr : false,
predef : false, // predef is deprecated and being replaced by globals
/**
* This option allows you to control which variables JSHint considers to be
* implicitly defined in the environment. Configure it with an array of
* string values. Prefixing a variable name with a hyphen (-) character will
* remove that name from the collection of predefined variables.
*
* JSHint will consider variables declared in this way to be read-only.
*
* This option cannot be specified in-line; it may only be used via the
* JavaScript API or from an external configuration file.
*/
predef : false,
/**
* This option can be used to specify a white list of global variables that
@ -772,6 +775,9 @@ exports.val = {
* See also the "environment" options: a set of options to be used as short
* hand for enabling global variables defined in common JavaScript
* environments.
*
* To configure `globals` within an individual file, see [Inline
* Configuration](http://jshint.com/docs/#inline-configuration).
*/
globals : false,
@ -871,6 +877,25 @@ exports.val = {
*/
shadow : false,
/**
* This option requires the code to run in ECMAScript 5's strict mode.
* [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode)
* is a way to opt in to a restricted variant of JavaScript. Strict mode
* eliminates some JavaScript pitfalls that didn't cause errors by changing
* them to produce errors. It also fixes mistakes that made it difficult
* for the JavaScript engines to perform certain optimizations.
*
* - "global" - there must be a `"use strict";` directive at global level
* - "implied" - lint the code as if there is the `"use strict";` directive
* - false - disable warnings about strict mode
* - true - there must be a `"use strict";` directive at function level;
* this is preferable for scripts intended to be loaded in web
* browsers directly because enabling strict mode globally
* could adversely effect other scripts running on the same
* page
*/
strict : true,
/**
* This option warns when you define and never use your variables. It is very
* useful for general code cleanup, especially when used in addition to
@ -921,8 +946,24 @@ exports.val = {
// end - stop ignoring lines, starting on the next line
// line - ignore warnings / errors for just a single line
// (this option does not bypass the lexer)
ignoreDelimiters: false // array of start/end delimiters used to ignore
// certain chunks from code
ignoreDelimiters: false, // array of start/end delimiters used to ignore
// certain chunks from code
/**
* This option is used to specify the ECMAScript version to which the code
* must adhere. It can assume one of the following values:
* - `3` - If you need your program to be executable
* in older browserssuch as Internet Explorer 6/7/8/9and other legacy
* JavaScript environments
* - `5` - To enable syntax first defined in [the ECMAScript 5.1
* specification](http://www.ecma-international.org/ecma-262/5.1/index.html).
* This includes allowing reserved keywords as object properties.
* - `6` - To tell JSHint that your code uses [ECMAScript
* 6](http://www.ecma-international.org/ecma-262/6.0/index.html) specific
* syntax. Note that not all browsers implement them.
*/
esversion: 5
};
// These are JSHint boolean options which are shared with JSLint

View File

@ -31,7 +31,7 @@ exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i;
// Catches /* falls through */ comments (ft)
exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/;
exports.fallsThrough = /^\s*falls?\sthrough\s*$/;
// very conservative rule (eg: only one space between the start of the comment and the first character)
// to relax the maxlen option

857
js/node/node_modules/jshint/src/scope-manager.js generated vendored Normal file
View File

@ -0,0 +1,857 @@
"use strict";
var _ = require("lodash");
var events = require("events");
// Used to denote membership in lookup tables (a primitive value such as `true`
// would be silently rejected for the property name "__proto__" in some
// environments)
var marker = {};
/**
* Creates a scope manager that handles variables and labels, storing usages
* and resolving when variables are used and undefined
*/
var scopeManager = function(state, predefined, exported, declared) {
var _current;
var _scopeStack = [];
function _newScope(type) {
_current = {
"(labels)": Object.create(null),
"(usages)": Object.create(null),
"(breakLabels)": Object.create(null),
"(parent)": _current,
"(type)": type,
"(params)": (type === "functionparams" || type === "catchparams") ? [] : null
};
_scopeStack.push(_current);
}
_newScope("global");
_current["(predefined)"] = predefined;
var _currentFunctBody = _current; // this is the block after the params = function
var usedPredefinedAndGlobals = Object.create(null);
var impliedGlobals = Object.create(null);
var unuseds = [];
var emitter = new events.EventEmitter();
function warning(code, token) {
emitter.emit("warning", {
code: code,
token: token,
data: _.slice(arguments, 2)
});
}
function error(code, token) {
emitter.emit("warning", {
code: code,
token: token,
data: _.slice(arguments, 2)
});
}
function _setupUsages(labelName) {
if (!_current["(usages)"][labelName]) {
_current["(usages)"][labelName] = {
"(modified)": [],
"(reassigned)": [],
"(tokens)": []
};
}
}
var _getUnusedOption = function(unused_opt) {
if (unused_opt === undefined) {
unused_opt = state.option.unused;
}
if (unused_opt === true) {
unused_opt = "last-param";
}
return unused_opt;
};
var _warnUnused = function(name, tkn, type, unused_opt) {
var line = tkn.line;
var chr = tkn.from;
var raw_name = tkn.raw_text || name;
unused_opt = _getUnusedOption(unused_opt);
var warnable_types = {
"vars": ["var"],
"last-param": ["var", "param"],
"strict": ["var", "param", "last-param"]
};
if (unused_opt) {
if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) {
warning("W098", { line: line, from: chr }, raw_name);
}
}
// inconsistent - see gh-1894
if (unused_opt || type === "var") {
unuseds.push({
name: name,
line: line,
character: chr
});
}
};
/**
* Checks the current scope for unused identifiers
*/
function _checkForUnused() {
// function params are handled specially
// assume that parameters are the only thing declared in the param scope
if (_current["(type)"] === "functionparams") {
_checkParams();
return;
}
var curentLabels = _current["(labels)"];
for (var labelName in curentLabels) {
if (curentLabels[labelName]) {
if (curentLabels[labelName]["(type)"] !== "exception" &&
curentLabels[labelName]["(unused)"]) {
_warnUnused(labelName, curentLabels[labelName]["(token)"], "var");
}
}
}
}
/**
* Checks the current scope for unused parameters
* Must be called in a function parameter scope
*/
function _checkParams() {
var params = _current["(params)"];
if (!params) {
return;
}
var param = params.pop();
var unused_opt;
while (param) {
var label = _current["(labels)"][param];
unused_opt = _getUnusedOption(state.funct["(unusedOption)"]);
// 'undefined' is a special case for (function(window, undefined) { ... })();
// patterns.
if (param === "undefined")
return;
if (label["(unused)"]) {
_warnUnused(param, label["(token)"], "param", state.funct["(unusedOption)"]);
} else if (unused_opt === "last-param") {
return;
}
param = params.pop();
}
}
/**
* Finds the relevant label's scope, searching from nearest outwards
* @returns {Object} the scope the label was found in
*/
function _getLabel(labelName) {
for (var i = _scopeStack.length - 1 ; i >= 0; --i) {
var scopeLabels = _scopeStack[i]["(labels)"];
if (scopeLabels[labelName]) {
return scopeLabels;
}
}
}
function usedSoFarInCurrentFunction(labelName) {
// used so far in this whole function and any sub functions
for (var i = _scopeStack.length - 1; i >= 0; i--) {
var current = _scopeStack[i];
if (current["(usages)"][labelName]) {
return current["(usages)"][labelName];
}
if (current === _currentFunctBody) {
break;
}
}
return false;
}
function _checkOuterShadow(labelName, token) {
// only check if shadow is outer
if (state.option.shadow !== "outer") {
return;
}
var isGlobal = _currentFunctBody["(type)"] === "global",
isNewFunction = _current["(type)"] === "functionparams";
var outsideCurrentFunction = !isGlobal;
for (var i = 0; i < _scopeStack.length; i++) {
var stackItem = _scopeStack[i];
if (!isNewFunction && _scopeStack[i + 1] === _currentFunctBody) {
outsideCurrentFunction = false;
}
if (outsideCurrentFunction && stackItem["(labels)"][labelName]) {
warning("W123", token, labelName);
}
if (stackItem["(breakLabels)"][labelName]) {
warning("W123", token, labelName);
}
}
}
function _latedefWarning(type, labelName, token) {
if (state.option.latedef) {
// if either latedef is strict and this is a function
// or this is not a function
if ((state.option.latedef === true && type === "function") ||
type !== "function") {
warning("W003", token, labelName);
}
}
}
var scopeManagerInst = {
on: function(names, listener) {
names.split(" ").forEach(function(name) {
emitter.on(name, listener);
});
},
isPredefined: function(labelName) {
return !this.has(labelName) && _.has(_scopeStack[0]["(predefined)"], labelName);
},
/**
* Tell the manager we are entering a new block of code
* @param {string} [type] - The type of the block. Valid values are
* "functionparams", "catchparams" and
* "functionouter"
*/
stack: function(type) {
var previousScope = _current;
_newScope(type);
if (!type && previousScope["(type)"] === "functionparams") {
_current["(isFuncBody)"] = true;
_current["(context)"] = _currentFunctBody;
_currentFunctBody = _current;
}
},
unstack: function() {
// jshint proto: true
var subScope = _scopeStack.length > 1 ? _scopeStack[_scopeStack.length - 2] : null;
var isUnstackingFunctionBody = _current === _currentFunctBody,
isUnstackingFunctionParams = _current["(type)"] === "functionparams",
isUnstackingFunctionOuter = _current["(type)"] === "functionouter";
var i, j;
var currentUsages = _current["(usages)"];
var currentLabels = _current["(labels)"];
var usedLabelNameList = Object.keys(currentUsages);
if (currentUsages.__proto__ && usedLabelNameList.indexOf("__proto__") === -1) {
usedLabelNameList.push("__proto__");
}
for (i = 0; i < usedLabelNameList.length; i++) {
var usedLabelName = usedLabelNameList[i];
var usage = currentUsages[usedLabelName];
var usedLabel = currentLabels[usedLabelName];
if (usedLabel) {
var usedLabelType = usedLabel["(type)"];
if (usedLabel["(useOutsideOfScope)"] && !state.option.funcscope) {
var usedTokens = usage["(tokens)"];
if (usedTokens) {
for (j = 0; j < usedTokens.length; j++) {
// Keep the consistency of https://github.com/jshint/jshint/issues/2409
if (usedLabel["(function)"] === usedTokens[j]["(function)"]) {
error("W038", usedTokens[j], usedLabelName);
}
}
}
}
// mark the label used
_current["(labels)"][usedLabelName]["(unused)"] = false;
// check for modifying a const
if (usedLabelType === "const" && usage["(modified)"]) {
for (j = 0; j < usage["(modified)"].length; j++) {
error("E013", usage["(modified)"][j], usedLabelName);
}
}
// check for re-assigning a function declaration
if ((usedLabelType === "function" || usedLabelType === "class") &&
usage["(reassigned)"]) {
for (j = 0; j < usage["(reassigned)"].length; j++) {
if (!usage["(reassigned)"][j].ignoreW021) {
warning("W021", usage["(reassigned)"][j], usedLabelName, usedLabelType);
}
}
}
continue;
}
if (isUnstackingFunctionOuter) {
state.funct["(isCapturing)"] = true;
}
if (subScope) {
// not exiting the global scope, so copy the usage down in case its an out of scope usage
if (!subScope["(usages)"][usedLabelName]) {
subScope["(usages)"][usedLabelName] = usage;
if (isUnstackingFunctionBody) {
subScope["(usages)"][usedLabelName]["(onlyUsedSubFunction)"] = true;
}
} else {
var subScopeUsage = subScope["(usages)"][usedLabelName];
subScopeUsage["(modified)"] = subScopeUsage["(modified)"].concat(usage["(modified)"]);
subScopeUsage["(tokens)"] = subScopeUsage["(tokens)"].concat(usage["(tokens)"]);
subScopeUsage["(reassigned)"] =
subScopeUsage["(reassigned)"].concat(usage["(reassigned)"]);
}
} else {
// this is exiting global scope, so we finalise everything here - we are at the end of the file
if (typeof _current["(predefined)"][usedLabelName] === "boolean") {
// remove the declared token, so we know it is used
delete declared[usedLabelName];
// note it as used so it can be reported
usedPredefinedAndGlobals[usedLabelName] = marker;
// check for re-assigning a read-only (set to false) predefined
if (_current["(predefined)"][usedLabelName] === false && usage["(reassigned)"]) {
for (j = 0; j < usage["(reassigned)"].length; j++) {
if (!usage["(reassigned)"][j].ignoreW020) {
warning("W020", usage["(reassigned)"][j]);
}
}
}
}
else {
// label usage is not predefined and we have not found a declaration
// so report as undeclared
if (usage["(tokens)"]) {
for (j = 0; j < usage["(tokens)"].length; j++) {
var undefinedToken = usage["(tokens)"][j];
// if its not a forgiven undefined (e.g. typof x)
if (!undefinedToken.forgiveUndef) {
// if undef is on and undef was on when the token was defined
if (state.option.undef && !undefinedToken.ignoreUndef) {
warning("W117", undefinedToken, usedLabelName);
}
if (impliedGlobals[usedLabelName]) {
impliedGlobals[usedLabelName].line.push(undefinedToken.line);
} else {
impliedGlobals[usedLabelName] = {
name: usedLabelName,
line: [undefinedToken.line]
};
}
}
}
}
}
}
}
// if exiting the global scope, we can warn about declared globals that haven't been used yet
if (!subScope) {
Object.keys(declared)
.forEach(function(labelNotUsed) {
_warnUnused(labelNotUsed, declared[labelNotUsed], "var");
});
}
// If this is not a function boundary, transfer function-scoped labels to
// the parent block (a rough simulation of variable hoisting). Previously
// existing labels in the parent block should take precedence so that things and stuff.
if (subScope && !isUnstackingFunctionBody &&
!isUnstackingFunctionParams && !isUnstackingFunctionOuter) {
var labelNames = Object.keys(currentLabels);
for (i = 0; i < labelNames.length; i++) {
var defLabelName = labelNames[i];
var defLabel = currentLabels[defLabelName];
if (!defLabel["(blockscoped)"] && defLabel["(type)"] !== "exception") {
var shadowed = subScope["(labels)"][defLabelName];
// Do not overwrite a label if it exists in the parent scope
// because it is shared by adjacent blocks. Copy the `unused`
// property so that any references found within the current block
// are counted toward that higher-level declaration.
if (shadowed) {
shadowed["(unused)"] &= defLabel["(unused)"];
// "Hoist" the variable to the parent block, decorating the label
// so that future references, though technically valid, can be
// reported as "out-of-scope" in the absence of the `funcscope`
// option.
} else {
defLabel["(useOutsideOfScope)"] =
// Do not warn about out-of-scope usages in the global scope
_currentFunctBody["(type)"] !== "global" &&
// When a higher scope contains a binding for the label, the
// label is a re-declaration and should not prompt "used
// out-of-scope" warnings.
!this.funct.has(defLabelName, { excludeCurrent: true });
subScope["(labels)"][defLabelName] = defLabel;
}
delete currentLabels[defLabelName];
}
}
}
_checkForUnused();
_scopeStack.pop();
if (isUnstackingFunctionBody) {
_currentFunctBody = _scopeStack[_.findLastIndex(_scopeStack, function(scope) {
// if function or if global (which is at the bottom so it will only return true if we call back)
return scope["(isFuncBody)"] || scope["(type)"] === "global";
})];
}
_current = subScope;
},
/**
* Add a param to the current scope
* @param {string} labelName
* @param {Token} token
* @param {string} [type="param"] param type
*/
addParam: function(labelName, token, type) {
type = type || "param";
if (type === "exception") {
// if defined in the current function
var previouslyDefinedLabelType = this.funct.labeltype(labelName);
if (previouslyDefinedLabelType && previouslyDefinedLabelType !== "exception") {
// and has not been used yet in the current function scope
if (!state.option.node) {
warning("W002", state.tokens.next, labelName);
}
}
}
// The variable was declared in the current scope
if (_.has(_current["(labels)"], labelName)) {
_current["(labels)"][labelName].duplicated = true;
// The variable was declared in an outer scope
} else {
// if this scope has the variable defined, it's a re-definition error
_checkOuterShadow(labelName, token, type);
_current["(labels)"][labelName] = {
"(type)" : type,
"(token)": token,
"(unused)": true };
_current["(params)"].push(labelName);
}
if (_.has(_current["(usages)"], labelName)) {
var usage = _current["(usages)"][labelName];
// if its in a sub function it is not necessarily an error, just latedef
if (usage["(onlyUsedSubFunction)"]) {
_latedefWarning(type, labelName, token);
} else {
// this is a clear illegal usage for block scoped variables
warning("E056", token, labelName, type);
}
}
},
validateParams: function() {
// This method only concerns errors for function parameters
if (_currentFunctBody["(type)"] === "global") {
return;
}
var isStrict = state.isStrict();
var currentFunctParamScope = _currentFunctBody["(parent)"];
if (!currentFunctParamScope["(params)"]) {
return;
}
currentFunctParamScope["(params)"].forEach(function(labelName) {
var label = currentFunctParamScope["(labels)"][labelName];
if (label && label.duplicated) {
if (isStrict) {
warning("E011", label["(token)"], labelName);
} else if (state.option.shadow !== true) {
warning("W004", label["(token)"], labelName);
}
}
});
},
getUsedOrDefinedGlobals: function() {
// jshint proto: true
var list = Object.keys(usedPredefinedAndGlobals);
// If `__proto__` is used as a global variable name, its entry in the
// lookup table may not be enumerated by `Object.keys` (depending on the
// environment).
if (usedPredefinedAndGlobals.__proto__ === marker &&
list.indexOf("__proto__") === -1) {
list.push("__proto__");
}
return list;
},
/**
* Gets an array of implied globals
* @returns {Array.<{ name: string, line: Array.<number>}>}
*/
getImpliedGlobals: function() {
// jshint proto: true
var values = _.values(impliedGlobals);
var hasProto = false;
// If `__proto__` is an implied global variable, its entry in the lookup
// table may not be enumerated by `_.values` (depending on the
// environment).
if (impliedGlobals.__proto__) {
hasProto = values.some(function(value) {
return value.name === "__proto__";
});
if (!hasProto) {
values.push(impliedGlobals.__proto__);
}
}
return values;
},
/**
* Returns a list of unused variables
* @returns {Array}
*/
getUnuseds: function() {
return unuseds;
},
has: function(labelName) {
return Boolean(_getLabel(labelName));
},
labeltype: function(labelName) {
// returns a labels type or null if not present
var scopeLabels = _getLabel(labelName);
if (scopeLabels) {
return scopeLabels[labelName]["(type)"];
}
return null;
},
/**
* for the exported options, indicating a variable is used outside the file
*/
addExported: function(labelName) {
var globalLabels = _scopeStack[0]["(labels)"];
if (_.has(declared, labelName)) {
// remove the declared token, so we know it is used
delete declared[labelName];
} else if (_.has(globalLabels, labelName)) {
globalLabels[labelName]["(unused)"] = false;
} else {
for (var i = 1; i < _scopeStack.length; i++) {
var scope = _scopeStack[i];
// if `scope.(type)` is not defined, it is a block scope
if (!scope["(type)"]) {
if (_.has(scope["(labels)"], labelName) &&
!scope["(labels)"][labelName]["(blockscoped)"]) {
scope["(labels)"][labelName]["(unused)"] = false;
return;
}
} else {
break;
}
}
exported[labelName] = true;
}
},
/**
* Mark an indentifier as es6 module exported
*/
setExported: function(labelName, token) {
this.block.use(labelName, token);
},
/**
* adds an indentifier to the relevant current scope and creates warnings/errors as necessary
* @param {string} labelName
* @param {Object} opts
* @param {String} opts.type - the type of the label e.g. "param", "var", "let, "const", "function"
* @param {Token} opts.token - the token pointing at the declaration
*/
addlabel: function(labelName, opts) {
var type = opts.type;
var token = opts.token;
var isblockscoped = type === "let" || type === "const" || type === "class";
var isexported = (isblockscoped ? _current : _currentFunctBody)["(type)"] === "global" &&
_.has(exported, labelName);
// outer shadow check (inner is only on non-block scoped)
_checkOuterShadow(labelName, token, type);
// if is block scoped (let or const)
if (isblockscoped) {
var declaredInCurrentScope = _current["(labels)"][labelName];
// for block scoped variables, params are seen in the current scope as the root function
// scope, so check these too.
if (!declaredInCurrentScope && _current === _currentFunctBody &&
_current["(type)"] !== "global") {
declaredInCurrentScope = !!_currentFunctBody["(parent)"]["(labels)"][labelName];
}
// if its not already defined (which is an error, so ignore) and is used in TDZ
if (!declaredInCurrentScope && _current["(usages)"][labelName]) {
var usage = _current["(usages)"][labelName];
// if its in a sub function it is not necessarily an error, just latedef
if (usage["(onlyUsedSubFunction)"]) {
_latedefWarning(type, labelName, token);
} else {
// this is a clear illegal usage for block scoped variables
warning("E056", token, labelName, type);
}
}
// if this scope has the variable defined, its a re-definition error
if (declaredInCurrentScope) {
warning("E011", token, labelName);
}
else if (state.option.shadow === "outer") {
// if shadow is outer, for block scope we want to detect any shadowing within this function
if (scopeManagerInst.funct.has(labelName)) {
warning("W004", token, labelName);
}
}
scopeManagerInst.block.add(labelName, type, token, !isexported);
} else {
var declaredInCurrentFunctionScope = scopeManagerInst.funct.has(labelName);
// check for late definition, ignore if already declared
if (!declaredInCurrentFunctionScope && usedSoFarInCurrentFunction(labelName)) {
_latedefWarning(type, labelName, token);
}
// defining with a var or a function when a block scope variable of the same name
// is in scope is an error
if (scopeManagerInst.funct.has(labelName, { onlyBlockscoped: true })) {
warning("E011", token, labelName);
} else if (state.option.shadow !== true) {
// now since we didn't get any block scope variables, test for var/function
// shadowing
if (declaredInCurrentFunctionScope && labelName !== "__proto__") {
// see https://github.com/jshint/jshint/issues/2400
if (_currentFunctBody["(type)"] !== "global") {
warning("W004", token, labelName);
}
}
}
scopeManagerInst.funct.add(labelName, type, token, !isexported);
if (_currentFunctBody["(type)"] === "global" && !state.impliedClosure()) {
usedPredefinedAndGlobals[labelName] = marker;
}
}
},
funct: {
/**
* Returns the label type given certain options
* @param labelName
* @param {Object=} options
* @param {Boolean=} options.onlyBlockscoped - only include block scoped labels
* @param {Boolean=} options.excludeParams - exclude the param scope
* @param {Boolean=} options.excludeCurrent - exclude the current scope
* @returns {String}
*/
labeltype: function(labelName, options) {
var onlyBlockscoped = options && options.onlyBlockscoped;
var excludeParams = options && options.excludeParams;
var currentScopeIndex = _scopeStack.length - (options && options.excludeCurrent ? 2 : 1);
for (var i = currentScopeIndex; i >= 0; i--) {
var current = _scopeStack[i];
if (current["(labels)"][labelName] &&
(!onlyBlockscoped || current["(labels)"][labelName]["(blockscoped)"])) {
return current["(labels)"][labelName]["(type)"];
}
var scopeCheck = excludeParams ? _scopeStack[ i - 1 ] : current;
if (scopeCheck && scopeCheck["(type)"] === "functionparams") {
return null;
}
}
return null;
},
/**
* Returns if a break label exists in the function scope
* @param {string} labelName
* @returns {boolean}
*/
hasBreakLabel: function(labelName) {
for (var i = _scopeStack.length - 1; i >= 0; i--) {
var current = _scopeStack[i];
if (current["(breakLabels)"][labelName]) {
return true;
}
if (current["(type)"] === "functionparams") {
return false;
}
}
return false;
},
/**
* Returns if the label is in the current function scope
* See scopeManager.funct.labelType for options
*/
has: function(labelName, options) {
return Boolean(this.labeltype(labelName, options));
},
/**
* Adds a new function scoped variable
* see block.add for block scoped
*/
add: function(labelName, type, tok, unused) {
_current["(labels)"][labelName] = {
"(type)" : type,
"(token)": tok,
"(blockscoped)": false,
"(function)": _currentFunctBody,
"(unused)": unused };
}
},
block: {
/**
* is the current block global?
* @returns Boolean
*/
isGlobal: function() {
return _current["(type)"] === "global";
},
use: function(labelName, token) {
// if resolves to current function params, then do not store usage just resolve
// this is because function(a) { var a; a = a; } will resolve to the param, not
// to the unset var
// first check the param is used
var paramScope = _currentFunctBody["(parent)"];
if (paramScope && paramScope["(labels)"][labelName] &&
paramScope["(labels)"][labelName]["(type)"] === "param") {
// then check its not declared by a block scope variable
if (!scopeManagerInst.funct.has(labelName,
{ excludeParams: true, onlyBlockscoped: true })) {
paramScope["(labels)"][labelName]["(unused)"] = false;
}
}
if (token && (state.ignored.W117 || state.option.undef === false)) {
token.ignoreUndef = true;
}
_setupUsages(labelName);
_current["(usages)"][labelName]["(onlyUsedSubFunction)"] = false;
if (token) {
token["(function)"] = _currentFunctBody;
_current["(usages)"][labelName]["(tokens)"].push(token);
}
},
reassign: function(labelName, token) {
token.ignoreW020 = state.ignored.W020;
token.ignoreW021 = state.ignored.W021;
this.modify(labelName, token);
_current["(usages)"][labelName]["(reassigned)"].push(token);
},
modify: function(labelName, token) {
_setupUsages(labelName);
_current["(usages)"][labelName]["(onlyUsedSubFunction)"] = false;
_current["(usages)"][labelName]["(modified)"].push(token);
},
/**
* Adds a new variable
*/
add: function(labelName, type, tok, unused) {
_current["(labels)"][labelName] = {
"(type)" : type,
"(token)": tok,
"(blockscoped)": true,
"(unused)": unused };
},
addBreakLabel: function(labelName, opts) {
var token = opts.token;
if (scopeManagerInst.funct.hasBreakLabel(labelName)) {
warning("E011", token, labelName);
}
else if (state.option.shadow === "outer") {
if (scopeManagerInst.funct.has(labelName)) {
warning("W004", token, labelName);
} else {
_checkOuterShadow(labelName, token);
}
}
_current["(breakLabels)"][labelName] = token;
}
}
};
return scopeManagerInst;
};
module.exports = scopeManager;

View File

@ -11,34 +11,117 @@ var state = {
*/
isStrict: function() {
return this.directive["use strict"] || this.inClassBody ||
this.option.module;
},
// Assumption: chronologically ES3 < ES5 < ES6/ESNext < Moz
inMoz: function() {
return this.option.moz && !this.option.esnext;
this.option.module || this.option.strict === "implied";
},
/**
* @param {object} options
* @param {boolean} options.strict - When `true`, only consider ESNext when
* in "esnext" code and *not* in "moz".
* Determine if the current state warrants a warning for statements outside
* of strict mode code.
*
* While emitting warnings based on function scope would be more intuitive
* (and less noisy), JSHint observes statement-based semantics in order to
* preserve legacy behavior.
*
* This method does not take the state of the parser into account, making no
* distinction between global code and function code. Because the "missing
* 'use strict'" warning is *also* reported at function boundaries, this
* function interprets `strict` option values `true` and `undefined` as
* equivalent.
*/
inESNext: function(strict) {
if (strict) {
return !this.option.moz && this.option.esnext;
stmtMissingStrict: function() {
if (this.option.strict === "global") {
return true;
}
return this.option.moz || this.option.esnext;
if (this.option.strict === false) {
return false;
}
if (this.option.globalstrict) {
return true;
}
return false;
},
inES5: function() {
return !this.option.es3;
allowsGlobalUsd: function() {
return this.option.strict === "global" || this.option.globalstrict ||
this.option.module || this.impliedClosure();
},
inES3: function() {
return this.option.es3;
/**
* Determine if the current configuration describes an environment that is
* wrapped in an immediately-invoked function expression prior to evaluation.
*
* @returns {boolean}
*/
impliedClosure: function() {
return this.option.node || this.option.phantom || this.option.browserify;
},
// Assumption: chronologically ES3 < ES5 < ES6 < Moz
inMoz: function() {
return this.option.moz;
},
/**
* @param {boolean} strict - When `true`, only consider ES6 when in
* "esversion: 6" code.
*/
inES6: function(strict) {
if (strict) {
return this.esVersion === 6;
}
return this.option.moz || this.esVersion >= 6;
},
/**
* @param {boolean} strict - When `true`, return `true` only when
* esVersion is exactly 5
*/
inES5: function(strict) {
if (strict) {
return (!this.esVersion || this.esVersion === 5) && !this.option.moz;
}
return !this.esVersion || this.esVersion >= 5 || this.option.moz;
},
/**
* Determine the current version of the input language by inspecting the
* value of all ECMAScript-version-related options. This logic is necessary
* to ensure compatibility with deprecated options `es3`, `es5`, and
* `esnext`, and it may be drastically simplified when those options are
* removed.
*
* @returns {string|null} - the name of any incompatible option detected,
* `null` otherwise
*/
inferEsVersion: function() {
var badOpt = null;
if (this.option.esversion) {
if (this.option.es3) {
badOpt = "es3";
} else if (this.option.es5) {
badOpt = "es5";
} else if (this.option.esnext) {
badOpt = "esnext";
}
if (badOpt) {
return badOpt;
}
this.esVersion = this.option.esversion;
} else if (this.option.es3) {
this.esVersion = 3;
} else if (this.option.esnext) {
this.esVersion = 6;
}
return null;
},
reset: function() {
this.tokens = {
@ -48,6 +131,8 @@ var state = {
};
this.option = {};
this.esVersion = 5;
this.funct = null;
this.ignored = {};
this.directive = {};
this.jsonMode = false;
@ -59,9 +144,6 @@ var state = {
this.forinifcheckneeded = false;
this.nameStack = new NameStack();
this.inClassBody = false;
// Blank out non-multi-line-commented lines when ignoring linter errors
this.ignoreLinterErrors = false;
}
};

View File

@ -13,13 +13,14 @@ exports.register = function(linter) {
linter.warn("W103", {
line: data.line,
char: data.char,
data: [ data.name ]
data: [ data.name, "6" ]
});
}
});
// Check for properties named __iterator__. This is a special property
// available only in browsers with JavaScript 1.7 implementation.
// available only in browsers with JavaScript 1.7 implementation, but
// it is deprecated for ES6
linter.on("Identifier", function style_scanIterator(data) {
if (linter.getOption("iterator")) {
@ -27,7 +28,7 @@ exports.register = function(linter) {
}
if (data.name === "__iterator__") {
linter.warn("W104", {
linter.warn("W103", {
line: data.line,
char: data.char,
data: [ data.name ]
@ -46,7 +47,7 @@ exports.register = function(linter) {
if (data.name.replace(/^_+|_+$/g, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) {
linter.warn("W106", {
line: data.line,
char: data.from,
char: data.char,
data: [ data.name ]
});
}

View File

@ -73,6 +73,7 @@ exports.browser = {
close : false,
closed : false,
Comment : false,
CompositionEvent : false,
CustomEvent : false,
DOMParser : false,
defaultStatus : false,
@ -84,6 +85,8 @@ exports.browser = {
Event : false,
event : false,
fetch : false,
File : false,
FileList : false,
FileReader : false,
FormData : false,
focus : false,
@ -97,6 +100,7 @@ exports.browser = {
HTMLBRElement : false,
HTMLButtonElement : false,
HTMLCanvasElement : false,
HTMLCollection : false,
HTMLDirectoryElement : false,
HTMLDivElement : false,
HTMLDListElement : false,
@ -176,6 +180,7 @@ exports.browser = {
opener : false,
Option : false,
parent : false,
performance : false,
print : false,
Range : false,
requestAnimationFrame : false,
@ -365,6 +370,7 @@ exports.browser = {
WebGLUniformLocation : false,
WebSocket : false,
window : false,
Window : false,
Worker : false,
XDomainRequest : false,
XMLHttpRequest : false,
@ -465,6 +471,7 @@ exports.qunit = {
module : false,
notDeepEqual : false,
notEqual : false,
notOk : false,
notPropEqual : false,
notStrictEqual : false,
ok : false,
@ -670,6 +677,8 @@ exports.yui = {
};
exports.mocha = {
// Global (for config etc.)
mocha : false,
// BDD
describe : false,
xdescribe : false,
@ -711,5 +720,6 @@ exports.jasmine = {
afterAll : false,
fail : false,
fdescribe : false,
fit : false
fit : false,
pending : false
};

View File

@ -15,7 +15,7 @@
"joi": "6.10.1",
"joi-to-json-schema": "2.0.5",
"js-yaml": "3.4.6",
"jshint": "2.8.0",
"jshint": "2.9.2",
"lodash": "3.10.1",
"marked": "0.3.5",
"media-typer": "0.3.0",