0.2.0 - Mid migration

This commit is contained in:
Daniel Mason 2022-04-25 14:47:15 +12:00
parent 139e6a915e
commit 7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions

136
web/node_modules/http-proxy-middleware/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,136 @@
# Changelog
## [v0.19.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.19.1)
- fix(log): handle case when error code is missing ([#303](https://github.com/chimurai/http-proxy-middleware/pull/303))
## [v0.19.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.19.0)
- feat(http-proxy): bump to v1.17.0 ([#261](https://github.com/chimurai/http-proxy-middleware/pull/261))
## [v0.18.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.18.0)
- fix(vulnerability): update micromatch to v3.x ([npm:braces:20180219](https://snyk.io/test/npm/http-proxy-middleware?tab=issues&severity=high&severity=medium&severity=low#npm:braces:20180219))
- test(node): drop node 0.x support ([#212](https://github.com/chimurai/http-proxy-middleware/pull/212))
## [v0.17.4](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.4)
- fix(ntlm authentication): fixed bug preventing proxying with ntlm authentication. ([#132](https://github.com/chimurai/http-proxy-middleware/pull/149)) (Thanks: [EladBezalel](https://github.com/EladBezalel), [oshri551](https://github.com/oshri551))
## [v0.17.3](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.3)
- fix(onError): improve default proxy error handling. http status codes (504, 502 and 500). ([#132](https://github.com/chimurai/http-proxy-middleware/pull/132)) ([graingert](https://github.com/graingert))
## [v0.17.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.2)
- feat(logging): improve error message & add link to Node errors page. ([#106](https://github.com/chimurai/http-proxy-middleware/pull/106)) ([cloudmu](https://github.com/cloudmu))
- feat(pathRewrite): path can be empty string. ([#110](https://github.com/chimurai/http-proxy-middleware/pull/110)) ([sunnylqm](https://github.com/sunnylqm))
- bug(websocket): memory leak when option 'ws:true' is used. ([#114](https://github.com/chimurai/http-proxy-middleware/pull/114)) ([julbra](https://github.com/julbra))
- chore(package.json): reduce package size. ([#109](https://github.com/chimurai/http-proxy-middleware/pull/109))
## [v0.17.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.1)
- fix(Express sub Router): 404 on non-proxy routes ([#94](https://github.com/chimurai/http-proxy-middleware/issues/94))
## [v0.17.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.0)
- fix(context matching): Use [RFC 3986 path](https://tools.ietf.org/html/rfc3986#section-3.3) in context matching. (excludes query parameters)
## [v0.16.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.16.0)
- deprecated(proxyTable): renamed `proxyTable` to `router`.
- feat(router): support for custom `router` function.
## [v0.15.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.2)
- fix(websocket): fixes websocket upgrade.
## [v0.15.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.1)
- feat(pathRewrite): expose `req` object to pathRewrite function.
- fix(websocket): fixes websocket upgrade when both config.ws and external .upgrade() are used.
## [v0.15.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.0)
- feat(pathRewrite): support for custom pathRewrite function.
## [v0.14.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.14.0)
- feat(proxy): support proxy creation without context.
- fix(connect mounting): use connect's `path` configuration to mount proxy.
## [v0.13.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.13.0)
- feat(context): custom context matcher; when simple `path` matching is not sufficient.
## [v0.12.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.12.0)
- add option `onProxyReqWs` (subscribe to http-proxy `proxyReqWs` event)
- add option `onOpen` (subscribe to http-proxy `open` event)
- add option `onClose` (subscribe to http-proxy `close` event)
## [v0.11.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.11.0)
- improved logging
## [v0.10.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.10.0)
- feat(proxyTable) - added proxyTable support for WebSockets.
- fixed(proxyTable) - ensure original path (not rewritten path) is being used when `proxyTable` is used in conjunction with `pathRewrite`.
## [v0.9.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.9.1)
- fix server crash when socket error not handled correctly.
## [v0.9.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.9.0)
- support subscribing to http-proxy `proxyReq` event ([trbngr](https://github.com/trbngr))
- add `logLevel` and `logProvider` support
## [v0.8.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.8.2)
- fix proxyError handler ([mTazelaar](https://github.com/mTazelaar))
## [v0.8.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.8.1)
- fix pathRewrite when `agent` is configured
## [v0.8.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.8.0)
- support external websocket upgrade
- fix websocket shorthand
## [v0.7.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.7.0)
- support shorthand syntax
- fix express/connect mounting
## [v0.6.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.6.0)
- support proxyTable
## [v0.5.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.5.0)
- support subscribing to http-proxy `error` event
- support subscribing to http-proxy `proxyRes` event
## [v0.4.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.4.0)
- support websocket
## [v0.3.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.3.0)
- support wildcard / glob
## [v0.2.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.2.0)
- support multiple paths
## [v0.1.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.1.0)
- support path rewrite
- deprecate proxyHost option
## [v0.0.5](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.0.5)
- initial release

22
web/node_modules/http-proxy-middleware/LICENSE generated vendored Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Steven Chim
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.

491
web/node_modules/http-proxy-middleware/README.md generated vendored Normal file
View file

@ -0,0 +1,491 @@
# http-proxy-middleware
[![Build Status](https://img.shields.io/travis/chimurai/http-proxy-middleware/master.svg?style=flat-square)](https://travis-ci.org/chimurai/http-proxy-middleware)
[![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware)
[![dependency Status](https://img.shields.io/david/chimurai/http-proxy-middleware.svg?style=flat-square)](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies)
[![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg)](https://snyk.io/test/npm/http-proxy-middleware)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers).
Powered by the popular Nodejitsu [`http-proxy`](https://github.com/nodejitsu/node-http-proxy). [![GitHub stars](https://img.shields.io/github/stars/nodejitsu/node-http-proxy.svg?style=social&label=Star)](https://github.com/nodejitsu/node-http-proxy)
## TL;DR
Proxy `/api` requests to `http://www.example.org`
```javascript
var express = require('express')
var proxy = require('http-proxy-middleware')
var app = express()
app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }))
app.listen(3000)
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```
_All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#options) can be used, along with some extra `http-proxy-middleware` [options](#options).
:bulb: **Tip:** Set the option `changeOrigin` to `true` for [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based).
## Table of Contents
<!-- MarkdownTOC autolink=true bracket=round depth=2 -->
- [Install](#install)
- [Core concept](#core-concept)
- [Example](#example)
- [Context matching](#context-matching)
- [Options](#options)
- [http-proxy-middleware options](#http-proxy-middleware-options)
- [http-proxy events](#http-proxy-events)
- [http-proxy options](#http-proxy-options)
- [Shorthand](#shorthand)
- [app.use\(path, proxy\)](#appusepath-proxy)
- [WebSocket](#websocket)
- [External WebSocket upgrade](#external-websocket-upgrade)
- [Working examples](#working-examples)
- [Recipes](#recipes)
- [Compatible servers](#compatible-servers)
- [Tests](#tests)
- [Changelog](#changelog)
- [License](#license)
<!-- /MarkdownTOC -->
## Install
```javascript
$ npm install --save-dev http-proxy-middleware
```
## Core concept
Proxy middleware configuration.
#### proxy([context,] config)
```javascript
var proxy = require('http-proxy-middleware')
var apiProxy = proxy('/api', { target: 'http://www.example.org' })
// \____/ \_____________________________/
// | |
// context options
// 'apiProxy' is now ready to be used as middleware in a server.
```
- **context**: Determine which requests should be proxied to the target host.
(more on [context matching](#context-matching))
- **options.target**: target host to proxy to. _(protocol + host)_
(full list of [`http-proxy-middleware` configuration options](#options))
#### proxy(uri [, config])
```javascript
// shorthand syntax for the example above:
var apiProxy = proxy('http://www.example.org/api')
```
More about the [shorthand configuration](#shorthand).
## Example
An example with `express` server.
```javascript
// include dependencies
var express = require('express')
var proxy = require('http-proxy-middleware')
// proxy middleware options
var options = {
target: 'http://www.example.org', // target host
changeOrigin: true, // needed for virtual hosted sites
ws: true, // proxy websockets
pathRewrite: {
'^/api/old-path': '/api/new-path', // rewrite path
'^/api/remove/path': '/path' // remove base path
},
router: {
// when request.headers.host == 'dev.localhost:3000',
// override target 'http://www.example.org' to 'http://localhost:8000'
'dev.localhost:3000': 'http://localhost:8000'
}
}
// create the proxy (without context)
var exampleProxy = proxy(options)
// mount `exampleProxy` in web server
var app = express()
app.use('/api', exampleProxy)
app.listen(3000)
```
## Context matching
Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility.
[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching.
```
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
```
- **path matching**
- `proxy({...})` - matches any path, all requests will be proxied.
- `proxy('/', {...})` - matches any path, all requests will be proxied.
- `proxy('/api', {...})` - matches paths starting with `/api`
- **multiple path matching**
- `proxy(['/api', '/ajax', '/someotherpath'], {...})`
- **wildcard path matching**
For fine-grained control you can use wildcard matching. Glob pattern matching is done by _micromatch_. Visit [micromatch](https://www.npmjs.com/package/micromatch) or [glob](https://www.npmjs.com/package/glob) for more globbing examples.
- `proxy('**', {...})` matches any path, all requests will be proxied.
- `proxy('**/*.html', {...})` matches any path which ends with `.html`
- `proxy('/*.html', {...})` matches paths directly under path-absolute
- `proxy('/api/**/*.html', {...})` matches requests ending with `.html` in the path of `/api`
- `proxy(['/api/**', '/ajax/**'], {...})` combine multiple patterns
- `proxy(['/api/**', '!**/bad.json'], {...})` exclusion
**Note**: In multiple path matching, you cannot use string paths and wildcard paths together.
- **custom matching**
For full control you can provide a custom function to determine which requests should be proxied or not.
```javascript
/**
* @return {Boolean}
*/
var filter = function(pathname, req) {
return pathname.match('^/api') && req.method === 'GET'
}
var apiProxy = proxy(filter, { target: 'http://www.example.org' })
```
## Options
### http-proxy-middleware options
- **option.pathRewrite**: object/function, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths.
```javascript
// rewrite path
pathRewrite: {'^/old/api' : '/new/api'}
// remove path
pathRewrite: {'^/remove/api' : ''}
// add base path
pathRewrite: {'^/' : '/basepath/'}
// custom rewriting
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }
```
- **option.router**: object/function, re-target `option.target` for specific requests.
```javascript
// Use `host` and/or `path` to match requests. First match will be used.
// The order of the configuration matters.
router: {
'integration.localhost:3000' : 'http://localhost:8001', // host only
'staging.localhost:3000' : 'http://localhost:8002', // host only
'localhost:3000/api' : 'http://localhost:8003', // host + path
'/rest' : 'http://localhost:8004' // path only
}
// Custom router function
router: function(req) {
return 'http://localhost:8004';
}
```
- **option.logLevel**: string, ['debug', 'info', 'warn', 'error', 'silent']. Default: `'info'`
- **option.logProvider**: function, modify or replace log provider. Default: `console`.
```javascript
// simple replace
function logProvider(provider) {
// replace the default console log provider.
return require('winston')
}
```
```javascript
// verbose replacement
function logProvider(provider) {
var logger = new (require('winston')).Logger()
var myCustomProvider = {
log: logger.log,
debug: logger.debug,
info: logger.info,
warn: logger.warn,
error: logger.error
}
return myCustomProvider
}
```
- (DEPRECATED) **option.proxyHost**: Use `option.changeOrigin = true` instead.
- (DEPRECATED) **option.proxyTable**: Use `option.router` instead.
### http-proxy events
Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events):
- **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling.
```javascript
function onError(err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
})
res.end(
'Something went wrong. And we are reporting a custom error message.'
)
}
```
- **option.onProxyRes**: function, subscribe to http-proxy's `proxyRes` event.
```javascript
function onProxyRes(proxyRes, req, res) {
proxyRes.headers['x-added'] = 'foobar' // add new header to response
delete proxyRes.headers['x-removed'] // remove header from response
}
```
- **option.onProxyReq**: function, subscribe to http-proxy's `proxyReq` event.
```javascript
function onProxyReq(proxyReq, req, res) {
// add custom header to request
proxyReq.setHeader('x-added', 'foobar')
// or log the req
}
```
- **option.onProxyReqWs**: function, subscribe to http-proxy's `proxyReqWs` event.
```javascript
function onProxyReqWs(proxyReq, req, socket, options, head) {
// add custom header
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar')
}
```
- **option.onOpen**: function, subscribe to http-proxy's `open` event.
```javascript
function onOpen(proxySocket) {
// listen for messages coming FROM the target here
proxySocket.on('data', hybiParseAndLogMessage)
}
```
- **option.onClose**: function, subscribe to http-proxy's `close` event.
```javascript
function onClose(res, socket, head) {
// view disconnected websocket connections
console.log('Client disconnected')
}
```
### http-proxy options
The following options are provided by the underlying [http-proxy](https://github.com/nodejitsu/node-http-proxy#options) library.
- **option.target**: url string to be parsed with the url module
- **option.forward**: url string to be parsed with the url module
- **option.agent**: object to be passed to http(s).request (see Node's [https agent](http://nodejs.org/api/https.html#https_class_https_agent) and [http agent](http://nodejs.org/api/http.html#http_class_http_agent) objects)
- **option.ssl**: object to be passed to https.createServer()
- **option.ws**: true/false: if you want to proxy websockets
- **option.xfwd**: true/false, adds x-forward headers
- **option.secure**: true/false, if you want to verify the SSL Certs
- **option.toProxy**: true/false, passes the absolute URL as the `path` (useful for proxying to proxies)
- **option.prependPath**: true/false, Default: true - specify whether you want to prepend the target's path to the proxy path
- **option.ignorePath**: true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request (note: you will have to append / manually if required).
- **option.localAddress** : Local interface string to bind for outgoing connections
- **option.changeOrigin**: true/false, Default: false - changes the origin of the host header to the target URL
- **option.preserveHeaderKeyCase**: true/false, Default: false - specify whether you want to keep letter case of response header key
- **option.auth** : Basic authentication i.e. 'user:password' to compute an Authorization header.
- **option.hostRewrite**: rewrites the location hostname on (301/302/307/308) redirects.
- **option.autoRewrite**: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.
- **option.protocolRewrite**: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
- **option.cookieDomainRewrite**: rewrites domain of `set-cookie` headers. Possible values:
- `false` (default): disable cookie rewriting
- String: new domain, for example `cookieDomainRewrite: "new.domain"`. To remove the domain, use `cookieDomainRewrite: ""`.
- Object: mapping of domains to new domains, use `"*"` to match all domains.
For example keep one domain unchanged, rewrite one domain and remove other domains:
```
cookieDomainRewrite: {
"unchanged.domain": "unchanged.domain",
"old.domain": "new.domain",
"*": ""
}
```
- **option.cookiePathRewrite**: rewrites path of `set-cookie` headers. Possible values:
- `false` (default): disable cookie rewriting
- String: new path, for example `cookiePathRewrite: "/newPath/"`. To remove the path, use `cookiePathRewrite: ""`. To set path to root use `cookiePathRewrite: "/"`.
- Object: mapping of paths to new paths, use `"*"` to match all paths.
For example, to keep one path unchanged, rewrite one path and remove other paths:
```
cookiePathRewrite: {
"/unchanged.path/": "/unchanged.path/",
"/old.path/": "/new.path/",
"*": ""
}
```
- **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`)
- **option.proxyTimeout**: timeout (in millis) when proxy receives no response from target
- **option.timeout**: timeout (in millis) for incoming requests
- **option.followRedirects**: true/false, Default: false - specify whether you want to follow redirects
- **option.selfHandleResponse** true/false, if set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the `proxyRes` event
- **option.buffer**: stream of data to send as the request body. Maybe you have some middleware that consumes the request stream before proxying it on e.g. If you read the body of a request into a field called 'req.rawbody' you could restream this field in the buffer option:
```
'use strict';
const streamify = require('stream-array');
const HttpProxy = require('http-proxy');
const proxy = new HttpProxy();
module.exports = (req, res, next) => {
proxy.web(req, res, {
target: 'http://localhost:4003/',
buffer: streamify(req.rawBody)
}, next);
};
```
## Shorthand
Use the shorthand syntax when verbose configuration is not needed. The `context` and `option.target` will be automatically configured when shorthand is used. Options can still be used if needed.
```javascript
proxy('http://www.example.org:8000/api')
// proxy('/api', {target: 'http://www.example.org:8000'});
proxy('http://www.example.org:8000/api/books/*/**.json')
// proxy('/api/books/*/**.json', {target: 'http://www.example.org:8000'});
proxy('http://www.example.org:8000/api', { changeOrigin: true })
// proxy('/api', {target: 'http://www.example.org:8000', changeOrigin: true});
```
### app.use(path, proxy)
If you want to use the server's `app.use` `path` parameter to match requests;
Create and mount the proxy without the http-proxy-middleware `context` parameter:
```javascript
app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }))
```
`app.use` documentation:
- express: http://expressjs.com/en/4x/api.html#app.use
- connect: https://github.com/senchalabs/connect#mount-middleware
## WebSocket
```javascript
// verbose api
proxy('/', { target: 'http://echo.websocket.org', ws: true })
// shorthand
proxy('http://echo.websocket.org', { ws: true })
// shorter shorthand
proxy('ws://echo.websocket.org')
```
### External WebSocket upgrade
In the previous WebSocket examples, http-proxy-middleware relies on a initial http request in order to listen to the http `upgrade` event. If you need to proxy WebSockets without the initial http request, you can subscribe to the server's http `upgrade` event manually.
```javascript
var wsProxy = proxy('ws://echo.websocket.org', { changeOrigin: true })
var app = express()
app.use(wsProxy)
var server = app.listen(3000)
server.on('upgrade', wsProxy.upgrade) // <-- subscribe to http 'upgrade'
```
## Working examples
View and play around with [working examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples).
- Browser-Sync ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/browser-sync/index.js))
- express ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express/index.js))
- connect ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect/index.js))
- WebSocket ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/websocket/index.js))
## Recipes
View the [recipes](https://github.com/chimurai/http-proxy-middleware/tree/master/recipes) for common use cases.
## Compatible servers
`http-proxy-middleware` is compatible with the following servers:
- [connect](https://www.npmjs.com/package/connect)
- [express](https://www.npmjs.com/package/express)
- [browser-sync](https://www.npmjs.com/package/browser-sync)
- [lite-server](https://www.npmjs.com/package/lite-server)
- [grunt-contrib-connect](https://www.npmjs.com/package/grunt-contrib-connect)
- [grunt-browser-sync](https://www.npmjs.com/package/grunt-browser-sync)
- [gulp-connect](https://www.npmjs.com/package/gulp-connect)
- [gulp-webserver](https://www.npmjs.com/package/gulp-webserver)
Sample implementations can be found in the [server recipes](https://github.com/chimurai/http-proxy-middleware/tree/master/recipes/servers.md).
## Tests
Run the test suite:
```bash
# install dependencies
$ npm install
# linting
$ npm run lint
# unit tests
$ npm test
# code coverage
$ npm run cover
```
## Changelog
- [View changelog](https://github.com/chimurai/http-proxy-middleware/blob/master/CHANGELOG.md)
## License
The MIT License (MIT)
Copyright (c) 2015-2018 Steven Chim

5
web/node_modules/http-proxy-middleware/index.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
var HPM = require('./lib')
module.exports = function(context, opts) {
return new HPM(context, opts)
}

View file

@ -0,0 +1,129 @@
var _ = require('lodash')
var url = require('url')
var ERRORS = require('./errors')
var logger = require('./logger').getInstance()
module.exports = {
createConfig: createConfig
}
function createConfig(context, opts) {
// structure of config object to be returned
var config = {
context: undefined,
options: {}
}
// app.use('/api', proxy({target:'http://localhost:9000'}));
if (isContextless(context, opts)) {
config.context = '/'
config.options = _.assign(config.options, context)
// app.use('/api', proxy('http://localhost:9000'));
// app.use(proxy('http://localhost:9000/api'));
} else if (isStringShortHand(context)) {
var oUrl = url.parse(context)
var target = [oUrl.protocol, '//', oUrl.host].join('')
config.context = oUrl.pathname || '/'
config.options = _.assign(config.options, { target: target }, opts)
if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') {
config.options.ws = true
}
// app.use('/api', proxy({target:'http://localhost:9000'}));
} else {
config.context = context
config.options = _.assign(config.options, opts)
}
configureLogger(config.options)
if (!config.options.target) {
throw new Error(ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING)
}
// Legacy option.proxyHost
config.options = mapLegacyProxyHostOption(config.options)
// Legacy option.proxyTable > option.router
config.options = mapLegacyProxyTableOption(config.options)
return config
}
/**
* Checks if a String only target/config is provided.
* This can be just the host or with the optional path.
*
* @example
* app.use('/api', proxy('http://localhost:9000'));
app.use(proxy('http://localhost:9000/api'));
*
* @param {String} context [description]
* @return {Boolean} [description]
*/
function isStringShortHand(context) {
if (_.isString(context)) {
return !!url.parse(context).host
}
}
/**
* Checks if a Object only config is provided, without a context.
* In this case the all paths will be proxied.
*
* @example
* app.use('/api', proxy({target:'http://localhost:9000'}));
*
* @param {Object} context [description]
* @param {*} opts [description]
* @return {Boolean} [description]
*/
function isContextless(context, opts) {
return _.isPlainObject(context) && _.isEmpty(opts)
}
function mapLegacyProxyHostOption(options) {
// set options.headers.host when option.proxyHost is provided
if (options.proxyHost) {
logger.warn('*************************************')
logger.warn('[HPM] Deprecated "option.proxyHost"')
logger.warn(
' Use "option.changeOrigin" or "option.headers.host" instead'
)
logger.warn(' "option.proxyHost" will be removed in future release.')
logger.warn('*************************************')
options.headers = options.headers || {}
options.headers.host = options.proxyHost
}
return options
}
// Warn deprecated proxyTable api usage
function mapLegacyProxyTableOption(options) {
if (options.proxyTable) {
logger.warn('*************************************')
logger.warn('[HPM] Deprecated "option.proxyTable"')
logger.warn(' Use "option.router" instead')
logger.warn(' "option.proxyTable" will be removed in future release.')
logger.warn('*************************************')
options.router = _.clone(options.proxyTable)
_.omit(options, 'proxyTable')
}
return options
}
function configureLogger(options) {
if (options.logLevel) {
logger.setLevel(options.logLevel)
}
if (options.logProvider) {
logger.setProvider(options.logProvider)
}
}

View file

@ -0,0 +1,94 @@
var _ = require('lodash')
var url = require('url')
var isGlob = require('is-glob')
var micromatch = require('micromatch')
var ERRORS = require('./errors')
module.exports = {
match: matchContext
}
function matchContext(context, uri, req) {
// single path
if (isStringPath(context)) {
return matchSingleStringPath(context, uri)
}
// single glob path
if (isGlobPath(context)) {
return matchSingleGlobPath(context, uri)
}
// multi path
if (Array.isArray(context)) {
if (context.every(isStringPath)) {
return matchMultiPath(context, uri)
}
if (context.every(isGlobPath)) {
return matchMultiGlobPath(context, uri)
}
throw new Error(ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY)
}
// custom matching
if (_.isFunction(context)) {
var pathname = getUrlPathName(uri)
return context(pathname, req)
}
throw new Error(ERRORS.ERR_CONTEXT_MATCHER_GENERIC)
}
/**
* @param {String} context '/api'
* @param {String} uri 'http://example.org/api/b/c/d.html'
* @return {Boolean}
*/
function matchSingleStringPath(context, uri) {
var pathname = getUrlPathName(uri)
return pathname.indexOf(context) === 0
}
function matchSingleGlobPath(pattern, uri) {
var pathname = getUrlPathName(uri)
var matches = micromatch(pathname, pattern)
return matches && matches.length > 0
}
function matchMultiGlobPath(patternList, uri) {
return matchSingleGlobPath(patternList, uri)
}
/**
* @param {String} contextList ['/api', '/ajax']
* @param {String} uri 'http://example.org/api/b/c/d.html'
* @return {Boolean}
*/
function matchMultiPath(contextList, uri) {
for (var i = 0; i < contextList.length; i++) {
var context = contextList[i]
if (matchSingleStringPath(context, uri)) {
return true
}
}
return false
}
/**
* Parses URI and returns RFC 3986 path
*
* @param {String} uri from req.url
* @return {String} RFC 3986 path
*/
function getUrlPathName(uri) {
return uri && url.parse(uri).pathname
}
function isStringPath(context) {
return _.isString(context) && !isGlob(context)
}
function isGlobPath(context) {
return isGlob(context)
}

12
web/node_modules/http-proxy-middleware/lib/errors.js generated vendored Normal file
View file

@ -0,0 +1,12 @@
/* eslint-disable max-len */
module.exports = {
ERR_CONFIG_FACTORY_TARGET_MISSING:
'[HPM] Missing "target" option. Example: {target: "http://www.example.org"}',
ERR_CONTEXT_MATCHER_GENERIC:
'[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]',
ERR_CONTEXT_MATCHER_INVALID_ARRAY:
'[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]',
ERR_PATH_REWRITER_CONFIG:
'[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function'
}

82
web/node_modules/http-proxy-middleware/lib/handlers.js generated vendored Normal file
View file

@ -0,0 +1,82 @@
var _ = require('lodash')
var logger = require('./logger').getInstance()
module.exports = {
init: init,
getHandlers: getProxyEventHandlers
}
function init(proxy, opts) {
var handlers = getProxyEventHandlers(opts)
_.forIn(handlers, function(handler, eventName) {
proxy.on(eventName, handlers[eventName])
})
logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers))
}
function getProxyEventHandlers(opts) {
// https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events
var proxyEvents = [
'error',
'proxyReq',
'proxyReqWs',
'proxyRes',
'open',
'close'
]
var handlers = {}
_.forEach(proxyEvents, function(event) {
// all handlers for the http-proxy events are prefixed with 'on'.
// loop through options and try to find these handlers
// and add them to the handlers object for subscription in init().
var eventName = _.camelCase('on ' + event)
var fnHandler = _.get(opts, eventName)
if (_.isFunction(fnHandler)) {
handlers[event] = fnHandler
}
})
// add default error handler in absence of error handler
if (!_.isFunction(handlers.error)) {
handlers.error = defaultErrorHandler
}
// add default close handler in absence of close handler
if (!_.isFunction(handlers.close)) {
handlers.close = logClose
}
return handlers
}
function defaultErrorHandler(err, req, res) {
var host = req.headers && req.headers.host
var code = err.code
if (res.writeHead && !res.headersSent) {
if (/HPE_INVALID/.test(code)) {
res.writeHead(502)
} else {
switch (code) {
case 'ECONNRESET':
case 'ENOTFOUND':
case 'ECONNREFUSED':
res.writeHead(504)
break
default:
res.writeHead(500)
}
}
}
res.end('Error occured while trying to proxy to: ' + host + req.url)
}
function logClose(req, socket, head) {
// view disconnected websocket connections
logger.info('[HPM] Client disconnected')
}

182
web/node_modules/http-proxy-middleware/lib/index.js generated vendored Normal file
View file

@ -0,0 +1,182 @@
var _ = require('lodash')
var httpProxy = require('http-proxy')
var configFactory = require('./config-factory')
var handlers = require('./handlers')
var contextMatcher = require('./context-matcher')
var PathRewriter = require('./path-rewriter')
var Router = require('./router')
var logger = require('./logger').getInstance()
var getArrow = require('./logger').getArrow
module.exports = HttpProxyMiddleware
function HttpProxyMiddleware(context, opts) {
// https://github.com/chimurai/http-proxy-middleware/issues/57
var wsUpgradeDebounced = _.debounce(handleUpgrade)
var wsInitialized = false
var config = configFactory.createConfig(context, opts)
var proxyOptions = config.options
// create proxy
var proxy = httpProxy.createProxyServer({})
logger.info(
'[HPM] Proxy created:',
config.context,
' -> ',
proxyOptions.target
)
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided
// attach handler to http-proxy events
handlers.init(proxy, proxyOptions)
// log errors for debug purpose
proxy.on('error', logError)
// https://github.com/chimurai/http-proxy-middleware/issues/19
// expose function to upgrade externally
middleware.upgrade = wsUpgradeDebounced
return middleware
function middleware(req, res, next) {
if (shouldProxy(config.context, req)) {
var activeProxyOptions = prepareProxyRequest(req)
proxy.web(req, res, activeProxyOptions)
} else {
next()
}
if (proxyOptions.ws === true) {
// use initial request to access the server object to subscribe to http upgrade event
catchUpgradeRequest(req.connection.server)
}
}
function catchUpgradeRequest(server) {
// subscribe once; don't subscribe on every request...
// https://github.com/chimurai/http-proxy-middleware/issues/113
if (!wsInitialized) {
server.on('upgrade', wsUpgradeDebounced)
wsInitialized = true
}
}
function handleUpgrade(req, socket, head) {
// set to initialized when used externally
wsInitialized = true
if (shouldProxy(config.context, req)) {
var activeProxyOptions = prepareProxyRequest(req)
proxy.ws(req, socket, head, activeProxyOptions)
logger.info('[HPM] Upgrading to WebSocket')
}
}
/**
* Determine whether request should be proxied.
*
* @private
* @param {String} context [description]
* @param {Object} req [description]
* @return {Boolean}
*/
function shouldProxy(context, req) {
var path = req.originalUrl || req.url
return contextMatcher.match(context, path, req)
}
/**
* Apply option.router and option.pathRewrite
* Order matters:
* Router uses original path for routing;
* NOT the modified path, after it has been rewritten by pathRewrite
* @param {Object} req
* @return {Object} proxy options
*/
function prepareProxyRequest(req) {
// https://github.com/chimurai/http-proxy-middleware/issues/17
// https://github.com/chimurai/http-proxy-middleware/issues/94
req.url = req.originalUrl || req.url
// store uri before it gets rewritten for logging
var originalPath = req.url
var newProxyOptions = _.assign({}, proxyOptions)
// Apply in order:
// 1. option.router
// 2. option.pathRewrite
__applyRouter(req, newProxyOptions)
__applyPathRewrite(req, pathRewriter)
// debug logging for both http(s) and websockets
if (proxyOptions.logLevel === 'debug') {
var arrow = getArrow(
originalPath,
req.url,
proxyOptions.target,
newProxyOptions.target
)
logger.debug(
'[HPM] %s %s %s %s',
req.method,
originalPath,
arrow,
newProxyOptions.target
)
}
return newProxyOptions
}
// Modify option.target when router present.
function __applyRouter(req, options) {
var newTarget
if (options.router) {
newTarget = Router.getTarget(req, options)
if (newTarget) {
logger.debug(
'[HPM] Router new target: %s -> "%s"',
options.target,
newTarget
)
options.target = newTarget
}
}
}
// rewrite path
function __applyPathRewrite(req, pathRewriter) {
if (pathRewriter) {
var path = pathRewriter(req.url, req)
if (typeof path === 'string') {
req.url = path
} else {
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url)
}
}
}
function logError(err, req, res) {
var hostname =
(req.headers && req.headers.host) || (req.hostname || req.host) // (websocket) || (node0.10 || node 4/5)
var target = proxyOptions.target.host || proxyOptions.target
var errorMessage =
'[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)'
var errReference =
'https://nodejs.org/api/errors.html#errors_common_system_errors' // link to Node Common Systems Errors page
logger.error(
errorMessage,
req.url,
hostname,
target,
err.code || err,
errReference
)
}
}

172
web/node_modules/http-proxy-middleware/lib/logger.js generated vendored Normal file
View file

@ -0,0 +1,172 @@
var util = require('util')
var _ = require('lodash')
var loggerInstance
var defaultProvider = {
log: console.log,
debug: console.log, // use .log(); since console does not have .debug()
info: console.info,
warn: console.warn,
error: console.error
}
// log level 'weight'
var LEVELS = {
debug: 10,
info: 20,
warn: 30,
error: 50,
silent: 80
}
module.exports = {
// singleton
getInstance: function() {
if (!loggerInstance) {
loggerInstance = new Logger()
}
return loggerInstance
},
getArrow: getArrow
}
function Logger() {
var logLevel
var provider
var api = {
log: log,
debug: debug,
info: info,
warn: warn,
error: error,
setLevel: function(v) {
if (isValidLevel(v)) {
logLevel = v
}
},
setProvider: function(fn) {
if (fn && isValidProvider(fn)) {
provider = fn(defaultProvider)
}
}
}
init()
return api
function init() {
api.setLevel('info')
api.setProvider(function() {
return defaultProvider
})
}
// log will log messages, regardless of logLevels
function log() {
provider.log(_interpolate.apply(null, arguments))
}
function debug() {
if (_showLevel('debug')) {
provider.debug(_interpolate.apply(null, arguments))
}
}
function info() {
if (_showLevel('info')) {
provider.info(_interpolate.apply(null, arguments))
}
}
function warn() {
if (_showLevel('warn')) {
provider.warn(_interpolate.apply(null, arguments))
}
}
function error() {
if (_showLevel('error')) {
provider.error(_interpolate.apply(null, arguments))
}
}
/**
* Decide to log or not to log, based on the log levels 'weight'
* @param {String} showLevel [debug, info, warn, error, silent]
* @return {Boolean}
*/
function _showLevel(showLevel) {
var result = false
var currentLogLevel = LEVELS[logLevel]
if (currentLogLevel && currentLogLevel <= LEVELS[showLevel]) {
result = true
}
return result
}
// make sure logged messages and its data are return interpolated
// make it possible for additional log data, such date/time or custom prefix.
function _interpolate() {
var fn = _.spread(util.format)
var result = fn(_.slice(arguments))
return result
}
function isValidProvider(fnProvider) {
var result = true
if (fnProvider && !_.isFunction(fnProvider)) {
throw new Error('[HPM] Log provider config error. Expecting a function.')
}
return result
}
function isValidLevel(levelName) {
var validLevels = _.keys(LEVELS)
var isValid = _.includes(validLevels, levelName)
if (!isValid) {
throw new Error('[HPM] Log level error. Invalid logLevel.')
}
return isValid
}
}
/**
* -> normal proxy
* => router
* ~> pathRewrite
* > router + pathRewrite
*
* @param {String} originalPath
* @param {String} newPath
* @param {String} originalTarget
* @param {String} newTarget
* @return {String}
*/
function getArrow(originalPath, newPath, originalTarget, newTarget) {
var arrow = ['>']
var isNewTarget = originalTarget !== newTarget // router
var isNewPath = originalPath !== newPath // pathRewrite
if (isNewPath && !isNewTarget) {
arrow.unshift('~')
} else if (!isNewPath && isNewTarget) {
arrow.unshift('=')
} else if (isNewPath && isNewTarget) {
arrow.unshift('≈')
} else {
arrow.unshift('-')
}
return arrow.join('')
}

View file

@ -0,0 +1,79 @@
var _ = require('lodash')
var logger = require('./logger').getInstance()
var ERRORS = require('./errors')
module.exports = {
create: createPathRewriter
}
/**
* Create rewrite function, to cache parsed rewrite rules.
*
* @param {Object} rewriteConfig
* @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter
*/
function createPathRewriter(rewriteConfig) {
var rulesCache
if (!isValidRewriteConfig(rewriteConfig)) {
return
}
if (_.isFunction(rewriteConfig)) {
var customRewriteFn = rewriteConfig
return customRewriteFn
} else {
rulesCache = parsePathRewriteRules(rewriteConfig)
return rewritePath
}
function rewritePath(path) {
var result = path
_.forEach(rulesCache, function(rule) {
if (rule.regex.test(path)) {
result = result.replace(rule.regex, rule.value)
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result)
return false
}
})
return result
}
}
function isValidRewriteConfig(rewriteConfig) {
if (_.isFunction(rewriteConfig)) {
return true
} else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) {
return true
} else if (
_.isUndefined(rewriteConfig) ||
_.isNull(rewriteConfig) ||
_.isEqual(rewriteConfig, {})
) {
return false
} else {
throw new Error(ERRORS.ERR_PATH_REWRITER_CONFIG)
}
}
function parsePathRewriteRules(rewriteConfig) {
var rules = []
if (_.isPlainObject(rewriteConfig)) {
_.forIn(rewriteConfig, function(value, key) {
rules.push({
regex: new RegExp(key),
value: rewriteConfig[key]
})
logger.info(
'[HPM] Proxy rewrite rule created: "%s" ~> "%s"',
key,
rewriteConfig[key]
)
})
}
return rules
}

51
web/node_modules/http-proxy-middleware/lib/router.js generated vendored Normal file
View file

@ -0,0 +1,51 @@
var _ = require('lodash')
var logger = require('./logger.js').getInstance()
module.exports = {
getTarget: getTarget
}
function getTarget(req, config) {
var newTarget
var router = config.router
if (_.isPlainObject(router)) {
newTarget = getTargetFromProxyTable(req, router)
} else if (_.isFunction(router)) {
newTarget = router(req)
}
return newTarget
}
function getTargetFromProxyTable(req, table) {
var result
var host = req.headers.host
var path = req.url
var hostAndPath = host + path
_.forIn(table, function(value, key) {
if (containsPath(key)) {
if (hostAndPath.indexOf(key) > -1) {
// match 'localhost:3000/api'
result = table[key]
logger.debug('[HPM] Router table match: "%s"', key)
return false
}
} else {
if (key === host) {
// match 'localhost:3000'
result = table[key]
logger.debug('[HPM] Router table match: "%s"', host)
return false
}
}
})
return result
}
function containsPath(v) {
return v.indexOf('/') > -1
}

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2018, Jon Schlinkert.
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,640 @@
# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces) [![Windows Build Status](https://img.shields.io/appveyor/ci/micromatch/braces.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/micromatch/braces)
> Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save braces
```
## Why use braces?
Brace patterns are great for matching ranges. Users (and implementors) shouldn't have to think about whether or not they will break their application (or yours) from accidentally defining an aggressive brace pattern. _Braces is the only library that offers a [solution to this problem](#performance)_.
* **Safe(r)**: Braces isn't vulnerable to DoS attacks like [brace-expansion](https://github.com/juliangruber/brace-expansion), [minimatch](https://github.com/isaacs/minimatch) and [multimatch](https://github.com/sindresorhus/multimatch) (a different bug than the [other regex DoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc)).
* **Accurate**: complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
* **[fast and performant](#benchmarks)**: Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
* **Organized code base**: with parser and compiler that are eas(y|ier) to maintain and update when edge cases crop up.
* **Well-tested**: thousands of test assertions. Passes 100% of the [minimatch](https://github.com/isaacs/minimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests as well (as of the writing of this).
## Usage
The main export is a function that takes one or more brace `patterns` and `options`.
```js
var braces = require('braces');
braces(pattern[, options]);
```
By default, braces returns an optimized regex-source string. To get an array of brace patterns, use `brace.expand()`.
The following section explains the difference in more detail. _(If you're curious about "why" braces does this by default, see [brace matching pitfalls](#brace-matching-pitfalls)_.
### Optimized vs. expanded braces
**Optimized**
By default, patterns are optimized for regex and matching:
```js
console.log(braces('a/{x,y,z}/b'));
//=> ['a/(x|y|z)/b']
```
**Expanded**
To expand patterns the same way as Bash or [minimatch](https://github.com/isaacs/minimatch), use the [.expand](#expand) method:
```js
console.log(braces.expand('a/{x,y,z}/b'));
//=> ['a/x/b', 'a/y/b', 'a/z/b']
```
Or use [options.expand](#optionsexpand):
```js
console.log(braces('a/{x,y,z}/b', {expand: true}));
//=> ['a/x/b', 'a/y/b', 'a/z/b']
```
## Features
* [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
* [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']`
* [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
* [escaping](#escaping)
* [options](#options)
### Lists
Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric lists:
```js
console.log(braces('a/{foo,bar,baz}/*.js'));
//=> ['a/(foo|bar|baz)/*.js']
console.log(braces.expand('a/{foo,bar,baz}/*.js'));
//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
```
### Sequences
Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
```js
console.log(braces.expand('{1..3}')); // ['1', '2', '3']
console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
console.log(braces.expand('a{1..3}b')); // ['a1b', 'a2b', 'a3b']
console.log(braces.expand('{a..c}')); // ['a', 'b', 'c']
console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
// supports padded ranges
console.log(braces('a{01..03}b')); //=> [ 'a(0[1-3])b' ]
console.log(braces('a{001..300}b')); //=> [ 'a(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)b' ]
```
### Steps
Steps, or increments, may be used with ranges:
```js
console.log(braces.expand('{2..10..2}'));
//=> ['2', '4', '6', '8', '10']
console.log(braces('{2..10..2}'));
//=> ['(2|4|6|8|10)']
```
When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.
### Nesting
Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
**"Expanded" braces**
```js
console.log(braces.expand('a{b,c,/{x,y}}/e'));
//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
console.log(braces.expand('a/{x,{1..5},y}/c'));
//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
```
**"Optimized" braces**
```js
console.log(braces('a{b,c,/{x,y}}/e'));
//=> ['a(b|c|/(x|y))/e']
console.log(braces('a/{x,{1..5},y}/c'));
//=> ['a/(x|([1-5])|y)/c']
```
### Escaping
**Escaping braces**
A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:
```js
console.log(braces.expand('a\\{d,c,b}e'));
//=> ['a{d,c,b}e']
console.log(braces.expand('a{d,c,b\\}e'));
//=> ['a{d,c,b}e']
```
**Escaping commas**
Commas inside braces may also be escaped:
```js
console.log(braces.expand('a{b\\,c}d'));
//=> ['a{b,c}d']
console.log(braces.expand('a{d\\,c,b}e'));
//=> ['ad,ce', 'abe']
```
**Single items**
Following bash conventions, a brace pattern is also not expanded when it contains a single character:
```js
console.log(braces.expand('a{b}c'));
//=> ['a{b}c']
```
## Options
### options.maxLength
**Type**: `Number`
**Default**: `65,536`
**Description**: Limit the length of the input string. Useful when the input string is generated or your application allows users to pass a string, et cetera.
```js
console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
```
### options.expand
**Type**: `Boolean`
**Default**: `undefined`
**Description**: Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
```js
console.log(braces('a/{b,c}/d', {expand: true}));
//=> [ 'a/b/d', 'a/c/d' ]
```
### options.optimize
**Type**: `Boolean`
**Default**: `true`
**Description**: Enabled by default.
```js
console.log(braces('a/{b,c}/d'));
//=> [ 'a/(b|c)/d' ]
```
### options.nodupes
**Type**: `Boolean`
**Default**: `true`
**Description**: Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
### options.rangeLimit
**Type**: `Number`
**Default**: `250`
**Description**: When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
**Examples**
```js
// pattern exceeds the "rangeLimit", so it's optimized automatically
console.log(braces.expand('{1..1000}'));
//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
// pattern does not exceed "rangeLimit", so it's NOT optimized
console.log(braces.expand('{1..100}'));
//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100']
```
### options.transform
**Type**: `Function`
**Default**: `undefined`
**Description**: Customize range expansion.
```js
var range = braces.expand('x{a..e}y', {
transform: function(str) {
return 'foo' + str;
}
});
console.log(range);
//=> [ 'xfooay', 'xfooby', 'xfoocy', 'xfoody', 'xfooey' ]
```
### options.quantifiers
**Type**: `Boolean`
**Default**: `undefined`
**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
**Examples**
```js
var braces = require('braces');
console.log(braces('a/b{1,3}/{x,y,z}'));
//=> [ 'a/b(1|3)/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
//=> [ 'a/b{1,3}/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
```
### options.unescape
**Type**: `Boolean`
**Default**: `undefined`
**Description**: Strip backslashes that were used for escaping from the result.
## What is "brace expansion"?
Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).
In addition to "expansion", braces are also used for matching. In other words:
* [brace expansion](#brace-expansion) is for generating new lists
* [brace matching](#brace-matching) is for filtering existing lists
<details>
<summary><strong>More about brace expansion</strong> (click to expand)</summary>
There are two main types of brace expansion:
1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".
Here are some example brace patterns to illustrate how they work:
**Sets**
```
{a,b,c} => a b c
{a,b,c}{1,2} => a1 a2 b1 b2 c1 c2
```
**Sequences**
```
{1..9} => 1 2 3 4 5 6 7 8 9
{4..-4} => 4 3 2 1 0 -1 -2 -3 -4
{1..20..3} => 1 4 7 10 13 16 19
{a..j} => a b c d e f g h i j
{j..a} => j i h g f e d c b a
{a..z..3} => a d g j m p s v y
```
**Combination**
Sets and sequences can be mixed together or used along with any other strings.
```
{a,b,c}{1..3} => a1 a2 a3 b1 b2 b3 c1 c2 c3
foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
```
The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.
## Brace matching
In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.
For example, the pattern `foo/{1..3}/bar` would match any of following strings:
```
foo/1/bar
foo/2/bar
foo/3/bar
```
But not:
```
baz/1/qux
baz/2/qux
baz/3/qux
```
Braces can also be combined with [glob patterns](https://github.com/jonschlinkert/micromatch) to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:
```
foo/1/bar
foo/2/bar
foo/3/bar
baz/1/qux
baz/2/qux
baz/3/qux
```
## Brace matching pitfalls
Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of.
### tldr
**"brace bombs"**
* brace expansion can eat up a huge amount of processing resources
* as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
* users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)
For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.
### The solution
Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.
### Geometric complexity
At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.
For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:
```
{1,2}{3,4} => (2X2) => 13 14 23 24
{1,2}{3,4}{5,6} => (2X2X2) => 135 136 145 146 235 236 245 246
```
But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:
```
{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248
249 257 258 259 267 268 269 347 348 349 357
358 359 367 368 369
```
Now, imagine how this complexity grows given that each element is a n-tuple:
```
{1..100}{1..100} => (100X100) => 10,000 elements (38.4 kB)
{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
```
Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control.
**More information**
Interested in learning more about brace expansion?
* [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
* [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
* [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)
</details>
## Performance
Braces is not only screaming fast, it's also more accurate the other brace expansion libraries.
### Better algorithms
Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_.
Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently.
**The proof is in the numbers**
Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.
| **Pattern** | **braces** | **[minimatch](https://github.com/isaacs/minimatch)** |
| --- | --- | --- |
| `{1..9007199254740991}`<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> | `298 B` (5ms 459μs) | N/A (freezes) |
| `{1..1000000000000000}` | `41 B` (1ms 15μs) | N/A (freezes) |
| `{1..100000000000000}` | `40 B` (890μs) | N/A (freezes) |
| `{1..10000000000000}` | `39 B` (2ms 49μs) | N/A (freezes) |
| `{1..1000000000000}` | `38 B` (608μs) | N/A (freezes) |
| `{1..100000000000}` | `37 B` (397μs) | N/A (freezes) |
| `{1..10000000000}` | `35 B` (983μs) | N/A (freezes) |
| `{1..1000000000}` | `34 B` (798μs) | N/A (freezes) |
| `{1..100000000}` | `33 B` (733μs) | N/A (freezes) |
| `{1..10000000}` | `32 B` (5ms 632μs) | `78.89 MB` (16s 388ms 569μs) |
| `{1..1000000}` | `31 B` (1ms 381μs) | `6.89 MB` (1s 496ms 887μs) |
| `{1..100000}` | `30 B` (950μs) | `588.89 kB` (146ms 921μs) |
| `{1..10000}` | `29 B` (1ms 114μs) | `48.89 kB` (14ms 187μs) |
| `{1..1000}` | `28 B` (760μs) | `3.89 kB` (1ms 453μs) |
| `{1..100}` | `22 B` (345μs) | `291 B` (196μs) |
| `{1..10}` | `10 B` (533μs) | `20 B` (37μs) |
| `{1..3}` | `7 B` (190μs) | `5 B` (27μs) |
### Faster algorithms
When you need expansion, braces is still much faster.
_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_
| **Pattern** | **braces** | **[minimatch](https://github.com/isaacs/minimatch)** |
| --- | --- | --- |
| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
| `{1..1000000}` | `6.89 MB` (458ms 576μs) | `6.89 MB` (1s 491ms 621μs) |
| `{1..100000}` | `588.89 kB` (20ms 728μs) | `588.89 kB` (156ms 919μs) |
| `{1..10000}` | `48.89 kB` (2ms 202μs) | `48.89 kB` (13ms 641μs) |
| `{1..1000}` | `3.89 kB` (1ms 796μs) | `3.89 kB` (1ms 958μs) |
| `{1..100}` | `291 B` (424μs) | `291 B` (211μs) |
| `{1..10}` | `20 B` (487μs) | `20 B` (72μs) |
| `{1..3}` | `5 B` (166μs) | `5 B` (27μs) |
If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).
## Benchmarks
### Running benchmarks
Install dev dependencies:
```bash
npm i -d && npm benchmark
```
### Latest results
```bash
Benchmarking: (8 of 8)
· combination-nested
· combination
· escaped
· list-basic
· list-multiple
· no-braces
· sequence-basic
· sequence-multiple
# benchmark/fixtures/combination-nested.js (52 bytes)
brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
fastest is braces
# benchmark/fixtures/combination.js (51 bytes)
brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
minimatch x 637 ops/sec ±0.84% (88 runs sampled)
fastest is braces
# benchmark/fixtures/escaped.js (44 bytes)
brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
fastest is braces
# benchmark/fixtures/list-basic.js (40 bytes)
brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
fastest is braces
# benchmark/fixtures/list-multiple.js (52 bytes)
brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
fastest is braces
# benchmark/fixtures/no-braces.js (48 bytes)
brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
fastest is braces
# benchmark/fixtures/sequence-basic.js (41 bytes)
brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
fastest is braces
# benchmark/fixtures/sequence-multiple.js (51 bytes)
brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
minimatch x 109 ops/sec ±1.16% (76 runs sampled)
fastest is braces
```
## About
<details>
<summary><strong>Contributing</strong></summary>
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
</details>
<details>
<summary><strong>Running Tests</strong></summary>
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
</details>
<details>
<summary><strong>Building docs</strong></summary>
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```
</details>
### Related projects
You might also be interested in these projects:
* [expand-brackets](https://www.npmjs.com/package/expand-brackets): Expand POSIX bracket expressions (character classes) in glob patterns. | [homepage](https://github.com/jonschlinkert/expand-brackets "Expand POSIX bracket expressions (character classes) in glob patterns.")
* [extglob](https://www.npmjs.com/package/extglob): Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob… [more](https://github.com/micromatch/extglob) | [homepage](https://github.com/micromatch/extglob "Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns.")
* [fill-range](https://www.npmjs.com/package/fill-range): Fill in a range of numbers or letters, optionally passing an increment or `step` to… [more](https://github.com/jonschlinkert/fill-range) | [homepage](https://github.com/jonschlinkert/fill-range "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`")
* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/micromatch/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
* [nanomatch](https://www.npmjs.com/package/nanomatch): Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash… [more](https://github.com/micromatch/nanomatch) | [homepage](https://github.com/micromatch/nanomatch "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)")
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 188 | [jonschlinkert](https://github.com/jonschlinkert) |
| 4 | [doowb](https://github.com/doowb) |
| 1 | [es128](https://github.com/es128) |
| 1 | [eush77](https://github.com/eush77) |
| 1 | [hemanth](https://github.com/hemanth) |
### Author
**Jon Schlinkert**
* [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert)
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
### License
Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on February 17, 2018._
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item">this is the largest safe integer allowed in JavaScript. <a href="#fnref1" class="footnote-backref"></a>
</li>
</ol>
</section>

View file

@ -0,0 +1,318 @@
'use strict';
/**
* Module dependencies
*/
var toRegex = require('to-regex');
var unique = require('array-unique');
var extend = require('extend-shallow');
/**
* Local dependencies
*/
var compilers = require('./lib/compilers');
var parsers = require('./lib/parsers');
var Braces = require('./lib/braces');
var utils = require('./lib/utils');
var MAX_LENGTH = 1024 * 64;
var cache = {};
/**
* Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)).
*
* ```js
* var braces = require('braces');
* console.log(braces('{a,b,c}'));
* //=> ['(a|b|c)']
*
* console.log(braces('{a,b,c}', {expand: true}));
* //=> ['a', 'b', 'c']
* ```
* @param {String} `str`
* @param {Object} `options`
* @return {String}
* @api public
*/
function braces(pattern, options) {
var key = utils.createKey(String(pattern), options);
var arr = [];
var disabled = options && options.cache === false;
if (!disabled && cache.hasOwnProperty(key)) {
return cache[key];
}
if (Array.isArray(pattern)) {
for (var i = 0; i < pattern.length; i++) {
arr.push.apply(arr, braces.create(pattern[i], options));
}
} else {
arr = braces.create(pattern, options);
}
if (options && options.nodupes === true) {
arr = unique(arr);
}
if (!disabled) {
cache[key] = arr;
}
return arr;
}
/**
* Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead.
*
* ```js
* var braces = require('braces');
* console.log(braces.expand('a/{b,c}/d'));
* //=> ['a/b/d', 'a/c/d'];
* ```
* @param {String} `pattern` Brace pattern
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.expand = function(pattern, options) {
return braces.create(pattern, extend({}, options, {expand: true}));
};
/**
* Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default.
*
* ```js
* var braces = require('braces');
* console.log(braces.expand('a/{b,c}/d'));
* //=> ['a/(b|c)/d']
* ```
* @param {String} `pattern` Brace pattern
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.optimize = function(pattern, options) {
return braces.create(pattern, options);
};
/**
* Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function.
*
* ```js
* var braces = require('braces');
* console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
* //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
* ```
* @param {String} `pattern` Brace pattern
* @param {Object} `options`
* @return {Array} Returns an array of expanded values.
* @api public
*/
braces.create = function(pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('expected a string');
}
var maxLength = (options && options.maxLength) || MAX_LENGTH;
if (pattern.length >= maxLength) {
throw new Error('expected pattern to be less than ' + maxLength + ' characters');
}
function create() {
if (pattern === '' || pattern.length < 3) {
return [pattern];
}
if (utils.isEmptySets(pattern)) {
return [];
}
if (utils.isQuotedString(pattern)) {
return [pattern.slice(1, -1)];
}
var proto = new Braces(options);
var result = !options || options.expand !== true
? proto.optimize(pattern, options)
: proto.expand(pattern, options);
// get the generated pattern(s)
var arr = result.output;
// filter out empty strings if specified
if (options && options.noempty === true) {
arr = arr.filter(Boolean);
}
// filter out duplicates if specified
if (options && options.nodupes === true) {
arr = unique(arr);
}
Object.defineProperty(arr, 'result', {
enumerable: false,
value: result
});
return arr;
}
return memoize('create', pattern, options, create);
};
/**
* Create a regular expression from the given string `pattern`.
*
* ```js
* var braces = require('braces');
*
* console.log(braces.makeRe('id-{200..300}'));
* //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/
* ```
* @param {String} `pattern` The pattern to convert to regex.
* @param {Object} `options`
* @return {RegExp}
* @api public
*/
braces.makeRe = function(pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('expected a string');
}
var maxLength = (options && options.maxLength) || MAX_LENGTH;
if (pattern.length >= maxLength) {
throw new Error('expected pattern to be less than ' + maxLength + ' characters');
}
function makeRe() {
var arr = braces(pattern, options);
var opts = extend({strictErrors: false}, options);
return toRegex(arr, opts);
}
return memoize('makeRe', pattern, options, makeRe);
};
/**
* Parse the given `str` with the given `options`.
*
* ```js
* var braces = require('braces');
* var ast = braces.parse('a/{b,c}/d');
* console.log(ast);
* // { type: 'root',
* // errors: [],
* // input: 'a/{b,c}/d',
* // nodes:
* // [ { type: 'bos', val: '' },
* // { type: 'text', val: 'a/' },
* // { type: 'brace',
* // nodes:
* // [ { type: 'brace.open', val: '{' },
* // { type: 'text', val: 'b,c' },
* // { type: 'brace.close', val: '}' } ] },
* // { type: 'text', val: '/d' },
* // { type: 'eos', val: '' } ] }
* ```
* @param {String} `pattern` Brace pattern to parse
* @param {Object} `options`
* @return {Object} Returns an AST
* @api public
*/
braces.parse = function(pattern, options) {
var proto = new Braces(options);
return proto.parse(pattern, options);
};
/**
* Compile the given `ast` or string with the given `options`.
*
* ```js
* var braces = require('braces');
* var ast = braces.parse('a/{b,c}/d');
* console.log(braces.compile(ast));
* // { options: { source: 'string' },
* // state: {},
* // compilers:
* // { eos: [Function],
* // noop: [Function],
* // bos: [Function],
* // brace: [Function],
* // 'brace.open': [Function],
* // text: [Function],
* // 'brace.close': [Function] },
* // output: [ 'a/(b|c)/d' ],
* // ast:
* // { ... },
* // parsingErrors: [] }
* ```
* @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first.
* @param {Object} `options`
* @return {Object} Returns an object that has an `output` property with the compiled string.
* @api public
*/
braces.compile = function(ast, options) {
var proto = new Braces(options);
return proto.compile(ast, options);
};
/**
* Clear the regex cache.
*
* ```js
* braces.clearCache();
* ```
* @api public
*/
braces.clearCache = function() {
cache = braces.cache = {};
};
/**
* Memoize a generated regex or function. A unique key is generated
* from the method name, pattern, and user-defined options. Set
* options.memoize to false to disable.
*/
function memoize(type, pattern, options, fn) {
var key = utils.createKey(type + ':' + pattern, options);
var disabled = options && options.cache === false;
if (disabled) {
braces.clearCache();
return fn(pattern, options);
}
if (cache.hasOwnProperty(key)) {
return cache[key];
}
var res = fn(pattern, options);
cache[key] = res;
return res;
}
/**
* Expose `Braces` constructor and methods
* @type {Function}
*/
braces.Braces = Braces;
braces.compilers = compilers;
braces.parsers = parsers;
braces.cache = cache;
/**
* Expose `braces`
* @type {Function}
*/
module.exports = braces;

View file

@ -0,0 +1,104 @@
'use strict';
var extend = require('extend-shallow');
var Snapdragon = require('snapdragon');
var compilers = require('./compilers');
var parsers = require('./parsers');
var utils = require('./utils');
/**
* Customize Snapdragon parser and renderer
*/
function Braces(options) {
this.options = extend({}, options);
}
/**
* Initialize braces
*/
Braces.prototype.init = function(options) {
if (this.isInitialized) return;
this.isInitialized = true;
var opts = utils.createOptions({}, this.options, options);
this.snapdragon = this.options.snapdragon || new Snapdragon(opts);
this.compiler = this.snapdragon.compiler;
this.parser = this.snapdragon.parser;
compilers(this.snapdragon, opts);
parsers(this.snapdragon, opts);
/**
* Call Snapdragon `.parse` method. When AST is returned, we check to
* see if any unclosed braces are left on the stack and, if so, we iterate
* over the stack and correct the AST so that compilers are called in the correct
* order and unbalance braces are properly escaped.
*/
utils.define(this.snapdragon, 'parse', function(pattern, options) {
var parsed = Snapdragon.prototype.parse.apply(this, arguments);
this.parser.ast.input = pattern;
var stack = this.parser.stack;
while (stack.length) {
addParent({type: 'brace.close', val: ''}, stack.pop());
}
function addParent(node, parent) {
utils.define(node, 'parent', parent);
parent.nodes.push(node);
}
// add non-enumerable parser reference
utils.define(parsed, 'parser', this.parser);
return parsed;
});
};
/**
* Decorate `.parse` method
*/
Braces.prototype.parse = function(ast, options) {
if (ast && typeof ast === 'object' && ast.nodes) return ast;
this.init(options);
return this.snapdragon.parse(ast, options);
};
/**
* Decorate `.compile` method
*/
Braces.prototype.compile = function(ast, options) {
if (typeof ast === 'string') {
ast = this.parse(ast, options);
} else {
this.init(options);
}
return this.snapdragon.compile(ast, options);
};
/**
* Expand
*/
Braces.prototype.expand = function(pattern) {
var ast = this.parse(pattern, {expand: true});
return this.compile(ast, {expand: true});
};
/**
* Optimize
*/
Braces.prototype.optimize = function(pattern) {
var ast = this.parse(pattern, {optimize: true});
return this.compile(ast, {optimize: true});
};
/**
* Expose `Braces`
*/
module.exports = Braces;

View file

@ -0,0 +1,282 @@
'use strict';
var utils = require('./utils');
module.exports = function(braces, options) {
braces.compiler
/**
* bos
*/
.set('bos', function() {
if (this.output) return;
this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : [];
this.ast.count = 1;
})
/**
* Square brackets
*/
.set('bracket', function(node) {
var close = node.close;
var open = !node.escaped ? '[' : '\\[';
var negated = node.negated;
var inner = node.inner;
inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\');
if (inner === ']-') {
inner = '\\]\\-';
}
if (negated && inner.indexOf('.') === -1) {
inner += '.';
}
if (negated && inner.indexOf('/') === -1) {
inner += '/';
}
var val = open + negated + inner + close;
var queue = node.parent.queue;
var last = utils.arrayify(queue.pop());
queue.push(utils.join(last, val));
queue.push.apply(queue, []);
})
/**
* Brace
*/
.set('brace', function(node) {
node.queue = isEscaped(node) ? [node.val] : [];
node.count = 1;
return this.mapVisit(node.nodes);
})
/**
* Open
*/
.set('brace.open', function(node) {
node.parent.open = node.val;
})
/**
* Inner
*/
.set('text', function(node) {
var queue = node.parent.queue;
var escaped = node.escaped;
var segs = [node.val];
if (node.optimize === false) {
options = utils.extend({}, options, {optimize: false});
}
if (node.multiplier > 1) {
node.parent.count *= node.multiplier;
}
if (options.quantifiers === true && utils.isQuantifier(node.val)) {
escaped = true;
} else if (node.val.length > 1) {
if (isType(node.parent, 'brace') && !isEscaped(node)) {
var expanded = utils.expand(node.val, options);
segs = expanded.segs;
if (expanded.isOptimized) {
node.parent.isOptimized = true;
}
// if nothing was expanded, we probably have a literal brace
if (!segs.length) {
var val = (expanded.val || node.val);
if (options.unescape !== false) {
// unescape unexpanded brace sequence/set separators
val = val.replace(/\\([,.])/g, '$1');
// strip quotes
val = val.replace(/["'`]/g, '');
}
segs = [val];
escaped = true;
}
}
} else if (node.val === ',') {
if (options.expand) {
node.parent.queue.push(['']);
segs = [''];
} else {
segs = ['|'];
}
} else {
escaped = true;
}
if (escaped && isType(node.parent, 'brace')) {
if (node.parent.nodes.length <= 4 && node.parent.count === 1) {
node.parent.escaped = true;
} else if (node.parent.length <= 3) {
node.parent.escaped = true;
}
}
if (!hasQueue(node.parent)) {
node.parent.queue = segs;
return;
}
var last = utils.arrayify(queue.pop());
if (node.parent.count > 1 && options.expand) {
last = multiply(last, node.parent.count);
node.parent.count = 1;
}
queue.push(utils.join(utils.flatten(last), segs.shift()));
queue.push.apply(queue, segs);
})
/**
* Close
*/
.set('brace.close', function(node) {
var queue = node.parent.queue;
var prev = node.parent.parent;
var last = prev.queue.pop();
var open = node.parent.open;
var close = node.val;
if (open && close && isOptimized(node, options)) {
open = '(';
close = ')';
}
// if a close brace exists, and the previous segment is one character
// don't wrap the result in braces or parens
var ele = utils.last(queue);
if (node.parent.count > 1 && options.expand) {
ele = multiply(queue.pop(), node.parent.count);
node.parent.count = 1;
queue.push(ele);
}
if (close && typeof ele === 'string' && ele.length === 1) {
open = '';
close = '';
}
if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) {
queue.push(utils.join(open, queue.pop() || ''));
queue = utils.flatten(utils.join(queue, close));
}
if (typeof last === 'undefined') {
prev.queue = [queue];
} else {
prev.queue.push(utils.flatten(utils.join(last, queue)));
}
})
/**
* eos
*/
.set('eos', function(node) {
if (this.input) return;
if (options.optimize !== false) {
this.output = utils.last(utils.flatten(this.ast.queue));
} else if (Array.isArray(utils.last(this.ast.queue))) {
this.output = utils.flatten(this.ast.queue.pop());
} else {
this.output = utils.flatten(this.ast.queue);
}
if (node.parent.count > 1 && options.expand) {
this.output = multiply(this.output, node.parent.count);
}
this.output = utils.arrayify(this.output);
this.ast.queue = [];
});
};
/**
* Multiply the segments in the current brace level
*/
function multiply(queue, n, options) {
return utils.flatten(utils.repeat(utils.arrayify(queue), n));
}
/**
* Return true if `node` is escaped
*/
function isEscaped(node) {
return node.escaped === true;
}
/**
* Returns true if regex parens should be used for sets. If the parent `type`
* is not `brace`, then we're on a root node, which means we should never
* expand segments and open/close braces should be `{}` (since this indicates
* a brace is missing from the set)
*/
function isOptimized(node, options) {
if (node.parent.isOptimized) return true;
return isType(node.parent, 'brace')
&& !isEscaped(node.parent)
&& options.expand !== true;
}
/**
* Returns true if the value in `node` should be wrapped in a literal brace.
* @return {Boolean}
*/
function isLiteralBrace(node, options) {
return isEscaped(node.parent) || options.optimize !== false;
}
/**
* Returns true if the given `node` does not have an inner value.
* @return {Boolean}
*/
function noInner(node, type) {
if (node.parent.queue.length === 1) {
return true;
}
var nodes = node.parent.nodes;
return nodes.length === 3
&& isType(nodes[0], 'brace.open')
&& !isType(nodes[1], 'text')
&& isType(nodes[2], 'brace.close');
}
/**
* Returns true if the given `node` is the given `type`
* @return {Boolean}
*/
function isType(node, type) {
return typeof node !== 'undefined' && node.type === type;
}
/**
* Returns true if the given `node` has a non-empty queue.
* @return {Boolean}
*/
function hasQueue(node) {
return Array.isArray(node.queue) && node.queue.length;
}

View file

@ -0,0 +1,360 @@
'use strict';
var Node = require('snapdragon-node');
var utils = require('./utils');
/**
* Braces parsers
*/
module.exports = function(braces, options) {
braces.parser
.set('bos', function() {
if (!this.parsed) {
this.ast = this.nodes[0] = new Node(this.ast);
}
})
/**
* Character parsers
*/
.set('escape', function() {
var pos = this.position();
var m = this.match(/^(?:\\(.)|\$\{)/);
if (!m) return;
var prev = this.prev();
var last = utils.last(prev.nodes);
var node = pos(new Node({
type: 'text',
multiplier: 1,
val: m[0]
}));
if (node.val === '\\\\') {
return node;
}
if (node.val === '${') {
var str = this.input;
var idx = -1;
var ch;
while ((ch = str[++idx])) {
this.consume(1);
node.val += ch;
if (ch === '\\') {
node.val += str[++idx];
continue;
}
if (ch === '}') {
break;
}
}
}
if (this.options.unescape !== false) {
node.val = node.val.replace(/\\([{}])/g, '$1');
}
if (last.val === '"' && this.input.charAt(0) === '"') {
last.val = node.val;
this.consume(1);
return;
}
return concatNodes.call(this, pos, node, prev, options);
})
/**
* Brackets: "[...]" (basic, this is overridden by
* other parsers in more advanced implementations)
*/
.set('bracket', function() {
var isInside = this.isInside('brace');
var pos = this.position();
var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]-)(\]|[^*+?]+)|\[)/);
if (!m) return;
var prev = this.prev();
var val = m[0];
var negated = m[1] ? '^' : '';
var inner = m[2] || '';
var close = m[3] || '';
if (isInside && prev.type === 'brace') {
prev.text = prev.text || '';
prev.text += val;
}
var esc = this.input.slice(0, 2);
if (inner === '' && esc === '\\]') {
inner += esc;
this.consume(2);
var str = this.input;
var idx = -1;
var ch;
while ((ch = str[++idx])) {
this.consume(1);
if (ch === ']') {
close = ch;
break;
}
inner += ch;
}
}
return pos(new Node({
type: 'bracket',
val: val,
escaped: close !== ']',
negated: negated,
inner: inner,
close: close
}));
})
/**
* Empty braces (we capture these early to
* speed up processing in the compiler)
*/
.set('multiplier', function() {
var isInside = this.isInside('brace');
var pos = this.position();
var m = this.match(/^\{((?:,|\{,+\})+)\}/);
if (!m) return;
this.multiplier = true;
var prev = this.prev();
var val = m[0];
if (isInside && prev.type === 'brace') {
prev.text = prev.text || '';
prev.text += val;
}
var node = pos(new Node({
type: 'text',
multiplier: 1,
match: m,
val: val
}));
return concatNodes.call(this, pos, node, prev, options);
})
/**
* Open
*/
.set('brace.open', function() {
var pos = this.position();
var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/);
if (!m) return;
var prev = this.prev();
var last = utils.last(prev.nodes);
// if the last parsed character was an extglob character
// we need to _not optimize_ the brace pattern because
// it might be mistaken for an extglob by a downstream parser
if (last && last.val && isExtglobChar(last.val.slice(-1))) {
last.optimize = false;
}
var open = pos(new Node({
type: 'brace.open',
val: m[0]
}));
var node = pos(new Node({
type: 'brace',
nodes: []
}));
node.push(open);
prev.push(node);
this.push('brace', node);
})
/**
* Close
*/
.set('brace.close', function() {
var pos = this.position();
var m = this.match(/^\}/);
if (!m || !m[0]) return;
var brace = this.pop('brace');
var node = pos(new Node({
type: 'brace.close',
val: m[0]
}));
if (!this.isType(brace, 'brace')) {
if (this.options.strict) {
throw new Error('missing opening "{"');
}
node.type = 'text';
node.multiplier = 0;
node.escaped = true;
return node;
}
var prev = this.prev();
var last = utils.last(prev.nodes);
if (last.text) {
var lastNode = utils.last(last.nodes);
if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) {
var open = last.nodes[0];
var text = last.nodes[1];
if (open.type === 'brace.open' && text && text.type === 'text') {
text.optimize = false;
}
}
}
if (brace.nodes.length > 2) {
var first = brace.nodes[1];
if (first.type === 'text' && first.val === ',') {
brace.nodes.splice(1, 1);
brace.nodes.push(first);
}
}
brace.push(node);
})
/**
* Capture boundary characters
*/
.set('boundary', function() {
var pos = this.position();
var m = this.match(/^[$^](?!\{)/);
if (!m) return;
return pos(new Node({
type: 'text',
val: m[0]
}));
})
/**
* One or zero, non-comma characters wrapped in braces
*/
.set('nobrace', function() {
var isInside = this.isInside('brace');
var pos = this.position();
var m = this.match(/^\{[^,]?\}/);
if (!m) return;
var prev = this.prev();
var val = m[0];
if (isInside && prev.type === 'brace') {
prev.text = prev.text || '';
prev.text += val;
}
return pos(new Node({
type: 'text',
multiplier: 0,
val: val
}));
})
/**
* Text
*/
.set('text', function() {
var isInside = this.isInside('brace');
var pos = this.position();
var m = this.match(/^((?!\\)[^${}[\]])+/);
if (!m) return;
var prev = this.prev();
var val = m[0];
if (isInside && prev.type === 'brace') {
prev.text = prev.text || '';
prev.text += val;
}
var node = pos(new Node({
type: 'text',
multiplier: 1,
val: val
}));
return concatNodes.call(this, pos, node, prev, options);
});
};
/**
* Returns true if the character is an extglob character.
*/
function isExtglobChar(ch) {
return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+';
}
/**
* Combine text nodes, and calculate empty sets (`{,,}`)
* @param {Function} `pos` Function to calculate node position
* @param {Object} `node` AST node
* @return {Object}
*/
function concatNodes(pos, node, parent, options) {
node.orig = node.val;
var prev = this.prev();
var last = utils.last(prev.nodes);
var isEscaped = false;
if (node.val.length > 1) {
var a = node.val.charAt(0);
var b = node.val.slice(-1);
isEscaped = (a === '"' && b === '"')
|| (a === "'" && b === "'")
|| (a === '`' && b === '`');
}
if (isEscaped && options.unescape !== false) {
node.val = node.val.slice(1, node.val.length - 1);
node.escaped = true;
}
if (node.match) {
var match = node.match[1];
if (!match || match.indexOf('}') === -1) {
match = node.match[0];
}
// replace each set with a single ","
var val = match.replace(/\{/g, ',').replace(/\}/g, '');
node.multiplier *= val.length;
node.val = '';
}
var simpleText = last.type === 'text'
&& last.multiplier === 1
&& node.multiplier === 1
&& node.val;
if (simpleText) {
last.val += node.val;
return;
}
prev.push(node);
}

View file

@ -0,0 +1,343 @@
'use strict';
var splitString = require('split-string');
var utils = module.exports;
/**
* Module dependencies
*/
utils.extend = require('extend-shallow');
utils.flatten = require('arr-flatten');
utils.isObject = require('isobject');
utils.fillRange = require('fill-range');
utils.repeat = require('repeat-element');
utils.unique = require('array-unique');
utils.define = function(obj, key, val) {
Object.defineProperty(obj, key, {
writable: true,
configurable: true,
enumerable: false,
value: val
});
};
/**
* Returns true if the given string contains only empty brace sets.
*/
utils.isEmptySets = function(str) {
return /^(?:\{,\})+$/.test(str);
};
/**
* Returns true if the given string contains only empty brace sets.
*/
utils.isQuotedString = function(str) {
var open = str.charAt(0);
if (open === '\'' || open === '"' || open === '`') {
return str.slice(-1) === open;
}
return false;
};
/**
* Create the key to use for memoization. The unique key is generated
* by iterating over the options and concatenating key-value pairs
* to the pattern string.
*/
utils.createKey = function(pattern, options) {
var id = pattern;
if (typeof options === 'undefined') {
return id;
}
var keys = Object.keys(options);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
id += ';' + key + '=' + String(options[key]);
}
return id;
};
/**
* Normalize options
*/
utils.createOptions = function(options) {
var opts = utils.extend.apply(null, arguments);
if (typeof opts.expand === 'boolean') {
opts.optimize = !opts.expand;
}
if (typeof opts.optimize === 'boolean') {
opts.expand = !opts.optimize;
}
if (opts.optimize === true) {
opts.makeRe = true;
}
return opts;
};
/**
* Join patterns in `a` to patterns in `b`
*/
utils.join = function(a, b, options) {
options = options || {};
a = utils.arrayify(a);
b = utils.arrayify(b);
if (!a.length) return b;
if (!b.length) return a;
var len = a.length;
var idx = -1;
var arr = [];
while (++idx < len) {
var val = a[idx];
if (Array.isArray(val)) {
for (var i = 0; i < val.length; i++) {
val[i] = utils.join(val[i], b, options);
}
arr.push(val);
continue;
}
for (var j = 0; j < b.length; j++) {
var bval = b[j];
if (Array.isArray(bval)) {
arr.push(utils.join(val, bval, options));
} else {
arr.push(val + bval);
}
}
}
return arr;
};
/**
* Split the given string on `,` if not escaped.
*/
utils.split = function(str, options) {
var opts = utils.extend({sep: ','}, options);
if (typeof opts.keepQuotes !== 'boolean') {
opts.keepQuotes = true;
}
if (opts.unescape === false) {
opts.keepEscaping = true;
}
return splitString(str, opts, utils.escapeBrackets(opts));
};
/**
* Expand ranges or sets in the given `pattern`.
*
* @param {String} `str`
* @param {Object} `options`
* @return {Object}
*/
utils.expand = function(str, options) {
var opts = utils.extend({rangeLimit: 10000}, options);
var segs = utils.split(str, opts);
var tok = { segs: segs };
if (utils.isQuotedString(str)) {
return tok;
}
if (opts.rangeLimit === true) {
opts.rangeLimit = 10000;
}
if (segs.length > 1) {
if (opts.optimize === false) {
tok.val = segs[0];
return tok;
}
tok.segs = utils.stringifyArray(tok.segs);
} else if (segs.length === 1) {
var arr = str.split('..');
if (arr.length === 1) {
tok.val = tok.segs[tok.segs.length - 1] || tok.val || str;
tok.segs = [];
return tok;
}
if (arr.length === 2 && arr[0] === arr[1]) {
tok.escaped = true;
tok.val = arr[0];
tok.segs = [];
return tok;
}
if (arr.length > 1) {
if (opts.optimize !== false) {
opts.optimize = true;
delete opts.expand;
}
if (opts.optimize !== true) {
var min = Math.min(arr[0], arr[1]);
var max = Math.max(arr[0], arr[1]);
var step = arr[2] || 1;
if (opts.rangeLimit !== false && ((max - min) / step >= opts.rangeLimit)) {
throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
}
}
arr.push(opts);
tok.segs = utils.fillRange.apply(null, arr);
if (!tok.segs.length) {
tok.escaped = true;
tok.val = str;
return tok;
}
if (opts.optimize === true) {
tok.segs = utils.stringifyArray(tok.segs);
}
if (tok.segs === '') {
tok.val = str;
} else {
tok.val = tok.segs[0];
}
return tok;
}
} else {
tok.val = str;
}
return tok;
};
/**
* Ensure commas inside brackets and parens are not split.
* @param {Object} `tok` Token from the `split-string` module
* @return {undefined}
*/
utils.escapeBrackets = function(options) {
return function(tok) {
if (tok.escaped && tok.val === 'b') {
tok.val = '\\b';
return;
}
if (tok.val !== '(' && tok.val !== '[') return;
var opts = utils.extend({}, options);
var brackets = [];
var parens = [];
var stack = [];
var val = tok.val;
var str = tok.str;
var i = tok.idx - 1;
while (++i < str.length) {
var ch = str[i];
if (ch === '\\') {
val += (opts.keepEscaping === false ? '' : ch) + str[++i];
continue;
}
if (ch === '(') {
parens.push(ch);
stack.push(ch);
}
if (ch === '[') {
brackets.push(ch);
stack.push(ch);
}
if (ch === ')') {
parens.pop();
stack.pop();
if (!stack.length) {
val += ch;
break;
}
}
if (ch === ']') {
brackets.pop();
stack.pop();
if (!stack.length) {
val += ch;
break;
}
}
val += ch;
}
tok.split = false;
tok.val = val.slice(1);
tok.idx = i;
};
};
/**
* Returns true if the given string looks like a regex quantifier
* @return {Boolean}
*/
utils.isQuantifier = function(str) {
return /^(?:[0-9]?,[0-9]|[0-9],)$/.test(str);
};
/**
* Cast `val` to an array.
* @param {*} `val`
*/
utils.stringifyArray = function(arr) {
return [utils.arrayify(arr).join('|')];
};
/**
* Cast `val` to an array.
* @param {*} `val`
*/
utils.arrayify = function(arr) {
if (typeof arr === 'undefined') {
return [];
}
if (typeof arr === 'string') {
return [arr];
}
return arr;
};
/**
* Returns true if the given `str` is a non-empty string
* @return {Boolean}
*/
utils.isString = function(str) {
return str != null && typeof str === 'string';
};
/**
* Get the last element from `array`
* @param {Array} `array`
* @return {*}
*/
utils.last = function(arr, n) {
return arr[arr.length - (n || 1)];
};
utils.escapeRegex = function(str) {
return str.replace(/\\?([!^*?()[\]{}+?/])/g, '\\$1');
};

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2015, Jon Schlinkert.
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,61 @@
# extend-shallow [![NPM version](https://badge.fury.io/js/extend-shallow.svg)](http://badge.fury.io/js/extend-shallow) [![Build Status](https://travis-ci.org/jonschlinkert/extend-shallow.svg)](https://travis-ci.org/jonschlinkert/extend-shallow)
> Extend an object with the properties of additional objects. node.js/javascript util.
## Install
Install with [npm](https://www.npmjs.com/)
```sh
$ npm i extend-shallow --save
```
## Usage
```js
var extend = require('extend-shallow');
extend({a: 'b'}, {c: 'd'})
//=> {a: 'b', c: 'd'}
```
Pass an empty object to shallow clone:
```js
var obj = {};
extend(obj, {a: 'b'}, {c: 'd'})
//=> {a: 'b', c: 'd'}
```
## Related
* [extend-shallow](https://github.com/jonschlinkert/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util.
* [for-own](https://github.com/jonschlinkert/for-own): Iterate over the own enumerable properties of an object, and return an object with properties… [more](https://github.com/jonschlinkert/for-own)
* [for-in](https://github.com/jonschlinkert/for-in): Iterate over the own and inherited enumerable properties of an objecte, and return an object… [more](https://github.com/jonschlinkert/for-in)
* [is-plain-object](https://github.com/jonschlinkert/is-plain-object): Returns true if an object was created by the `Object` constructor.
* [isobject](https://github.com/jonschlinkert/isobject): Returns true if the value is an object and not an array or null.
* [kind-of](https://github.com/jonschlinkert/kind-of): Get the native type of a value.
## Running tests
Install dev dependencies:
```sh
$ npm i -d && npm test
```
## Author
**Jon Schlinkert**
+ [github/jonschlinkert](https://github.com/jonschlinkert)
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
## License
Copyright © 2015 Jon Schlinkert
Released under the MIT license.
***
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on June 29, 2015._

View file

@ -0,0 +1,33 @@
'use strict';
var isObject = require('is-extendable');
module.exports = function extend(o/*, objects*/) {
if (!isObject(o)) { o = {}; }
var len = arguments.length;
for (var i = 1; i < len; i++) {
var obj = arguments[i];
if (isObject(obj)) {
assign(o, obj);
}
}
return o;
};
function assign(a, b) {
for (var key in b) {
if (hasOwn(b, key)) {
a[key] = b[key];
}
}
}
/**
* Returns true if the given `key` is an own property of `obj`.
*/
function hasOwn(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}

View file

@ -0,0 +1,56 @@
{
"name": "extend-shallow",
"description": "Extend an object with the properties of additional objects. node.js/javascript util.",
"version": "2.0.1",
"homepage": "https://github.com/jonschlinkert/extend-shallow",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"repository": "jonschlinkert/extend-shallow",
"bugs": {
"url": "https://github.com/jonschlinkert/extend-shallow/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"is-extendable": "^0.1.0"
},
"devDependencies": {
"array-slice": "^0.2.3",
"benchmarked": "^0.1.4",
"chalk": "^1.0.0",
"for-own": "^0.1.3",
"glob": "^5.0.12",
"is-plain-object": "^2.0.1",
"kind-of": "^2.0.0",
"minimist": "^1.1.1",
"mocha": "^2.2.5",
"should": "^7.0.1"
},
"keywords": [
"assign",
"extend",
"javascript",
"js",
"keys",
"merge",
"obj",
"object",
"prop",
"properties",
"property",
"props",
"shallow",
"util",
"utility",
"utils",
"value"
]
}

View file

@ -0,0 +1,108 @@
{
"name": "braces",
"description": "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.",
"version": "2.3.2",
"homepage": "https://github.com/micromatch/braces",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"Brian Woodward (https://twitter.com/doowb)",
"Elan Shanker (https://github.com/es128)",
"Eugene Sharygin (https://github.com/eush77)",
"hemanth.hm (http://h3manth.com)",
"Jon Schlinkert (http://twitter.com/jonschlinkert)"
],
"repository": "micromatch/braces",
"bugs": {
"url": "https://github.com/micromatch/braces/issues"
},
"license": "MIT",
"files": [
"index.js",
"lib"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha",
"benchmark": "node benchmark"
},
"dependencies": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
"extend-shallow": "^2.0.1",
"fill-range": "^4.0.0",
"isobject": "^3.0.1",
"repeat-element": "^1.1.2",
"snapdragon": "^0.8.1",
"snapdragon-node": "^2.0.1",
"split-string": "^3.0.2",
"to-regex": "^3.0.1"
},
"devDependencies": {
"ansi-cyan": "^0.1.1",
"benchmarked": "^2.0.0",
"brace-expansion": "^1.1.8",
"cross-spawn": "^5.1.0",
"gulp": "^3.9.1",
"gulp-eslint": "^4.0.0",
"gulp-format-md": "^1.0.0",
"gulp-istanbul": "^1.1.2",
"gulp-mocha": "^3.0.1",
"gulp-unused": "^0.2.1",
"is-windows": "^1.0.1",
"minimatch": "^3.0.4",
"mocha": "^3.2.0",
"noncharacters": "^1.1.0",
"text-table": "^0.2.0",
"time-diff": "^0.3.1",
"yargs-parser": "^8.0.0"
},
"keywords": [
"alpha",
"alphabetical",
"bash",
"brace",
"braces",
"expand",
"expansion",
"filepath",
"fill",
"fs",
"glob",
"globbing",
"letter",
"match",
"matches",
"matching",
"number",
"numerical",
"path",
"range",
"ranges",
"sh"
],
"verb": {
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"lint": {
"reflinks": true
},
"plugins": [
"gulp-format-md"
],
"related": {
"list": [
"expand-brackets",
"extglob",
"fill-range",
"micromatch",
"nanomatch"
]
}
}
}

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert
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,250 @@
# fill-range [![NPM version](https://img.shields.io/npm/v/fill-range.svg?style=flat)](https://www.npmjs.com/package/fill-range) [![NPM monthly downloads](https://img.shields.io/npm/dm/fill-range.svg?style=flat)](https://npmjs.org/package/fill-range) [![NPM total downloads](https://img.shields.io/npm/dt/fill-range.svg?style=flat)](https://npmjs.org/package/fill-range) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/fill-range.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/fill-range)
> Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`
## Table of Contents
- [Install](#install)
- [Usage](#usage)
- [Examples](#examples)
- [Options](#options)
* [options.step](#optionsstep)
* [options.strictRanges](#optionsstrictranges)
* [options.stringify](#optionsstringify)
* [options.toRegex](#optionstoregex)
* [options.transform](#optionstransform)
- [About](#about)
_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save fill-range
```
Install with [yarn](https://yarnpkg.com):
```sh
$ yarn add fill-range
```
## Usage
Expands numbers and letters, optionally using a `step` as the last argument. _(Numbers may be defined as JavaScript numbers or strings)_.
```js
var fill = require('fill-range');
fill(from, to[, step, options]);
// examples
console.log(fill('1', '10')); //=> '[ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ]'
console.log(fill('1', '10', {toRegex: true})); //=> [1-9]|10
```
**Params**
* `from`: **{String|Number}** the number or letter to start with
* `to`: **{String|Number}** the number or letter to end with
* `step`: **{String|Number|Object|Function}** Optionally pass a [step](#optionsstep) to use.
* `options`: **{Object|Function}**: See all available [options](#options)
## Examples
By default, an array of values is returned.
**Alphabetical ranges**
```js
console.log(fill('a', 'e')); //=> ['a', 'b', 'c', 'd', 'e']
console.log(fill('A', 'E')); //=> [ 'A', 'B', 'C', 'D', 'E' ]
```
**Numerical ranges**
Numbers can be defined as actual numbers or strings.
```js
console.log(fill(1, 5)); //=> [ 1, 2, 3, 4, 5 ]
console.log(fill('1', '5')); //=> [ 1, 2, 3, 4, 5 ]
```
**Negative ranges**
Numbers can be defined as actual numbers or strings.
```js
console.log(fill('-5', '-1')); //=> [ '-5', '-4', '-3', '-2', '-1' ]
console.log(fill('-5', '5')); //=> [ '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5' ]
```
**Steps (increments)**
```js
// numerical ranges with increments
console.log(fill('0', '25', 4)); //=> [ '0', '4', '8', '12', '16', '20', '24' ]
console.log(fill('0', '25', 5)); //=> [ '0', '5', '10', '15', '20', '25' ]
console.log(fill('0', '25', 6)); //=> [ '0', '6', '12', '18', '24' ]
// alphabetical ranges with increments
console.log(fill('a', 'z', 4)); //=> [ 'a', 'e', 'i', 'm', 'q', 'u', 'y' ]
console.log(fill('a', 'z', 5)); //=> [ 'a', 'f', 'k', 'p', 'u', 'z' ]
console.log(fill('a', 'z', 6)); //=> [ 'a', 'g', 'm', 's', 'y' ]
```
## Options
### options.step
**Type**: `number` (formatted as a string or number)
**Default**: `undefined`
**Description**: The increment to use for the range. Can be used with letters or numbers.
**Example(s)**
```js
// numbers
console.log(fill('1', '10', 2)); //=> [ '1', '3', '5', '7', '9' ]
console.log(fill('1', '10', 3)); //=> [ '1', '4', '7', '10' ]
console.log(fill('1', '10', 4)); //=> [ '1', '5', '9' ]
// letters
console.log(fill('a', 'z', 5)); //=> [ 'a', 'f', 'k', 'p', 'u', 'z' ]
console.log(fill('a', 'z', 7)); //=> [ 'a', 'h', 'o', 'v' ]
console.log(fill('a', 'z', 9)); //=> [ 'a', 'j', 's' ]
```
### options.strictRanges
**Type**: `boolean`
**Default**: `false`
**Description**: By default, `null` is returned when an invalid range is passed. Enable this option to throw a `RangeError` on invalid ranges.
**Example(s)**
The following are all invalid:
```js
fill('1.1', '2'); // decimals not supported in ranges
fill('a', '2'); // incompatible range values
fill(1, 10, 'foo'); // invalid "step" argument
```
### options.stringify
**Type**: `boolean`
**Default**: `undefined`
**Description**: Cast all returned values to strings. By default, integers are returned as numbers.
**Example(s)**
```js
console.log(fill(1, 5)); //=> [ 1, 2, 3, 4, 5 ]
console.log(fill(1, 5, {stringify: true})); //=> [ '1', '2', '3', '4', '5' ]
```
### options.toRegex
**Type**: `boolean`
**Default**: `undefined`
**Description**: Create a regex-compatible source string, instead of expanding values to an array.
**Example(s)**
```js
// alphabetical range
console.log(fill('a', 'e', {toRegex: true})); //=> '[a-e]'
// alphabetical with step
console.log(fill('a', 'z', 3, {toRegex: true})); //=> 'a|d|g|j|m|p|s|v|y'
// numerical range
console.log(fill('1', '100', {toRegex: true})); //=> '[1-9]|[1-9][0-9]|100'
// numerical range with zero padding
console.log(fill('000001', '100000', {toRegex: true}));
//=> '0{5}[1-9]|0{4}[1-9][0-9]|0{3}[1-9][0-9]{2}|0{2}[1-9][0-9]{3}|0[1-9][0-9]{4}|100000'
```
### options.transform
**Type**: `function`
**Default**: `undefined`
**Description**: Customize each value in the returned array (or [string](#optionstoRegex)). _(you can also pass this function as the last argument to `fill()`)_.
**Example(s)**
```js
// increase padding by two
var arr = fill('01', '05', function(val, a, b, step, idx, arr, options) {
return repeat('0', (options.maxLength + 2) - val.length) + val;
});
console.log(arr);
//=> ['0001', '0002', '0003', '0004', '0005']
```
## About
### Related projects
* [braces](https://www.npmjs.com/package/braces): Fast, comprehensive, bash-like brace expansion implemented in JavaScript. Complete support for the Bash 4.3 braces… [more](https://github.com/jonschlinkert/braces) | [homepage](https://github.com/jonschlinkert/braces "Fast, comprehensive, bash-like brace expansion implemented in JavaScript. Complete support for the Bash 4.3 braces specification, without sacrificing speed.")
* [expand-range](https://www.npmjs.com/package/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See… [more](https://github.com/jonschlinkert/expand-range) | [homepage](https://github.com/jonschlinkert/expand-range "Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See the benchmarks. Used by micromatch.")
* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
* [to-regex-range](https://www.npmjs.com/package/to-regex-range): Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than… [more](https://github.com/jonschlinkert/to-regex-range) | [homepage](https://github.com/jonschlinkert/to-regex-range "Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.87 million test assertions.")
### Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 103 | [jonschlinkert](https://github.com/jonschlinkert) |
| 2 | [paulmillr](https://github.com/paulmillr) |
| 1 | [edorivai](https://github.com/edorivai) |
| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
### Building docs
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```
### Running tests
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
### Author
**Jon Schlinkert**
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
### License
Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.5.0, on April 23, 2017._

View file

@ -0,0 +1,208 @@
/*!
* fill-range <https://github.com/jonschlinkert/fill-range>
*
* Copyright (c) 2014-2015, 2017, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
var util = require('util');
var isNumber = require('is-number');
var extend = require('extend-shallow');
var repeat = require('repeat-string');
var toRegex = require('to-regex-range');
/**
* Return a range of numbers or letters.
*
* @param {String} `start` Start of the range
* @param {String} `stop` End of the range
* @param {String} `step` Increment or decrement to use.
* @param {Function} `fn` Custom function to modify each element in the range.
* @return {Array}
*/
function fillRange(start, stop, step, options) {
if (typeof start === 'undefined') {
return [];
}
if (typeof stop === 'undefined' || start === stop) {
// special case, for handling negative zero
var isString = typeof start === 'string';
if (isNumber(start) && !toNumber(start)) {
return [isString ? '0' : 0];
}
return [start];
}
if (typeof step !== 'number' && typeof step !== 'string') {
options = step;
step = undefined;
}
if (typeof options === 'function') {
options = { transform: options };
}
var opts = extend({step: step}, options);
if (opts.step && !isValidNumber(opts.step)) {
if (opts.strictRanges === true) {
throw new TypeError('expected options.step to be a number');
}
return [];
}
opts.isNumber = isValidNumber(start) && isValidNumber(stop);
if (!opts.isNumber && !isValid(start, stop)) {
if (opts.strictRanges === true) {
throw new RangeError('invalid range arguments: ' + util.inspect([start, stop]));
}
return [];
}
opts.isPadded = isPadded(start) || isPadded(stop);
opts.toString = opts.stringify
|| typeof opts.step === 'string'
|| typeof start === 'string'
|| typeof stop === 'string'
|| !opts.isNumber;
if (opts.isPadded) {
opts.maxLength = Math.max(String(start).length, String(stop).length);
}
// support legacy minimatch/fill-range options
if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize;
if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe;
return expand(start, stop, opts);
}
function expand(start, stop, options) {
var a = options.isNumber ? toNumber(start) : start.charCodeAt(0);
var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0);
var step = Math.abs(toNumber(options.step)) || 1;
if (options.toRegex && step === 1) {
return toRange(a, b, start, stop, options);
}
var zero = {greater: [], lesser: []};
var asc = a < b;
var arr = new Array(Math.round((asc ? b - a : a - b) / step));
var idx = 0;
while (asc ? a <= b : a >= b) {
var val = options.isNumber ? a : String.fromCharCode(a);
if (options.toRegex && (val >= 0 || !options.isNumber)) {
zero.greater.push(val);
} else {
zero.lesser.push(Math.abs(val));
}
if (options.isPadded) {
val = zeros(val, options);
}
if (options.toString) {
val = String(val);
}
if (typeof options.transform === 'function') {
arr[idx++] = options.transform(val, a, b, step, idx, arr, options);
} else {
arr[idx++] = val;
}
if (asc) {
a += step;
} else {
a -= step;
}
}
if (options.toRegex === true) {
return toSequence(arr, zero, options);
}
return arr;
}
function toRange(a, b, start, stop, options) {
if (options.isPadded) {
return toRegex(start, stop, options);
}
if (options.isNumber) {
return toRegex(Math.min(a, b), Math.max(a, b), options);
}
var start = String.fromCharCode(Math.min(a, b));
var stop = String.fromCharCode(Math.max(a, b));
return '[' + start + '-' + stop + ']';
}
function toSequence(arr, zeros, options) {
var greater = '', lesser = '';
if (zeros.greater.length) {
greater = zeros.greater.join('|');
}
if (zeros.lesser.length) {
lesser = '-(' + zeros.lesser.join('|') + ')';
}
var res = greater && lesser
? greater + '|' + lesser
: greater || lesser;
if (options.capture) {
return '(' + res + ')';
}
return res;
}
function zeros(val, options) {
if (options.isPadded) {
var str = String(val);
var len = str.length;
var dash = '';
if (str.charAt(0) === '-') {
dash = '-';
str = str.slice(1);
}
var diff = options.maxLength - len;
var pad = repeat('0', diff);
val = (dash + pad + str);
}
if (options.stringify) {
return String(val);
}
return val;
}
function toNumber(val) {
return Number(val) || 0;
}
function isPadded(str) {
return /^-?0\d/.test(str);
}
function isValid(min, max) {
return (isValidNumber(min) || isValidLetter(min))
&& (isValidNumber(max) || isValidLetter(max));
}
function isValidLetter(ch) {
return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch);
}
function isValidNumber(n) {
return isNumber(n) && !/\./.test(n);
}
/**
* Expose `fillRange`
* @type {Function}
*/
module.exports = fillRange;

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2015, Jon Schlinkert.
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,61 @@
# extend-shallow [![NPM version](https://badge.fury.io/js/extend-shallow.svg)](http://badge.fury.io/js/extend-shallow) [![Build Status](https://travis-ci.org/jonschlinkert/extend-shallow.svg)](https://travis-ci.org/jonschlinkert/extend-shallow)
> Extend an object with the properties of additional objects. node.js/javascript util.
## Install
Install with [npm](https://www.npmjs.com/)
```sh
$ npm i extend-shallow --save
```
## Usage
```js
var extend = require('extend-shallow');
extend({a: 'b'}, {c: 'd'})
//=> {a: 'b', c: 'd'}
```
Pass an empty object to shallow clone:
```js
var obj = {};
extend(obj, {a: 'b'}, {c: 'd'})
//=> {a: 'b', c: 'd'}
```
## Related
* [extend-shallow](https://github.com/jonschlinkert/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util.
* [for-own](https://github.com/jonschlinkert/for-own): Iterate over the own enumerable properties of an object, and return an object with properties… [more](https://github.com/jonschlinkert/for-own)
* [for-in](https://github.com/jonschlinkert/for-in): Iterate over the own and inherited enumerable properties of an objecte, and return an object… [more](https://github.com/jonschlinkert/for-in)
* [is-plain-object](https://github.com/jonschlinkert/is-plain-object): Returns true if an object was created by the `Object` constructor.
* [isobject](https://github.com/jonschlinkert/isobject): Returns true if the value is an object and not an array or null.
* [kind-of](https://github.com/jonschlinkert/kind-of): Get the native type of a value.
## Running tests
Install dev dependencies:
```sh
$ npm i -d && npm test
```
## Author
**Jon Schlinkert**
+ [github/jonschlinkert](https://github.com/jonschlinkert)
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
## License
Copyright © 2015 Jon Schlinkert
Released under the MIT license.
***
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on June 29, 2015._

View file

@ -0,0 +1,33 @@
'use strict';
var isObject = require('is-extendable');
module.exports = function extend(o/*, objects*/) {
if (!isObject(o)) { o = {}; }
var len = arguments.length;
for (var i = 1; i < len; i++) {
var obj = arguments[i];
if (isObject(obj)) {
assign(o, obj);
}
}
return o;
};
function assign(a, b) {
for (var key in b) {
if (hasOwn(b, key)) {
a[key] = b[key];
}
}
}
/**
* Returns true if the given `key` is an own property of `obj`.
*/
function hasOwn(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}

View file

@ -0,0 +1,56 @@
{
"name": "extend-shallow",
"description": "Extend an object with the properties of additional objects. node.js/javascript util.",
"version": "2.0.1",
"homepage": "https://github.com/jonschlinkert/extend-shallow",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"repository": "jonschlinkert/extend-shallow",
"bugs": {
"url": "https://github.com/jonschlinkert/extend-shallow/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"is-extendable": "^0.1.0"
},
"devDependencies": {
"array-slice": "^0.2.3",
"benchmarked": "^0.1.4",
"chalk": "^1.0.0",
"for-own": "^0.1.3",
"glob": "^5.0.12",
"is-plain-object": "^2.0.1",
"kind-of": "^2.0.0",
"minimist": "^1.1.1",
"mocha": "^2.2.5",
"should": "^7.0.1"
},
"keywords": [
"assign",
"extend",
"javascript",
"js",
"keys",
"merge",
"obj",
"object",
"prop",
"properties",
"property",
"props",
"shallow",
"util",
"utility",
"utils",
"value"
]
}

View file

@ -0,0 +1,82 @@
{
"name": "fill-range",
"description": "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`",
"version": "4.0.0",
"homepage": "https://github.com/jonschlinkert/fill-range",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"<wtgtybhertgeghgtwtg@gmail.com> (https://github.com/wtgtybhertgeghgtwtg)",
"Edo Rivai <edo.rivai@gmail.com> (edo.rivai.nl)",
"Jon Schlinkert <jon.schlinkert@sellside.com> (http://twitter.com/jonschlinkert)",
"Paul Miller <paul+gh@paulmillr.com> (paulmillr.com)"
],
"repository": "jonschlinkert/fill-range",
"bugs": {
"url": "https://github.com/jonschlinkert/fill-range/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
"repeat-string": "^1.6.1",
"to-regex-range": "^2.1.0"
},
"devDependencies": {
"ansi-cyan": "^0.1.1",
"benchmarked": "^1.0.0",
"gulp-format-md": "^0.1.12",
"minimist": "^1.2.0",
"mocha": "^3.2.0"
},
"keywords": [
"alpha",
"alphabetical",
"array",
"bash",
"brace",
"expand",
"expansion",
"fill",
"glob",
"match",
"matches",
"matching",
"number",
"numerical",
"range",
"ranges",
"regex",
"sh"
],
"verb": {
"related": {
"list": [
"braces",
"expand-range",
"micromatch",
"to-regex-range"
]
},
"toc": true,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
}
}
}

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015, Jon Schlinkert.
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,72 @@
# is-extendable [![NPM version](https://badge.fury.io/js/is-extendable.svg)](http://badge.fury.io/js/is-extendable)
> Returns true if a value is any of the object types: array, regexp, plain object, function or date. This is useful for determining if a value can be extended, e.g. "can the value have keys?"
## Install
Install with [npm](https://www.npmjs.com/)
```sh
$ npm i is-extendable --save
```
## Usage
```js
var isExtendable = require('is-extendable');
```
Returns true if the value is any of the following:
* `array`
* `regexp`
* `plain object`
* `function`
* `date`
* `error`
## Notes
All objects in JavaScript can have keys, but it's a pain to check for this, since we ether need to verify that the value is not `null` or `undefined` and:
* the value is not a primitive, or
* that the object is an `object`, `function`
Also note that an `extendable` object is not the same as an [extensible object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible), which is one that (in es6) is not sealed, frozen, or marked as non-extensible using `preventExtensions`.
## Related projects
* [assign-deep](https://github.com/jonschlinkert/assign-deep): Deeply assign the enumerable properties of source objects to a destination object.
* [extend-shallow](https://github.com/jonschlinkert/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util.
* [isobject](https://github.com/jonschlinkert/isobject): Returns true if the value is an object and not an array or null.
* [is-plain-object](https://github.com/jonschlinkert/is-plain-object): Returns true if an object was created by the `Object` constructor.
* [is-equal-shallow](https://github.com/jonschlinkert/is-equal-shallow): Does a shallow comparison of two objects, returning false if the keys or values differ.
* [kind-of](https://github.com/jonschlinkert/kind-of): Get the native type of a value.
## Running tests
Install dev dependencies:
```sh
$ npm i -d && npm test
```
## Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/is-extendable/issues/new)
## Author
**Jon Schlinkert**
+ [github/jonschlinkert](https://github.com/jonschlinkert)
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
## License
Copyright © 2015 Jon Schlinkert
Released under the MIT license.
***
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on July 04, 2015._

View file

@ -0,0 +1,13 @@
/*!
* is-extendable <https://github.com/jonschlinkert/is-extendable>
*
* Copyright (c) 2015, Jon Schlinkert.
* Licensed under the MIT License.
*/
'use strict';
module.exports = function isExtendable(val) {
return typeof val !== 'undefined' && val !== null
&& (typeof val === 'object' || typeof val === 'function');
};

View file

@ -0,0 +1,51 @@
{
"name": "is-extendable",
"description": "Returns true if a value is any of the object types: array, regexp, plain object, function or date. This is useful for determining if a value can be extended, e.g. \"can the value have keys?\"",
"version": "0.1.1",
"homepage": "https://github.com/jonschlinkert/is-extendable",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"repository": "jonschlinkert/is-extendable",
"bugs": {
"url": "https://github.com/jonschlinkert/is-extendable/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"devDependencies": {
"mocha": "*"
},
"keywords": [
"array",
"assign",
"check",
"date",
"extend",
"extensible",
"function",
"is",
"object",
"regex",
"test"
],
"verbiage": {
"related": {
"list": [
"isobject",
"is-plain-object",
"kind-of",
"is-extendable",
"is-equal-shallow",
"extend-shallow",
"assign-deep"
]
}
}
}

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2016, Jon Schlinkert
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,115 @@
# is-number [![NPM version](https://img.shields.io/npm/v/is-number.svg?style=flat)](https://www.npmjs.com/package/is-number) [![NPM downloads](https://img.shields.io/npm/dm/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![Build Status](https://img.shields.io/travis/jonschlinkert/is-number.svg?style=flat)](https://travis-ci.org/jonschlinkert/is-number)
> Returns true if the value is a number. comprehensive tests.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save is-number
```
## Usage
To understand some of the rationale behind the decisions made in this library (and to learn about some oddities of number evaluation in JavaScript), [see this gist](https://gist.github.com/jonschlinkert/e30c70c713da325d0e81).
```js
var isNumber = require('is-number');
```
### true
See the [tests](./test.js) for more examples.
```js
isNumber(5e3) //=> 'true'
isNumber(0xff) //=> 'true'
isNumber(-1.1) //=> 'true'
isNumber(0) //=> 'true'
isNumber(1) //=> 'true'
isNumber(1.1) //=> 'true'
isNumber(10) //=> 'true'
isNumber(10.10) //=> 'true'
isNumber(100) //=> 'true'
isNumber('-1.1') //=> 'true'
isNumber('0') //=> 'true'
isNumber('012') //=> 'true'
isNumber('0xff') //=> 'true'
isNumber('1') //=> 'true'
isNumber('1.1') //=> 'true'
isNumber('10') //=> 'true'
isNumber('10.10') //=> 'true'
isNumber('100') //=> 'true'
isNumber('5e3') //=> 'true'
isNumber(parseInt('012')) //=> 'true'
isNumber(parseFloat('012')) //=> 'true'
```
### False
See the [tests](./test.js) for more examples.
```js
isNumber('foo') //=> 'false'
isNumber([1]) //=> 'false'
isNumber([]) //=> 'false'
isNumber(function () {}) //=> 'false'
isNumber(Infinity) //=> 'false'
isNumber(NaN) //=> 'false'
isNumber(new Array('abc')) //=> 'false'
isNumber(new Array(2)) //=> 'false'
isNumber(new Buffer('abc')) //=> 'false'
isNumber(null) //=> 'false'
isNumber(undefined) //=> 'false'
isNumber({abc: 'abc'}) //=> 'false'
```
## About
### Related projects
* [even](https://www.npmjs.com/package/even): Get the even numbered items from an array. | [homepage](https://github.com/jonschlinkert/even "Get the even numbered items from an array.")
* [is-even](https://www.npmjs.com/package/is-even): Return true if the given number is even. | [homepage](https://github.com/jonschlinkert/is-even "Return true if the given number is even.")
* [is-odd](https://www.npmjs.com/package/is-odd): Returns true if the given number is odd. | [homepage](https://github.com/jonschlinkert/is-odd "Returns true if the given number is odd.")
* [is-primitive](https://www.npmjs.com/package/is-primitive): Returns `true` if the value is a primitive. | [homepage](https://github.com/jonschlinkert/is-primitive "Returns `true` if the value is a primitive. ")
* [kind-of](https://www.npmjs.com/package/kind-of): Get the native type of a value. | [homepage](https://github.com/jonschlinkert/kind-of "Get the native type of a value.")
* [odd](https://www.npmjs.com/package/odd): Get the odd numbered items from an array. | [homepage](https://github.com/jonschlinkert/odd "Get the odd numbered items from an array.")
### Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
### Building docs
_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
```sh
$ npm install -g verb verb-generate-readme && verb
```
### Running tests
Install dev dependencies:
```sh
$ npm install -d && npm test
```
### Author
**Jon Schlinkert**
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
### License
Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT license](https://github.com/jonschlinkert/is-number/blob/master/LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.30, on September 10, 2016._

View file

@ -0,0 +1,22 @@
/*!
* is-number <https://github.com/jonschlinkert/is-number>
*
* Copyright (c) 2014-2015, Jon Schlinkert.
* Licensed under the MIT License.
*/
'use strict';
var typeOf = require('kind-of');
module.exports = function isNumber(num) {
var type = typeOf(num);
if (type === 'string') {
if (!num.trim()) return false;
} else if (type !== 'number') {
return false;
}
return (num - num + 1) >= 0;
};

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert
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,261 @@
# kind-of [![NPM version](https://img.shields.io/npm/v/kind-of.svg?style=flat)](https://www.npmjs.com/package/kind-of) [![NPM monthly downloads](https://img.shields.io/npm/dm/kind-of.svg?style=flat)](https://npmjs.org/package/kind-of) [![NPM total downloads](https://img.shields.io/npm/dt/kind-of.svg?style=flat)](https://npmjs.org/package/kind-of) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/kind-of.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/kind-of)
> Get the native type of a value.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save kind-of
```
## Install
Install with [bower](https://bower.io/)
```sh
$ bower install kind-of --save
```
## Usage
> es5, browser and es6 ready
```js
var kindOf = require('kind-of');
kindOf(undefined);
//=> 'undefined'
kindOf(null);
//=> 'null'
kindOf(true);
//=> 'boolean'
kindOf(false);
//=> 'boolean'
kindOf(new Boolean(true));
//=> 'boolean'
kindOf(new Buffer(''));
//=> 'buffer'
kindOf(42);
//=> 'number'
kindOf(new Number(42));
//=> 'number'
kindOf('str');
//=> 'string'
kindOf(new String('str'));
//=> 'string'
kindOf(arguments);
//=> 'arguments'
kindOf({});
//=> 'object'
kindOf(Object.create(null));
//=> 'object'
kindOf(new Test());
//=> 'object'
kindOf(new Date());
//=> 'date'
kindOf([]);
//=> 'array'
kindOf([1, 2, 3]);
//=> 'array'
kindOf(new Array());
//=> 'array'
kindOf(/foo/);
//=> 'regexp'
kindOf(new RegExp('foo'));
//=> 'regexp'
kindOf(function () {});
//=> 'function'
kindOf(function * () {});
//=> 'function'
kindOf(new Function());
//=> 'function'
kindOf(new Map());
//=> 'map'
kindOf(new WeakMap());
//=> 'weakmap'
kindOf(new Set());
//=> 'set'
kindOf(new WeakSet());
//=> 'weakset'
kindOf(Symbol('str'));
//=> 'symbol'
kindOf(new Int8Array());
//=> 'int8array'
kindOf(new Uint8Array());
//=> 'uint8array'
kindOf(new Uint8ClampedArray());
//=> 'uint8clampedarray'
kindOf(new Int16Array());
//=> 'int16array'
kindOf(new Uint16Array());
//=> 'uint16array'
kindOf(new Int32Array());
//=> 'int32array'
kindOf(new Uint32Array());
//=> 'uint32array'
kindOf(new Float32Array());
//=> 'float32array'
kindOf(new Float64Array());
//=> 'float64array'
```
## Benchmarks
Benchmarked against [typeof](http://github.com/CodingFu/typeof) and [type-of](https://github.com/ForbesLindesay/type-of).
Note that performaces is slower for es6 features `Map`, `WeakMap`, `Set` and `WeakSet`.
```bash
#1: array
current x 23,329,397 ops/sec ±0.82% (94 runs sampled)
lib-type-of x 4,170,273 ops/sec ±0.55% (94 runs sampled)
lib-typeof x 9,686,935 ops/sec ±0.59% (98 runs sampled)
#2: boolean
current x 27,197,115 ops/sec ±0.85% (94 runs sampled)
lib-type-of x 3,145,791 ops/sec ±0.73% (97 runs sampled)
lib-typeof x 9,199,562 ops/sec ±0.44% (99 runs sampled)
#3: date
current x 20,190,117 ops/sec ±0.86% (92 runs sampled)
lib-type-of x 5,166,970 ops/sec ±0.74% (94 runs sampled)
lib-typeof x 9,610,821 ops/sec ±0.50% (96 runs sampled)
#4: function
current x 23,855,460 ops/sec ±0.60% (97 runs sampled)
lib-type-of x 5,667,740 ops/sec ±0.54% (100 runs sampled)
lib-typeof x 10,010,644 ops/sec ±0.44% (100 runs sampled)
#5: null
current x 27,061,047 ops/sec ±0.97% (96 runs sampled)
lib-type-of x 13,965,573 ops/sec ±0.62% (97 runs sampled)
lib-typeof x 8,460,194 ops/sec ±0.61% (97 runs sampled)
#6: number
current x 25,075,682 ops/sec ±0.53% (99 runs sampled)
lib-type-of x 2,266,405 ops/sec ±0.41% (98 runs sampled)
lib-typeof x 9,821,481 ops/sec ±0.45% (99 runs sampled)
#7: object
current x 3,348,980 ops/sec ±0.49% (99 runs sampled)
lib-type-of x 3,245,138 ops/sec ±0.60% (94 runs sampled)
lib-typeof x 9,262,952 ops/sec ±0.59% (99 runs sampled)
#8: regex
current x 21,284,827 ops/sec ±0.72% (96 runs sampled)
lib-type-of x 4,689,241 ops/sec ±0.43% (100 runs sampled)
lib-typeof x 8,957,593 ops/sec ±0.62% (98 runs sampled)
#9: string
current x 25,379,234 ops/sec ±0.58% (96 runs sampled)
lib-type-of x 3,635,148 ops/sec ±0.76% (93 runs sampled)
lib-typeof x 9,494,134 ops/sec ±0.49% (98 runs sampled)
#10: undef
current x 27,459,221 ops/sec ±1.01% (93 runs sampled)
lib-type-of x 14,360,433 ops/sec ±0.52% (99 runs sampled)
lib-typeof x 23,202,868 ops/sec ±0.59% (94 runs sampled)
```
## Optimizations
In 7 out of 8 cases, this library is 2x-10x faster than other top libraries included in the benchmarks. There are a few things that lead to this performance advantage, none of them hard and fast rules, but all of them simple and repeatable in almost any code library:
1. Optimize around the fastest and most common use cases first. Of course, this will change from project-to-project, but I took some time to understand how and why `typeof` checks were being used in my own libraries and other libraries I use a lot.
2. Optimize around bottlenecks - In other words, the order in which conditionals are implemented is significant, because each check is only as fast as the failing checks that came before it. Here, the biggest bottleneck by far is checking for plain objects (an object that was created by the `Object` constructor). I opted to make this check happen by process of elimination rather than brute force up front (e.g. by using something like `val.constructor.name`), so that every other type check would not be penalized it.
3. Don't do uneccessary processing - why do `.slice(8, -1).toLowerCase();` just to get the word `regex`? It's much faster to do `if (type === '[object RegExp]') return 'regex'`
## About
### Related projects
* [is-glob](https://www.npmjs.com/package/is-glob): Returns `true` if the given string looks like a glob pattern or an extglob pattern… [more](https://github.com/jonschlinkert/is-glob) | [homepage](https://github.com/jonschlinkert/is-glob "Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a bet")
* [is-number](https://www.npmjs.com/package/is-number): Returns true if the value is a number. comprehensive tests. | [homepage](https://github.com/jonschlinkert/is-number "Returns true if the value is a number. comprehensive tests.")
* [is-primitive](https://www.npmjs.com/package/is-primitive): Returns `true` if the value is a primitive. | [homepage](https://github.com/jonschlinkert/is-primitive "Returns `true` if the value is a primitive. ")
### Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 59 | [jonschlinkert](https://github.com/jonschlinkert) |
| 2 | [miguelmota](https://github.com/miguelmota) |
| 1 | [dtothefp](https://github.com/dtothefp) |
| 1 | [ksheedlo](https://github.com/ksheedlo) |
| 1 | [pdehaan](https://github.com/pdehaan) |
| 1 | [laggingreflex](https://github.com/laggingreflex) |
### Building docs
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```
### Running tests
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
### Author
**Jon Schlinkert**
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
### License
Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on May 16, 2017._

View file

@ -0,0 +1,116 @@
var isBuffer = require('is-buffer');
var toString = Object.prototype.toString;
/**
* Get the native `typeof` a value.
*
* @param {*} `val`
* @return {*} Native javascript type
*/
module.exports = function kindOf(val) {
// primitivies
if (typeof val === 'undefined') {
return 'undefined';
}
if (val === null) {
return 'null';
}
if (val === true || val === false || val instanceof Boolean) {
return 'boolean';
}
if (typeof val === 'string' || val instanceof String) {
return 'string';
}
if (typeof val === 'number' || val instanceof Number) {
return 'number';
}
// functions
if (typeof val === 'function' || val instanceof Function) {
return 'function';
}
// array
if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) {
return 'array';
}
// check for instances of RegExp and Date before calling `toString`
if (val instanceof RegExp) {
return 'regexp';
}
if (val instanceof Date) {
return 'date';
}
// other objects
var type = toString.call(val);
if (type === '[object RegExp]') {
return 'regexp';
}
if (type === '[object Date]') {
return 'date';
}
if (type === '[object Arguments]') {
return 'arguments';
}
if (type === '[object Error]') {
return 'error';
}
// buffer
if (isBuffer(val)) {
return 'buffer';
}
// es6: Map, WeakMap, Set, WeakSet
if (type === '[object Set]') {
return 'set';
}
if (type === '[object WeakSet]') {
return 'weakset';
}
if (type === '[object Map]') {
return 'map';
}
if (type === '[object WeakMap]') {
return 'weakmap';
}
if (type === '[object Symbol]') {
return 'symbol';
}
// typed arrays
if (type === '[object Int8Array]') {
return 'int8array';
}
if (type === '[object Uint8Array]') {
return 'uint8array';
}
if (type === '[object Uint8ClampedArray]') {
return 'uint8clampedarray';
}
if (type === '[object Int16Array]') {
return 'int16array';
}
if (type === '[object Uint16Array]') {
return 'uint16array';
}
if (type === '[object Int32Array]') {
return 'int32array';
}
if (type === '[object Uint32Array]') {
return 'uint32array';
}
if (type === '[object Float32Array]') {
return 'float32array';
}
if (type === '[object Float64Array]') {
return 'float64array';
}
// must be a plain object
return 'object';
};

View file

@ -0,0 +1,90 @@
{
"name": "kind-of",
"description": "Get the native type of a value.",
"version": "3.2.2",
"homepage": "https://github.com/jonschlinkert/kind-of",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"David Fox-Powell (https://dtothefp.github.io/me)",
"Jon Schlinkert (http://twitter.com/jonschlinkert)",
"Ken Sheedlo (kensheedlo.com)",
"laggingreflex (https://github.com/laggingreflex)",
"Miguel Mota (https://miguelmota.com)",
"Peter deHaan (http://about.me/peterdehaan)"
],
"repository": "jonschlinkert/kind-of",
"bugs": {
"url": "https://github.com/jonschlinkert/kind-of/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha",
"prepublish": "browserify -o browser.js -e index.js -s index --bare"
},
"dependencies": {
"is-buffer": "^1.1.5"
},
"devDependencies": {
"ansi-bold": "^0.1.1",
"benchmarked": "^1.0.0",
"browserify": "^14.3.0",
"glob": "^7.1.1",
"gulp-format-md": "^0.1.12",
"mocha": "^3.3.0",
"type-of": "^2.0.1",
"typeof": "^1.0.0"
},
"keywords": [
"arguments",
"array",
"boolean",
"check",
"date",
"function",
"is",
"is-type",
"is-type-of",
"kind",
"kind-of",
"number",
"object",
"of",
"regexp",
"string",
"test",
"type",
"type-of",
"typeof",
"types"
],
"verb": {
"related": {
"list": [
"is-glob",
"is-number",
"is-primitive"
]
},
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"reflinks": [
"verb"
]
}
}

View file

@ -0,0 +1,83 @@
{
"name": "is-number",
"description": "Returns true if the value is a number. comprehensive tests.",
"version": "3.0.0",
"homepage": "https://github.com/jonschlinkert/is-number",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"Charlike Mike Reagent (http://www.tunnckocore.tk)",
"Jon Schlinkert <jon.schlinkert@sellside.com> (http://twitter.com/jonschlinkert)"
],
"repository": "jonschlinkert/is-number",
"bugs": {
"url": "https://github.com/jonschlinkert/is-number/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"kind-of": "^3.0.2"
},
"devDependencies": {
"benchmarked": "^0.2.5",
"chalk": "^1.1.3",
"gulp-format-md": "^0.1.10",
"mocha": "^3.0.2"
},
"keywords": [
"check",
"coerce",
"coercion",
"integer",
"is",
"is-nan",
"is-num",
"is-number",
"istype",
"kind",
"math",
"nan",
"num",
"number",
"numeric",
"test",
"type",
"typeof",
"value"
],
"verb": {
"related": {
"list": [
"even",
"is-even",
"is-odd",
"is-primitive",
"kind-of",
"odd"
]
},
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"reflinks": [
"verb",
"verb-generate-readme"
]
}
}

View file

@ -0,0 +1,37 @@
## History
### key
Changelog entries are classified using the following labels _(from [keep-a-changelog][]_):
- `added`: for new features
- `changed`: for changes in existing functionality
- `deprecated`: for once-stable features removed in upcoming releases
- `removed`: for deprecated features removed in this release
- `fixed`: for any bug fixes
- `bumped`: updated dependencies, only minor or higher will be listed.
### [3.0.0] - 2017-04-11
TODO. There should be no breaking changes. Please report any regressions. I will [reformat these release notes](https://github.com/micromatch/micromatch/pull/76) and add them to the changelog as soon as I have a chance.
### [1.0.1] - 2016-12-12
**Added**
- Support for windows path edge cases where backslashes are used in brackets or other unusual combinations.
### [1.0.0] - 2016-12-12
Stable release.
### [0.1.0] - 2016-10-08
First release.
[Unreleased]: https://github.com/jonschlinkert/micromatch/compare/0.1.0...HEAD
[0.2.0]: https://github.com/jonschlinkert/micromatch/compare/0.1.0...0.2.0
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2018, Jon Schlinkert.
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.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,877 @@
'use strict';
/**
* Module dependencies
*/
var util = require('util');
var braces = require('braces');
var toRegex = require('to-regex');
var extend = require('extend-shallow');
/**
* Local dependencies
*/
var compilers = require('./lib/compilers');
var parsers = require('./lib/parsers');
var cache = require('./lib/cache');
var utils = require('./lib/utils');
var MAX_LENGTH = 1024 * 64;
/**
* The main function takes a list of strings and one or more
* glob patterns to use for matching.
*
* ```js
* var mm = require('micromatch');
* mm(list, patterns[, options]);
*
* console.log(mm(['a.js', 'a.txt'], ['*.js']));
* //=> [ 'a.js' ]
* ```
* @param {Array} `list` A list of strings to match
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Array} Returns an array of matches
* @summary false
* @api public
*/
function micromatch(list, patterns, options) {
patterns = utils.arrayify(patterns);
list = utils.arrayify(list);
var len = patterns.length;
if (list.length === 0 || len === 0) {
return [];
}
if (len === 1) {
return micromatch.match(list, patterns[0], options);
}
var omit = [];
var keep = [];
var idx = -1;
while (++idx < len) {
var pattern = patterns[idx];
if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) {
omit.push.apply(omit, micromatch.match(list, pattern.slice(1), options));
} else {
keep.push.apply(keep, micromatch.match(list, pattern, options));
}
}
var matches = utils.diff(keep, omit);
if (!options || options.nodupes !== false) {
return utils.unique(matches);
}
return matches;
}
/**
* Similar to the main function, but `pattern` must be a string.
*
* ```js
* var mm = require('micromatch');
* mm.match(list, pattern[, options]);
*
* console.log(mm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a'));
* //=> ['a.a', 'a.aa']
* ```
* @param {Array} `list` Array of strings to match
* @param {String} `pattern` Glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Array} Returns an array of matches
* @api public
*/
micromatch.match = function(list, pattern, options) {
if (Array.isArray(pattern)) {
throw new TypeError('expected pattern to be a string');
}
var unixify = utils.unixify(options);
var isMatch = memoize('match', pattern, options, micromatch.matcher);
var matches = [];
list = utils.arrayify(list);
var len = list.length;
var idx = -1;
while (++idx < len) {
var ele = list[idx];
if (ele === pattern || isMatch(ele)) {
matches.push(utils.value(ele, unixify, options));
}
}
// if no options were passed, uniquify results and return
if (typeof options === 'undefined') {
return utils.unique(matches);
}
if (matches.length === 0) {
if (options.failglob === true) {
throw new Error('no matches found for "' + pattern + '"');
}
if (options.nonull === true || options.nullglob === true) {
return [options.unescape ? utils.unescape(pattern) : pattern];
}
}
// if `opts.ignore` was defined, diff ignored list
if (options.ignore) {
matches = micromatch.not(matches, options.ignore, options);
}
return options.nodupes !== false ? utils.unique(matches) : matches;
};
/**
* Returns true if the specified `string` matches the given glob `pattern`.
*
* ```js
* var mm = require('micromatch');
* mm.isMatch(string, pattern[, options]);
*
* console.log(mm.isMatch('a.a', '*.a'));
* //=> true
* console.log(mm.isMatch('a.b', '*.a'));
* //=> false
* ```
* @param {String} `string` String to match
* @param {String} `pattern` Glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if the string matches the glob pattern.
* @api public
*/
micromatch.isMatch = function(str, pattern, options) {
if (typeof str !== 'string') {
throw new TypeError('expected a string: "' + util.inspect(str) + '"');
}
if (isEmptyString(str) || isEmptyString(pattern)) {
return false;
}
var equals = utils.equalsPattern(options);
if (equals(str)) {
return true;
}
var isMatch = memoize('isMatch', pattern, options, micromatch.matcher);
return isMatch(str);
};
/**
* Returns true if some of the strings in the given `list` match any of the
* given glob `patterns`.
*
* ```js
* var mm = require('micromatch');
* mm.some(list, patterns[, options]);
*
* console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
* // true
* console.log(mm.some(['foo.js'], ['*.js', '!foo.js']));
* // false
* ```
* @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.some = function(list, patterns, options) {
if (typeof list === 'string') {
list = [list];
}
for (var i = 0; i < list.length; i++) {
if (micromatch(list[i], patterns, options).length === 1) {
return true;
}
}
return false;
};
/**
* Returns true if every string in the given `list` matches
* any of the given glob `patterns`.
*
* ```js
* var mm = require('micromatch');
* mm.every(list, patterns[, options]);
*
* console.log(mm.every('foo.js', ['foo.js']));
* // true
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js']));
* // true
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
* // false
* console.log(mm.every(['foo.js'], ['*.js', '!foo.js']));
* // false
* ```
* @param {String|Array} `list` The string or array of strings to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.every = function(list, patterns, options) {
if (typeof list === 'string') {
list = [list];
}
for (var i = 0; i < list.length; i++) {
if (micromatch(list[i], patterns, options).length !== 1) {
return false;
}
}
return true;
};
/**
* Returns true if **any** of the given glob `patterns`
* match the specified `string`.
*
* ```js
* var mm = require('micromatch');
* mm.any(string, patterns[, options]);
*
* console.log(mm.any('a.a', ['b.*', '*.a']));
* //=> true
* console.log(mm.any('a.a', 'b.*'));
* //=> false
* ```
* @param {String|Array} `str` The string to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.any = function(str, patterns, options) {
if (typeof str !== 'string') {
throw new TypeError('expected a string: "' + util.inspect(str) + '"');
}
if (isEmptyString(str) || isEmptyString(patterns)) {
return false;
}
if (typeof patterns === 'string') {
patterns = [patterns];
}
for (var i = 0; i < patterns.length; i++) {
if (micromatch.isMatch(str, patterns[i], options)) {
return true;
}
}
return false;
};
/**
* Returns true if **all** of the given `patterns` match
* the specified string.
*
* ```js
* var mm = require('micromatch');
* mm.all(string, patterns[, options]);
*
* console.log(mm.all('foo.js', ['foo.js']));
* // true
*
* console.log(mm.all('foo.js', ['*.js', '!foo.js']));
* // false
*
* console.log(mm.all('foo.js', ['*.js', 'foo.js']));
* // true
*
* console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
* // true
* ```
* @param {String|Array} `str` The string to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.all = function(str, patterns, options) {
if (typeof str !== 'string') {
throw new TypeError('expected a string: "' + util.inspect(str) + '"');
}
if (typeof patterns === 'string') {
patterns = [patterns];
}
for (var i = 0; i < patterns.length; i++) {
if (!micromatch.isMatch(str, patterns[i], options)) {
return false;
}
}
return true;
};
/**
* Returns a list of strings that _**do not match any**_ of the given `patterns`.
*
* ```js
* var mm = require('micromatch');
* mm.not(list, patterns[, options]);
*
* console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a'));
* //=> ['b.b', 'c.c']
* ```
* @param {Array} `list` Array of strings to match.
* @param {String|Array} `patterns` One or more glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Array} Returns an array of strings that **do not match** the given patterns.
* @api public
*/
micromatch.not = function(list, patterns, options) {
var opts = extend({}, options);
var ignore = opts.ignore;
delete opts.ignore;
var unixify = utils.unixify(opts);
list = utils.arrayify(list).map(unixify);
var matches = utils.diff(list, micromatch(list, patterns, opts));
if (ignore) {
matches = utils.diff(matches, micromatch(list, ignore));
}
return opts.nodupes !== false ? utils.unique(matches) : matches;
};
/**
* Returns true if the given `string` contains the given pattern. Similar
* to [.isMatch](#isMatch) but the pattern can match any part of the string.
*
* ```js
* var mm = require('micromatch');
* mm.contains(string, pattern[, options]);
*
* console.log(mm.contains('aa/bb/cc', '*b'));
* //=> true
* console.log(mm.contains('aa/bb/cc', '*d'));
* //=> false
* ```
* @param {String} `str` The string to match.
* @param {String|Array} `patterns` Glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if the patter matches any part of `str`.
* @api public
*/
micromatch.contains = function(str, patterns, options) {
if (typeof str !== 'string') {
throw new TypeError('expected a string: "' + util.inspect(str) + '"');
}
if (typeof patterns === 'string') {
if (isEmptyString(str) || isEmptyString(patterns)) {
return false;
}
var equals = utils.equalsPattern(patterns, options);
if (equals(str)) {
return true;
}
var contains = utils.containsPattern(patterns, options);
if (contains(str)) {
return true;
}
}
var opts = extend({}, options, {contains: true});
return micromatch.any(str, patterns, opts);
};
/**
* Returns true if the given pattern and options should enable
* the `matchBase` option.
* @return {Boolean}
* @api private
*/
micromatch.matchBase = function(pattern, options) {
if (pattern && pattern.indexOf('/') !== -1 || !options) return false;
return options.basename === true || options.matchBase === true;
};
/**
* Filter the keys of the given object with the given `glob` pattern
* and `options`. Does not attempt to match nested keys. If you need this feature,
* use [glob-object][] instead.
*
* ```js
* var mm = require('micromatch');
* mm.matchKeys(object, patterns[, options]);
*
* var obj = { aa: 'a', ab: 'b', ac: 'c' };
* console.log(mm.matchKeys(obj, '*b'));
* //=> { ab: 'b' }
* ```
* @param {Object} `object` The object with keys to filter.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Object} Returns an object with only keys that match the given patterns.
* @api public
*/
micromatch.matchKeys = function(obj, patterns, options) {
if (!utils.isObject(obj)) {
throw new TypeError('expected the first argument to be an object');
}
var keys = micromatch(Object.keys(obj), patterns, options);
return utils.pick(obj, keys);
};
/**
* Returns a memoized matcher function from the given glob `pattern` and `options`.
* The returned function takes a string to match as its only argument and returns
* true if the string is a match.
*
* ```js
* var mm = require('micromatch');
* mm.matcher(pattern[, options]);
*
* var isMatch = mm.matcher('*.!(*a)');
* console.log(isMatch('a.a'));
* //=> false
* console.log(isMatch('a.b'));
* //=> true
* ```
* @param {String} `pattern` Glob pattern
* @param {Object} `options` See available [options](#options) for changing how matches are performed.
* @return {Function} Returns a matcher function.
* @api public
*/
micromatch.matcher = function matcher(pattern, options) {
if (Array.isArray(pattern)) {
return compose(pattern, options, matcher);
}
// if pattern is a regex
if (pattern instanceof RegExp) {
return test(pattern);
}
// if pattern is invalid
if (!utils.isString(pattern)) {
throw new TypeError('expected pattern to be an array, string or regex');
}
// if pattern is a non-glob string
if (!utils.hasSpecialChars(pattern)) {
if (options && options.nocase === true) {
pattern = pattern.toLowerCase();
}
return utils.matchPath(pattern, options);
}
// if pattern is a glob string
var re = micromatch.makeRe(pattern, options);
// if `options.matchBase` or `options.basename` is defined
if (micromatch.matchBase(pattern, options)) {
return utils.matchBasename(re, options);
}
function test(regex) {
var equals = utils.equalsPattern(options);
var unixify = utils.unixify(options);
return function(str) {
if (equals(str)) {
return true;
}
if (regex.test(unixify(str))) {
return true;
}
return false;
};
}
var fn = test(re);
Object.defineProperty(fn, 'result', {
configurable: true,
enumerable: false,
value: re.result
});
return fn;
};
/**
* Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match.
*
* ```js
* var mm = require('micromatch');
* mm.capture(pattern, string[, options]);
*
* console.log(mm.capture('test/*.js', 'test/foo.js'));
* //=> ['foo']
* console.log(mm.capture('test/*.js', 'foo/bar.css'));
* //=> null
* ```
* @param {String} `pattern` Glob pattern to use for matching.
* @param {String} `string` String to match
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`.
* @api public
*/
micromatch.capture = function(pattern, str, options) {
var re = micromatch.makeRe(pattern, extend({capture: true}, options));
var unixify = utils.unixify(options);
function match() {
return function(string) {
var match = re.exec(unixify(string));
if (!match) {
return null;
}
return match.slice(1);
};
}
var capture = memoize('capture', pattern, options, match);
return capture(str);
};
/**
* Create a regular expression from the given glob `pattern`.
*
* ```js
* var mm = require('micromatch');
* mm.makeRe(pattern[, options]);
*
* console.log(mm.makeRe('*.js'));
* //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
* ```
* @param {String} `pattern` A glob pattern to convert to regex.
* @param {Object} `options` See available [options](#options) for changing how matches are performed.
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
micromatch.makeRe = function(pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('expected pattern to be a string');
}
if (pattern.length > MAX_LENGTH) {
throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
}
function makeRe() {
var result = micromatch.create(pattern, options);
var ast_array = [];
var output = result.map(function(obj) {
obj.ast.state = obj.state;
ast_array.push(obj.ast);
return obj.output;
});
var regex = toRegex(output.join('|'), options);
Object.defineProperty(regex, 'result', {
configurable: true,
enumerable: false,
value: ast_array
});
return regex;
}
return memoize('makeRe', pattern, options, makeRe);
};
/**
* Expand the given brace `pattern`.
*
* ```js
* var mm = require('micromatch');
* console.log(mm.braces('foo/{a,b}/bar'));
* //=> ['foo/(a|b)/bar']
*
* console.log(mm.braces('foo/{a,b}/bar', {expand: true}));
* //=> ['foo/(a|b)/bar']
* ```
* @param {String} `pattern` String with brace pattern to expand.
* @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options.
* @return {Array}
* @api public
*/
micromatch.braces = function(pattern, options) {
if (typeof pattern !== 'string' && !Array.isArray(pattern)) {
throw new TypeError('expected pattern to be an array or string');
}
function expand() {
if (options && options.nobrace === true || !/\{.*\}/.test(pattern)) {
return utils.arrayify(pattern);
}
return braces(pattern, options);
}
return memoize('braces', pattern, options, expand);
};
/**
* Proxy to the [micromatch.braces](#method), for parity with
* minimatch.
*/
micromatch.braceExpand = function(pattern, options) {
var opts = extend({}, options, {expand: true});
return micromatch.braces(pattern, opts);
};
/**
* Parses the given glob `pattern` and returns an array of abstract syntax
* trees (ASTs), with the compiled `output` and optional source `map` on
* each AST.
*
* ```js
* var mm = require('micromatch');
* mm.create(pattern[, options]);
*
* console.log(mm.create('abc/*.js'));
* // [{ options: { source: 'string', sourcemap: true },
* // state: {},
* // compilers:
* // { ... },
* // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js',
* // ast:
* // { type: 'root',
* // errors: [],
* // nodes:
* // [ ... ],
* // dot: false,
* // input: 'abc/*.js' },
* // parsingErrors: [],
* // map:
* // { version: 3,
* // sources: [ 'string' ],
* // names: [],
* // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE',
* // sourcesContent: [ 'abc/*.js' ] },
* // position: { line: 1, column: 28 },
* // content: {},
* // files: {},
* // idx: 6 }]
* ```
* @param {String} `pattern` Glob pattern to parse and compile.
* @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed.
* @return {Object} Returns an object with the parsed AST, compiled string and optional source map.
* @api public
*/
micromatch.create = function(pattern, options) {
return memoize('create', pattern, options, function() {
function create(str, opts) {
return micromatch.compile(micromatch.parse(str, opts), opts);
}
pattern = micromatch.braces(pattern, options);
var len = pattern.length;
var idx = -1;
var res = [];
while (++idx < len) {
res.push(create(pattern[idx], options));
}
return res;
});
};
/**
* Parse the given `str` with the given `options`.
*
* ```js
* var mm = require('micromatch');
* mm.parse(pattern[, options]);
*
* var ast = mm.parse('a/{b,c}/d');
* console.log(ast);
* // { type: 'root',
* // errors: [],
* // input: 'a/{b,c}/d',
* // nodes:
* // [ { type: 'bos', val: '' },
* // { type: 'text', val: 'a/' },
* // { type: 'brace',
* // nodes:
* // [ { type: 'brace.open', val: '{' },
* // { type: 'text', val: 'b,c' },
* // { type: 'brace.close', val: '}' } ] },
* // { type: 'text', val: '/d' },
* // { type: 'eos', val: '' } ] }
* ```
* @param {String} `str`
* @param {Object} `options`
* @return {Object} Returns an AST
* @api public
*/
micromatch.parse = function(pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('expected a string');
}
function parse() {
var snapdragon = utils.instantiate(null, options);
parsers(snapdragon, options);
var ast = snapdragon.parse(pattern, options);
utils.define(ast, 'snapdragon', snapdragon);
ast.input = pattern;
return ast;
}
return memoize('parse', pattern, options, parse);
};
/**
* Compile the given `ast` or string with the given `options`.
*
* ```js
* var mm = require('micromatch');
* mm.compile(ast[, options]);
*
* var ast = mm.parse('a/{b,c}/d');
* console.log(mm.compile(ast));
* // { options: { source: 'string' },
* // state: {},
* // compilers:
* // { eos: [Function],
* // noop: [Function],
* // bos: [Function],
* // brace: [Function],
* // 'brace.open': [Function],
* // text: [Function],
* // 'brace.close': [Function] },
* // output: [ 'a/(b|c)/d' ],
* // ast:
* // { ... },
* // parsingErrors: [] }
* ```
* @param {Object|String} `ast`
* @param {Object} `options`
* @return {Object} Returns an object that has an `output` property with the compiled string.
* @api public
*/
micromatch.compile = function(ast, options) {
if (typeof ast === 'string') {
ast = micromatch.parse(ast, options);
}
return memoize('compile', ast.input, options, function() {
var snapdragon = utils.instantiate(ast, options);
compilers(snapdragon, options);
return snapdragon.compile(ast, options);
});
};
/**
* Clear the regex cache.
*
* ```js
* mm.clearCache();
* ```
* @api public
*/
micromatch.clearCache = function() {
micromatch.cache.caches = {};
};
/**
* Returns true if the given value is effectively an empty string
*/
function isEmptyString(val) {
return String(val) === '' || String(val) === './';
}
/**
* Compose a matcher function with the given patterns.
* This allows matcher functions to be compiled once and
* called multiple times.
*/
function compose(patterns, options, matcher) {
var matchers;
return memoize('compose', String(patterns), options, function() {
return function(file) {
// delay composition until it's invoked the first time,
// after that it won't be called again
if (!matchers) {
matchers = [];
for (var i = 0; i < patterns.length; i++) {
matchers.push(matcher(patterns[i], options));
}
}
var len = matchers.length;
while (len--) {
if (matchers[len](file) === true) {
return true;
}
}
return false;
};
});
}
/**
* Memoize a generated regex or function. A unique key is generated
* from the `type` (usually method name), the `pattern`, and
* user-defined options.
*/
function memoize(type, pattern, options, fn) {
var key = utils.createKey(type + '=' + pattern, options);
if (options && options.cache === false) {
return fn(pattern, options);
}
if (cache.has(type, key)) {
return cache.get(type, key);
}
var val = fn(pattern, options);
cache.set(type, key, val);
return val;
}
/**
* Expose compiler, parser and cache on `micromatch`
*/
micromatch.compilers = compilers;
micromatch.parsers = parsers;
micromatch.caches = cache.caches;
/**
* Expose `micromatch`
* @type {Function}
*/
module.exports = micromatch;

Binary file not shown.

View file

@ -0,0 +1 @@
module.exports = new (require('fragment-cache'))();

View file

@ -0,0 +1,77 @@
'use strict';
var nanomatch = require('nanomatch');
var extglob = require('extglob');
module.exports = function(snapdragon) {
var compilers = snapdragon.compiler.compilers;
var opts = snapdragon.options;
// register nanomatch compilers
snapdragon.use(nanomatch.compilers);
// get references to some specific nanomatch compilers before they
// are overridden by the extglob and/or custom compilers
var escape = compilers.escape;
var qmark = compilers.qmark;
var slash = compilers.slash;
var star = compilers.star;
var text = compilers.text;
var plus = compilers.plus;
var dot = compilers.dot;
// register extglob compilers or escape exglobs if disabled
if (opts.extglob === false || opts.noext === true) {
snapdragon.compiler.use(escapeExtglobs);
} else {
snapdragon.use(extglob.compilers);
}
snapdragon.use(function() {
this.options.star = this.options.star || function(/*node*/) {
return '[^\\\\/]*?';
};
});
// custom micromatch compilers
snapdragon.compiler
// reset referenced compiler
.set('dot', dot)
.set('escape', escape)
.set('plus', plus)
.set('slash', slash)
.set('qmark', qmark)
.set('star', star)
.set('text', text);
};
function escapeExtglobs(compiler) {
compiler.set('paren', function(node) {
var val = '';
visit(node, function(tok) {
if (tok.val) val += (/^\W/.test(tok.val) ? '\\' : '') + tok.val;
});
return this.emit(val, node);
});
/**
* Visit `node` with the given `fn`
*/
function visit(node, fn) {
return node.nodes ? mapVisit(node.nodes, fn) : fn(node);
}
/**
* Map visit over array of `nodes`.
*/
function mapVisit(nodes, fn) {
var len = nodes.length;
var idx = -1;
while (++idx < len) {
visit(nodes[idx], fn);
}
}
}

View file

@ -0,0 +1,83 @@
'use strict';
var extglob = require('extglob');
var nanomatch = require('nanomatch');
var regexNot = require('regex-not');
var toRegex = require('to-regex');
var not;
/**
* Characters to use in negation regex (we want to "not" match
* characters that are matched by other parsers)
*/
var TEXT = '([!@*?+]?\\(|\\)|\\[:?(?=.*?:?\\])|:?\\]|[*+?!^$.\\\\/])+';
var createNotRegex = function(opts) {
return not || (not = textRegex(TEXT));
};
/**
* Parsers
*/
module.exports = function(snapdragon) {
var parsers = snapdragon.parser.parsers;
// register nanomatch parsers
snapdragon.use(nanomatch.parsers);
// get references to some specific nanomatch parsers before they
// are overridden by the extglob and/or parsers
var escape = parsers.escape;
var slash = parsers.slash;
var qmark = parsers.qmark;
var plus = parsers.plus;
var star = parsers.star;
var dot = parsers.dot;
// register extglob parsers
snapdragon.use(extglob.parsers);
// custom micromatch parsers
snapdragon.parser
.use(function() {
// override "notRegex" created in nanomatch parser
this.notRegex = /^\!+(?!\()/;
})
// reset the referenced parsers
.capture('escape', escape)
.capture('slash', slash)
.capture('qmark', qmark)
.capture('star', star)
.capture('plus', plus)
.capture('dot', dot)
/**
* Override `text` parser
*/
.capture('text', function() {
if (this.isInside('bracket')) return;
var pos = this.position();
var m = this.match(createNotRegex(this.options));
if (!m || !m[0]) return;
// escape regex boundary characters and simple brackets
var val = m[0].replace(/([[\]^$])/g, '\\$1');
return pos({
type: 'text',
val: val
});
});
};
/**
* Create text regex
*/
function textRegex(pattern) {
var notStr = regexNot.create(pattern, {contains: true, strictClose: false});
var prefix = '(?:[\\^]|\\\\|';
return toRegex(prefix + notStr + ')', {strictClose: false});
}

View file

@ -0,0 +1,309 @@
'use strict';
var utils = module.exports;
var path = require('path');
/**
* Module dependencies
*/
var Snapdragon = require('snapdragon');
utils.define = require('define-property');
utils.diff = require('arr-diff');
utils.extend = require('extend-shallow');
utils.pick = require('object.pick');
utils.typeOf = require('kind-of');
utils.unique = require('array-unique');
/**
* Returns true if the platform is windows, or `path.sep` is `\\`.
* This is defined as a function to allow `path.sep` to be set in unit tests,
* or by the user, if there is a reason to do so.
* @return {Boolean}
*/
utils.isWindows = function() {
return path.sep === '\\' || process.platform === 'win32';
};
/**
* Get the `Snapdragon` instance to use
*/
utils.instantiate = function(ast, options) {
var snapdragon;
// if an instance was created by `.parse`, use that instance
if (utils.typeOf(ast) === 'object' && ast.snapdragon) {
snapdragon = ast.snapdragon;
// if the user supplies an instance on options, use that instance
} else if (utils.typeOf(options) === 'object' && options.snapdragon) {
snapdragon = options.snapdragon;
// create a new instance
} else {
snapdragon = new Snapdragon(options);
}
utils.define(snapdragon, 'parse', function(str, options) {
var parsed = Snapdragon.prototype.parse.apply(this, arguments);
parsed.input = str;
// escape unmatched brace/bracket/parens
var last = this.parser.stack.pop();
if (last && this.options.strictErrors !== true) {
var open = last.nodes[0];
var inner = last.nodes[1];
if (last.type === 'bracket') {
if (inner.val.charAt(0) === '[') {
inner.val = '\\' + inner.val;
}
} else {
open.val = '\\' + open.val;
var sibling = open.parent.nodes[1];
if (sibling.type === 'star') {
sibling.loose = true;
}
}
}
// add non-enumerable parser reference
utils.define(parsed, 'parser', this.parser);
return parsed;
});
return snapdragon;
};
/**
* Create the key to use for memoization. The key is generated
* by iterating over the options and concatenating key-value pairs
* to the pattern string.
*/
utils.createKey = function(pattern, options) {
if (utils.typeOf(options) !== 'object') {
return pattern;
}
var val = pattern;
var keys = Object.keys(options);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
val += ';' + key + '=' + String(options[key]);
}
return val;
};
/**
* Cast `val` to an array
* @return {Array}
*/
utils.arrayify = function(val) {
if (typeof val === 'string') return [val];
return val ? (Array.isArray(val) ? val : [val]) : [];
};
/**
* Return true if `val` is a non-empty string
*/
utils.isString = function(val) {
return typeof val === 'string';
};
/**
* Return true if `val` is a non-empty string
*/
utils.isObject = function(val) {
return utils.typeOf(val) === 'object';
};
/**
* Returns true if the given `str` has special characters
*/
utils.hasSpecialChars = function(str) {
return /(?:(?:(^|\/)[!.])|[*?+()|\[\]{}]|[+@]\()/.test(str);
};
/**
* Escape regex characters in the given string
*/
utils.escapeRegex = function(str) {
return str.replace(/[-[\]{}()^$|*+?.\\\/\s]/g, '\\$&');
};
/**
* Normalize slashes in the given filepath.
*
* @param {String} `filepath`
* @return {String}
*/
utils.toPosixPath = function(str) {
return str.replace(/\\+/g, '/');
};
/**
* Strip backslashes before special characters in a string.
*
* @param {String} `str`
* @return {String}
*/
utils.unescape = function(str) {
return utils.toPosixPath(str.replace(/\\(?=[*+?!.])/g, ''));
};
/**
* Strip the prefix from a filepath
* @param {String} `fp`
* @return {String}
*/
utils.stripPrefix = function(str) {
if (str.charAt(0) !== '.') {
return str;
}
var ch = str.charAt(1);
if (utils.isSlash(ch)) {
return str.slice(2);
}
return str;
};
/**
* Returns true if the given str is an escaped or
* unescaped path character
*/
utils.isSlash = function(str) {
return str === '/' || str === '\\/' || str === '\\' || str === '\\\\';
};
/**
* Returns a function that returns true if the given
* pattern matches or contains a `filepath`
*
* @param {String} `pattern`
* @return {Function}
*/
utils.matchPath = function(pattern, options) {
return (options && options.contains)
? utils.containsPattern(pattern, options)
: utils.equalsPattern(pattern, options);
};
/**
* Returns true if the given (original) filepath or unixified path are equal
* to the given pattern.
*/
utils._equals = function(filepath, unixPath, pattern) {
return pattern === filepath || pattern === unixPath;
};
/**
* Returns true if the given (original) filepath or unixified path contain
* the given pattern.
*/
utils._contains = function(filepath, unixPath, pattern) {
return filepath.indexOf(pattern) !== -1 || unixPath.indexOf(pattern) !== -1;
};
/**
* Returns a function that returns true if the given
* pattern is the same as a given `filepath`
*
* @param {String} `pattern`
* @return {Function}
*/
utils.equalsPattern = function(pattern, options) {
var unixify = utils.unixify(options);
options = options || {};
return function fn(filepath) {
var equal = utils._equals(filepath, unixify(filepath), pattern);
if (equal === true || options.nocase !== true) {
return equal;
}
var lower = filepath.toLowerCase();
return utils._equals(lower, unixify(lower), pattern);
};
};
/**
* Returns a function that returns true if the given
* pattern contains a `filepath`
*
* @param {String} `pattern`
* @return {Function}
*/
utils.containsPattern = function(pattern, options) {
var unixify = utils.unixify(options);
options = options || {};
return function(filepath) {
var contains = utils._contains(filepath, unixify(filepath), pattern);
if (contains === true || options.nocase !== true) {
return contains;
}
var lower = filepath.toLowerCase();
return utils._contains(lower, unixify(lower), pattern);
};
};
/**
* Returns a function that returns true if the given
* regex matches the `filename` of a file path.
*
* @param {RegExp} `re` Matching regex
* @return {Function}
*/
utils.matchBasename = function(re) {
return function(filepath) {
return re.test(path.basename(filepath));
};
};
/**
* Determines the filepath to return based on the provided options.
* @return {any}
*/
utils.value = function(str, unixify, options) {
if (options && options.unixify === false) {
return str;
}
return unixify(str);
};
/**
* Returns a function that normalizes slashes in a string to forward
* slashes, strips `./` from beginning of paths, and optionally unescapes
* special characters.
* @return {Function}
*/
utils.unixify = function(options) {
options = options || {};
return function(filepath) {
if (utils.isWindows() || options.unixify === true) {
filepath = utils.toPosixPath(filepath);
}
if (options.stripPrefix !== false) {
filepath = utils.stripPrefix(filepath);
}
if (options.unescape === true) {
filepath = utils.unescape(filepath);
}
return filepath;
};
};

View file

@ -0,0 +1,147 @@
{
"name": "micromatch",
"description": "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.",
"version": "3.1.10",
"homepage": "https://github.com/micromatch/micromatch",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"Amila Welihinda (amilajack.com)",
"Bogdan Chadkin (https://github.com/TrySound)",
"Brian Woodward (https://twitter.com/doowb)",
"Devon Govett (http://badassjs.com)",
"Elan Shanker (https://github.com/es128)",
"Fabrício Matté (https://ultcombo.js.org)",
"Jon Schlinkert (http://twitter.com/jonschlinkert)",
"Martin Kolárik (https://kolarik.sk)",
"Olsten Larck (https://i.am.charlike.online)",
"Paul Miller (paulmillr.com)",
"Tom Byrer (https://github.com/tomByrer)",
"Tyler Akins (http://rumkin.com)",
"(https://github.com/DianeLooney)"
],
"repository": "micromatch/micromatch",
"bugs": {
"url": "https://github.com/micromatch/micromatch/issues"
},
"license": "MIT",
"files": [
"index.js",
"lib"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
"braces": "^2.3.1",
"define-property": "^2.0.2",
"extend-shallow": "^3.0.2",
"extglob": "^2.0.4",
"fragment-cache": "^0.2.1",
"kind-of": "^6.0.2",
"nanomatch": "^1.2.9",
"object.pick": "^1.3.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.2"
},
"devDependencies": {
"bash-match": "^1.0.2",
"for-own": "^1.0.0",
"gulp": "^3.9.1",
"gulp-format-md": "^1.0.0",
"gulp-istanbul": "^1.1.3",
"gulp-mocha": "^5.0.0",
"gulp-unused": "^0.2.1",
"is-windows": "^1.0.2",
"minimatch": "^3.0.4",
"minimist": "^1.2.0",
"mocha": "^3.5.3",
"multimatch": "^2.1.0"
},
"keywords": [
"bash",
"expand",
"expansion",
"expression",
"file",
"files",
"filter",
"find",
"glob",
"globbing",
"globs",
"globstar",
"match",
"matcher",
"matches",
"matching",
"micromatch",
"minimatch",
"multimatch",
"path",
"pattern",
"patterns",
"regex",
"regexp",
"regular",
"shell",
"wildcard"
],
"lintDeps": {
"dependencies": {
"options": {
"lock": {
"snapdragon": "^0.8.1"
}
}
},
"devDependencies": {
"files": {
"options": {
"ignore": [
"benchmark/**"
]
}
}
}
},
"verb": {
"toc": "collapsible",
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"helpers": [
"./benchmark/helper.js"
],
"related": {
"list": [
"braces",
"expand-brackets",
"extglob",
"fill-range",
"nanomatch"
]
},
"lint": {
"reflinks": true
},
"reflinks": [
"expand-brackets",
"extglob",
"glob-object",
"minimatch",
"multimatch",
"snapdragon"
]
}
}

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2017, Jon Schlinkert
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,281 @@
# to-regex-range [![NPM version](https://img.shields.io/npm/v/to-regex-range.svg?style=flat)](https://www.npmjs.com/package/to-regex-range) [![NPM monthly downloads](https://img.shields.io/npm/dm/to-regex-range.svg?style=flat)](https://npmjs.org/package/to-regex-range) [![NPM total downloads](https://img.shields.io/npm/dt/to-regex-range.svg?style=flat)](https://npmjs.org/package/to-regex-range) [![Linux Build Status](https://img.shields.io/travis/micromatch/to-regex-range.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/to-regex-range)
> Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.78 million test assertions.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save to-regex-range
```
Install with [yarn](https://yarnpkg.com):
```sh
$ yarn add to-regex-range
```
<details>
<summary><strong>What does this do?</strong></summary>
<br>
This libary generates the `source` string to be passed to `new RegExp()` for matching a range of numbers.
**Example**
```js
var toRegexRange = require('to-regex-range');
var regex = new RegExp(toRegexRange('15', '95'));
```
A string is returned so that you can do whatever you need with it before passing it to `new RegExp()` (like adding `^` or `$` boundaries, defining flags, or combining it another string).
<br>
</details>
<details>
<summary><strong>Why use this library?</strong></summary>
<br>
### Convenience
Creating regular expressions for matching numbers gets deceptively complicated pretty fast.
For example, let's say you need a validation regex for matching part of a user-id, postal code, social security number, tax id, etc:
* regex for matching `1` => `/1/` (easy enough)
* regex for matching `1` through `5` => `/[1-5]/` (not bad...)
* regex for matching `1` or `5` => `/(1|5)/` (still easy...)
* regex for matching `1` through `50` => `/([1-9]|[1-4][0-9]|50)/` (uh-oh...)
* regex for matching `1` through `55` => `/([1-9]|[1-4][0-9]|5[0-5])/` (no prob, I can do this...)
* regex for matching `1` through `555` => `/([1-9]|[1-9][0-9]|[1-4][0-9]{2}|5[0-4][0-9]|55[0-5])/` (maybe not...)
* regex for matching `0001` through `5555` => `/(0{3}[1-9]|0{2}[1-9][0-9]|0[1-9][0-9]{2}|[1-4][0-9]{3}|5[0-4][0-9]{2}|55[0-4][0-9]|555[0-5])/` (okay, I get the point!)
The numbers are contrived, but they're also really basic. In the real world you might need to generate a regex on-the-fly for validation.
**Learn more**
If you're interested in learning more about [character classes](http://www.regular-expressions.info/charclass.html) and other regex features, I personally have always found [regular-expressions.info](http://www.regular-expressions.info/charclass.html) to be pretty useful.
### Heavily tested
As of April 27, 2017, this library runs [2,783,483 test assertions](./test/test.js) against generated regex-ranges to provide brute-force verification that results are indeed correct.
Tests run in ~870ms on my MacBook Pro, 2.5 GHz Intel Core i7.
### Highly optimized
Generated regular expressions are highly optimized:
* duplicate sequences and character classes are reduced using quantifiers
* smart enough to use `?` conditionals when number(s) or range(s) can be positive or negative
* uses fragment caching to avoid processing the same exact string more than once
<br>
</details>
## Usage
Add this library to your javascript application with the following line of code
```js
var toRegexRange = require('to-regex-range');
```
The main export is a function that takes two integers: the `min` value and `max` value (formatted as strings or numbers).
```js
var source = toRegexRange('15', '95');
//=> 1[5-9]|[2-8][0-9]|9[0-5]
var re = new RegExp('^' + source + '$');
console.log(re.test('14')); //=> false
console.log(re.test('50')); //=> true
console.log(re.test('94')); //=> true
console.log(re.test('96')); //=> false
```
## Options
### options.capture
**Type**: `boolean`
**Deafault**: `undefined`
Wrap the returned value in parentheses when there is more than one regex condition. Useful when you're dynamically generating ranges.
```js
console.log(toRegexRange('-10', '10'));
//=> -[1-9]|-?10|[0-9]
console.log(toRegexRange('-10', '10', {capture: true}));
//=> (-[1-9]|-?10|[0-9])
```
### options.shorthand
**Type**: `boolean`
**Deafault**: `undefined`
Use the regex shorthand for `[0-9]`:
```js
console.log(toRegexRange('0', '999999'));
//=> [0-9]|[1-9][0-9]{1,5}
console.log(toRegexRange('0', '999999', {shorthand: true}));
//=> \d|[1-9]\d{1,5}
```
### options.relaxZeros
**Type**: `boolean`
**Default**: `true`
This option only applies to **negative zero-padded ranges**. By default, when a negative zero-padded range is defined, the number of leading zeros is relaxed using `-0*`.
```js
console.log(toRegexRange('-001', '100'));
//=> -0*1|0{2}[0-9]|0[1-9][0-9]|100
console.log(toRegexRange('-001', '100', {relaxZeros: false}));
//=> -0{2}1|0{2}[0-9]|0[1-9][0-9]|100
```
<details>
<summary><strong>Why are zeros relaxed for negative zero-padded ranges by default?</strong></summary>
Consider the following.
```js
var regex = toRegexRange('-001', '100');
```
_Note that `-001` and `100` are both three digits long_.
In most zero-padding implementations, only a single leading zero is enough to indicate that zero-padding should be applied. Thus, the leading zeros would be "corrected" on the negative range in the example to `-01`, instead of `-001`, to make total length of each string no greater than the length of the largest number in the range (in other words, `-001` is 4 digits, but `100` is only three digits).
If zeros were not relaxed by default, you might expect the resulting regex of the above pattern to match `-001` - given that it's defined that way in the arguments - _but it wouldn't_. It would, however, match `-01`. This gets even more ambiguous with large ranges, like `-01` to `1000000`.
Thus, we relax zeros by default to provide a more predictable experience for users.
</details>
## Examples
| **Range** | **Result** | **Compile time** |
| --- | --- | --- |
| `toRegexRange('5, 5')` | `5` | _33μs_ |
| `toRegexRange('5, 6')` | `5\|6` | _53μs_ |
| `toRegexRange('29, 51')` | `29\|[34][0-9]\|5[01]` | _699μs_ |
| `toRegexRange('31, 877')` | `3[1-9]\|[4-9][0-9]\|[1-7][0-9]{2}\|8[0-6][0-9]\|87[0-7]` | _711μs_ |
| `toRegexRange('111, 555')` | `11[1-9]\|1[2-9][0-9]\|[2-4][0-9]{2}\|5[0-4][0-9]\|55[0-5]` | _62μs_ |
| `toRegexRange('-10, 10')` | `-[1-9]\|-?10\|[0-9]` | _74μs_ |
| `toRegexRange('-100, -10')` | `-1[0-9]\|-[2-9][0-9]\|-100` | _49μs_ |
| `toRegexRange('-100, 100')` | `-[1-9]\|-?[1-9][0-9]\|-?100\|[0-9]` | _45μs_ |
| `toRegexRange('001, 100')` | `0{2}[1-9]\|0[1-9][0-9]\|100` | _158μs_ |
| `toRegexRange('0010, 1000')` | `0{2}1[0-9]\|0{2}[2-9][0-9]\|0[1-9][0-9]{2}\|1000` | _61μs_ |
| `toRegexRange('1, 2')` | `1\|2` | _10μs_ |
| `toRegexRange('1, 5')` | `[1-5]` | _24μs_ |
| `toRegexRange('1, 10')` | `[1-9]\|10` | _23μs_ |
| `toRegexRange('1, 100')` | `[1-9]\|[1-9][0-9]\|100` | _30μs_ |
| `toRegexRange('1, 1000')` | `[1-9]\|[1-9][0-9]{1,2}\|1000` | _52μs_ |
| `toRegexRange('1, 10000')` | `[1-9]\|[1-9][0-9]{1,3}\|10000` | _47μs_ |
| `toRegexRange('1, 100000')` | `[1-9]\|[1-9][0-9]{1,4}\|100000` | _44μs_ |
| `toRegexRange('1, 1000000')` | `[1-9]\|[1-9][0-9]{1,5}\|1000000` | _49μs_ |
| `toRegexRange('1, 10000000')` | `[1-9]\|[1-9][0-9]{1,6}\|10000000` | _63μs_ |
## Heads up!
**Order of arguments**
When the `min` is larger than the `max`, values will be flipped to create a valid range:
```js
toRegexRange('51', '29');
```
Is effectively flipped to:
```js
toRegexRange('29', '51');
//=> 29|[3-4][0-9]|5[0-1]
```
**Steps / increments**
This library does not support steps (increments). A pr to add support would be welcome.
## History
### v2.0.0 - 2017-04-21
**New features**
Adds support for zero-padding!
### v1.0.0
**Optimizations**
Repeating ranges are now grouped using quantifiers. rocessing time is roughly the same, but the generated regex is much smaller, which should result in faster matching.
## Attribution
Inspired by the python library [range-regex](https://github.com/dimka665/range-regex).
## About
### Related projects
* [expand-range](https://www.npmjs.com/package/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See… [more](https://github.com/jonschlinkert/expand-range) | [homepage](https://github.com/jonschlinkert/expand-range "Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See the benchmarks. Used by micromatch.")
* [fill-range](https://www.npmjs.com/package/fill-range): Fill in a range of numbers or letters, optionally passing an increment or `step` to… [more](https://github.com/jonschlinkert/fill-range) | [homepage](https://github.com/jonschlinkert/fill-range "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`")
* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
* [repeat-element](https://www.npmjs.com/package/repeat-element): Create an array by repeating the given value n times. | [homepage](https://github.com/jonschlinkert/repeat-element "Create an array by repeating the given value n times.")
* [repeat-string](https://www.npmjs.com/package/repeat-string): Repeat the given string n times. Fastest implementation for repeating a string. | [homepage](https://github.com/jonschlinkert/repeat-string "Repeat the given string n times. Fastest implementation for repeating a string.")
### Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
### Building docs
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```
### Running tests
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
### Author
**Jon Schlinkert**
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
### License
Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on April 27, 2017._

View file

@ -0,0 +1,294 @@
/*!
* to-regex-range <https://github.com/jonschlinkert/to-regex-range>
*
* Copyright (c) 2015, 2017, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
var repeat = require('repeat-string');
var isNumber = require('is-number');
var cache = {};
function toRegexRange(min, max, options) {
if (isNumber(min) === false) {
throw new RangeError('toRegexRange: first argument is invalid.');
}
if (typeof max === 'undefined' || min === max) {
return String(min);
}
if (isNumber(max) === false) {
throw new RangeError('toRegexRange: second argument is invalid.');
}
options = options || {};
var relax = String(options.relaxZeros);
var shorthand = String(options.shorthand);
var capture = String(options.capture);
var key = min + ':' + max + '=' + relax + shorthand + capture;
if (cache.hasOwnProperty(key)) {
return cache[key].result;
}
var a = Math.min(min, max);
var b = Math.max(min, max);
if (Math.abs(a - b) === 1) {
var result = min + '|' + max;
if (options.capture) {
return '(' + result + ')';
}
return result;
}
var isPadded = padding(min) || padding(max);
var positives = [];
var negatives = [];
var tok = {min: min, max: max, a: a, b: b};
if (isPadded) {
tok.isPadded = isPadded;
tok.maxLen = String(tok.max).length;
}
if (a < 0) {
var newMin = b < 0 ? Math.abs(b) : 1;
var newMax = Math.abs(a);
negatives = splitToPatterns(newMin, newMax, tok, options);
a = tok.a = 0;
}
if (b >= 0) {
positives = splitToPatterns(a, b, tok, options);
}
tok.negatives = negatives;
tok.positives = positives;
tok.result = siftPatterns(negatives, positives, options);
if (options.capture && (positives.length + negatives.length) > 1) {
tok.result = '(' + tok.result + ')';
}
cache[key] = tok;
return tok.result;
}
function siftPatterns(neg, pos, options) {
var onlyNegative = filterPatterns(neg, pos, '-', false, options) || [];
var onlyPositive = filterPatterns(pos, neg, '', false, options) || [];
var intersected = filterPatterns(neg, pos, '-?', true, options) || [];
var subpatterns = onlyNegative.concat(intersected).concat(onlyPositive);
return subpatterns.join('|');
}
function splitToRanges(min, max) {
min = Number(min);
max = Number(max);
var nines = 1;
var stops = [max];
var stop = +countNines(min, nines);
while (min <= stop && stop <= max) {
stops = push(stops, stop);
nines += 1;
stop = +countNines(min, nines);
}
var zeros = 1;
stop = countZeros(max + 1, zeros) - 1;
while (min < stop && stop <= max) {
stops = push(stops, stop);
zeros += 1;
stop = countZeros(max + 1, zeros) - 1;
}
stops.sort(compare);
return stops;
}
/**
* Convert a range to a regex pattern
* @param {Number} `start`
* @param {Number} `stop`
* @return {String}
*/
function rangeToPattern(start, stop, options) {
if (start === stop) {
return {pattern: String(start), digits: []};
}
var zipped = zip(String(start), String(stop));
var len = zipped.length, i = -1;
var pattern = '';
var digits = 0;
while (++i < len) {
var numbers = zipped[i];
var startDigit = numbers[0];
var stopDigit = numbers[1];
if (startDigit === stopDigit) {
pattern += startDigit;
} else if (startDigit !== '0' || stopDigit !== '9') {
pattern += toCharacterClass(startDigit, stopDigit);
} else {
digits += 1;
}
}
if (digits) {
pattern += options.shorthand ? '\\d' : '[0-9]';
}
return { pattern: pattern, digits: [digits] };
}
function splitToPatterns(min, max, tok, options) {
var ranges = splitToRanges(min, max);
var len = ranges.length;
var idx = -1;
var tokens = [];
var start = min;
var prev;
while (++idx < len) {
var range = ranges[idx];
var obj = rangeToPattern(start, range, options);
var zeros = '';
if (!tok.isPadded && prev && prev.pattern === obj.pattern) {
if (prev.digits.length > 1) {
prev.digits.pop();
}
prev.digits.push(obj.digits[0]);
prev.string = prev.pattern + toQuantifier(prev.digits);
start = range + 1;
continue;
}
if (tok.isPadded) {
zeros = padZeros(range, tok);
}
obj.string = zeros + obj.pattern + toQuantifier(obj.digits);
tokens.push(obj);
start = range + 1;
prev = obj;
}
return tokens;
}
function filterPatterns(arr, comparison, prefix, intersection, options) {
var res = [];
for (var i = 0; i < arr.length; i++) {
var tok = arr[i];
var ele = tok.string;
if (options.relaxZeros !== false) {
if (prefix === '-' && ele.charAt(0) === '0') {
if (ele.charAt(1) === '{') {
ele = '0*' + ele.replace(/^0\{\d+\}/, '');
} else {
ele = '0*' + ele.slice(1);
}
}
}
if (!intersection && !contains(comparison, 'string', ele)) {
res.push(prefix + ele);
}
if (intersection && contains(comparison, 'string', ele)) {
res.push(prefix + ele);
}
}
return res;
}
/**
* Zip strings (`for in` can be used on string characters)
*/
function zip(a, b) {
var arr = [];
for (var ch in a) arr.push([a[ch], b[ch]]);
return arr;
}
function compare(a, b) {
return a > b ? 1 : b > a ? -1 : 0;
}
function push(arr, ele) {
if (arr.indexOf(ele) === -1) arr.push(ele);
return arr;
}
function contains(arr, key, val) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][key] === val) {
return true;
}
}
return false;
}
function countNines(min, len) {
return String(min).slice(0, -len) + repeat('9', len);
}
function countZeros(integer, zeros) {
return integer - (integer % Math.pow(10, zeros));
}
function toQuantifier(digits) {
var start = digits[0];
var stop = digits[1] ? (',' + digits[1]) : '';
if (!stop && (!start || start === 1)) {
return '';
}
return '{' + start + stop + '}';
}
function toCharacterClass(a, b) {
return '[' + a + ((b - a === 1) ? '' : '-') + b + ']';
}
function padding(str) {
return /^-?(0+)\d/.exec(str);
}
function padZeros(val, tok) {
if (tok.isPadded) {
var diff = Math.abs(tok.maxLen - String(val).length);
switch (diff) {
case 0:
return '';
case 1:
return '0';
default: {
return '0{' + diff + '}';
}
}
}
return val;
}
/**
* Expose `toRegexRange`
*/
module.exports = toRegexRange;

View file

@ -0,0 +1,86 @@
{
"name": "to-regex-range",
"description": "Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.78 million test assertions.",
"version": "2.1.1",
"homepage": "https://github.com/micromatch/to-regex-range",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"repository": "micromatch/to-regex-range",
"bugs": {
"url": "https://github.com/micromatch/to-regex-range/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"is-number": "^3.0.0",
"repeat-string": "^1.6.1"
},
"devDependencies": {
"fill-range": "^3.1.1",
"gulp-format-md": "^0.1.12",
"mocha": "^3.2.0",
"text-table": "^0.2.0",
"time-diff": "^0.3.1"
},
"keywords": [
"alpha",
"alphabetical",
"bash",
"brace",
"date",
"expand",
"expansion",
"glob",
"match",
"matches",
"matching",
"number",
"numerical",
"range",
"ranges",
"regex",
"sequence",
"sh",
"to",
"year"
],
"verb": {
"related": {
"list": [
"expand-range",
"fill-range",
"micromatch",
"repeat-element",
"repeat-string"
]
},
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"helpers": [
"./examples.js"
],
"reflinks": [
"0-5",
"0-9",
"1-5",
"1-9"
]
}
}

81
web/node_modules/http-proxy-middleware/package.json generated vendored Normal file
View file

@ -0,0 +1,81 @@
{
"name": "http-proxy-middleware",
"version": "0.19.1",
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
"main": "index.js",
"files": [
"index.js",
"lib"
],
"scripts": {
"clean": "rm -rf coverage",
"lint": "prettier \"**/*.{js,md}\" --list-different",
"lint:fix": "prettier \"**/*.{js,md}\" --write",
"test": "npm run lint && mocha --recursive --colors --reporter spec",
"cover": "npm run clean && istanbul cover ./node_modules/mocha/bin/_mocha -- --recursive",
"coveralls": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- --recursive --reporter spec && istanbul-coveralls && npm run clean"
},
"repository": {
"type": "git",
"url": "https://github.com/chimurai/http-proxy-middleware.git"
},
"keywords": [
"reverse",
"proxy",
"middleware",
"http",
"https",
"connect",
"express",
"browser-sync",
"gulp",
"grunt-contrib-connect",
"websocket",
"ws",
"cors"
],
"author": "Steven Chim",
"license": "MIT",
"bugs": {
"url": "https://github.com/chimurai/http-proxy-middleware/issues"
},
"homepage": "https://github.com/chimurai/http-proxy-middleware",
"devDependencies": {
"@commitlint/cli": "^7.2.1",
"@commitlint/config-conventional": "^7.1.2",
"browser-sync": "^2.26.3",
"chai": "^4.2.0",
"connect": "^3.6.6",
"coveralls": "^3.0.2",
"express": "^4.16.4",
"husky": "^1.2.0",
"istanbul": "^0.4.5",
"istanbul-coveralls": "^1.0.3",
"mocha": "^5.2.0",
"mocha-lcov-reporter": "1.3.0",
"opn": "^5.4.0",
"precise-commits": "^1.0.2",
"prettier": "^1.15.2",
"ws": "^6.1.2"
},
"dependencies": {
"http-proxy": "^1.17.0",
"is-glob": "^4.0.0",
"lodash": "^4.17.11",
"micromatch": "^3.1.10"
},
"engines": {
"node": ">=4.0.0"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"pre-commit": "precise-commits"
}
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
}
}