initial commit
This commit is contained in:
commit
d3fcd7f10c
3159 files changed
+349702
No files matched your search
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"strict": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"node": true,
|
||||
"globals": {
|
||||
"describe": true,
|
||||
"it": true,
|
||||
"beforeEach": true,
|
||||
"afterEach": true
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
.DS_Store
|
||||
*.log
|
||||
node_modules
|
||||
build
|
||||
*.node
|
||||
components
|
||||
coverage
|
||||
*.orig
|
||||
.idea
|
||||
sandbox
|
||||
test/out-fixtures/*
|
||||
test/watch-*.txt
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.9"
|
||||
- "0.10"
|
||||
after_script:
|
||||
- npm run coveralls
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
# gulp changelog
|
||||
|
||||
# 3.5.2
|
||||
|
||||
- add -V for version on CLI (unix standard)
|
||||
- -v is deprecated, use -V
|
||||
- add -T as an alias for --tasks
|
||||
- documentation
|
||||
|
||||
# 3.5
|
||||
|
||||
- added `gulp.watch(globs, tasksArray)` sugar
|
||||
- remove gulp.taskQueue
|
||||
- deprecate gulp.run
|
||||
- deprecate gulp.env
|
||||
- add engineStrict to prevent people with node < 0.9 from installing
|
||||
|
||||
# 3.4
|
||||
|
||||
- added `--tasks` that prints out the tree of tasks + deps
|
||||
- global cli + local install mismatch is no longer fatal
|
||||
- remove tests for fs stuff
|
||||
- switch core src, dest, and watch to vinyl-fs
|
||||
- internal cleaning
|
||||
|
||||
## 3.3.4
|
||||
|
||||
- `--base` is now `--cwd`
|
||||
|
||||
## 3.3.3
|
||||
|
||||
- support for `--base` CLI arg to change where the search for gulpfile/`--require`s starts
|
||||
- support for `--gulpfile` CLI arg to point to a gulpfile specifically
|
||||
|
||||
## 3.3.0
|
||||
|
||||
- file.contents streams are no longer paused coming out of src
|
||||
- dest now passes files through before they are empty to fix passing to multiple dests
|
||||
|
||||
## 3.2.4
|
||||
|
||||
- Bug fix - we didn't have any CLI tests
|
||||
|
||||
## 3.2.3
|
||||
|
||||
- Update dependencies for bug fixes
|
||||
- autocomplete stuff in the completion folder
|
||||
|
||||
## 3.2
|
||||
|
||||
- File object is now [vinyl](https://github.com/wearefractal/vinyl)
|
||||
- .watch() is now [glob-watcher](https://github.com/wearefractal/glob-watcher)
|
||||
- Fix CLI -v when no gulpfile found
|
||||
- gulp-util updated
|
||||
- Logging moved to CLI bin file
|
||||
- Will cause double logging if you update global CLI to 3.2 but not local
|
||||
- Will cause no logging if you update local to 3.1 but not global CLI
|
||||
- Drop support for < 0.9
|
||||
|
||||
## 3.1.3
|
||||
|
||||
- Move isStream and isBuffer to gulp-util
|
||||
|
||||
## 3.1
|
||||
|
||||
- Move file class to gulp-util
|
||||
|
||||
## 3.0
|
||||
|
||||
- Ability to pass multiple globs and glob negations to glob-stream
|
||||
- Breaking change to the way glob-stream works
|
||||
- File object is now a class
|
||||
- file.shortened changed to file.relative
|
||||
- file.cwd added
|
||||
- Break out getStats to avoid nesting
|
||||
- Major code reorganization
|
||||
|
||||
## 2.7
|
||||
|
||||
- Breaking change to the way options are passed to glob-stream
|
||||
- Introduce new File object to ease pain of computing shortened names (now a getter)
|
||||
|
||||
## 2.4 - 2.6
|
||||
|
||||
- Moved stuff to gulp-util
|
||||
- Quit exposing createGlobStream (just use the glob-stream module)
|
||||
- More logging
|
||||
- Prettier time durations
|
||||
- Tons of documentation changes
|
||||
- gulp.trigger(tasks...) as a through stream
|
||||
|
||||
## 1.2-2.4 (11/12/13)
|
||||
|
||||
- src buffer=false fixed for 0.8 and 0.9 (remember to .resume() on these versions before consuming)
|
||||
- CLI completely rewritten
|
||||
- Colorful logging
|
||||
- Uses local version of gulp to run tasks
|
||||
- Uses findup to locate gulpfile (so you can run it anywhere in your project)
|
||||
- chdir to gulpfile directory before loading it
|
||||
- Correct exit codes on errors
|
||||
- silent flag added to gulp to disable logging
|
||||
- Fixes to task orchestration (3rd party)
|
||||
- Better support for globbed directories (thanks @robrich)
|
||||
|
||||
## 1.2 (10/28/13)
|
||||
|
||||
- Can specify buffer=false on src streams to make file.content a stream
|
||||
- Can specify read=false on src streams to disable file.content
|
||||
|
||||
## 1.1 (10/21/13)
|
||||
|
||||
- Can specify run callback
|
||||
- Can specify task dependencies
|
||||
- Tasks can accept callback or return promise
|
||||
- `gulp.verbose` exposes run-time internals
|
||||
|
||||
## 1.0 (9/26/13)
|
||||
|
||||
- Specify dependency versions
|
||||
- Updated docs
|
||||
|
||||
## 0.2 (8/6/13)
|
||||
|
||||
- Rename .files() to .src() and .folder() to .dest()
|
||||
|
||||
## 0.1 (7/18/13)
|
||||
|
||||
- Initial Release
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 Fractal <contact@wearefractal.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<p align="center">
|
||||
<a href="http://gulpjs.com">
|
||||
<img height="194" width="98" src="https://raw.github.com/gulpjs/artwork/master/gulp.png"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="http://gulpjs.com/">Visit our website!</a>
|
||||
</p>
|
||||
|
||||
# gulp [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Dependency Status][daviddm-url]][daviddm-image]
|
||||
> The streaming build system
|
||||
|
||||
## Like what we do?
|
||||
|
||||
[Support us via gittip](https://www.gittip.com/Contra/)
|
||||
|
||||
## Documentation
|
||||
|
||||
For a Getting started guide, API docs, recipes, making a plugin, etc. see the [documentation page](/docs/README.md)!
|
||||
|
||||
## Sample gulpfile
|
||||
|
||||
This file is just a quick sample to give you a taste of what gulp does.
|
||||
|
||||
```javascript
|
||||
var gulp = require('gulp');
|
||||
|
||||
var coffee = require('gulp-coffee');
|
||||
var concat = require('gulp-concat');
|
||||
var uglify = require('gulp-uglify');
|
||||
var imagemin = require('gulp-imagemin');
|
||||
|
||||
var paths = {
|
||||
scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'],
|
||||
images: 'client/img/**/*'
|
||||
};
|
||||
|
||||
gulp.task('scripts', function() {
|
||||
// Minify and copy all JavaScript (except vendor scripts)
|
||||
return gulp.src(paths.scripts)
|
||||
.pipe(coffee())
|
||||
.pipe(uglify())
|
||||
.pipe(concat('all.min.js'))
|
||||
.pipe(gulp.dest('build/js'));
|
||||
});
|
||||
|
||||
// Copy all static images
|
||||
gulp.task('images', function() {
|
||||
return gulp.src(paths.images)
|
||||
// Pass in options to the task
|
||||
.pipe(imagemin({optimizationLevel: 5}))
|
||||
.pipe(gulp.dest('build/img'));
|
||||
});
|
||||
|
||||
// Rerun the task when a file changes
|
||||
gulp.task('watch', function () {
|
||||
gulp.watch(paths.scripts, ['scripts']);
|
||||
gulp.watch(paths.images, ['images']);
|
||||
});
|
||||
|
||||
// The default task (called when you run `gulp` from cli)
|
||||
gulp.task('default', ['scripts', 'images', 'watch']);
|
||||
|
||||
```
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
[npm-url]: https://npmjs.org/package/gulp
|
||||
[npm-image]: https://badge.fury.io/js/gulp.png
|
||||
[travis-url]: https://travis-ci.org/gulpjs/gulp
|
||||
[travis-image]: https://travis-ci.org/gulpjs/gulp.png?branch=master
|
||||
[coveralls-url]: https://coveralls.io/r/gulpjs/gulp
|
||||
[coveralls-image]: https://coveralls.io/repos/gulpjs/gulp/badge.png
|
||||
[depstat-url]: https://david-dm.org/gulpjs/gulp
|
||||
[depstat-image]: https://david-dm.org/gulpjs/gulp.png
|
||||
[daviddm-url]: https://david-dm.org/gulpjs/gulp.png?theme=shields.io
|
||||
[daviddm-image]: https://david-dm.org/gulpjs/gulp
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var resolve = require('resolve');
|
||||
var findup = require('findup-sync');
|
||||
var gutil = require('gulp-util');
|
||||
var prettyTime = require('pretty-hrtime');
|
||||
var minimist = require('minimist');
|
||||
var semver = require('semver');
|
||||
var archy = require('archy');
|
||||
|
||||
var completion = require('../lib/completion');
|
||||
var taskTree = require('../lib/taskTree');
|
||||
var cliPkg = require('../package.json');
|
||||
|
||||
// parse what we want off the CLI
|
||||
var argv = minimist(process.argv.slice(2));
|
||||
|
||||
if (argv.completion) {
|
||||
return completion(argv.completion);
|
||||
}
|
||||
|
||||
var tasks = argv._;
|
||||
var tasksFlag = argv.T || argv.tasks;
|
||||
|
||||
// TODO: drop argv.v in the next breaking release
|
||||
var versionFlag = argv.v || argv.V || argv.version;
|
||||
var cwdFlag = argv.cwd;
|
||||
var fileFlag = argv.gulpfile;
|
||||
|
||||
var cwd;
|
||||
|
||||
if (cwdFlag) {
|
||||
cwd = path.resolve(cwdFlag);
|
||||
} else {
|
||||
cwd = process.cwd();
|
||||
}
|
||||
|
||||
loadRequires(argv.require, cwd);
|
||||
|
||||
var gulpFile;
|
||||
|
||||
if (fileFlag) {
|
||||
gulpFile = path.join(cwd, fileFlag);
|
||||
} else {
|
||||
gulpFile = getGulpFile(cwd);
|
||||
}
|
||||
|
||||
// find the local gulp
|
||||
var localGulp = findLocalGulp(gulpFile);
|
||||
var localPkg = findLocalGulpPackage(gulpFile);
|
||||
|
||||
// print some versions and shit
|
||||
if (versionFlag) {
|
||||
gutil.log('CLI version', cliPkg.version);
|
||||
if (localGulp) {
|
||||
gutil.log('Local version', localPkg.version);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!localGulp) {
|
||||
gutil.log(gutil.colors.red('No local gulp install found in'), gutil.colors.magenta(getLocalBase(gulpFile)));
|
||||
gutil.log(gutil.colors.red('Try running: npm install gulp'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// check for semver difference in running gulp vs locally installed and warn if running gulp > locally installed
|
||||
if (semver.gt(cliPkg.version, localPkg.version)) {
|
||||
gutil.log(gutil.colors.red('gulp version mismatch:'));
|
||||
gutil.log(gutil.colors.red('Running gulp is', cliPkg.version));
|
||||
gutil.log(gutil.colors.red('Local gulp (installed in gulpfile dir) is', localPkg.version));
|
||||
}
|
||||
|
||||
if (!gulpFile) {
|
||||
gutil.log(gutil.colors.red('No gulpfile found'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Wire up logging for tasks
|
||||
// on local gulp singleton
|
||||
logEvents(localGulp);
|
||||
|
||||
// Load their gulpfile and run it
|
||||
gutil.log('Using file', gutil.colors.magenta(gulpFile));
|
||||
loadGulpFile(localGulp, gulpFile, tasks);
|
||||
|
||||
function loadRequires(requires, baseDir) {
|
||||
if (typeof requires === 'undefined') requires = [];
|
||||
if (!Array.isArray(requires)) requires = [requires];
|
||||
return requires.map(function(modName){
|
||||
gutil.log('Requiring external module', gutil.colors.magenta(modName));
|
||||
var mod = findLocalModule(modName, baseDir);
|
||||
if (typeof mod === 'undefined') {
|
||||
gutil.log('Failed to load external module', gutil.colors.magenta(modName));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getLocalBase(gulpFile) {
|
||||
return path.resolve(path.dirname(gulpFile));
|
||||
}
|
||||
|
||||
function findLocalGulp(gulpFile){
|
||||
var baseDir = getLocalBase(gulpFile);
|
||||
return findLocalModule('gulp', baseDir);
|
||||
}
|
||||
|
||||
function findLocalModule(modName, baseDir){
|
||||
try {
|
||||
return require(resolve.sync(modName, {basedir: baseDir}));
|
||||
} catch(e) {}
|
||||
return;
|
||||
}
|
||||
|
||||
function findLocalGulpPackage(gulpFile){
|
||||
var baseDir = getLocalBase(gulpFile);
|
||||
var packageBase;
|
||||
try {
|
||||
packageBase = path.dirname(resolve.sync('gulp', {basedir: baseDir}));
|
||||
return require(path.join(packageBase, 'package.json'));
|
||||
} catch(e) {}
|
||||
return;
|
||||
}
|
||||
|
||||
function loadGulpFile(localGulp, gulpFile, tasks){
|
||||
var gulpFileCwd = path.dirname(gulpFile);
|
||||
process.chdir(gulpFileCwd);
|
||||
gutil.log('Working directory changed to', gutil.colors.magenta(gulpFileCwd));
|
||||
|
||||
var theGulpfile = require(gulpFile);
|
||||
|
||||
// just for good measure
|
||||
process.nextTick(function(){
|
||||
if (tasksFlag) {
|
||||
return logTasks(gulpFile, localGulp);
|
||||
}
|
||||
|
||||
startGulp(localGulp, tasks);
|
||||
});
|
||||
return theGulpfile;
|
||||
}
|
||||
|
||||
function startGulp(localGulp, tasks) {
|
||||
// impose our opinion of "default" tasks onto orchestrator
|
||||
var toRun = tasks.length ? tasks : ['default'];
|
||||
return localGulp.start.apply(localGulp, toRun);
|
||||
}
|
||||
|
||||
function logTasks(gulpFile, localGulp) {
|
||||
var tree = taskTree(localGulp.tasks);
|
||||
tree.label = 'Tasks for '+gutil.colors.magenta(gulpFile);
|
||||
archy(tree).split('\n').forEach(function(v){
|
||||
if (v.trim().length === 0) return;
|
||||
gutil.log(v);
|
||||
});
|
||||
}
|
||||
|
||||
function getGulpFile() {
|
||||
var extensions;
|
||||
if (require.extensions) {
|
||||
extensions = Object.keys(require.extensions).join(',');
|
||||
} else {
|
||||
extensions = ['.js'];
|
||||
}
|
||||
var gulpFile = findup('gulpfile{'+extensions+'}', {cwd: cwd, nocase: true});
|
||||
return gulpFile;
|
||||
}
|
||||
|
||||
// format orchestrator errors
|
||||
function formatError (e) {
|
||||
if (!e.err) return e.message;
|
||||
if (e.err.message) return e.err.message;
|
||||
return JSON.stringify(e.err);
|
||||
}
|
||||
|
||||
// wire up logging events
|
||||
function logEvents(gulp) {
|
||||
gulp.on('task_start', function(e){
|
||||
gutil.log('Running', "'"+gutil.colors.cyan(e.task)+"'...");
|
||||
});
|
||||
|
||||
gulp.on('task_stop', function(e){
|
||||
var time = prettyTime(e.hrDuration);
|
||||
gutil.log('Finished', "'"+gutil.colors.cyan(e.task)+"'", 'in', gutil.colors.magenta(time));
|
||||
});
|
||||
|
||||
gulp.on('task_err', function(e){
|
||||
var msg = formatError(e);
|
||||
var time = prettyTime(e.hrDuration);
|
||||
gutil.log('Errored', "'"+gutil.colors.cyan(e.task)+"'", 'in', gutil.colors.magenta(time), gutil.colors.red(msg));
|
||||
});
|
||||
|
||||
gulp.on('task_not_found', function(err){
|
||||
gutil.log(gutil.colors.red("Task '"+err.task+"' was not defined in your gulpfile but you tried to run it."));
|
||||
gutil.log('Please check the documentation for proper gulpfile formatting.');
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Completion for gulp
|
||||
> Thanks to grunt team and Tyler Kellen
|
||||
|
||||
To enable tasks auto-completion in shell you should add `eval "$(gulp --completion=shell)"` in your `.shellrc` file.
|
||||
|
||||
## Bash
|
||||
|
||||
Add `eval "$(gulp --completion=bash)"` to `~/.bashrc`.
|
||||
|
||||
## Zsh
|
||||
|
||||
Add `eval "$(gulp --completion=zsh)"` to `~/.zshrc`.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Borrowed from grunt-cli
|
||||
# http://gruntjs.com/
|
||||
#
|
||||
# Copyright (c) 2012 Tyler Kellen, contributors
|
||||
# Licensed under the MIT license.
|
||||
# https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# To enable bash <tab> completion for grunt, add the following line (minus the
|
||||
# leading #, which is the bash comment character) to your ~/.bashrc file:
|
||||
#
|
||||
# eval "$(gulp --completion=bash)"
|
||||
|
||||
# Enable bash autocompletion.
|
||||
function _gulp_completions() {
|
||||
# The currently-being-completed word.
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
#Grab tasks
|
||||
local compls=$(node -e "try { var gulp = require('gulp'); require('./gulpfile'); console.log(Object.keys(gulp.tasks).join(' ')); } catch (e) {}")
|
||||
# Trim whitespace.
|
||||
compls=$(echo "$compls" | sed -e 's/^ *//g' -e 's/ *$//g')
|
||||
# Tell complete what stuff to show.
|
||||
COMPREPLY=($(compgen -W "$compls" -- "$cur"))
|
||||
}
|
||||
|
||||
complete -o default -F _gulp_completions gulp
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/zsh
|
||||
|
||||
# Borrowed from grunt-cli
|
||||
# http://gruntjs.com/
|
||||
#
|
||||
# Copyright (c) 2012 Tyler Kellen, contributors
|
||||
# Licensed under the MIT license.
|
||||
# https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# To enable zsh <tab> completion for grunt, add the following line (minus the
|
||||
# leading #, which is the zsh comment character) to your ~/.zshrc file:
|
||||
#
|
||||
# eval "$(gulp --completion=zsh)"
|
||||
|
||||
# Enable zsh autocompletion.
|
||||
function _gulp_completion() {
|
||||
# Grap tasks
|
||||
compls=$(node -e "try { var gulp = require('gulp'); require('./gulpfile'); console.log(Object.keys(gulp.tasks).join(' ')); } catch (e) {}")
|
||||
# Trim whitespace.
|
||||
compls=$(echo "$compls" | sed -e 's/^ *//g' -e 's/ *$//g')
|
||||
completions=(${=compls})
|
||||
compadd -- $completions
|
||||
}
|
||||
|
||||
compdef _gulp_completion gulp
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
## gulp API docs
|
||||
|
||||
### gulp.src(globs[, options])
|
||||
|
||||
Takes a glob and represents a file structure. Can be piped to plugins.
|
||||
|
||||
```javascript
|
||||
gulp.src('./client/templates/*.jade')
|
||||
.pipe(jade())
|
||||
.pipe(minify())
|
||||
.pipe(gulp.dest('./build/minified_templates'));
|
||||
```
|
||||
|
||||
#### globs
|
||||
Type: `String` or `Array`
|
||||
|
||||
Glob or globs to read.
|
||||
|
||||
#### options
|
||||
Type: `Object`
|
||||
|
||||
Options to pass to [node-glob] through [glob-stream].
|
||||
|
||||
gulp adds two additional options in addition to the [options supported by node-glob][node-glob documentation]:
|
||||
|
||||
#### options.buffer
|
||||
Type: `Boolean`
|
||||
Default: `true`
|
||||
|
||||
Setting this to `false` will return `file.contents` as a stream and not buffer files. This is useful when working with large files. **Note:** Plugins may not implement support for streams.
|
||||
|
||||
#### options.read
|
||||
Type: `Boolean`
|
||||
Default: `true`
|
||||
|
||||
Setting this to `false` will return `file.contents` as null and not read the file at all.
|
||||
|
||||
|
||||
### gulp.dest(path)
|
||||
|
||||
Can be piped to and it will write files. Re-emits all data passed to it so you can pipe to multiple folders.
|
||||
|
||||
```javascript
|
||||
gulp.src('./client/templates/*.jade')
|
||||
.pipe(jade())
|
||||
.pipe(gulp.dest('./build/templates'))
|
||||
.pipe(minify())
|
||||
.pipe(gulp.dest('./build/minified_templates'));
|
||||
```
|
||||
|
||||
#### path
|
||||
Type: `String`
|
||||
|
||||
The path (folder) to write files to.
|
||||
|
||||
|
||||
### gulp.task(name[, deps], fn)
|
||||
|
||||
Define a task using [Orchestrator].
|
||||
|
||||
```javascript
|
||||
gulp.task('somename', function() {
|
||||
// Do stuff
|
||||
});
|
||||
```
|
||||
|
||||
#### name
|
||||
|
||||
The name of the task. Tasks that you want to run from the command line should not have spaces in them.
|
||||
|
||||
#### deps
|
||||
Type: `Array`
|
||||
|
||||
An array of tasks to be executed and completed before your task will run.
|
||||
|
||||
```javascript
|
||||
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
|
||||
// Do stuff
|
||||
});
|
||||
```
|
||||
|
||||
**Note:** Are your tasks running before the dependencies are complete? Make sure your dependency tasks
|
||||
are correctly using the async run hints: take in a callback or return a promise or event stream.
|
||||
|
||||
#### fn
|
||||
|
||||
The function that performs the task's operations. Generally this takes the form of `gulp.src().pipe(someplugin())`.
|
||||
|
||||
#### Async task support
|
||||
|
||||
Tasks can be made asynchronous if its `fn` does one of the following:
|
||||
|
||||
##### Accept a callback
|
||||
|
||||
```javascript
|
||||
gulp.task('somename', function(cb) {
|
||||
// Do stuff
|
||||
cb(err);
|
||||
});
|
||||
```
|
||||
|
||||
##### Return a stream
|
||||
|
||||
```javascript
|
||||
gulp.task('somename', function() {
|
||||
var stream = gulp.src('./client/**/*.js')
|
||||
.pipe(minify())
|
||||
.pipe(gulp.dest('/build'));
|
||||
return stream;
|
||||
});
|
||||
```
|
||||
|
||||
##### Return a promise
|
||||
|
||||
```javascript
|
||||
var Q = require('q');
|
||||
|
||||
gulp.task('somename', function() {
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Do async stuff
|
||||
setTimeout(function() {
|
||||
deferred.resolve();
|
||||
}, 1);
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
```
|
||||
|
||||
**Note:** By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing.
|
||||
If you want to create a series where tasks run in a particular order, you need to do two things:
|
||||
|
||||
- give it a hint to tell it when the task is done,
|
||||
- and give it a hint that a task depends on completion of another.
|
||||
|
||||
For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order:
|
||||
|
||||
1. In task "one" you add a hint to tell it when the task is done. Either take in a callback and call it when you're
|
||||
done or return a promise or stream that the engine should wait to resolve or end respectively.
|
||||
|
||||
2. In task "two" you add a hint telling the engine that it depends on completion of the first task.
|
||||
|
||||
So this example would look like this:
|
||||
|
||||
```javascript
|
||||
var gulp = require('gulp');
|
||||
|
||||
// takes in a callback so the engine knows when it'll be done
|
||||
gulp.task('one', function (cb) {
|
||||
// do stuff -- async or otherwise
|
||||
cb(err); // if err is not null and not undefined, the run will stop, and note that it failed
|
||||
});
|
||||
|
||||
// identifies a dependent task must be complete before this one begins
|
||||
gulp.task('two', ['one'], function () {
|
||||
// task 'one' is done now
|
||||
});
|
||||
|
||||
gulp.task('default', ['one', 'two']);
|
||||
```
|
||||
|
||||
|
||||
### gulp.watch(glob [, opts], tasks) or gulp.watch(glob [, opts, cb])
|
||||
|
||||
Watch files and do something when a file changes. This always returns an EventEmitter that emits `changed` events.
|
||||
|
||||
### gulp.watch(glob[, opts], tasks)
|
||||
|
||||
#### glob
|
||||
Type: `String` or `Array`
|
||||
|
||||
A single glob or array of globs that indicate which files to watch for changes.
|
||||
|
||||
#### opts
|
||||
Type: `Object`
|
||||
|
||||
Options, that are passed to [`gaze`](https://github.com/shama/gaze).
|
||||
|
||||
#### tasks
|
||||
Type: `Array`
|
||||
|
||||
Names of task(s) to run when a file changes, added with `gulp.task()`
|
||||
|
||||
```javascript
|
||||
var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
|
||||
watcher.on('changed', function(event){
|
||||
console.log('File '+event.path+' was '+event.type+', running tasks...');
|
||||
});
|
||||
```
|
||||
|
||||
### gulp.watch(glob[, opts, cb])
|
||||
|
||||
#### glob
|
||||
Type: `String` or `Array`
|
||||
|
||||
A single glob or array of globs that indicate which files to watch for changes.
|
||||
|
||||
#### opts
|
||||
Type: `Object`
|
||||
|
||||
Options, that are passed to [`gaze`](https://github.com/shama/gaze).
|
||||
|
||||
#### cb(event)
|
||||
Type: `Function`
|
||||
|
||||
Callback to be called on each change.
|
||||
|
||||
```javascript
|
||||
gulp.watch('js/**/*.js', function(event) {
|
||||
console.log('File '+event.path+' was '+event.type+', running tasks...');
|
||||
});
|
||||
```
|
||||
|
||||
The callback will be passed an object, `event`, that describes the change:
|
||||
|
||||
##### event.type
|
||||
Type: `String`
|
||||
|
||||
The type of change that occurred, either `added`, `changed` or `deleted`.
|
||||
|
||||
##### event.path
|
||||
Type: `String`
|
||||
|
||||
The path to the file that triggered the event.
|
||||
|
||||
|
||||
[node-optimist]: https://github.com/substack/node-optimist
|
||||
[node-glob documentation]: https://github.com/isaacs/node-glob#options
|
||||
[node-glob]: https://github.com/isaacs/node-glob
|
||||
[glob-stream]: https://github.com/wearefractal/glob-stream
|
||||
[gulp-if]: https://github.com/robrich/gulp-if
|
||||
[Orchestrator]: https://github.com/robrich/orchestrator
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
## gulp CLI docs
|
||||
|
||||
### Flags
|
||||
|
||||
gulp has very few flags to know about. All other flags are for tasks to use if needed.
|
||||
|
||||
- `-V` or `--version` will display the global and local gulp versions
|
||||
- `--require <module path>` will require a module before running the gulpfile. This is useful for transpilers but also has other applications. You can use multiple `--require` flags
|
||||
- `--gulpfile <gulpfile path>` manually set path of gulpfile. Useful if you have multiple gulpfiles. This will set the CWD to the gulpfile directory as well.
|
||||
- `--cwd <dir path>` manually set the CWD. The search for the gulpfile, as well as the relativity of all requires will be from here.
|
||||
- `-T` or `--tasks` will display the task dependency tree for the loaded gulpfile
|
||||
|
||||
### Tasks
|
||||
|
||||
Tasks can be executed by running `gulp <task> <othertask>`. Just running `gulp` will execute the task you registered called `default`. If there is no `default` task gulp will error.
|
||||
|
||||
### Compilers
|
||||
|
||||
You can use any language you want for your gulpfile. You will have to specify the language module name so the CLI can load it (and its associated extensions) before attempting to find your gulpfile. Make sure you have this module installed accessible by the folder you are running the CLI in.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
gulp dosomething --require coffee-script/register
|
||||
```
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# FAQ
|
||||
|
||||
## Why gulp? Why not ____?
|
||||
|
||||
See the [gulp introduction slideshow] for a rundown on how gulp came to be.
|
||||
|
||||
## Is it "gulp" or "Gulp"?
|
||||
|
||||
gulp is always lowercase.
|
||||
|
||||
## Where can I find a list of gulp plugins?
|
||||
|
||||
gulp plugins always include the `gulpplugin` keyword. [Search gulp plugins][search-gulp-plugins] or [view all plugins][npm plugin search].
|
||||
|
||||
## I want to write a gulp plugin, how do I get started?
|
||||
|
||||
See the [Writing a gulp plugin] wiki page for guidelines and an example to get you started.
|
||||
|
||||
## My plugin does ____, is it doing too much?
|
||||
|
||||
Probably. Ask yourself:
|
||||
|
||||
1. Is my plugin doing something that other plugins may need to do?
|
||||
- If so, that piece of functionality should be a separate plugin. [Check if it already exists on npm][npm plugin search].
|
||||
1. Is my plugin doing two, completely different things based on a configuration option?
|
||||
- If so, it may serve the community better to release it as two separate plugins
|
||||
- If the two tasks are different, but very closely related, it's probably OK
|
||||
|
||||
## How should newlines be represented in plugin output?
|
||||
|
||||
Always use gulp-util.newline (which is \n) to prevent diff issues between operating systems.
|
||||
|
||||
## Where can I get updates on gulp?
|
||||
|
||||
gulp updates can be found on the following twitters:
|
||||
|
||||
- [@wearefractal](https://twitter.com/wearefractal)
|
||||
- [@eschoff](https://twitter.com/eschoff)
|
||||
- [@funkytek](https://twitter.com/funkytek)
|
||||
- [@gulpjs](https://twitter.com/gulpjs)
|
||||
|
||||
## Does gulp have an IRC channel?
|
||||
|
||||
Yes, come chat with us in #gulpjs on [Freenode].
|
||||
|
||||
[Writing a gulp plugin]: writing-a-plugin/README.md
|
||||
[gulp introduction slideshow]: http://slid.es/contra/gulp
|
||||
[Freenode]: http://freenode.net/
|
||||
[search-gulp-plugins]: http://gratimax.github.io/search-gulp-plugins/
|
||||
[npm plugin search]: https://npmjs.org/browse/keyword/gulpplugin
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# gulp documentation
|
||||
|
||||
* [Getting Started](getting-started.md) - How to get going with gulp
|
||||
* [API documentation](API.md) - Learn the ins and outs of using gulp
|
||||
* [CLI documentation](CLI.md) - Learn how to call tasks and use compilers
|
||||
* [Writing a Plugin](writing-a-plugin/README.md) - So you're writing a gulp plugin? Go here for the essential dos and don'ts.
|
||||
|
||||
## FAQ
|
||||
|
||||
See [the FAQ](FAQ.md) for the answers to commonly asked questions.
|
||||
|
||||
## Recipes
|
||||
|
||||
The community has compiled guides on how to use gulp for common use cases. Check out the [recipes folder](recipes) for a full list.
|
||||
|
||||
## Articles
|
||||
|
||||
* [Introduction to node.js streams](https://github.com/substack/stream-handbook)
|
||||
* [Video introduction to node.js streams](http://www.youtube.com/watch?v=QgEuZ52OZtU)
|
||||
* [Getting started with gulp (by @markgdyr)](http://markgoodyear.com/2014/01/getting-started-with-gulp/)
|
||||
* [Why you shouldn’t create a gulp plugin (or, how to stop worrying and learn to love existing node packages)](http://blog.overzealous.com/post/74121048393/why-you-shouldnt-create-a-gulp-plugin-or-how-to-stop)
|
||||
* [Inspiration (slides) about why gulp was made](http://slid.es/contra/gulp)
|
||||
|
||||
## License
|
||||
|
||||
All the documentation is covered by the CC0 license *(do whatever you want with it - public domain)*.
|
||||
|
||||
[](http://creativecommons.org/publicdomain/zero/1.0/)
|
||||
|
||||
To the extent possible under law, [Fractal](http://wearefractal.com) has waived all copyright and related or neighboring rights to this work.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
## Getting Started
|
||||
|
||||
### 1. Install gulp globally:
|
||||
|
||||
```
|
||||
npm install -g gulp
|
||||
```
|
||||
|
||||
### 2. Install gulp and gulp-util in your project devDependencies:
|
||||
|
||||
```
|
||||
npm install --save-dev gulp gulp-util
|
||||
```
|
||||
|
||||
### 3. Create a `gulpfile.js` at the root of your project:
|
||||
|
||||
```javascript
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
|
||||
gulp.task('default', function(){
|
||||
// place code for your default task here
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Run gulp
|
||||
|
||||
```
|
||||
gulp
|
||||
```
|
||||
|
||||
The default task will run and do nothing.
|
||||
|
||||
To run individual tasks, use `gulp <task> <othertask>`
|
||||
|
||||
## Where do I go now?
|
||||
|
||||
You have an empty gulpfile and everything is installed. How do you REALLY get started? Check out the [recipes and articles section](README.md#articles-and-recipes) for more information
|
||||
|
||||
## .src, .watch, .dest, CLI args - How do I use these things?
|
||||
|
||||
For API specific documentation you can check out the [documentation for that](API.md)
|
||||
|
||||
## Available Plugins
|
||||
|
||||
The gulp community is growing, with new plugins being added daily. See the [main website](http://gulpjs.com/) for a complete list.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Combining streams to handle errors #
|
||||
|
||||
By default, emitting an error on a stream will cause it to be thrown
|
||||
unless it already has a listener attached to the `error` event. This
|
||||
gets a bit tricky when you're working with longer pipelines of streams.
|
||||
|
||||
By using [gulp-util](https://github.com/gulpjs/gulp-util)'s `combine`
|
||||
method you can turn a series of streams into a single stream, meaning you
|
||||
only need to listen to the `error` event in one place in your code.
|
||||
|
||||
Here's an example of using it in a gulpfile:
|
||||
|
||||
``` javascript
|
||||
var combine = require('gulp-util').combine
|
||||
var uglify = require('gulp-uglify')
|
||||
var gulp = require('gulp')
|
||||
|
||||
gulp.task('test', function() {
|
||||
var combined = combine(
|
||||
gulp.src('bootstrap/js/*.js'),
|
||||
uglify(),
|
||||
gulp.dest('public/bootstrap')
|
||||
)
|
||||
|
||||
// any errors in the above streams
|
||||
// will get caught by this listener,
|
||||
// instead of being thrown:
|
||||
combined.on('error', function(err) {
|
||||
console.warn(err.message)
|
||||
})
|
||||
|
||||
return combined
|
||||
})
|
||||
```
|
||||
|
||||
You can use this technique in your gulp plugins and node modules too using
|
||||
the [multipipe](http://npmjs.org/package/multipipe) module, and is a generally useful means of ensuring that errors aren't thrown from inaccessible streams
|
||||
somewhere deep inside `./node_modules`.
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
# Mocha test-runner with gulp
|
||||
|
||||
### Passing shared module in all tests
|
||||
|
||||
```js
|
||||
var gulp = require('gulp');
|
||||
var mocha = require('gulp-mocha');
|
||||
|
||||
gulp.task('tests', function() {
|
||||
return gulp.src(['test/test-*.js'], { read: false })
|
||||
.pipe(mocha({
|
||||
reporter: 'spec',
|
||||
globals: {
|
||||
should: require('should')
|
||||
}
|
||||
}));
|
||||
});
|
||||
```
|
||||
|
||||
### Running mocha tests when files change
|
||||
|
||||
With bundled `gulp.watch` and [`gulp-batch`](https://github.com/floatdrop/gulp-batch) (see readme of gulp-batch for reasons):
|
||||
|
||||
```js
|
||||
// npm install gulp gulp-watch gulp-mocha gulp-batch
|
||||
|
||||
var gulp = require('gulp');
|
||||
var mocha = require('gulp-mocha');
|
||||
var batch = require('gulp-batch');
|
||||
var gutil = require('gulp-util');
|
||||
|
||||
gulp.task('mocha', function () {
|
||||
return gulp.src(['test/*.js'], { read: false })
|
||||
.pipe(mocha({ reporter: 'list' }))
|
||||
.on('error', gutil.log);
|
||||
});
|
||||
|
||||
gulp.watch(['lib/**', 'test/**'], batch(function(events, cb) {
|
||||
gulp.run('mocha', cb);
|
||||
}));
|
||||
```
|
||||
|
||||
With [`gulp-watch`](https://github.com/floatdrop/gulp-watch) plugin:
|
||||
|
||||
```js
|
||||
// npm i gulp gulp-watch gulp-mocha
|
||||
|
||||
var gulp = require('gulp');
|
||||
var mocha = require('gulp-mocha');
|
||||
var watch = require('gulp-watch');
|
||||
var gutil = require('gulp-util')
|
||||
|
||||
gulp.task('mocha', function () {
|
||||
return gulp.src(['test/*.js'], { read: false })
|
||||
.pipe(mocha({ reporter: 'list' }))
|
||||
.on('error', gutil.log);
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
return gulp.src(['lib/**', 'test/**'], { read: false })
|
||||
.pipe(watch(function(events, cb) {
|
||||
gulp.run('mocha', cb);
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('default', ['mocha', 'watch']);
|
||||
|
||||
// run `gulp watch` or just `gulp` for watching and rerunning tests
|
||||
```
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Only pass through changed files
|
||||
|
||||
Files are passed through the whole pipe chain on every run by default. By using [gulp-changed](https://github.com/sindresorhus/gulp-changed) only changed files will be passed through. This can speed up consecutive runs considerably.
|
||||
|
||||
|
||||
```js
|
||||
// run `npm install gulp gulp-changed gulp-jscs gulp-uglify`
|
||||
var gulp = require('gulp');
|
||||
var changed = require('gulp-changed');
|
||||
var jscs = require('gulp-jscs');
|
||||
var uglify = require('gulp-uglify');
|
||||
|
||||
// we define some constants here so they can be reused
|
||||
var SRC = 'src/*.js';
|
||||
var DEST = 'dist';
|
||||
|
||||
gulp.task('default', function () {
|
||||
return gulp.src(SRC)
|
||||
// the `changed` task needs to know the destination directory
|
||||
// upfront to be able to figure out which files changed
|
||||
.pipe(changed(DEST))
|
||||
// only files that has changed will pass through here
|
||||
.pipe(jscs())
|
||||
.pipe(uglify())
|
||||
.pipe(gulp.dest(DEST));
|
||||
});
|
||||
```
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Pass parameters from the command line
|
||||
## bonus: keeping those tasks DRY
|
||||
|
||||
---
|
||||
|
||||
`gulpfile.js`
|
||||
|
||||
```js
|
||||
// npm install gulp gulp-util gulp-if gulp-uglify
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var gulpif = require('gulp-if');
|
||||
var uglify = require('gulp-uglify');
|
||||
|
||||
var isProduction = gutil.env.type === 'production';
|
||||
|
||||
gulp.task('scripts', function () {
|
||||
return gulp.src('**/*.js')
|
||||
.pipe(gulpif(isProduction, uglify())) // only minify if production
|
||||
.pipe(gulp.dest('dist'));
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
`cli`
|
||||
|
||||
`gulp scripts --type production`
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# Rebuild only files that change
|
||||
|
||||
With [`gulp-watch`](https://github.com/floatdrop/gulp-watch):
|
||||
|
||||
```js
|
||||
var gulp = require('gulp');
|
||||
var sass = require('gulp-sass');
|
||||
var watch = require('gulp-watch');
|
||||
|
||||
gulp.task('default', function() {
|
||||
return gulp.src('./sass/*.scss')
|
||||
.pipe(watch())
|
||||
.pipe(sass())
|
||||
.pipe(gulp.dest('./dist/'));
|
||||
});
|
||||
```
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Running tasks in series
|
||||
|
||||
By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing.
|
||||
If you want to create a series where tasks run in a particular order, you need to do two things:
|
||||
|
||||
- give it a hint to tell it when the task is done,
|
||||
- and give it a hint that a task depends on completion of another.
|
||||
|
||||
For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order:
|
||||
|
||||
1. In task "one" you add a hint to tell it when the task is done. Either take in a callback and call it when you're
|
||||
done or return a promise or stream that the engine should wait to resolve or end respectively.
|
||||
|
||||
2. In task "two" you add a hint telling the engine that it depends on completion of the first task.
|
||||
|
||||
So this example would look like this:
|
||||
|
||||
```javascript
|
||||
var gulp = require('gulp');
|
||||
|
||||
// takes in a callback so the engine knows when it'll be done
|
||||
gulp.task('one', function (cb) {
|
||||
// do stuff -- async or otherwise
|
||||
cb(err); // if err is not null and not undefined, the orchestration will stop, and 'two' will not run
|
||||
});
|
||||
|
||||
// identifies a dependent task must be complete before this one begins
|
||||
gulp.task('two', ['one'], function () {
|
||||
// task 'one' is done now
|
||||
});
|
||||
|
||||
gulp.task('default', ['one', 'two']);
|
||||
// alternatively: gulp.task('default', ['two']);
|
||||
```
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Specifying a new cwd (current working directory)
|
||||
|
||||
This is helpful for projects using a nested directory structure, such as:
|
||||
|
||||
```
|
||||
/project
|
||||
/layer1
|
||||
/layer2
|
||||
```
|
||||
|
||||
You can use the gulp CLI option `--cwd`
|
||||
|
||||
From the `project/` directory
|
||||
|
||||
```bash
|
||||
gulp --cwd ./layer1/
|
||||
```
|
||||
|
||||
Another option is to use `process.chdir` which is just vanilla node.
|
||||
|
||||
`gulpfile.js`
|
||||
|
||||
```js
|
||||
var gulp = require('gulp');
|
||||
|
||||
try {
|
||||
process.chdir(gulp.env.cwd);
|
||||
} catch (err) {
|
||||
console.error('Unable to chdir to %s', gulp.env.cwd);
|
||||
}
|
||||
```
|
||||
|
||||
If you only need to specify a cwd for a certain glob, you can use the `cwd` option on a [glob-stream](https://github.com/wearefractal/glob-stream)
|
||||
|
||||
```js
|
||||
gulp.src('./some/dir/**/*.js', { cwd: './public' });
|
||||
```
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Using coffee-script for gulpfile
|
||||
|
||||
As discussed in [issue #103](https://github.com/gulpjs/gulp/issues/103), there are 2 ways to do this.
|
||||
|
||||
1. Use `gulp --require coffee-script/register` at the command line
|
||||
|
||||
2. Require in `gulpfile.coffee` after requiring `coffee-script` in `gulpfile.js`
|
||||
|
||||
`gulpfile.js`
|
||||
|
||||
```js
|
||||
require('coffee-script/register');
|
||||
require('./gulpfile.coffee');
|
||||
```
|
||||
|
||||
`gulpfile.coffee`
|
||||
|
||||
```coffeescript
|
||||
gulp = require 'gulp'
|
||||
|
||||
gulp.task 'default', ->
|
||||
console.log('default task called')
|
||||
```
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Using external config file
|
||||
## bonus: keeping those tasks DRY
|
||||
## bonus2: config.json can be used by another task runner, like `Grunt`
|
||||
|
||||
---
|
||||
|
||||
`config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"desktop" : {
|
||||
"src" : [
|
||||
"dev/desktop/js/**/*.js",
|
||||
"!dev/desktop/js/vendor/**"
|
||||
],
|
||||
"dest" : "build/desktop/js"
|
||||
},
|
||||
"mobile" : {
|
||||
"src" : [
|
||||
"dev/mobile/js/**/*.js",
|
||||
"!dev/mobile/js/vendor/**"
|
||||
],
|
||||
"dest" : "build/mobile/js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
`gulpfile.js`
|
||||
|
||||
```js
|
||||
// npm install gulp gulp-uglify
|
||||
var gulp = require('gulp');
|
||||
var uglify = require('gulp-uglify');
|
||||
var config = require('./config.json');
|
||||
|
||||
function doStuff(cfg) {
|
||||
return gulp.src(cfg.src)
|
||||
.pipe(uglify())
|
||||
.pipe(gulp.dest(cfg.dest));
|
||||
}
|
||||
|
||||
gulp.task('dry', function () {
|
||||
doStuff(config.desktop);
|
||||
doStuff(config.mobile);
|
||||
});
|
||||
```
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# Using multiple sources in one task
|
||||
|
||||
```js
|
||||
// npm install gulp event-stream
|
||||
|
||||
var gulp = require('gulp');
|
||||
var es = require('event-stream');
|
||||
|
||||
gulp.task('test', function(cb) {
|
||||
return es.concat(
|
||||
gulp.src('bootstrap/js/*.js')
|
||||
.pipe(gulp.dest('public/bootstrap')),
|
||||
gulp.src('jquery.cookie/jquery.cookie.js')
|
||||
.pipe(gulp.dest('public/jquery'))
|
||||
);
|
||||
});
|
||||
```
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
# Writing a plugin
|
||||
|
||||
If you plan to create your own Gulp plugin, you will save time by reading the full documentation.
|
||||
|
||||
* [Guidelines](guidelines.md) (a MUST read)
|
||||
* [Using buffers](using-buffers.md)
|
||||
* [Dealing with streams](dealing-with-streams.md)
|
||||
* [Testing](testing.md)
|
||||
* [README conventions](readme-conventions.md)
|
||||
|
||||
## What it does
|
||||
|
||||
### Streaming file objects
|
||||
|
||||
A gulp plugin always returns a stream in [object mode](http://nodejs.org/api/stream.html#stream_object_mode) that does the following:
|
||||
|
||||
1. Takes in [vinyl File objects](http://github.com/wearefractal/vinyl)
|
||||
2. Outputs [vinyl File objects](http://github.com/wearefractal/vinyl)
|
||||
|
||||
These are known as [transform streams](http://nodejs.org/api/stream.html#stream_class_stream_transform_1) (also sometimes called through streams). Transform streams are streams that are readable and writable which manipulate objects as they're being passed through.
|
||||
|
||||
### Modifying file content
|
||||
|
||||
Vinyl files can have 3 possible forms for the contents attribute:
|
||||
|
||||
- [Streams](dealing-with-streams.md)
|
||||
- [Buffers](using-buffers.md)
|
||||
- Empty (null) - Useful for things like rimraf, clean, where contents is not neeeded.
|
||||
|
||||
## Useful resources
|
||||
|
||||
* [File object](https://github.com/wearefractal/gulp-util/#new-fileobj)
|
||||
* [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options)
|
||||
* [event-stream](https://github.com/dominictarr/event-stream)
|
||||
* [BufferStream](https://github.com/nfroidure/BufferStream)
|
||||
* [gulp-util](https://github.com/wearefractal/gulp-util)
|
||||
|
||||
|
||||
## Sample plugins
|
||||
|
||||
* [sindresorhus' gulp plugins](https://github.com/search?q=%40sindresorhus+gulp-)
|
||||
* [Fractal's gulp plugins](https://github.com/search?q=%40wearefractal+gulp-)
|
||||
* [gulp-replace](https://github.com/lazd/gulp-replace)
|
||||
|
||||
|
||||
## About streams
|
||||
|
||||
If you're unfamiliar with streams, you will need to read up on them:
|
||||
|
||||
* https://github.com/substack/stream-handbook (a MUST read)
|
||||
* http://nodejs.org/api/stream.html
|
||||
|
||||
Other libraries that are not file manipulating through streams but are made for use with gulp are tagged with the [gulpfriendly](https://npmjs.org/browse/keyword/gulpfriendly) keyword on npm.
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
# Dealing with streams
|
||||
|
||||
> It is highly recommended to write plugins supporting streams. Here is some information on creating a gulp plugin that supports streams.
|
||||
|
||||
> Make sure to follow the best practice regarging error handling and add the line that make the gulp plugin re-emit the first error catched during the transformation of the content
|
||||
|
||||
[Writing a Plugin](README.md) > Writing stream based plugins
|
||||
|
||||
## Dealing with streams
|
||||
|
||||
Let's implement a plugin prepending some text to files. This plugin supports all possible forms of file.contents.
|
||||
|
||||
```js
|
||||
var through = require('through2');
|
||||
var gutil = require('gulp-util');
|
||||
var PluginError = gutil.PluginError;
|
||||
|
||||
// Consts
|
||||
const PLUGIN_NAME = 'gulp-prefixer';
|
||||
|
||||
function prefixStream(prefixText) {
|
||||
var stream = through();
|
||||
stream.write(prefixText);
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Plugin level function (dealing with files)
|
||||
function gulpPrefixer(prefixText) {
|
||||
|
||||
if (!prefixText) {
|
||||
throw PluginError(PLUGIN_NAME, "Missing prefix text!");
|
||||
}
|
||||
prefixText = new Buffer(prefixText); // allocate ahead of time
|
||||
|
||||
// Creating a stream through which each file will pass
|
||||
var stream = through.obj(function (file, enc, callback) {
|
||||
if (file.isNull()) {
|
||||
this.push(file); // Do nothing if no contents
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (file.isBuffer()) {
|
||||
this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!'));
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (file.isStream()) {
|
||||
// define the streamer that will transform the content
|
||||
var streamer = prefixStream(prefixText);
|
||||
// catch errors from the streamer and emit a gulp plugin error
|
||||
streamer.on('error', this.emit.bind(this, 'error'));
|
||||
// start the transformation
|
||||
file.contents = file.contents.pipe(streamer);
|
||||
// make sure the file goes through the next gulp plugin
|
||||
this.push(file);
|
||||
// tell the stream engine that we are done with this file
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
|
||||
// returning the file stream
|
||||
return stream;
|
||||
};
|
||||
|
||||
// Exporting the plugin main function
|
||||
module.exports = gulpPrefixer;
|
||||
```
|
||||
|
||||
The above plugin can be used like this:
|
||||
|
||||
```js
|
||||
var gulp = require('gulp');
|
||||
var gulpPrefixer = require('gulp-prefixer');
|
||||
|
||||
gulp.src('files/**/*.js')
|
||||
.pipe(gulpPrefixer('prepended string'))
|
||||
.pipe(gulp.dest('/modified-files/'));
|
||||
```
|
||||
|
||||
## Some plugins using streams
|
||||
|
||||
* [gulp-svgicons2svgfont](https://github.com/nfroidure/gulp-svgiconstosvgfont)
|
||||
* gulp-browserify (Soon)
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# Guidelines
|
||||
|
||||
> While these guidelines are totally optional, we **HIGHLY** recommend that everyone follows them. Nobody wants to use a bad plugin. These guidelines will actually help make your life easier by giving you assurance that your plugin fits well within gulp.
|
||||
|
||||
[Writing a Plugin](README.md) > Guidelines
|
||||
|
||||
1. Your plugin should not do something that can be done easily with an existing node module
|
||||
- Wrapping every possible thing just for the sake of wrapping it will pollute the ecosystem with low quality plugins that don't make sense
|
||||
1. Your plugin should only do **one thing**, and do it well.
|
||||
- Avoid config options that make your plugin do completely different tasks
|
||||
1. Your plugin shouldn't do things that other plugins are responsible for
|
||||
- It should not concat, [gulp-concat](https://github.com/wearefractal/gulp-concat) does that
|
||||
- It should not add headers, [gulp-header](https://github.com/godaddy/gulp-header) does that
|
||||
- It should not add footers, [gulp-footer](https://github.com/godaddy/gulp-footer) does that
|
||||
- If it's a common but optional use case, document that your plugin is often used with another plugin
|
||||
- If it's an internal requirement, make use of existing plugins by piping your plugin's output to them
|
||||
1. Your plugin **must be tested**
|
||||
- Testing a gulp plugin is easy, you don't even need gulp to test it
|
||||
- Look at other plugins for examples
|
||||
1. Add `gulpplugin` as a keyword in your `package.json` so you show up on our search
|
||||
1. Do not throw errors inside a stream
|
||||
- Instead, you should emit it as an **error** event.
|
||||
- If you encounter an error **outside** the stream, such as invalid configuration, you may throw it.
|
||||
1. Prefix any errors with the name of your plugin
|
||||
- For example: `gulp-replace: Cannot do regexp replace on a stream`
|
||||
- Use gulp-util's [PluginError](https://github.com/gulpjs/gulp-util#new-pluginerrorpluginname-message-options) class to make this easy
|
||||
1. The type of `file.contents` should always be the same going out as it was when it came in
|
||||
- If file.contents is null (non-read) just ignore the file and pass it along
|
||||
- If file.contents is a Stream and you don't support that just emit an error
|
||||
- If you choose to support file.contents as a Stream, use [BufferStream](https://github.com/nfroidure/BufferStream) to make this easy
|
||||
1. Do not pass the `file` object downstream until you are done with it
|
||||
1. Make use of the [gulp-util](https://github.com/gulpjs/gulp-util) library
|
||||
- It provides templating, CLI colors, logging, changing file extensions
|
||||
- Try looking for common things there first and add it if it doesn't exist
|
||||
1. Do NOT require `gulp` as a dependency or peerDependency
|
||||
- There is no reason you should have to do this and it will cause problems if you do
|
||||
|
||||
## Why are these guidelines so strict?
|
||||
|
||||
gulp aims to be simple for users. By providing strict guidelines we are able to provide a consistent and high-quality ecosystem for everyone. While this does add a little more work and thought for plugin authors, it removes a lot of problems later down the road.
|
||||
|
||||
### What happens if I don't follow them?
|
||||
|
||||
npm is open for everyone, and you are free to make whatever you want but these guidelines were prescribed for a reason. There are acceptances tests coming soon that will be integrated into the plugin search. If you fail to adhere to the plugin guidelines it will be publicly visible/sortable via a scoring system. People will always prefer to use plugins that match "the gulp way".
|
||||
|
||||
### What does a good plugin look like?
|
||||
|
||||
```js
|
||||
// through2 is a thin wrapper around node transform streams
|
||||
var through = require('through2');
|
||||
var gutil = require('gulp-util');
|
||||
var PluginError = gutil.PluginError;
|
||||
|
||||
// Consts
|
||||
const PLUGIN_NAME = 'gulp-prefixer';
|
||||
|
||||
function prefixStream(prefixText) {
|
||||
var stream = through();
|
||||
stream.write(prefixText);
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Plugin level function (dealing with files)
|
||||
function gulpPrefixer(prefixText) {
|
||||
|
||||
if (!prefixText) {
|
||||
throw PluginError(PLUGIN_NAME, "Missing prefix text!");
|
||||
}
|
||||
prefixText = new Buffer(prefixText); // allocate ahead of time
|
||||
|
||||
// Creating a stream through which each file will pass
|
||||
var stream = through.obj(function (file, enc, callback) {
|
||||
if (file.isNull()) {
|
||||
this.push(file); // Do nothing if no contents
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (file.isBuffer()) {
|
||||
file.contents = Buffer.concat([prefixText, file.contents]);
|
||||
this.push(file);
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (file.isStream()) {
|
||||
file.contents = file.contents.pipe(prefixStream(prefixText));
|
||||
this.push(file);
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
|
||||
// returning the file stream
|
||||
return stream;
|
||||
};
|
||||
|
||||
// Exporting the plugin main function
|
||||
module.exports = gulpPrefixer;
|
||||
```
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
# README conventions
|
||||
|
||||
> Follow README conventions to increase your plugin readability and usability.
|
||||
|
||||
[Writing a Gulp Plugin](README.md) > README conventions
|
||||
|
||||
A plugin README should have the following parts at a minimum:
|
||||
|
||||
1. **Heading**: Give a short introduction to your plugin
|
||||
* Include the version of gulp your plugin was designed for
|
||||
* Include any badges on the same line as the heading
|
||||
1. **Usage**: Include a simple usage example
|
||||
* Show the plugin used on the context of a `gulpfile.js`
|
||||
* Cover the most common or simplest use case
|
||||
1. **API**: Document your plugin's API
|
||||
* For individual parameters, include:
|
||||
1. Parameter name
|
||||
1. Type
|
||||
1. Default value
|
||||
1. Description
|
||||
1. Optional example for complex options
|
||||
* If a parameter accepts an Object, do the above for each property
|
||||
* If your plugin accepts overloaded arguments, document each style individually
|
||||
|
||||
|
||||
# Example: Options object
|
||||
|
||||
The following is an example of a plugin that takes a single options object (from [gulp-handlebars](https://github.com/lazd/gulp-handlebars)).
|
||||
|
||||
|
||||
***
|
||||
|
||||
# gulp-handlebars [![NPM version][handlebars-npm-image]][handlebars-npm-url] [![Build status][handlebars-travis-image]][handlebars-travis-url]
|
||||
> Handlebars plugin for gulp 3
|
||||
|
||||
## Usage
|
||||
|
||||
First, install `gulp-handlebars` as a development dependency:
|
||||
|
||||
```shell
|
||||
npm install --save-dev gulp-handlebars
|
||||
```
|
||||
|
||||
Then, add it to your `gulpfile.js`:
|
||||
|
||||
```js
|
||||
var handlebars = require('gulp-handlebars');
|
||||
|
||||
gulp.task('templates', function(){
|
||||
gulp.src(['client/templates/*.hbs'])
|
||||
.pipe(handlebars({
|
||||
namespace: 'MyApp.templates',
|
||||
outputType: 'hybrid'
|
||||
}))
|
||||
.pipe(concat('templates.js'))
|
||||
.pipe(gulp.dest('build/js/'));
|
||||
});
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### handlebars(options)
|
||||
|
||||
#### options.namespace
|
||||
Type: `String`
|
||||
Default: `templates`
|
||||
|
||||
The namespace in which the precompiled templates will be assigned. Use dot notation (e.g. `App.Templates`) for nested namespaces or false to declare templates in the global namespace.
|
||||
|
||||
#### options.outputType
|
||||
Type: `String`
|
||||
Default: `browser`
|
||||
|
||||
The desired output type. One of the following:
|
||||
|
||||
* `browser` - Produce plain JavaScript files for the browser
|
||||
* `hybrid` - Produce Node modules that can optionally be used on the frontend
|
||||
* `node` - Produce Node modules
|
||||
* `amd` - Produce AMD modules
|
||||
* `commonjs` - Produce CommonJS modules
|
||||
* `bare` - Return an unmolested function definition
|
||||
|
||||
#### options.declareNamespace
|
||||
Type: `Boolean`
|
||||
Default: `true`
|
||||
|
||||
If true, non-destructively declare all parts of the namespace. This option is only necessary when `options.type` is `browser` or `hybrid`.
|
||||
|
||||
For example, if the namespace is `MyApp.Templates` and a template is named `App.Header.hbs`, the following declaration will be present in the output file for that template:
|
||||
|
||||
```js
|
||||
this["MyApp"] = this["MyApp"] || {};
|
||||
this["MyApp"]["templates"] = this["MyApp"]["templates"] || {};
|
||||
this["MyApp"]["templates"]["App"] = this["MyApp"]["templates"]["App"] || {};
|
||||
this["MyApp"]["templates"]["App"]["Header"] = function () {};
|
||||
```
|
||||
|
||||
When processing multiple templates under a given namespace, this will result in duplicate declarations. That is, the non-destructive declaration of the namespace will be repeated for each template compiled.
|
||||
|
||||
#### options.processName
|
||||
Type: `Function`
|
||||
Default: Strip file extension
|
||||
|
||||
This option accepts a function which takes one argument (the template filepath) and returns a string which will be used as the key for the precompiled template object. By default, the filename minus the extension is used.
|
||||
|
||||
If this function returns a string containing periods (not including the file extension), they will be represented as a sub-namespace. See `options.declareNamespace` for an example of the effect.
|
||||
|
||||
#### options.compilerOptions
|
||||
Type: `Object`
|
||||
|
||||
Compiler options to pass to `Handlebars.precompile()`.
|
||||
|
||||
|
||||
[handlebars-travis-url]: http://travis-ci.org/lazd/gulp-handlebars
|
||||
[handlebars-travis-image]: https://secure.travis-ci.org/lazd/gulp-handlebars.png?branch=master
|
||||
[handlebars-npm-url]: https://npmjs.org/package/gulp-handlebars
|
||||
[handlebars-npm-image]: https://badge.fury.io/js/gulp-handlebars.png
|
||||
***
|
||||
|
||||
|
||||
# Example: Overloaded API
|
||||
|
||||
The following is an example of a plugin that has an overloaded API (from [gulp-csslint](https://github.com/lazd/gulp-csslint)).
|
||||
|
||||
|
||||
***
|
||||
# gulp-csslint [![NPM version][csslint-npm-image]][csslint-npm-url] [![Build status][csslint-travis-image]][csslint-travis-url]
|
||||
> CSSLint plugin for gulp 3
|
||||
|
||||
## Usage
|
||||
|
||||
First, install `gulp-csslint` as a development dependency:
|
||||
|
||||
```shell
|
||||
npm install --save-dev gulp-csslint
|
||||
```
|
||||
|
||||
Then, add it to your `gulpfile.js`:
|
||||
|
||||
```javascript
|
||||
var csslint = require('gulp-csslint');
|
||||
|
||||
gulp.task('css', function() {
|
||||
gulp.src('./client/css/*.css')
|
||||
.pipe(csslint())
|
||||
.pipe(csslint.reporter());
|
||||
});
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### csslint(ruleConfiguration)
|
||||
|
||||
#### ruleConfiguration
|
||||
Type: `Object`
|
||||
|
||||
You can pass rule configuration as an object. See the [list of rules by ID on the CSSLint wiki](https://github.com/stubbornella/csslint/wiki/Rules-by-ID) for valid rule IDs.
|
||||
|
||||
```javascript
|
||||
gulp.src('./client/css/*.css')
|
||||
.pipe(csslint({
|
||||
'shorthand': false
|
||||
}))
|
||||
.pipe(csslint.reporter());
|
||||
```
|
||||
|
||||
### csslint(csslintrc)
|
||||
|
||||
#### csslintrc
|
||||
Type: `String`
|
||||
|
||||
You can also pass the path to your csslintrc file instead of a rule configuration object.
|
||||
|
||||
```javascript
|
||||
gulp.src('./client/css/*.css')
|
||||
.pipe(csslint('csslintrc.json'))
|
||||
.pipe(csslint.reporter());
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
Adds the following properties to the file object:
|
||||
|
||||
```javascript
|
||||
file.csslint.success = true; // or false
|
||||
file.csslint.errorCount = 0; // number of errors returned by CSSLint
|
||||
file.csslint.results = []; // CSSLint errors
|
||||
file.csslint.opt = {}; // The options you passed to CSSLint
|
||||
```
|
||||
|
||||
## Custom Reporters
|
||||
|
||||
Custom reporter functions can be passed as `cssline.reporter(reporterFunc)`. The reporter function will be called for each linted file and passed the file object as described above.
|
||||
|
||||
```javascript
|
||||
var csslint = require('gulp-csslint');
|
||||
var gutil = require('gulp-util');
|
||||
|
||||
var customReporter = function(file) {
|
||||
gutil.log(gutil.colors.cyan(file.csslint.errorCount)+' errors in '+gutil.colors.magenta(file.path));
|
||||
|
||||
file.csslint.results.forEach(function(result) {
|
||||
gutil.log(result.error.message+' on line '+result.error.line);
|
||||
});
|
||||
};
|
||||
|
||||
gulp.task('lint', function() {
|
||||
gulp.files('./lib/*.js')
|
||||
.pipe(csslint())
|
||||
.pipe(csslint.reporter(customReporter));
|
||||
});
|
||||
```
|
||||
|
||||
[csslint-travis-url]: http://travis-ci.org/lazd/gulp-csslint
|
||||
[csslint-travis-image]: https://secure.travis-ci.org/lazd/gulp-csslint.png?branch=master
|
||||
[csslint-npm-url]: https://npmjs.org/package/gulp-csslint
|
||||
[csslint-npm-image]: https://badge.fury.io/js/gulp-csslint.png
|
||||
***
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
# Testing
|
||||
|
||||
> Testing your plugin is the only way to ensure quality. It brings confidence to your users and makes your life easier.
|
||||
|
||||
[Writing a Plugin](README.md) > Testing
|
||||
|
||||
## Tooling
|
||||
|
||||
Most plugins use [mocha](https://github.com/visionmedia/mocha) and [event-stream](https://github.com/dominictarr/event-stream) to help them test. The following examples will use these tools.
|
||||
|
||||
## Testing plugins for streaming mode
|
||||
|
||||
```js
|
||||
var assert = require('assert');
|
||||
var es = require('event-stream');
|
||||
var gutil = require('gulp-util');
|
||||
var prefixer = require('../index');
|
||||
|
||||
describe('gulp-prefixer', function () {
|
||||
describe('in streaming mode', function () {
|
||||
|
||||
it('should prepend text', function (done) {
|
||||
|
||||
// create the fake file
|
||||
var fakeFile = new gutil.File({
|
||||
contents: es.readArray(['stream', 'with', 'those', 'contents'])
|
||||
});
|
||||
|
||||
// Create a prefixer plugin stream
|
||||
var myPrefixer = prefixer('prependthis');
|
||||
|
||||
// write the fake file to it
|
||||
myPrefixer.write(fakeFile);
|
||||
|
||||
// wait for the file to come back out
|
||||
myPrefixer.once('data', function (file) {
|
||||
// make sure it came out the same way it went in
|
||||
assert(file.isStream());
|
||||
|
||||
// buffer the contents to make sure it got prepended to
|
||||
file.contents.pipe(es.wait(function (err, data) {
|
||||
// check the contents
|
||||
assert.equal(data, 'prependthistostreamwiththosecontents');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Testing plugins for buffer mode
|
||||
|
||||
```js
|
||||
var assert = require('assert');
|
||||
var es = require('event-stream');
|
||||
var gutil = require('gulp-util');
|
||||
var prefixer = require('../index');
|
||||
|
||||
describe('gulp-prefixer', function () {
|
||||
describe('in buffer mode', function () {
|
||||
|
||||
it('should prepend text', function (done) {
|
||||
|
||||
// create the fake file
|
||||
var fakeFile = new gutil.File({
|
||||
contents: new Buffer('abufferwiththiscontent')
|
||||
});
|
||||
|
||||
// Create a prefixer plugin stream
|
||||
var myPrefixer = prefixer('prependthis');
|
||||
|
||||
// write the fake file to it
|
||||
myPrefixer.write(fakeFile);
|
||||
|
||||
// wait for the file to come back out
|
||||
myPrefixer.once('data', function (file) {
|
||||
// make sure it came out the same way it went in
|
||||
assert(file.isBuffer());
|
||||
|
||||
// check the contents
|
||||
assert.equal(file.contents.toString('utf8'), 'prependthisabufferwiththiscontent');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Sample tests
|
||||
* [gulp-cat](https://github.com/ben-eb/gulp-cat/blob/master/test.js)
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
# Using buffers
|
||||
|
||||
> Here is some information on creating gulp plugin that manipulates buffers.
|
||||
|
||||
[Writing a Plugin](README.md) > Using buffers
|
||||
|
||||
## Using buffers
|
||||
If your plugin is relying on a buffer based library, you will probably choose to base your plugin around file.contents as a buffer. Let's implement a plugin prepending some text to files:
|
||||
|
||||
```js
|
||||
var through = require('through2');
|
||||
var gutil = require('gulp-util');
|
||||
var PluginError = gutil.PluginError;
|
||||
|
||||
// Consts
|
||||
const PLUGIN_NAME = 'gulp-prefixer';
|
||||
|
||||
// Plugin level function (dealing with files)
|
||||
function gulpPrefixer(prefixText) {
|
||||
|
||||
if (!prefixText) {
|
||||
throw new PluginError(PLUGIN_NAME, "Missing prefix text!");
|
||||
}
|
||||
prefixText = new Buffer(prefixText); // allocate ahead of time
|
||||
|
||||
// Creating a stream through which each file will pass
|
||||
var stream = through.obj(function (file, enc, callback) {
|
||||
if (file.isNull()) {
|
||||
this.push(file); // Do nothing if no contents
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (file.isBuffer()) {
|
||||
file.contents = Buffer.concat([prefixText, file.contents]);
|
||||
this.push(file);
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (file.isStream()) {
|
||||
this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!'));
|
||||
return callback();
|
||||
}
|
||||
});
|
||||
|
||||
// returning the file stream
|
||||
return stream;
|
||||
};
|
||||
|
||||
// Exporting the plugin main function
|
||||
module.exports = gulpPrefixer;
|
||||
```
|
||||
The above plugin can be used like this:
|
||||
|
||||
```js
|
||||
var gulp = require('gulp');
|
||||
var gulpPrefixer = require('gulp-prefixer');
|
||||
|
||||
gulp.src('files/**/*.js')
|
||||
.pipe(gulpPrefixer('prepended string'))
|
||||
.pipe(gulp.dest('/modified-files/'));
|
||||
```
|
||||
|
||||
## Handling streams
|
||||
|
||||
Unfortunately, the above plugin will error when using gulp.src in non-buffered (streaming) mode. You should support streams too if possible. See [Dealing with streams](dealing-with-streams.md) for more information.
|
||||
|
||||
## Some plugins based on buffers
|
||||
|
||||
* [gulp-coffee](https://github.com/wearefractal/gulp-coffee)
|
||||
* [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
|
||||
* [gulp-marked](https://github.com/lmtm/gulp-marked)
|
||||
* [gulp-svg2ttf](https://github.com/nfroidure/gulp-svg2ttf)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
var gulp = require('./');
|
||||
var env = require('gulp-util').env;
|
||||
var log = require('gulp-util').log;
|
||||
|
||||
var jshint = require('gulp-jshint');
|
||||
|
||||
var codeFiles = ['**/*.js', '!node_modules/**'];
|
||||
|
||||
gulp.task('lint', function(){
|
||||
log('Linting Files');
|
||||
return gulp.src(codeFiles)
|
||||
.pipe(jshint('.jshintrc'))
|
||||
.pipe(jshint.reporter());
|
||||
});
|
||||
|
||||
gulp.task('watch', function(){
|
||||
log('Watching Files');
|
||||
gulp.watch(codeFiles, ['lint']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['lint', 'watch']);
|
||||
|
||||
var taskToRun = env.dev ? 'default' : 'lint';
|
||||
|
||||
gulp.start(taskToRun);
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var Orchestrator = require('orchestrator');
|
||||
var gutil = require('gulp-util');
|
||||
var deprecated = require('deprecated');
|
||||
var vfs = require('vinyl-fs');
|
||||
|
||||
function Gulp(){
|
||||
Orchestrator.call(this);
|
||||
}
|
||||
util.inherits(Gulp, Orchestrator);
|
||||
|
||||
Gulp.prototype.task = Gulp.prototype.add;
|
||||
Gulp.prototype.run = function(){
|
||||
// run() is deprecated as of 3.5 and will be removed in 4.0
|
||||
// use task dependencies instead
|
||||
|
||||
// impose our opinion of "default" tasks onto orchestrator
|
||||
var tasks = arguments.length ? arguments : ['default'];
|
||||
|
||||
this.start.apply(this, tasks);
|
||||
};
|
||||
|
||||
Gulp.prototype.src = vfs.src;
|
||||
Gulp.prototype.dest = vfs.dest;
|
||||
Gulp.prototype.watch = function (glob, opt, fn) {
|
||||
if (!fn) {
|
||||
fn = opt;
|
||||
opt = null;
|
||||
}
|
||||
|
||||
// array of tasks given
|
||||
if (Array.isArray(fn)) {
|
||||
return vfs.watch(glob, opt, function(){
|
||||
this.start.apply(this, fn);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
return vfs.watch(glob, opt, fn);
|
||||
};
|
||||
|
||||
// let people use this class from our instance
|
||||
Gulp.prototype.Gulp = Gulp;
|
||||
|
||||
// deprecations
|
||||
deprecated.field('gulp.env has been deprecated. Use gulp-util.env or your own CLI parser instead.', console.log, Gulp.prototype, 'env', gutil.env);
|
||||
Gulp.prototype.run = deprecated.method('gulp.run() has been deprecated. Use task dependencies or gulp.watch task triggering instead.', console.log, Gulp.prototype.run);
|
||||
|
||||
var inst = new Gulp();
|
||||
module.exports = inst;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(name) {
|
||||
if (typeof name !== 'string') throw new Error('Missing completion type');
|
||||
var file = path.join(__dirname, '../completion', name);
|
||||
try {
|
||||
console.log(fs.readFileSync(file, 'utf8'));
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
console.log('echo "Specified gulp shell auto-completion rules for \''+name+'\' not found"');
|
||||
process.exit(5);
|
||||
}
|
||||
};
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(tasks) {
|
||||
return Object.keys(tasks).reduce(function(prev, task) {
|
||||
prev.nodes.push({
|
||||
label: task,
|
||||
nodes: tasks[task].dep
|
||||
});
|
||||
return prev;
|
||||
}, {nodes: []});
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
../semver/bin/semver
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.4
|
||||
- 0.6
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
archy
|
||||
=====
|
||||
|
||||
Render nested hierarchies `npm ls` style with unicode pipes.
|
||||
|
||||
[](http://travis-ci.org/substack/node-archy)
|
||||
|
||||
example
|
||||
=======
|
||||
|
||||
``` js
|
||||
var archy = require('archy');
|
||||
var s = archy({
|
||||
label : 'beep',
|
||||
nodes : [
|
||||
'ity',
|
||||
{
|
||||
label : 'boop',
|
||||
nodes : [
|
||||
{
|
||||
label : 'o_O',
|
||||
nodes : [
|
||||
{
|
||||
label : 'oh',
|
||||
nodes : [ 'hello', 'puny' ]
|
||||
},
|
||||
'human'
|
||||
]
|
||||
},
|
||||
'party\ntime!'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
console.log(s);
|
||||
```
|
||||
|
||||
output
|
||||
|
||||
```
|
||||
beep
|
||||
├── ity
|
||||
└─┬ boop
|
||||
├─┬ o_O
|
||||
│ ├─┬ oh
|
||||
│ │ ├── hello
|
||||
│ │ └── puny
|
||||
│ └── human
|
||||
└── party
|
||||
time!
|
||||
```
|
||||
|
||||
methods
|
||||
=======
|
||||
|
||||
var archy = require('archy')
|
||||
|
||||
archy(obj, prefix='', opts={})
|
||||
------------------------------
|
||||
|
||||
Return a string representation of `obj` with unicode pipe characters like how
|
||||
`npm ls` looks.
|
||||
|
||||
`obj` should be a tree of nested objects with `'label'` and `'nodes'` fields.
|
||||
`'label'` is a string of text to display at a node level and `'nodes'` is an
|
||||
array of the descendents of the current node.
|
||||
|
||||
If a node is a string, that string will be used as the `'label'` and an empty
|
||||
array of `'nodes'` will be used.
|
||||
|
||||
`prefix` gets prepended to all the lines and is used by the algorithm to
|
||||
recursively update.
|
||||
|
||||
If `'label'` has newlines they will be indented at the present indentation level
|
||||
with the current prefix.
|
||||
|
||||
To disable unicode results in favor of all-ansi output set `opts.unicode` to
|
||||
`false`.
|
||||
|
||||
install
|
||||
=======
|
||||
|
||||
With [npm](http://npmjs.org) do:
|
||||
|
||||
```
|
||||
npm install archy
|
||||
```
|
||||
|
||||
license
|
||||
=======
|
||||
|
||||
MIT/X11
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
var archy = require('../');
|
||||
var s = archy({
|
||||
label : 'beep',
|
||||
nodes : [
|
||||
'ity',
|
||||
{
|
||||
label : 'boop',
|
||||
nodes : [
|
||||
{
|
||||
label : 'o_O',
|
||||
nodes : [
|
||||
{
|
||||
label : 'oh',
|
||||
nodes : [ 'hello', 'puny' ]
|
||||
},
|
||||
'human'
|
||||
]
|
||||
},
|
||||
'party\ntime!'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
console.log(s);
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
var archy = require('../');
|
||||
|
||||
var s = archy({
|
||||
label : 'beep\none\ntwo',
|
||||
nodes : [
|
||||
'ity',
|
||||
{
|
||||
label : 'boop',
|
||||
nodes : [
|
||||
{
|
||||
label : 'o_O\nwheee',
|
||||
nodes : [
|
||||
{
|
||||
label : 'oh',
|
||||
nodes : [ 'hello', 'puny\nmeat' ]
|
||||
},
|
||||
'creature'
|
||||
]
|
||||
},
|
||||
'party\ntime!'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
console.log(s);
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
module.exports = function archy (obj, prefix, opts) {
|
||||
if (prefix === undefined) prefix = '';
|
||||
if (!opts) opts = {};
|
||||
var chr = function (s) {
|
||||
var chars = {
|
||||
'│' : '|',
|
||||
'└' : '`',
|
||||
'├' : '+',
|
||||
'─' : '-',
|
||||
'┬' : '-'
|
||||
};
|
||||
return opts.unicode === false ? chars[s] : s;
|
||||
};
|
||||
|
||||
if (typeof obj === 'string') obj = { label : obj };
|
||||
|
||||
var nodes = obj.nodes || [];
|
||||
var lines = (obj.label || '').split('\n');
|
||||
var splitter = '\n' + prefix + (nodes.length ? chr('│') : ' ') + ' ';
|
||||
|
||||
return prefix
|
||||
+ lines.join(splitter) + '\n'
|
||||
+ nodes.map(function (node, ix) {
|
||||
var last = ix === nodes.length - 1;
|
||||
var more = node.nodes && node.nodes.length;
|
||||
var prefix_ = prefix + (last ? ' ' : chr('│')) + ' ';
|
||||
|
||||
return prefix
|
||||
+ (last ? chr('└') : chr('├')) + chr('─')
|
||||
+ (more ? chr('┬') : chr('─')) + ' '
|
||||
+ archy(node, prefix_, opts).slice(prefix.length + 2)
|
||||
;
|
||||
}).join('')
|
||||
;
|
||||
};
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "archy",
|
||||
"version": "0.0.2",
|
||||
"description": "render nested hierarchies `npm ls` style with unicode pipes",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"lib": ".",
|
||||
"example": "example",
|
||||
"test": "test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "~0.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/substack/node-archy.git"
|
||||
},
|
||||
"keywords": [
|
||||
"hierarchy",
|
||||
"npm ls",
|
||||
"unicode",
|
||||
"pretty",
|
||||
"print"
|
||||
],
|
||||
"author": {
|
||||
"name": "James Halliday",
|
||||
"email": "mail@substack.net",
|
||||
"url": "http://substack.net"
|
||||
},
|
||||
"license": "MIT/X11",
|
||||
"engine": {
|
||||
"node": ">=0.4"
|
||||
},
|
||||
"readme": "archy\n=====\n\nRender nested hierarchies `npm ls` style with unicode pipes.\n\n[](http://travis-ci.org/substack/node-archy)\n\nexample\n=======\n\n``` js\nvar archy = require('archy');\nvar s = archy({\n label : 'beep',\n nodes : [\n 'ity',\n {\n label : 'boop',\n nodes : [\n {\n label : 'o_O',\n nodes : [\n {\n label : 'oh',\n nodes : [ 'hello', 'puny' ]\n },\n 'human'\n ]\n },\n 'party\\ntime!'\n ]\n }\n ]\n});\nconsole.log(s);\n```\n\noutput\n\n```\nbeep\n├── ity\n└─┬ boop\n ├─┬ o_O\n │ ├─┬ oh\n │ │ ├── hello\n │ │ └── puny\n │ └── human\n └── party\n time!\n```\n\nmethods\n=======\n\nvar archy = require('archy')\n\narchy(obj, prefix='', opts={})\n------------------------------\n\nReturn a string representation of `obj` with unicode pipe characters like how\n`npm ls` looks.\n\n`obj` should be a tree of nested objects with `'label'` and `'nodes'` fields.\n`'label'` is a string of text to display at a node level and `'nodes'` is an\narray of the descendents of the current node.\n\nIf a node is a string, that string will be used as the `'label'` and an empty\narray of `'nodes'` will be used.\n\n`prefix` gets prepended to all the lines and is used by the algorithm to\nrecursively update.\n\nIf `'label'` has newlines they will be indented at the present indentation level\nwith the current prefix.\n\nTo disable unicode results in favor of all-ansi output set `opts.unicode` to\n`false`.\n\ninstall\n=======\n\nWith [npm](http://npmjs.org) do:\n\n```\nnpm install archy\n```\n\nlicense\n=======\n\nMIT/X11\n",
|
||||
"readmeFilename": "README.markdown",
|
||||
"bugs": {
|
||||
"url": "https://github.com/substack/node-archy/issues"
|
||||
},
|
||||
"homepage": "https://github.com/substack/node-archy",
|
||||
"_id": "archy@0.0.2",
|
||||
"dist": {
|
||||
"shasum": "309338c61a5ef6d8249e772a14bfea549b8382dc"
|
||||
},
|
||||
"_from": "archy@~0.0.2",
|
||||
"_resolved": "https://registry.npmjs.org/archy/-/archy-0.0.2.tgz"
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
var test = require('tap').test;
|
||||
var archy = require('../');
|
||||
|
||||
test('beep', function (t) {
|
||||
var s = archy({
|
||||
label : 'beep',
|
||||
nodes : [
|
||||
'ity',
|
||||
{
|
||||
label : 'boop',
|
||||
nodes : [
|
||||
{
|
||||
label : 'o_O',
|
||||
nodes : [
|
||||
{
|
||||
label : 'oh',
|
||||
nodes : [ 'hello', 'puny' ]
|
||||
},
|
||||
'human'
|
||||
]
|
||||
},
|
||||
'party!'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(s, [
|
||||
'beep',
|
||||
'├── ity',
|
||||
'└─┬ boop',
|
||||
' ├─┬ o_O',
|
||||
' │ ├─┬ oh',
|
||||
' │ │ ├── hello',
|
||||
' │ │ └── puny',
|
||||
' │ └── human',
|
||||
' └── party!',
|
||||
''
|
||||
].join('\n'));
|
||||
t.end();
|
||||
});
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
var test = require('tap').test;
|
||||
var archy = require('../');
|
||||
|
||||
test('multi-line', function (t) {
|
||||
var s = archy({
|
||||
label : 'beep\none\ntwo',
|
||||
nodes : [
|
||||
'ity',
|
||||
{
|
||||
label : 'boop',
|
||||
nodes : [
|
||||
{
|
||||
label : 'o_O\nwheee',
|
||||
nodes : [
|
||||
{
|
||||
label : 'oh',
|
||||
nodes : [ 'hello', 'puny\nmeat' ]
|
||||
},
|
||||
'creature'
|
||||
]
|
||||
},
|
||||
'party\ntime!'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
t.equal(s, [
|
||||
'beep',
|
||||
'│ one',
|
||||
'│ two',
|
||||
'├── ity',
|
||||
'└─┬ boop',
|
||||
' ├─┬ o_O',
|
||||
' │ │ wheee',
|
||||
' │ ├─┬ oh',
|
||||
' │ │ ├── hello',
|
||||
' │ │ └── puny',
|
||||
' │ │ meat',
|
||||
' │ └── creature',
|
||||
' └── party',
|
||||
' time!',
|
||||
''
|
||||
].join('\n'));
|
||||
t.end();
|
||||
});
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
var test = require('tap').test;
|
||||
var archy = require('../');
|
||||
|
||||
test('beep', function (t) {
|
||||
var s = archy({
|
||||
label : 'beep',
|
||||
nodes : [
|
||||
'ity',
|
||||
{
|
||||
label : 'boop',
|
||||
nodes : [
|
||||
{
|
||||
label : 'o_O',
|
||||
nodes : [
|
||||
{
|
||||
label : 'oh',
|
||||
nodes : [ 'hello', 'puny' ]
|
||||
},
|
||||
'human'
|
||||
]
|
||||
},
|
||||
'party!'
|
||||
]
|
||||
}
|
||||
]
|
||||
}, '', { unicode : false });
|
||||
t.equal(s, [
|
||||
'beep',
|
||||
'+-- ity',
|
||||
'`-- boop',
|
||||
' +-- o_O',
|
||||
' | +-- oh',
|
||||
' | | +-- hello',
|
||||
' | | `-- puny',
|
||||
' | `-- human',
|
||||
' `-- party!',
|
||||
''
|
||||
].join('\n'));
|
||||
t.end();
|
||||
});
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
.DS_Store
|
||||
*.log
|
||||
node_modules
|
||||
build
|
||||
*.node
|
||||
components
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.9"
|
||||
- "0.10"
|
||||
after_script:
|
||||
- npm run coveralls
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 Fractal <contact@wearefractal.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# deprecated [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Dependency Status][david-image]][david-url]
|
||||
|
||||
|
||||
## Information
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Package</td><td>deprecated</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>Tool for deprecating things</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node Version</td>
|
||||
<td>>= 0.9</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
var oldfn = function(a,b) {
|
||||
return a+b;
|
||||
};
|
||||
|
||||
// returns a new wrapper function that logs the deprecated function once
|
||||
var somefn = deprecated('dont use this anymore', console.log, oldfn);
|
||||
|
||||
var someobj = {};
|
||||
|
||||
// set up a getter/set for field that logs deprecated message once
|
||||
deprecated('dont use this anymore', console.log, someobj, 'a', 123);
|
||||
|
||||
console.log(someobj.a); // 123
|
||||
```
|
||||
|
||||
[npm-url]: https://npmjs.org/package/deprecated
|
||||
[npm-image]: https://badge.fury.io/js/deprecated.png
|
||||
|
||||
[travis-url]: https://travis-ci.org/wearefractal/deprecated
|
||||
[travis-image]: https://travis-ci.org/wearefractal/deprecated.png?branch=master
|
||||
|
||||
[coveralls-url]: https://coveralls.io/r/wearefractal/deprecated
|
||||
[coveralls-image]: https://coveralls.io/repos/wearefractal/deprecated/badge.png
|
||||
|
||||
[depstat-url]: https://david-dm.org/wearefractal/deprecated
|
||||
[depstat-image]: https://david-dm.org/wearefractal/deprecated.png
|
||||
|
||||
[david-url]: https://david-dm.org/wearefractal/deprecated
|
||||
[david-image]: https://david-dm.org/wearefractal/deprecated.png?theme=shields.io
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
var deprecated = {
|
||||
method: function(msg, log, fn) {
|
||||
var called = false;
|
||||
return function(){
|
||||
if (!called) {
|
||||
called = true;
|
||||
log(msg);
|
||||
}
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
},
|
||||
|
||||
field: function(msg, log, parent, field, val) {
|
||||
var called = false;
|
||||
var getter = function(){
|
||||
if (!called) {
|
||||
called = true;
|
||||
log(msg);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
var setter = function(v) {
|
||||
if (!called) {
|
||||
called = true;
|
||||
log(msg);
|
||||
}
|
||||
val = v;
|
||||
return v;
|
||||
};
|
||||
Object.defineProperty(parent, field, {
|
||||
get: getter,
|
||||
set: setter,
|
||||
enumerable: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = deprecated;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "deprecated",
|
||||
"description": "Tool for deprecating things",
|
||||
"version": "0.0.1",
|
||||
"homepage": "http://github.com/wearefractal/deprecated",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/wearefractal/deprecated.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Fractal",
|
||||
"email": "contact@wearefractal.com",
|
||||
"url": "http://wearefractal.com/"
|
||||
},
|
||||
"main": "./index.js",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.17.0",
|
||||
"should": "~3.1.0",
|
||||
"mocha-lcov-reporter": "~0.0.1",
|
||||
"coveralls": "~2.6.1",
|
||||
"istanbul": "~0.2.3",
|
||||
"rimraf": "~2.2.5",
|
||||
"jshint": "~2.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --reporter spec && jshint",
|
||||
"coveralls": "istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.9"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/wearefractal/deprecated/raw/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"readme": "# deprecated [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Dependency Status][david-image]][david-url]\n\n\n## Information\n\n<table>\n<tr> \n<td>Package</td><td>deprecated</td>\n</tr>\n<tr>\n<td>Description</td>\n<td>Tool for deprecating things</td>\n</tr>\n<tr>\n<td>Node Version</td>\n<td>>= 0.9</td>\n</tr>\n</table>\n\n## Usage\n\n```javascript\nvar oldfn = function(a,b) {\n return a+b;\n};\n\n// returns a new wrapper function that logs the deprecated function once\nvar somefn = deprecated('dont use this anymore', console.log, oldfn);\n\nvar someobj = {};\n\n// set up a getter/set for field that logs deprecated message once\ndeprecated('dont use this anymore', console.log, someobj, 'a', 123);\n\nconsole.log(someobj.a); // 123\n```\n\n[npm-url]: https://npmjs.org/package/deprecated\n[npm-image]: https://badge.fury.io/js/deprecated.png\n\n[travis-url]: https://travis-ci.org/wearefractal/deprecated\n[travis-image]: https://travis-ci.org/wearefractal/deprecated.png?branch=master\n\n[coveralls-url]: https://coveralls.io/r/wearefractal/deprecated\n[coveralls-image]: https://coveralls.io/repos/wearefractal/deprecated/badge.png\n\n[depstat-url]: https://david-dm.org/wearefractal/deprecated\n[depstat-image]: https://david-dm.org/wearefractal/deprecated.png\n\n[david-url]: https://david-dm.org/wearefractal/deprecated\n[david-image]: https://david-dm.org/wearefractal/deprecated.png?theme=shields.io",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wearefractal/deprecated/issues"
|
||||
},
|
||||
"_id": "deprecated@0.0.1",
|
||||
"dist": {
|
||||
"shasum": "f9c9af5464afa1e7a971458a8bdef2aa94d5bb19"
|
||||
},
|
||||
"_from": "deprecated@~0.0.1",
|
||||
"_resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz"
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
var deprecated = require('../');
|
||||
var should = require('should');
|
||||
require('mocha');
|
||||
|
||||
describe('field()', function() {
|
||||
it('should return a wrapped function that logs once on get', function(done) {
|
||||
var message = 'testing';
|
||||
var scope = {
|
||||
a: 1
|
||||
};
|
||||
var obj = {};
|
||||
var logged = false;
|
||||
var log = function(msg){
|
||||
msg.should.equal(message);
|
||||
logged.should.equal(false);
|
||||
logged = true;
|
||||
};
|
||||
deprecated.field(message, log, obj, 'a', 123);
|
||||
|
||||
obj.a.should.equal(123);
|
||||
obj.a = 1234;
|
||||
obj.a.should.equal(1234);
|
||||
logged.should.equal(true);
|
||||
done();
|
||||
});
|
||||
it('should return a wrapped function that logs once on set', function(done) {
|
||||
var message = 'testing';
|
||||
var scope = {
|
||||
a: 1
|
||||
};
|
||||
var obj = {};
|
||||
var logged = false;
|
||||
var log = function(msg){
|
||||
msg.should.equal(message);
|
||||
logged.should.equal(false);
|
||||
logged = true;
|
||||
};
|
||||
deprecated.field(message, log, obj, 'a', 123);
|
||||
|
||||
obj.a = 1234;
|
||||
logged.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
var deprecated = require('../');
|
||||
var should = require('should');
|
||||
require('mocha');
|
||||
|
||||
describe('method()', function() {
|
||||
it('should return a wrapped function that logs once', function(done) {
|
||||
var message = 'testing';
|
||||
var scope = {
|
||||
a: 1
|
||||
};
|
||||
var logged = false;
|
||||
var log = function(msg){
|
||||
msg.should.equal(message);
|
||||
logged.should.equal(false);
|
||||
logged = true;
|
||||
};
|
||||
var fn = deprecated.method(message, log, function(one, two){
|
||||
this.should.equal(scope);
|
||||
one.should.equal(1);
|
||||
two.should.equal(2);
|
||||
return one+two;
|
||||
});
|
||||
|
||||
fn.bind(scope)(1,2).should.equal(3);
|
||||
fn.bind(scope)(1,2).should.equal(3);
|
||||
fn.bind(scope)(1,2).should.equal(3);
|
||||
fn.bind(scope)(1,2).should.equal(3);
|
||||
|
||||
logged.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"loopfunc": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"sub": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"boss": true,
|
||||
"eqnull": true,
|
||||
"node": true,
|
||||
"es5": true
|
||||
}
|
||||
Whitespace-only changes.
+25
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
nodeunit: {
|
||||
files: ['test/**/*_test.js'],
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
all: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js']
|
||||
}
|
||||
});
|
||||
|
||||
// Load plugins.
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-nodeunit');
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('default', ['jshint', 'nodeunit']);
|
||||
|
||||
};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
|
||||
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.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# findup-sync
|
||||
|
||||
Find the first file matching a given pattern in the current directory or the nearest ancestor directory.
|
||||
|
||||
## Getting Started
|
||||
Install the module with: `npm install findup-sync`
|
||||
|
||||
```js
|
||||
var findup = require('findup-sync');
|
||||
|
||||
// Start looking in the CWD.
|
||||
var filepath1 = findup('{a,b}*.txt');
|
||||
|
||||
// Start looking somewhere else, and ignore case (probably a good idea).
|
||||
var filepath2 = findup('{a,b}*.txt', {cwd: '/some/path', nocase: true});
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
findup(patternOrPatterns [, minimatchOptions])
|
||||
```
|
||||
|
||||
### patternOrPatterns
|
||||
Type: `String` or `Array`
|
||||
Default: none
|
||||
|
||||
One or more wildcard glob patterns. Or just filenames.
|
||||
|
||||
### minimatchOptions
|
||||
Type: `Object`
|
||||
Default: `{}`
|
||||
|
||||
Options to be passed to [minimatch](https://github.com/isaacs/minimatch).
|
||||
|
||||
Note that if you want to start in a different directory than the current working directory, specify a `cwd` property here.
|
||||
|
||||
## Contributing
|
||||
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).
|
||||
|
||||
## Release History
|
||||
2013-03-08 - v0.1.2 - Updated dependencies. Fixed a Node 0.9.x bug. Updated unit tests to work cross-platform.
|
||||
2012-11-15 - v0.1.1 - Now works without an options object.
|
||||
2012-11-01 - v0.1.0 - Initial release.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* findup-sync
|
||||
* https://github.com/cowboy/node-findup-sync
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Nodejs libs.
|
||||
var path = require('path');
|
||||
|
||||
// External libs.
|
||||
var glob = require('glob');
|
||||
var _ = require('lodash');
|
||||
|
||||
// Search for a filename in the given directory or all parent directories.
|
||||
module.exports = function(patterns, options) {
|
||||
// Normalize patterns to an array.
|
||||
if (!Array.isArray(patterns)) { patterns = [patterns]; }
|
||||
// Create globOptions so that it can be modified without mutating the
|
||||
// original object.
|
||||
var globOptions = Object.create(options || {});
|
||||
globOptions.maxDepth = 1;
|
||||
globOptions.cwd = path.resolve(globOptions.cwd || '.');
|
||||
|
||||
var files, lastpath;
|
||||
do {
|
||||
// Search for files matching patterns.
|
||||
files = _(patterns).map(function(pattern) {
|
||||
return glob.sync(pattern, globOptions);
|
||||
}).flatten().uniq().value();
|
||||
// Return file if found.
|
||||
if (files.length > 0) {
|
||||
return path.resolve(path.join(globOptions.cwd, files[0]));
|
||||
}
|
||||
// Go up a directory.
|
||||
lastpath = globOptions.cwd;
|
||||
globOptions.cwd = path.resolve(globOptions.cwd, '..');
|
||||
// If parentpath is the same as basedir, we can't go any higher.
|
||||
} while (globOptions.cwd !== lastpath);
|
||||
|
||||
// No files were found!
|
||||
return null;
|
||||
};
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
.*.swp
|
||||
test/a/
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) Isaac Z. Schlueter ("Author")
|
||||
All rights reserved.
|
||||
|
||||
The BSD License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
# Glob
|
||||
|
||||
This is a glob implementation in JavaScript. It uses the `minimatch`
|
||||
library to do its matching.
|
||||
|
||||
## Attention: node-glob users!
|
||||
|
||||
The API has changed dramatically between 2.x and 3.x. This library is
|
||||
now 100% JavaScript, and the integer flags have been replaced with an
|
||||
options object.
|
||||
|
||||
Also, there's an event emitter class, proper tests, and all the other
|
||||
things you've come to expect from node modules.
|
||||
|
||||
And best of all, no compilation!
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
var glob = require("glob")
|
||||
|
||||
// options is optional
|
||||
glob("**/*.js", options, function (er, files) {
|
||||
// files is an array of filenames.
|
||||
// If the `nonull` option is set, and nothing
|
||||
// was found, then files is ["**/*.js"]
|
||||
// er is an error object or null.
|
||||
})
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
Please see the [minimatch
|
||||
documentation](https://github.com/isaacs/minimatch) for more details.
|
||||
|
||||
Supports these glob features:
|
||||
|
||||
* Brace Expansion
|
||||
* Extended glob matching
|
||||
* "Globstar" `**` matching
|
||||
|
||||
See:
|
||||
|
||||
* `man sh`
|
||||
* `man bash`
|
||||
* `man 3 fnmatch`
|
||||
* `man 5 gitignore`
|
||||
* [minimatch documentation](https://github.com/isaacs/minimatch)
|
||||
|
||||
## glob(pattern, [options], cb)
|
||||
|
||||
* `pattern` {String} Pattern to be matched
|
||||
* `options` {Object}
|
||||
* `cb` {Function}
|
||||
* `err` {Error | null}
|
||||
* `matches` {Array<String>} filenames found matching the pattern
|
||||
|
||||
Perform an asynchronous glob search.
|
||||
|
||||
## glob.sync(pattern, [options]
|
||||
|
||||
* `pattern` {String} Pattern to be matched
|
||||
* `options` {Object}
|
||||
* return: {Array<String>} filenames found matching the pattern
|
||||
|
||||
Perform a synchronous glob search.
|
||||
|
||||
## Class: glob.Glob
|
||||
|
||||
Create a Glob object by instanting the `glob.Glob` class.
|
||||
|
||||
```javascript
|
||||
var Glob = require("glob").Glob
|
||||
var mg = new Glob(pattern, options, cb)
|
||||
```
|
||||
|
||||
It's an EventEmitter, and starts walking the filesystem to find matches
|
||||
immediately.
|
||||
|
||||
### new glob.Glob(pattern, [options], [cb])
|
||||
|
||||
* `pattern` {String} pattern to search for
|
||||
* `options` {Object}
|
||||
* `cb` {Function} Called when an error occurs, or matches are found
|
||||
* `err` {Error | null}
|
||||
* `matches` {Array<String>} filenames found matching the pattern
|
||||
|
||||
Note that if the `sync` flag is set in the options, then matches will
|
||||
be immediately available on the `g.found` member.
|
||||
|
||||
### Properties
|
||||
|
||||
* `minimatch` The minimatch object that the glob uses.
|
||||
* `options` The options object passed in.
|
||||
* `error` The error encountered. When an error is encountered, the
|
||||
glob object is in an undefined state, and should be discarded.
|
||||
* `aborted` Boolean which is set to true when calling `abort()`. There
|
||||
is no way at this time to continue a glob search after aborting, but
|
||||
you can re-use the statCache to avoid having to duplicate syscalls.
|
||||
|
||||
### Events
|
||||
|
||||
* `end` When the matching is finished, this is emitted with all the
|
||||
matches found. If the `nonull` option is set, and no match was found,
|
||||
then the `matches` list contains the original pattern. The matches
|
||||
are sorted, unless the `nosort` flag is set.
|
||||
* `match` Every time a match is found, this is emitted with the matched.
|
||||
* `error` Emitted when an unexpected error is encountered, or whenever
|
||||
any fs error occurs if `options.strict` is set.
|
||||
* `abort` When `abort()` is called, this event is raised.
|
||||
|
||||
### Methods
|
||||
|
||||
* `abort` Stop the search.
|
||||
|
||||
### Options
|
||||
|
||||
All the options that can be passed to Minimatch can also be passed to
|
||||
Glob to change pattern matching behavior. Also, some have been added,
|
||||
or have glob-specific ramifications.
|
||||
|
||||
All options are false by default, unless otherwise noted.
|
||||
|
||||
All options are added to the glob object, as well.
|
||||
|
||||
* `cwd` The current working directory in which to search. Defaults
|
||||
to `process.cwd()`.
|
||||
* `root` The place where patterns starting with `/` will be mounted
|
||||
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
|
||||
systems, and `C:\` or some such on Windows.)
|
||||
* `nomount` By default, a pattern starting with a forward-slash will be
|
||||
"mounted" onto the root setting, so that a valid filesystem path is
|
||||
returned. Set this flag to disable that behavior.
|
||||
* `mark` Add a `/` character to directory matches. Note that this
|
||||
requires additional stat calls.
|
||||
* `nosort` Don't sort the results.
|
||||
* `stat` Set to true to stat *all* results. This reduces performance
|
||||
somewhat, and is completely unnecessary, unless `readdir` is presumed
|
||||
to be an untrustworthy indicator of file existence. It will cause
|
||||
ELOOP to be triggered one level sooner in the case of cyclical
|
||||
symbolic links.
|
||||
* `silent` When an unusual error is encountered
|
||||
when attempting to read a directory, a warning will be printed to
|
||||
stderr. Set the `silent` option to true to suppress these warnings.
|
||||
* `strict` When an unusual error is encountered
|
||||
when attempting to read a directory, the process will just continue on
|
||||
in search of other matches. Set the `strict` option to raise an error
|
||||
in these cases.
|
||||
* `statCache` A cache of results of filesystem information, to prevent
|
||||
unnecessary stat calls. While it should not normally be necessary to
|
||||
set this, you may pass the statCache from one glob() call to the
|
||||
options object of another, if you know that the filesystem will not
|
||||
change between calls. (See "Race Conditions" below.)
|
||||
* `sync` Perform a synchronous glob search.
|
||||
* `nounique` In some cases, brace-expanded patterns can result in the
|
||||
same file showing up multiple times in the result set. By default,
|
||||
this implementation prevents duplicates in the result set.
|
||||
Set this flag to disable that behavior.
|
||||
* `nonull` Set to never return an empty set, instead returning a set
|
||||
containing the pattern itself. This is the default in glob(3).
|
||||
* `nocase` Perform a case-insensitive match. Note that case-insensitive
|
||||
filesystems will sometimes result in glob returning results that are
|
||||
case-insensitively matched anyway, since readdir and stat will not
|
||||
raise an error.
|
||||
* `debug` Set to enable debug logging in minimatch and glob.
|
||||
* `globDebug` Set to enable debug logging in glob, but not minimatch.
|
||||
|
||||
## Comparisons to other fnmatch/glob implementations
|
||||
|
||||
While strict compliance with the existing standards is a worthwhile
|
||||
goal, some discrepancies exist between node-glob and other
|
||||
implementations, and are intentional.
|
||||
|
||||
If the pattern starts with a `!` character, then it is negated. Set the
|
||||
`nonegate` flag to suppress this behavior, and treat leading `!`
|
||||
characters normally. This is perhaps relevant if you wish to start the
|
||||
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
|
||||
characters at the start of a pattern will negate the pattern multiple
|
||||
times.
|
||||
|
||||
If a pattern starts with `#`, then it is treated as a comment, and
|
||||
will not match anything. Use `\#` to match a literal `#` at the
|
||||
start of a line, or set the `nocomment` flag to suppress this behavior.
|
||||
|
||||
The double-star character `**` is supported by default, unless the
|
||||
`noglobstar` flag is set. This is supported in the manner of bsdglob
|
||||
and bash 4.1, where `**` only has special significance if it is the only
|
||||
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
|
||||
`a/**b` will not. **Note that this is different from the way that `**` is
|
||||
handled by ruby's `Dir` class.**
|
||||
|
||||
If an escaped pattern has no matches, and the `nonull` flag is set,
|
||||
then glob returns the pattern as-provided, rather than
|
||||
interpreting the character escapes. For example,
|
||||
`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
|
||||
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
|
||||
that it does not resolve escaped pattern characters.
|
||||
|
||||
If brace expansion is not disabled, then it is performed before any
|
||||
other interpretation of the glob pattern. Thus, a pattern like
|
||||
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
|
||||
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
|
||||
checked for validity. Since those two are valid, matching proceeds.
|
||||
|
||||
## Windows
|
||||
|
||||
**Please only use forward-slashes in glob expressions.**
|
||||
|
||||
Though windows uses either `/` or `\` as its path separator, only `/`
|
||||
characters are used by this glob implementation. You must use
|
||||
forward-slashes **only** in glob expressions. Back-slashes will always
|
||||
be interpreted as escape characters, not path separators.
|
||||
|
||||
Results from absolute patterns such as `/foo/*` are mounted onto the
|
||||
root setting using `path.join`. On windows, this will by default result
|
||||
in `/foo/*` matching `C:\foo\bar.txt`.
|
||||
|
||||
## Race Conditions
|
||||
|
||||
Glob searching, by its very nature, is susceptible to race conditions,
|
||||
since it relies on directory walking and such.
|
||||
|
||||
As a result, it is possible that a file that exists when glob looks for
|
||||
it may have been deleted or modified by the time it returns the result.
|
||||
|
||||
As part of its internal implementation, this program caches all stat
|
||||
and readdir calls that it makes, in order to cut down on system
|
||||
overhead. However, this also makes it even more susceptible to races,
|
||||
especially if the statCache object is reused between glob calls.
|
||||
|
||||
Users are thus advised not to use a glob result as a
|
||||
guarantee of filesystem state in the face of rapid changes.
|
||||
For the vast majority of operations, this is never a problem.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
var Glob = require("../").Glob
|
||||
|
||||
var pattern = "test/a/**/[cg]/../[cg]"
|
||||
console.log(pattern)
|
||||
|
||||
var mg = new Glob(pattern, {mark: true, sync:true}, function (er, matches) {
|
||||
console.log("matches", matches)
|
||||
})
|
||||
console.log("after")
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
var Glob = require("../").Glob
|
||||
|
||||
var pattern = "{./*/*,/*,/usr/local/*}"
|
||||
console.log(pattern)
|
||||
|
||||
var mg = new Glob(pattern, {mark: true}, function (er, matches) {
|
||||
console.log("matches", matches)
|
||||
})
|
||||
console.log("after")
|
||||
+643
@@ -0,0 +1,643 @@
|
||||
// Approach:
|
||||
//
|
||||
// 1. Get the minimatch set
|
||||
// 2. For each pattern in the set, PROCESS(pattern)
|
||||
// 3. Store matches per-set, then uniq them
|
||||
//
|
||||
// PROCESS(pattern)
|
||||
// Get the first [n] items from pattern that are all strings
|
||||
// Join these together. This is PREFIX.
|
||||
// If there is no more remaining, then stat(PREFIX) and
|
||||
// add to matches if it succeeds. END.
|
||||
// readdir(PREFIX) as ENTRIES
|
||||
// If fails, END
|
||||
// If pattern[n] is GLOBSTAR
|
||||
// // handle the case where the globstar match is empty
|
||||
// // by pruning it out, and testing the resulting pattern
|
||||
// PROCESS(pattern[0..n] + pattern[n+1 .. $])
|
||||
// // handle other cases.
|
||||
// for ENTRY in ENTRIES (not dotfiles)
|
||||
// // attach globstar + tail onto the entry
|
||||
// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $])
|
||||
//
|
||||
// else // not globstar
|
||||
// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
|
||||
// Test ENTRY against pattern[n]
|
||||
// If fails, continue
|
||||
// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
|
||||
//
|
||||
// Caveat:
|
||||
// Cache all stats and readdirs results to minimize syscall. Since all
|
||||
// we ever care about is existence and directory-ness, we can just keep
|
||||
// `true` for files, and [children,...] for directories, or `false` for
|
||||
// things that don't exist.
|
||||
|
||||
|
||||
|
||||
module.exports = glob
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, minimatch = require("minimatch")
|
||||
, Minimatch = minimatch.Minimatch
|
||||
, inherits = require("inherits")
|
||||
, EE = require("events").EventEmitter
|
||||
, path = require("path")
|
||||
, isDir = {}
|
||||
, assert = require("assert").ok
|
||||
|
||||
function glob (pattern, options, cb) {
|
||||
if (typeof options === "function") cb = options, options = {}
|
||||
if (!options) options = {}
|
||||
|
||||
if (typeof options === "number") {
|
||||
deprecated()
|
||||
return
|
||||
}
|
||||
|
||||
var g = new Glob(pattern, options, cb)
|
||||
return g.sync ? g.found : g
|
||||
}
|
||||
|
||||
glob.fnmatch = deprecated
|
||||
|
||||
function deprecated () {
|
||||
throw new Error("glob's interface has changed. Please see the docs.")
|
||||
}
|
||||
|
||||
glob.sync = globSync
|
||||
function globSync (pattern, options) {
|
||||
if (typeof options === "number") {
|
||||
deprecated()
|
||||
return
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
options.sync = true
|
||||
return glob(pattern, options)
|
||||
}
|
||||
|
||||
|
||||
glob.Glob = Glob
|
||||
inherits(Glob, EE)
|
||||
function Glob (pattern, options, cb) {
|
||||
if (!(this instanceof Glob)) {
|
||||
return new Glob(pattern, options, cb)
|
||||
}
|
||||
|
||||
if (typeof cb === "function") {
|
||||
this.on("error", cb)
|
||||
this.on("end", function (matches) {
|
||||
cb(null, matches)
|
||||
})
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
|
||||
this.EOF = {}
|
||||
this._emitQueue = []
|
||||
|
||||
this.maxDepth = options.maxDepth || 1000
|
||||
this.maxLength = options.maxLength || Infinity
|
||||
this.statCache = options.statCache || {}
|
||||
|
||||
this.changedCwd = false
|
||||
var cwd = process.cwd()
|
||||
if (!options.hasOwnProperty("cwd")) this.cwd = cwd
|
||||
else {
|
||||
this.cwd = options.cwd
|
||||
this.changedCwd = path.resolve(options.cwd) !== cwd
|
||||
}
|
||||
|
||||
this.root = options.root || path.resolve(this.cwd, "/")
|
||||
this.root = path.resolve(this.root)
|
||||
if (process.platform === "win32")
|
||||
this.root = this.root.replace(/\\/g, "/")
|
||||
|
||||
this.nomount = !!options.nomount
|
||||
|
||||
if (!pattern) {
|
||||
throw new Error("must provide pattern")
|
||||
}
|
||||
|
||||
// base-matching: just use globstar for that.
|
||||
if (options.matchBase && -1 === pattern.indexOf("/")) {
|
||||
if (options.noglobstar) {
|
||||
throw new Error("base matching requires globstar")
|
||||
}
|
||||
pattern = "**/" + pattern
|
||||
}
|
||||
|
||||
this.strict = options.strict !== false
|
||||
this.dot = !!options.dot
|
||||
this.mark = !!options.mark
|
||||
this.sync = !!options.sync
|
||||
this.nounique = !!options.nounique
|
||||
this.nonull = !!options.nonull
|
||||
this.nosort = !!options.nosort
|
||||
this.nocase = !!options.nocase
|
||||
this.stat = !!options.stat
|
||||
|
||||
this.debug = !!options.debug || !!options.globDebug
|
||||
if (this.debug)
|
||||
this.log = console.error
|
||||
|
||||
this.silent = !!options.silent
|
||||
|
||||
var mm = this.minimatch = new Minimatch(pattern, options)
|
||||
this.options = mm.options
|
||||
pattern = this.pattern = mm.pattern
|
||||
|
||||
this.error = null
|
||||
this.aborted = false
|
||||
|
||||
EE.call(this)
|
||||
|
||||
// process each pattern in the minimatch set
|
||||
var n = this.minimatch.set.length
|
||||
|
||||
// The matches are stored as {<filename>: true,...} so that
|
||||
// duplicates are automagically pruned.
|
||||
// Later, we do an Object.keys() on these.
|
||||
// Keep them as a list so we can fill in when nonull is set.
|
||||
this.matches = new Array(n)
|
||||
|
||||
this.minimatch.set.forEach(iterator.bind(this))
|
||||
function iterator (pattern, i, set) {
|
||||
this._process(pattern, 0, i, function (er) {
|
||||
if (er) this.emit("error", er)
|
||||
if (-- n <= 0) this._finish()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype.log = function () {}
|
||||
|
||||
Glob.prototype._finish = function () {
|
||||
assert(this instanceof Glob)
|
||||
|
||||
var nou = this.nounique
|
||||
, all = nou ? [] : {}
|
||||
|
||||
for (var i = 0, l = this.matches.length; i < l; i ++) {
|
||||
var matches = this.matches[i]
|
||||
this.log("matches[%d] =", i, matches)
|
||||
// do like the shell, and spit out the literal glob
|
||||
if (!matches) {
|
||||
if (this.nonull) {
|
||||
var literal = this.minimatch.globSet[i]
|
||||
if (nou) all.push(literal)
|
||||
else all[literal] = true
|
||||
}
|
||||
} else {
|
||||
// had matches
|
||||
var m = Object.keys(matches)
|
||||
if (nou) all.push.apply(all, m)
|
||||
else m.forEach(function (m) {
|
||||
all[m] = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!nou) all = Object.keys(all)
|
||||
|
||||
if (!this.nosort) {
|
||||
all = all.sort(this.nocase ? alphasorti : alphasort)
|
||||
}
|
||||
|
||||
if (this.mark) {
|
||||
// at *some* point we statted all of these
|
||||
all = all.map(function (m) {
|
||||
var sc = this.statCache[m]
|
||||
if (!sc)
|
||||
return m
|
||||
var isDir = (Array.isArray(sc) || sc === 2)
|
||||
if (isDir && m.slice(-1) !== "/") {
|
||||
return m + "/"
|
||||
}
|
||||
if (!isDir && m.slice(-1) === "/") {
|
||||
return m.replace(/\/+$/, "")
|
||||
}
|
||||
return m
|
||||
}, this)
|
||||
}
|
||||
|
||||
this.log("emitting end", all)
|
||||
|
||||
this.EOF = this.found = all
|
||||
this.emitMatch(this.EOF)
|
||||
}
|
||||
|
||||
function alphasorti (a, b) {
|
||||
a = a.toLowerCase()
|
||||
b = b.toLowerCase()
|
||||
return alphasort(a, b)
|
||||
}
|
||||
|
||||
function alphasort (a, b) {
|
||||
return a > b ? 1 : a < b ? -1 : 0
|
||||
}
|
||||
|
||||
Glob.prototype.abort = function () {
|
||||
this.aborted = true
|
||||
this.emit("abort")
|
||||
}
|
||||
|
||||
Glob.prototype.pause = function () {
|
||||
if (this.paused) return
|
||||
if (this.sync)
|
||||
this.emit("error", new Error("Can't pause/resume sync glob"))
|
||||
this.paused = true
|
||||
this.emit("pause")
|
||||
}
|
||||
|
||||
Glob.prototype.resume = function () {
|
||||
if (!this.paused) return
|
||||
if (this.sync)
|
||||
this.emit("error", new Error("Can't pause/resume sync glob"))
|
||||
this.paused = false
|
||||
this.emit("resume")
|
||||
this._processEmitQueue()
|
||||
//process.nextTick(this.emit.bind(this, "resume"))
|
||||
}
|
||||
|
||||
Glob.prototype.emitMatch = function (m) {
|
||||
this._emitQueue.push(m)
|
||||
this._processEmitQueue()
|
||||
}
|
||||
|
||||
Glob.prototype._processEmitQueue = function (m) {
|
||||
while (!this._processingEmitQueue &&
|
||||
!this.paused) {
|
||||
this._processingEmitQueue = true
|
||||
var m = this._emitQueue.shift()
|
||||
if (!m) {
|
||||
this._processingEmitQueue = false
|
||||
break
|
||||
}
|
||||
|
||||
this.log('emit!', m === this.EOF ? "end" : "match")
|
||||
|
||||
this.emit(m === this.EOF ? "end" : "match", m)
|
||||
this._processingEmitQueue = false
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._process = function (pattern, depth, index, cb_) {
|
||||
assert(this instanceof Glob)
|
||||
|
||||
var cb = function cb (er, res) {
|
||||
assert(this instanceof Glob)
|
||||
if (this.paused) {
|
||||
if (!this._processQueue) {
|
||||
this._processQueue = []
|
||||
this.once("resume", function () {
|
||||
var q = this._processQueue
|
||||
this._processQueue = null
|
||||
q.forEach(function (cb) { cb() })
|
||||
})
|
||||
}
|
||||
this._processQueue.push(cb_.bind(this, er, res))
|
||||
} else {
|
||||
cb_.call(this, er, res)
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
if (this.aborted) return cb()
|
||||
|
||||
if (depth > this.maxDepth) return cb()
|
||||
|
||||
// Get the first [n] parts of pattern that are all strings.
|
||||
var n = 0
|
||||
while (typeof pattern[n] === "string") {
|
||||
n ++
|
||||
}
|
||||
// now n is the index of the first one that is *not* a string.
|
||||
|
||||
// see if there's anything else
|
||||
var prefix
|
||||
switch (n) {
|
||||
// if not, then this is rather simple
|
||||
case pattern.length:
|
||||
prefix = pattern.join("/")
|
||||
this._stat(prefix, function (exists, isDir) {
|
||||
// either it's there, or it isn't.
|
||||
// nothing more to do, either way.
|
||||
if (exists) {
|
||||
if (prefix && isAbsolute(prefix) && !this.nomount) {
|
||||
if (prefix.charAt(0) === "/") {
|
||||
prefix = path.join(this.root, prefix)
|
||||
} else {
|
||||
prefix = path.resolve(this.root, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === "win32")
|
||||
prefix = prefix.replace(/\\/g, "/")
|
||||
|
||||
this.matches[index] = this.matches[index] || {}
|
||||
this.matches[index][prefix] = true
|
||||
this.emitMatch(prefix)
|
||||
}
|
||||
return cb()
|
||||
})
|
||||
return
|
||||
|
||||
case 0:
|
||||
// pattern *starts* with some non-trivial item.
|
||||
// going to readdir(cwd), but not include the prefix in matches.
|
||||
prefix = null
|
||||
break
|
||||
|
||||
default:
|
||||
// pattern has some string bits in the front.
|
||||
// whatever it starts with, whether that's "absolute" like /foo/bar,
|
||||
// or "relative" like "../baz"
|
||||
prefix = pattern.slice(0, n)
|
||||
prefix = prefix.join("/")
|
||||
break
|
||||
}
|
||||
|
||||
// get the list of entries.
|
||||
var read
|
||||
if (prefix === null) read = "."
|
||||
else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {
|
||||
if (!prefix || !isAbsolute(prefix)) {
|
||||
prefix = path.join("/", prefix)
|
||||
}
|
||||
read = prefix = path.resolve(prefix)
|
||||
|
||||
// if (process.platform === "win32")
|
||||
// read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/")
|
||||
|
||||
this.log('absolute: ', prefix, this.root, pattern, read)
|
||||
} else {
|
||||
read = prefix
|
||||
}
|
||||
|
||||
this.log('readdir(%j)', read, this.cwd, this.root)
|
||||
|
||||
return this._readdir(read, function (er, entries) {
|
||||
if (er) {
|
||||
// not a directory!
|
||||
// this means that, whatever else comes after this, it can never match
|
||||
return cb()
|
||||
}
|
||||
|
||||
// globstar is special
|
||||
if (pattern[n] === minimatch.GLOBSTAR) {
|
||||
// test without the globstar, and with every child both below
|
||||
// and replacing the globstar.
|
||||
var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]
|
||||
entries.forEach(function (e) {
|
||||
if (e.charAt(0) === "." && !this.dot) return
|
||||
// instead of the globstar
|
||||
s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))
|
||||
// below the globstar
|
||||
s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
|
||||
}, this)
|
||||
|
||||
// now asyncForEach over this
|
||||
var l = s.length
|
||||
, errState = null
|
||||
s.forEach(function (gsPattern) {
|
||||
this._process(gsPattern, depth + 1, index, function (er) {
|
||||
if (errState) return
|
||||
if (er) return cb(errState = er)
|
||||
if (--l <= 0) return cb()
|
||||
})
|
||||
}, this)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// not a globstar
|
||||
// It will only match dot entries if it starts with a dot, or if
|
||||
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
|
||||
var pn = pattern[n]
|
||||
if (typeof pn === "string") {
|
||||
var found = entries.indexOf(pn) !== -1
|
||||
entries = found ? entries[pn] : []
|
||||
} else {
|
||||
var rawGlob = pattern[n]._glob
|
||||
, dotOk = this.dot || rawGlob.charAt(0) === "."
|
||||
|
||||
entries = entries.filter(function (e) {
|
||||
return (e.charAt(0) !== "." || dotOk) &&
|
||||
(typeof pattern[n] === "string" && e === pattern[n] ||
|
||||
e.match(pattern[n]))
|
||||
})
|
||||
}
|
||||
|
||||
// If n === pattern.length - 1, then there's no need for the extra stat
|
||||
// *unless* the user has specified "mark" or "stat" explicitly.
|
||||
// We know that they exist, since the readdir returned them.
|
||||
if (n === pattern.length - 1 &&
|
||||
!this.mark &&
|
||||
!this.stat) {
|
||||
entries.forEach(function (e) {
|
||||
if (prefix) {
|
||||
if (prefix !== "/") e = prefix + "/" + e
|
||||
else e = prefix + e
|
||||
}
|
||||
if (e.charAt(0) === "/" && !this.nomount) {
|
||||
e = path.join(this.root, e)
|
||||
}
|
||||
|
||||
if (process.platform === "win32")
|
||||
e = e.replace(/\\/g, "/")
|
||||
|
||||
this.matches[index] = this.matches[index] || {}
|
||||
this.matches[index][e] = true
|
||||
this.emitMatch(e)
|
||||
}, this)
|
||||
return cb.call(this)
|
||||
}
|
||||
|
||||
|
||||
// now test all the remaining entries as stand-ins for that part
|
||||
// of the pattern.
|
||||
var l = entries.length
|
||||
, errState = null
|
||||
if (l === 0) return cb() // no matches possible
|
||||
entries.forEach(function (e) {
|
||||
var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))
|
||||
this._process(p, depth + 1, index, function (er) {
|
||||
if (errState) return
|
||||
if (er) return cb(errState = er)
|
||||
if (--l === 0) return cb.call(this)
|
||||
})
|
||||
}, this)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
Glob.prototype._stat = function (f, cb) {
|
||||
assert(this instanceof Glob)
|
||||
var abs = f
|
||||
if (f.charAt(0) === "/") {
|
||||
abs = path.join(this.root, f)
|
||||
} else if (this.changedCwd) {
|
||||
abs = path.resolve(this.cwd, f)
|
||||
}
|
||||
this.log('stat', [this.cwd, f, '=', abs])
|
||||
if (f.length > this.maxLength) {
|
||||
var er = new Error("Path name too long")
|
||||
er.code = "ENAMETOOLONG"
|
||||
er.path = f
|
||||
return this._afterStat(f, abs, cb, er)
|
||||
}
|
||||
|
||||
if (this.statCache.hasOwnProperty(f)) {
|
||||
var exists = this.statCache[f]
|
||||
, isDir = exists && (Array.isArray(exists) || exists === 2)
|
||||
if (this.sync) return cb.call(this, !!exists, isDir)
|
||||
return process.nextTick(cb.bind(this, !!exists, isDir))
|
||||
}
|
||||
|
||||
if (this.sync) {
|
||||
var er, stat
|
||||
try {
|
||||
stat = fs.statSync(abs)
|
||||
} catch (e) {
|
||||
er = e
|
||||
}
|
||||
this._afterStat(f, abs, cb, er, stat)
|
||||
} else {
|
||||
fs.stat(abs, this._afterStat.bind(this, f, abs, cb))
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._afterStat = function (f, abs, cb, er, stat) {
|
||||
var exists
|
||||
assert(this instanceof Glob)
|
||||
|
||||
if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) {
|
||||
this.log("should be ENOTDIR, fake it")
|
||||
|
||||
er = new Error("ENOTDIR, not a directory '" + abs + "'")
|
||||
er.path = abs
|
||||
er.code = "ENOTDIR"
|
||||
stat = null
|
||||
}
|
||||
|
||||
if (er || !stat) {
|
||||
exists = false
|
||||
} else {
|
||||
exists = stat.isDirectory() ? 2 : 1
|
||||
}
|
||||
this.statCache[f] = this.statCache[f] || exists
|
||||
cb.call(this, !!exists, exists === 2)
|
||||
}
|
||||
|
||||
Glob.prototype._readdir = function (f, cb) {
|
||||
assert(this instanceof Glob)
|
||||
var abs = f
|
||||
if (f.charAt(0) === "/") {
|
||||
abs = path.join(this.root, f)
|
||||
} else if (isAbsolute(f)) {
|
||||
abs = f
|
||||
} else if (this.changedCwd) {
|
||||
abs = path.resolve(this.cwd, f)
|
||||
}
|
||||
|
||||
this.log('readdir', [this.cwd, f, abs])
|
||||
if (f.length > this.maxLength) {
|
||||
var er = new Error("Path name too long")
|
||||
er.code = "ENAMETOOLONG"
|
||||
er.path = f
|
||||
return this._afterReaddir(f, abs, cb, er)
|
||||
}
|
||||
|
||||
if (this.statCache.hasOwnProperty(f)) {
|
||||
var c = this.statCache[f]
|
||||
if (Array.isArray(c)) {
|
||||
if (this.sync) return cb.call(this, null, c)
|
||||
return process.nextTick(cb.bind(this, null, c))
|
||||
}
|
||||
|
||||
if (!c || c === 1) {
|
||||
// either ENOENT or ENOTDIR
|
||||
var code = c ? "ENOTDIR" : "ENOENT"
|
||||
, er = new Error((c ? "Not a directory" : "Not found") + ": " + f)
|
||||
er.path = f
|
||||
er.code = code
|
||||
this.log(f, er)
|
||||
if (this.sync) return cb.call(this, er)
|
||||
return process.nextTick(cb.bind(this, er))
|
||||
}
|
||||
|
||||
// at this point, c === 2, meaning it's a dir, but we haven't
|
||||
// had to read it yet, or c === true, meaning it's *something*
|
||||
// but we don't have any idea what. Need to read it, either way.
|
||||
}
|
||||
|
||||
if (this.sync) {
|
||||
var er, entries
|
||||
try {
|
||||
entries = fs.readdirSync(abs)
|
||||
} catch (e) {
|
||||
er = e
|
||||
}
|
||||
return this._afterReaddir(f, abs, cb, er, entries)
|
||||
}
|
||||
|
||||
fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))
|
||||
}
|
||||
|
||||
Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
|
||||
assert(this instanceof Glob)
|
||||
if (entries && !er) {
|
||||
this.statCache[f] = entries
|
||||
// if we haven't asked to stat everything for suresies, then just
|
||||
// assume that everything in there exists, so we can avoid
|
||||
// having to stat it a second time. This also gets us one step
|
||||
// further into ELOOP territory.
|
||||
if (!this.mark && !this.stat) {
|
||||
entries.forEach(function (e) {
|
||||
if (f === "/") e = f + e
|
||||
else e = f + "/" + e
|
||||
this.statCache[e] = true
|
||||
}, this)
|
||||
}
|
||||
|
||||
return cb.call(this, er, entries)
|
||||
}
|
||||
|
||||
// now handle errors, and cache the information
|
||||
if (er) switch (er.code) {
|
||||
case "ENOTDIR": // totally normal. means it *does* exist.
|
||||
this.statCache[f] = 1
|
||||
return cb.call(this, er)
|
||||
case "ENOENT": // not terribly unusual
|
||||
case "ELOOP":
|
||||
case "ENAMETOOLONG":
|
||||
case "UNKNOWN":
|
||||
this.statCache[f] = false
|
||||
return cb.call(this, er)
|
||||
default: // some unusual error. Treat as failure.
|
||||
this.statCache[f] = false
|
||||
if (this.strict) this.emit("error", er)
|
||||
if (!this.silent) console.error("glob error", er)
|
||||
return cb.call(this, er)
|
||||
}
|
||||
}
|
||||
|
||||
var isAbsolute = process.platform === "win32" ? absWin : absUnix
|
||||
|
||||
function absWin (p) {
|
||||
if (absUnix(p)) return true
|
||||
// pull off the device/UNC bit from a windows path.
|
||||
// from node's lib/path.js
|
||||
var splitDeviceRe =
|
||||
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/
|
||||
, result = splitDeviceRe.exec(p)
|
||||
, device = result[1] || ''
|
||||
, isUnc = device && device.charAt(1) !== ':'
|
||||
, isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
|
||||
|
||||
return isAbsolute
|
||||
}
|
||||
|
||||
function absUnix (p) {
|
||||
return p.charAt(0) === "/" || p === ""
|
||||
}
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) Isaac Z. Schlueter ("Author")
|
||||
All rights reserved.
|
||||
|
||||
The BSD License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
# graceful-fs
|
||||
|
||||
graceful-fs functions as a drop-in replacement for the fs module,
|
||||
making various improvements.
|
||||
|
||||
The improvements are meant to normalize behavior across different
|
||||
platforms and environments, and to make filesystem access more
|
||||
resilient to errors.
|
||||
|
||||
## Improvements over fs module
|
||||
|
||||
graceful-fs:
|
||||
|
||||
* keeps track of how many file descriptors are open, and by default
|
||||
limits this to 1024. Any further requests to open a file are put in a
|
||||
queue until new slots become available. If 1024 turns out to be too
|
||||
much, it decreases the limit further.
|
||||
* fixes `lchmod` for Node versions prior to 0.6.2.
|
||||
* implements `fs.lutimes` if possible. Otherwise it becomes a noop.
|
||||
* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or
|
||||
`lchown` if the user isn't root.
|
||||
* makes `lchmod` and `lchown` become noops, if not available.
|
||||
* retries reading a file if `read` results in EAGAIN error.
|
||||
|
||||
On Windows, it retries renaming a file for up to one second if `EACCESS`
|
||||
or `EPERM` error occurs, likely because antivirus software has locked
|
||||
the directory.
|
||||
|
||||
## Configuration
|
||||
|
||||
The maximum number of open file descriptors that graceful-fs manages may
|
||||
be adjusted by setting `fs.MAX_OPEN` to a different number. The default
|
||||
is 1024.
|
||||
node_modules/gulp/node_modules/findup-sync/node_modules/glob/node_modules/graceful-fs/graceful-fs.js
Generated
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
// this keeps a queue of opened file descriptors, and will make
|
||||
// fs operations wait until some have closed before trying to open more.
|
||||
|
||||
var fs = exports = module.exports = {}
|
||||
fs._originalFs = require("fs")
|
||||
|
||||
Object.getOwnPropertyNames(fs._originalFs).forEach(function(prop) {
|
||||
var desc = Object.getOwnPropertyDescriptor(fs._originalFs, prop)
|
||||
Object.defineProperty(fs, prop, desc)
|
||||
})
|
||||
|
||||
var queue = []
|
||||
, constants = require("constants")
|
||||
|
||||
fs._curOpen = 0
|
||||
|
||||
fs.MIN_MAX_OPEN = 64
|
||||
fs.MAX_OPEN = 1024
|
||||
|
||||
// prevent EMFILE errors
|
||||
function OpenReq (path, flags, mode, cb) {
|
||||
this.path = path
|
||||
this.flags = flags
|
||||
this.mode = mode
|
||||
this.cb = cb
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
||||
fs.open = gracefulOpen
|
||||
|
||||
function gracefulOpen (path, flags, mode, cb) {
|
||||
if (typeof mode === "function") cb = mode, mode = null
|
||||
if (typeof cb !== "function") cb = noop
|
||||
|
||||
if (fs._curOpen >= fs.MAX_OPEN) {
|
||||
queue.push(new OpenReq(path, flags, mode, cb))
|
||||
setTimeout(flush)
|
||||
return
|
||||
}
|
||||
open(path, flags, mode, function (er, fd) {
|
||||
if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
|
||||
// that was too many. reduce max, get back in queue.
|
||||
// this should only happen once in a great while, and only
|
||||
// if the ulimit -n is set lower than 1024.
|
||||
fs.MAX_OPEN = fs._curOpen - 1
|
||||
return fs.open(path, flags, mode, cb)
|
||||
}
|
||||
cb(er, fd)
|
||||
})
|
||||
}
|
||||
|
||||
function open (path, flags, mode, cb) {
|
||||
cb = cb || noop
|
||||
fs._curOpen ++
|
||||
fs._originalFs.open.call(fs, path, flags, mode, function (er, fd) {
|
||||
if (er) onclose()
|
||||
cb(er, fd)
|
||||
})
|
||||
}
|
||||
|
||||
fs.openSync = function (path, flags, mode) {
|
||||
var ret
|
||||
ret = fs._originalFs.openSync.call(fs, path, flags, mode)
|
||||
fs._curOpen ++
|
||||
return ret
|
||||
}
|
||||
|
||||
function onclose () {
|
||||
fs._curOpen --
|
||||
flush()
|
||||
}
|
||||
|
||||
function flush () {
|
||||
while (fs._curOpen < fs.MAX_OPEN) {
|
||||
var req = queue.shift()
|
||||
if (!req) return
|
||||
switch (req.constructor.name) {
|
||||
case 'OpenReq':
|
||||
open(req.path, req.flags || "r", req.mode || 0777, req.cb)
|
||||
break
|
||||
case 'ReaddirReq':
|
||||
readdir(req.path, req.cb)
|
||||
break
|
||||
case 'ReadFileReq':
|
||||
readFile(req.path, req.options, req.cb)
|
||||
break
|
||||
case 'WriteFileReq':
|
||||
writeFile(req.path, req.data, req.options, req.cb)
|
||||
break
|
||||
default:
|
||||
throw new Error('Unknown req type: ' + req.constructor.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs.close = function (fd, cb) {
|
||||
cb = cb || noop
|
||||
fs._originalFs.close.call(fs, fd, function (er) {
|
||||
onclose()
|
||||
cb(er)
|
||||
})
|
||||
}
|
||||
|
||||
fs.closeSync = function (fd) {
|
||||
try {
|
||||
return fs._originalFs.closeSync.call(fs, fd)
|
||||
} finally {
|
||||
onclose()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// readdir takes a fd as well.
|
||||
// however, the sync version closes it right away, so
|
||||
// there's no need to wrap.
|
||||
// It would be nice to catch when it throws an EMFILE,
|
||||
// but that's relatively rare anyway.
|
||||
|
||||
fs.readdir = gracefulReaddir
|
||||
|
||||
function gracefulReaddir (path, cb) {
|
||||
if (fs._curOpen >= fs.MAX_OPEN) {
|
||||
queue.push(new ReaddirReq(path, cb))
|
||||
setTimeout(flush)
|
||||
return
|
||||
}
|
||||
|
||||
readdir(path, function (er, files) {
|
||||
if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
|
||||
fs.MAX_OPEN = fs._curOpen - 1
|
||||
return fs.readdir(path, cb)
|
||||
}
|
||||
cb(er, files)
|
||||
})
|
||||
}
|
||||
|
||||
function readdir (path, cb) {
|
||||
cb = cb || noop
|
||||
fs._curOpen ++
|
||||
fs._originalFs.readdir.call(fs, path, function (er, files) {
|
||||
onclose()
|
||||
cb(er, files)
|
||||
})
|
||||
}
|
||||
|
||||
function ReaddirReq (path, cb) {
|
||||
this.path = path
|
||||
this.cb = cb
|
||||
}
|
||||
|
||||
|
||||
fs.readFile = gracefulReadFile
|
||||
|
||||
function gracefulReadFile(path, options, cb) {
|
||||
if (typeof options === "function") cb = options, options = null
|
||||
if (typeof cb !== "function") cb = noop
|
||||
|
||||
if (fs._curOpen >= fs.MAX_OPEN) {
|
||||
queue.push(new ReadFileReq(path, options, cb))
|
||||
setTimeout(flush)
|
||||
return
|
||||
}
|
||||
|
||||
readFile(path, options, function (er, data) {
|
||||
if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
|
||||
fs.MAX_OPEN = fs._curOpen - 1
|
||||
return fs.readFile(path, options, cb)
|
||||
}
|
||||
cb(er, data)
|
||||
})
|
||||
}
|
||||
|
||||
function readFile (path, options, cb) {
|
||||
cb = cb || noop
|
||||
fs._curOpen ++
|
||||
fs._originalFs.readFile.call(fs, path, options, function (er, data) {
|
||||
onclose()
|
||||
cb(er, data)
|
||||
})
|
||||
}
|
||||
|
||||
function ReadFileReq (path, options, cb) {
|
||||
this.path = path
|
||||
this.options = options
|
||||
this.cb = cb
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fs.writeFile = gracefulWriteFile
|
||||
|
||||
function gracefulWriteFile(path, data, options, cb) {
|
||||
if (typeof options === "function") cb = options, options = null
|
||||
if (typeof cb !== "function") cb = noop
|
||||
|
||||
if (fs._curOpen >= fs.MAX_OPEN) {
|
||||
queue.push(new WriteFileReq(path, data, options, cb))
|
||||
setTimeout(flush)
|
||||
return
|
||||
}
|
||||
|
||||
writeFile(path, data, options, function (er) {
|
||||
if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
|
||||
fs.MAX_OPEN = fs._curOpen - 1
|
||||
return fs.writeFile(path, data, options, cb)
|
||||
}
|
||||
cb(er)
|
||||
})
|
||||
}
|
||||
|
||||
function writeFile (path, data, options, cb) {
|
||||
cb = cb || noop
|
||||
fs._curOpen ++
|
||||
fs._originalFs.writeFile.call(fs, path, data, options, function (er) {
|
||||
onclose()
|
||||
cb(er)
|
||||
})
|
||||
}
|
||||
|
||||
function WriteFileReq (path, data, options, cb) {
|
||||
this.path = path
|
||||
this.data = data
|
||||
this.options = options
|
||||
this.cb = cb
|
||||
}
|
||||
|
||||
|
||||
// (re-)implement some things that are known busted or missing.
|
||||
|
||||
var constants = require("constants")
|
||||
|
||||
// lchmod, broken prior to 0.6.2
|
||||
// back-port the fix here.
|
||||
if (constants.hasOwnProperty('O_SYMLINK') &&
|
||||
process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
||||
fs.lchmod = function (path, mode, callback) {
|
||||
callback = callback || noop
|
||||
fs.open( path
|
||||
, constants.O_WRONLY | constants.O_SYMLINK
|
||||
, mode
|
||||
, function (err, fd) {
|
||||
if (err) {
|
||||
callback(err)
|
||||
return
|
||||
}
|
||||
// prefer to return the chmod error, if one occurs,
|
||||
// but still try to close, and report closing errors if they occur.
|
||||
fs.fchmod(fd, mode, function (err) {
|
||||
fs.close(fd, function(err2) {
|
||||
callback(err || err2)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fs.lchmodSync = function (path, mode) {
|
||||
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
|
||||
|
||||
// prefer to return the chmod error, if one occurs,
|
||||
// but still try to close, and report closing errors if they occur.
|
||||
var err, err2
|
||||
try {
|
||||
var ret = fs.fchmodSync(fd, mode)
|
||||
} catch (er) {
|
||||
err = er
|
||||
}
|
||||
try {
|
||||
fs.closeSync(fd)
|
||||
} catch (er) {
|
||||
err2 = er
|
||||
}
|
||||
if (err || err2) throw (err || err2)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// lutimes implementation, or no-op
|
||||
if (!fs.lutimes) {
|
||||
if (constants.hasOwnProperty("O_SYMLINK")) {
|
||||
fs.lutimes = function (path, at, mt, cb) {
|
||||
fs.open(path, constants.O_SYMLINK, function (er, fd) {
|
||||
cb = cb || noop
|
||||
if (er) return cb(er)
|
||||
fs.futimes(fd, at, mt, function (er) {
|
||||
fs.close(fd, function (er2) {
|
||||
return cb(er || er2)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fs.lutimesSync = function (path, at, mt) {
|
||||
var fd = fs.openSync(path, constants.O_SYMLINK)
|
||||
, err
|
||||
, err2
|
||||
, ret
|
||||
|
||||
try {
|
||||
var ret = fs.futimesSync(fd, at, mt)
|
||||
} catch (er) {
|
||||
err = er
|
||||
}
|
||||
try {
|
||||
fs.closeSync(fd)
|
||||
} catch (er) {
|
||||
err2 = er
|
||||
}
|
||||
if (err || err2) throw (err || err2)
|
||||
return ret
|
||||
}
|
||||
|
||||
} else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
|
||||
// maybe utimensat will be bound soonish?
|
||||
fs.lutimes = function (path, at, mt, cb) {
|
||||
fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
|
||||
}
|
||||
|
||||
fs.lutimesSync = function (path, at, mt) {
|
||||
return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
|
||||
}
|
||||
|
||||
} else {
|
||||
fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
|
||||
fs.lutimesSync = function () {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// https://github.com/isaacs/node-graceful-fs/issues/4
|
||||
// Chown should not fail on einval or eperm if non-root.
|
||||
|
||||
fs.chown = chownFix(fs.chown)
|
||||
fs.fchown = chownFix(fs.fchown)
|
||||
fs.lchown = chownFix(fs.lchown)
|
||||
|
||||
fs.chownSync = chownFixSync(fs.chownSync)
|
||||
fs.fchownSync = chownFixSync(fs.fchownSync)
|
||||
fs.lchownSync = chownFixSync(fs.lchownSync)
|
||||
|
||||
function chownFix (orig) {
|
||||
if (!orig) return orig
|
||||
return function (target, uid, gid, cb) {
|
||||
return orig.call(fs, target, uid, gid, function (er, res) {
|
||||
if (chownErOk(er)) er = null
|
||||
cb(er, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function chownFixSync (orig) {
|
||||
if (!orig) return orig
|
||||
return function (target, uid, gid) {
|
||||
try {
|
||||
return orig.call(fs, target, uid, gid)
|
||||
} catch (er) {
|
||||
if (!chownErOk(er)) throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function chownErOk (er) {
|
||||
// if there's no getuid, or if getuid() is something other than 0,
|
||||
// and the error is EINVAL or EPERM, then just ignore it.
|
||||
// This specific case is a silent failure in cp, install, tar,
|
||||
// and most other unix tools that manage permissions.
|
||||
// When running as root, or if other types of errors are encountered,
|
||||
// then it's strict.
|
||||
if (!er || (!process.getuid || process.getuid() !== 0)
|
||||
&& (er.code === "EINVAL" || er.code === "EPERM")) return true
|
||||
}
|
||||
|
||||
|
||||
// if lchmod/lchown do not exist, then make them no-ops
|
||||
if (!fs.lchmod) {
|
||||
fs.lchmod = function (path, mode, cb) {
|
||||
process.nextTick(cb)
|
||||
}
|
||||
fs.lchmodSync = function () {}
|
||||
}
|
||||
if (!fs.lchown) {
|
||||
fs.lchown = function (path, uid, gid, cb) {
|
||||
process.nextTick(cb)
|
||||
}
|
||||
fs.lchownSync = function () {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// on Windows, A/V software can lock the directory, causing this
|
||||
// to fail with an EACCES or EPERM if the directory contains newly
|
||||
// created files. Try again on failure, for up to 1 second.
|
||||
if (process.platform === "win32") {
|
||||
var rename_ = fs.rename
|
||||
fs.rename = function rename (from, to, cb) {
|
||||
var start = Date.now()
|
||||
rename_(from, to, function CB (er) {
|
||||
if (er
|
||||
&& (er.code === "EACCES" || er.code === "EPERM")
|
||||
&& Date.now() - start < 1000) {
|
||||
return rename_(from, to, CB)
|
||||
}
|
||||
cb(er)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if read() returns EAGAIN, then just try it again.
|
||||
var read = fs.read
|
||||
fs.read = function (fd, buffer, offset, length, position, callback_) {
|
||||
var callback
|
||||
if (callback_ && typeof callback_ === 'function') {
|
||||
var eagCounter = 0
|
||||
callback = function (er, _, __) {
|
||||
if (er && er.code === 'EAGAIN' && eagCounter < 10) {
|
||||
eagCounter ++
|
||||
return read.call(fs, fd, buffer, offset, length, position, callback)
|
||||
}
|
||||
callback_.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
return read.call(fs, fd, buffer, offset, length, position, callback)
|
||||
}
|
||||
|
||||
var readSync = fs.readSync
|
||||
fs.readSync = function (fd, buffer, offset, length, position) {
|
||||
var eagCounter = 0
|
||||
while (true) {
|
||||
try {
|
||||
return readSync.call(fs, fd, buffer, offset, length, position)
|
||||
} catch (er) {
|
||||
if (er.code === 'EAGAIN' && eagCounter < 10) {
|
||||
eagCounter ++
|
||||
continue
|
||||
}
|
||||
throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"author": {
|
||||
"name": "Isaac Z. Schlueter",
|
||||
"email": "i@izs.me",
|
||||
"url": "http://blog.izs.me"
|
||||
},
|
||||
"name": "graceful-fs",
|
||||
"description": "A drop-in replacement for fs, making various improvements.",
|
||||
"version": "1.2.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/node-graceful-fs.git"
|
||||
},
|
||||
"main": "graceful-fs.js",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
},
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test/*.js"
|
||||
},
|
||||
"keywords": [
|
||||
"fs",
|
||||
"module",
|
||||
"reading",
|
||||
"retry",
|
||||
"retries",
|
||||
"queue",
|
||||
"error",
|
||||
"errors",
|
||||
"handling",
|
||||
"EMFILE",
|
||||
"EAGAIN",
|
||||
"EINVAL",
|
||||
"EPERM",
|
||||
"EACCESS"
|
||||
],
|
||||
"license": "BSD",
|
||||
"readme": "# graceful-fs\n\ngraceful-fs functions as a drop-in replacement for the fs module,\nmaking various improvements.\n\nThe improvements are meant to normalize behavior across different\nplatforms and environments, and to make filesystem access more\nresilient to errors.\n\n## Improvements over fs module\n\ngraceful-fs:\n\n* keeps track of how many file descriptors are open, and by default\n limits this to 1024. Any further requests to open a file are put in a\n queue until new slots become available. If 1024 turns out to be too\n much, it decreases the limit further.\n* fixes `lchmod` for Node versions prior to 0.6.2.\n* implements `fs.lutimes` if possible. Otherwise it becomes a noop.\n* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or\n `lchown` if the user isn't root.\n* makes `lchmod` and `lchown` become noops, if not available.\n* retries reading a file if `read` results in EAGAIN error.\n\nOn Windows, it retries renaming a file for up to one second if `EACCESS`\nor `EPERM` error occurs, likely because antivirus software has locked\nthe directory.\n\n## Configuration\n\nThe maximum number of open file descriptors that graceful-fs manages may\nbe adjusted by setting `fs.MAX_OPEN` to a different number. The default\nis 1024.\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/node-graceful-fs/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/node-graceful-fs",
|
||||
"_id": "graceful-fs@1.2.3",
|
||||
"_from": "graceful-fs@~1.2.0"
|
||||
}
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
var test = require('tap').test
|
||||
var fs = require('../graceful-fs.js')
|
||||
|
||||
test('graceful fs is not fs', function (t) {
|
||||
t.notEqual(fs, require('fs'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('open an existing file works', function (t) {
|
||||
var start = fs._curOpen
|
||||
var fd = fs.openSync(__filename, 'r')
|
||||
t.equal(fs._curOpen, start + 1)
|
||||
fs.closeSync(fd)
|
||||
t.equal(fs._curOpen, start)
|
||||
fs.open(__filename, 'r', function (er, fd) {
|
||||
if (er) throw er
|
||||
t.equal(fs._curOpen, start + 1)
|
||||
fs.close(fd, function (er) {
|
||||
if (er) throw er
|
||||
t.equal(fs._curOpen, start)
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('open a non-existing file throws', function (t) {
|
||||
var start = fs._curOpen
|
||||
var er
|
||||
try {
|
||||
var fd = fs.openSync('this file does not exist', 'r')
|
||||
} catch (x) {
|
||||
er = x
|
||||
}
|
||||
t.ok(er, 'should throw')
|
||||
t.notOk(fd, 'should not get an fd')
|
||||
t.equal(er.code, 'ENOENT')
|
||||
t.equal(fs._curOpen, start)
|
||||
|
||||
fs.open('neither does this file', 'r', function (er, fd) {
|
||||
t.ok(er, 'should throw')
|
||||
t.notOk(fd, 'should not get an fd')
|
||||
t.equal(er.code, 'ENOENT')
|
||||
t.equal(fs._curOpen, start)
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
node_modules/gulp/node_modules/findup-sync/node_modules/glob/node_modules/graceful-fs/test/ulimit.js
Generated
Vendored
+158
@@ -0,0 +1,158 @@
|
||||
var test = require('tap').test
|
||||
|
||||
// simulated ulimit
|
||||
// this is like graceful-fs, but in reverse
|
||||
var fs_ = require('fs')
|
||||
var fs = require('../graceful-fs.js')
|
||||
var files = fs.readdirSync(__dirname)
|
||||
|
||||
// Ok, no more actual file reading!
|
||||
|
||||
var fds = 0
|
||||
var nextFd = 60
|
||||
var limit = 8
|
||||
fs_.open = function (path, flags, mode, cb) {
|
||||
process.nextTick(function() {
|
||||
++fds
|
||||
if (fds >= limit) {
|
||||
--fds
|
||||
var er = new Error('EMFILE Curses!')
|
||||
er.code = 'EMFILE'
|
||||
er.path = path
|
||||
return cb(er)
|
||||
} else {
|
||||
cb(null, nextFd++)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fs_.openSync = function (path, flags, mode) {
|
||||
if (fds >= limit) {
|
||||
var er = new Error('EMFILE Curses!')
|
||||
er.code = 'EMFILE'
|
||||
er.path = path
|
||||
throw er
|
||||
} else {
|
||||
++fds
|
||||
return nextFd++
|
||||
}
|
||||
}
|
||||
|
||||
fs_.close = function (fd, cb) {
|
||||
process.nextTick(function () {
|
||||
--fds
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
fs_.closeSync = function (fd) {
|
||||
--fds
|
||||
}
|
||||
|
||||
fs_.readdir = function (path, cb) {
|
||||
process.nextTick(function() {
|
||||
if (fds >= limit) {
|
||||
var er = new Error('EMFILE Curses!')
|
||||
er.code = 'EMFILE'
|
||||
er.path = path
|
||||
return cb(er)
|
||||
} else {
|
||||
++fds
|
||||
process.nextTick(function () {
|
||||
--fds
|
||||
cb(null, [__filename, "some-other-file.js"])
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fs_.readdirSync = function (path) {
|
||||
if (fds >= limit) {
|
||||
var er = new Error('EMFILE Curses!')
|
||||
er.code = 'EMFILE'
|
||||
er.path = path
|
||||
throw er
|
||||
} else {
|
||||
return [__filename, "some-other-file.js"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test('open emfile autoreduce', function (t) {
|
||||
fs.MIN_MAX_OPEN = 4
|
||||
t.equal(fs.MAX_OPEN, 1024)
|
||||
|
||||
var max = 12
|
||||
for (var i = 0; i < max; i++) {
|
||||
fs.open(__filename, 'r', next(i))
|
||||
}
|
||||
|
||||
var phase = 0
|
||||
|
||||
var expect =
|
||||
[ [ 0, 60, null, 1024, 4, 12, 1 ],
|
||||
[ 1, 61, null, 1024, 4, 12, 2 ],
|
||||
[ 2, 62, null, 1024, 4, 12, 3 ],
|
||||
[ 3, 63, null, 1024, 4, 12, 4 ],
|
||||
[ 4, 64, null, 1024, 4, 12, 5 ],
|
||||
[ 5, 65, null, 1024, 4, 12, 6 ],
|
||||
[ 6, 66, null, 1024, 4, 12, 7 ],
|
||||
[ 7, 67, null, 6, 4, 5, 1 ],
|
||||
[ 8, 68, null, 6, 4, 5, 2 ],
|
||||
[ 9, 69, null, 6, 4, 5, 3 ],
|
||||
[ 10, 70, null, 6, 4, 5, 4 ],
|
||||
[ 11, 71, null, 6, 4, 5, 5 ] ]
|
||||
|
||||
var actual = []
|
||||
|
||||
function next (i) { return function (er, fd) {
|
||||
if (er)
|
||||
throw er
|
||||
actual.push([i, fd, er, fs.MAX_OPEN, fs.MIN_MAX_OPEN, fs._curOpen, fds])
|
||||
|
||||
if (i === max - 1) {
|
||||
t.same(actual, expect)
|
||||
t.ok(fs.MAX_OPEN < limit)
|
||||
t.end()
|
||||
}
|
||||
|
||||
fs.close(fd)
|
||||
} }
|
||||
})
|
||||
|
||||
test('readdir emfile autoreduce', function (t) {
|
||||
fs.MAX_OPEN = 1024
|
||||
var max = 12
|
||||
for (var i = 0; i < max; i ++) {
|
||||
fs.readdir(__dirname, next(i))
|
||||
}
|
||||
|
||||
var expect =
|
||||
[ [0,[__filename,"some-other-file.js"],null,7,4,7,7],
|
||||
[1,[__filename,"some-other-file.js"],null,7,4,7,6],
|
||||
[2,[__filename,"some-other-file.js"],null,7,4,7,5],
|
||||
[3,[__filename,"some-other-file.js"],null,7,4,7,4],
|
||||
[4,[__filename,"some-other-file.js"],null,7,4,7,3],
|
||||
[5,[__filename,"some-other-file.js"],null,7,4,6,2],
|
||||
[6,[__filename,"some-other-file.js"],null,7,4,5,1],
|
||||
[7,[__filename,"some-other-file.js"],null,7,4,4,0],
|
||||
[8,[__filename,"some-other-file.js"],null,7,4,3,3],
|
||||
[9,[__filename,"some-other-file.js"],null,7,4,2,2],
|
||||
[10,[__filename,"some-other-file.js"],null,7,4,1,1],
|
||||
[11,[__filename,"some-other-file.js"],null,7,4,0,0] ]
|
||||
|
||||
var actual = []
|
||||
|
||||
function next (i) { return function (er, files) {
|
||||
if (er)
|
||||
throw er
|
||||
var line = [i, files, er, fs.MAX_OPEN, fs.MIN_MAX_OPEN, fs._curOpen, fds ]
|
||||
actual.push(line)
|
||||
|
||||
if (i === max - 1) {
|
||||
t.ok(fs.MAX_OPEN < limit)
|
||||
t.same(actual, expect)
|
||||
t.end()
|
||||
}
|
||||
} }
|
||||
})
|
||||
Generated
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
A dead simple way to do inheritance in JS.
|
||||
|
||||
var inherits = require("inherits")
|
||||
|
||||
function Animal () {
|
||||
this.alive = true
|
||||
}
|
||||
Animal.prototype.say = function (what) {
|
||||
console.log(what)
|
||||
}
|
||||
|
||||
inherits(Dog, Animal)
|
||||
function Dog () {
|
||||
Dog.super.apply(this)
|
||||
}
|
||||
Dog.prototype.sniff = function () {
|
||||
this.say("sniff sniff")
|
||||
}
|
||||
Dog.prototype.bark = function () {
|
||||
this.say("woof woof")
|
||||
}
|
||||
|
||||
inherits(Chihuahua, Dog)
|
||||
function Chihuahua () {
|
||||
Chihuahua.super.apply(this)
|
||||
}
|
||||
Chihuahua.prototype.bark = function () {
|
||||
this.say("yip yip")
|
||||
}
|
||||
|
||||
// also works
|
||||
function Cat () {
|
||||
Cat.super.apply(this)
|
||||
}
|
||||
Cat.prototype.hiss = function () {
|
||||
this.say("CHSKKSS!!")
|
||||
}
|
||||
inherits(Cat, Animal, {
|
||||
meow: function () { this.say("miao miao") }
|
||||
})
|
||||
Cat.prototype.purr = function () {
|
||||
this.say("purr purr")
|
||||
}
|
||||
|
||||
|
||||
var c = new Chihuahua
|
||||
assert(c instanceof Chihuahua)
|
||||
assert(c instanceof Dog)
|
||||
assert(c instanceof Animal)
|
||||
|
||||
The actual function is laughably small. 10-lines small.
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
module.exports = inherits
|
||||
|
||||
function inherits (c, p, proto) {
|
||||
proto = proto || {}
|
||||
var e = {}
|
||||
;[c.prototype, proto].forEach(function (s) {
|
||||
Object.getOwnPropertyNames(s).forEach(function (k) {
|
||||
e[k] = Object.getOwnPropertyDescriptor(s, k)
|
||||
})
|
||||
})
|
||||
c.prototype = Object.create(p.prototype, e)
|
||||
c.super = p
|
||||
}
|
||||
|
||||
//function Child () {
|
||||
// Child.super.call(this)
|
||||
// console.error([this
|
||||
// ,this.constructor
|
||||
// ,this.constructor === Child
|
||||
// ,this.constructor.super === Parent
|
||||
// ,Object.getPrototypeOf(this) === Child.prototype
|
||||
// ,Object.getPrototypeOf(Object.getPrototypeOf(this))
|
||||
// === Parent.prototype
|
||||
// ,this instanceof Child
|
||||
// ,this instanceof Parent])
|
||||
//}
|
||||
//function Parent () {}
|
||||
//inherits(Child, Parent)
|
||||
//new Child
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "inherits",
|
||||
"description": "A tiny simple way to do classic inheritance in js",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"inheritance",
|
||||
"class",
|
||||
"klass",
|
||||
"oop",
|
||||
"object-oriented"
|
||||
],
|
||||
"main": "./inherits.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/isaacs/inherits"
|
||||
},
|
||||
"author": {
|
||||
"name": "Isaac Z. Schlueter",
|
||||
"email": "i@izs.me",
|
||||
"url": "http://blog.izs.me/"
|
||||
},
|
||||
"readme": "A dead simple way to do inheritance in JS.\n\n var inherits = require(\"inherits\")\n\n function Animal () {\n this.alive = true\n }\n Animal.prototype.say = function (what) {\n console.log(what)\n }\n\n inherits(Dog, Animal)\n function Dog () {\n Dog.super.apply(this)\n }\n Dog.prototype.sniff = function () {\n this.say(\"sniff sniff\")\n }\n Dog.prototype.bark = function () {\n this.say(\"woof woof\")\n }\n\n inherits(Chihuahua, Dog)\n function Chihuahua () {\n Chihuahua.super.apply(this)\n }\n Chihuahua.prototype.bark = function () {\n this.say(\"yip yip\")\n }\n\n // also works\n function Cat () {\n Cat.super.apply(this)\n }\n Cat.prototype.hiss = function () {\n this.say(\"CHSKKSS!!\")\n }\n inherits(Cat, Animal, {\n meow: function () { this.say(\"miao miao\") }\n })\n Cat.prototype.purr = function () {\n this.say(\"purr purr\")\n }\n\n\n var c = new Chihuahua\n assert(c instanceof Chihuahua)\n assert(c instanceof Dog)\n assert(c instanceof Animal)\n\nThe actual function is laughably small. 10-lines small.\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/inherits/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/inherits",
|
||||
"_id": "inherits@1.0.0",
|
||||
"_from": "inherits@1"
|
||||
}
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
Generated
Vendored
+218
@@ -0,0 +1,218 @@
|
||||
# minimatch
|
||||
|
||||
A minimal matching utility.
|
||||
|
||||
[](http://travis-ci.org/isaacs/minimatch)
|
||||
|
||||
|
||||
This is the matching library used internally by npm.
|
||||
|
||||
Eventually, it will replace the C binding in node-glob.
|
||||
|
||||
It works by converting glob expressions into JavaScript `RegExp`
|
||||
objects.
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
var minimatch = require("minimatch")
|
||||
|
||||
minimatch("bar.foo", "*.foo") // true!
|
||||
minimatch("bar.foo", "*.bar") // false!
|
||||
minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
Supports these glob features:
|
||||
|
||||
* Brace Expansion
|
||||
* Extended glob matching
|
||||
* "Globstar" `**` matching
|
||||
|
||||
See:
|
||||
|
||||
* `man sh`
|
||||
* `man bash`
|
||||
* `man 3 fnmatch`
|
||||
* `man 5 gitignore`
|
||||
|
||||
## Minimatch Class
|
||||
|
||||
Create a minimatch object by instanting the `minimatch.Minimatch` class.
|
||||
|
||||
```javascript
|
||||
var Minimatch = require("minimatch").Minimatch
|
||||
var mm = new Minimatch(pattern, options)
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
* `pattern` The original pattern the minimatch object represents.
|
||||
* `options` The options supplied to the constructor.
|
||||
* `set` A 2-dimensional array of regexp or string expressions.
|
||||
Each row in the
|
||||
array corresponds to a brace-expanded pattern. Each item in the row
|
||||
corresponds to a single path-part. For example, the pattern
|
||||
`{a,b/c}/d` would expand to a set of patterns like:
|
||||
|
||||
[ [ a, d ]
|
||||
, [ b, c, d ] ]
|
||||
|
||||
If a portion of the pattern doesn't have any "magic" in it
|
||||
(that is, it's something like `"foo"` rather than `fo*o?`), then it
|
||||
will be left as a string rather than converted to a regular
|
||||
expression.
|
||||
|
||||
* `regexp` Created by the `makeRe` method. A single regular expression
|
||||
expressing the entire pattern. This is useful in cases where you wish
|
||||
to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
|
||||
* `negate` True if the pattern is negated.
|
||||
* `comment` True if the pattern is a comment.
|
||||
* `empty` True if the pattern is `""`.
|
||||
|
||||
### Methods
|
||||
|
||||
* `makeRe` Generate the `regexp` member if necessary, and return it.
|
||||
Will return `false` if the pattern is invalid.
|
||||
* `match(fname)` Return true if the filename matches the pattern, or
|
||||
false otherwise.
|
||||
* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
|
||||
filename, and match it against a single row in the `regExpSet`. This
|
||||
method is mainly for internal use, but is exposed so that it can be
|
||||
used by a glob-walker that needs to avoid excessive filesystem calls.
|
||||
|
||||
All other methods are internal, and will be called as necessary.
|
||||
|
||||
## Functions
|
||||
|
||||
The top-level exported function has a `cache` property, which is an LRU
|
||||
cache set to store 100 items. So, calling these methods repeatedly
|
||||
with the same pattern and options will use the same Minimatch object,
|
||||
saving the cost of parsing it multiple times.
|
||||
|
||||
### minimatch(path, pattern, options)
|
||||
|
||||
Main export. Tests a path against the pattern using the options.
|
||||
|
||||
```javascript
|
||||
var isJS = minimatch(file, "*.js", { matchBase: true })
|
||||
```
|
||||
|
||||
### minimatch.filter(pattern, options)
|
||||
|
||||
Returns a function that tests its
|
||||
supplied argument, suitable for use with `Array.filter`. Example:
|
||||
|
||||
```javascript
|
||||
var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
|
||||
```
|
||||
|
||||
### minimatch.match(list, pattern, options)
|
||||
|
||||
Match against the list of
|
||||
files, in the style of fnmatch or glob. If nothing is matched, and
|
||||
options.nonull is set, then return a list containing the pattern itself.
|
||||
|
||||
```javascript
|
||||
var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
|
||||
```
|
||||
|
||||
### minimatch.makeRe(pattern, options)
|
||||
|
||||
Make a regular expression object from the pattern.
|
||||
|
||||
## Options
|
||||
|
||||
All options are `false` by default.
|
||||
|
||||
### debug
|
||||
|
||||
Dump a ton of stuff to stderr.
|
||||
|
||||
### nobrace
|
||||
|
||||
Do not expand `{a,b}` and `{1..3}` brace sets.
|
||||
|
||||
### noglobstar
|
||||
|
||||
Disable `**` matching against multiple folder names.
|
||||
|
||||
### dot
|
||||
|
||||
Allow patterns to match filenames starting with a period, even if
|
||||
the pattern does not explicitly have a period in that spot.
|
||||
|
||||
Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
|
||||
is set.
|
||||
|
||||
### noext
|
||||
|
||||
Disable "extglob" style patterns like `+(a|b)`.
|
||||
|
||||
### nocase
|
||||
|
||||
Perform a case-insensitive match.
|
||||
|
||||
### nonull
|
||||
|
||||
When a match is not found by `minimatch.match`, return a list containing
|
||||
the pattern itself. When set, an empty list is returned if there are
|
||||
no matches.
|
||||
|
||||
### matchBase
|
||||
|
||||
If set, then patterns without slashes will be matched
|
||||
against the basename of the path if it contains slashes. For example,
|
||||
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
|
||||
|
||||
### nocomment
|
||||
|
||||
Suppress the behavior of treating `#` at the start of a pattern as a
|
||||
comment.
|
||||
|
||||
### nonegate
|
||||
|
||||
Suppress the behavior of treating a leading `!` character as negation.
|
||||
|
||||
### flipNegate
|
||||
|
||||
Returns from negate expressions the same as if they were not negated.
|
||||
(Ie, true on a hit, false on a miss.)
|
||||
|
||||
|
||||
## Comparisons to other fnmatch/glob implementations
|
||||
|
||||
While strict compliance with the existing standards is a worthwhile
|
||||
goal, some discrepancies exist between minimatch and other
|
||||
implementations, and are intentional.
|
||||
|
||||
If the pattern starts with a `!` character, then it is negated. Set the
|
||||
`nonegate` flag to suppress this behavior, and treat leading `!`
|
||||
characters normally. This is perhaps relevant if you wish to start the
|
||||
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
|
||||
characters at the start of a pattern will negate the pattern multiple
|
||||
times.
|
||||
|
||||
If a pattern starts with `#`, then it is treated as a comment, and
|
||||
will not match anything. Use `\#` to match a literal `#` at the
|
||||
start of a line, or set the `nocomment` flag to suppress this behavior.
|
||||
|
||||
The double-star character `**` is supported by default, unless the
|
||||
`noglobstar` flag is set. This is supported in the manner of bsdglob
|
||||
and bash 4.1, where `**` only has special significance if it is the only
|
||||
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
|
||||
`a/**b` will not.
|
||||
|
||||
If an escaped pattern has no matches, and the `nonull` flag is set,
|
||||
then minimatch.match returns the pattern as-provided, rather than
|
||||
interpreting the character escapes. For example,
|
||||
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
|
||||
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
|
||||
that it does not resolve escaped pattern characters.
|
||||
|
||||
If brace expansion is not disabled, then it is performed before any
|
||||
other interpretation of the glob pattern. Thus, a pattern like
|
||||
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
|
||||
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
|
||||
checked for validity. Since those two are valid, matching proceeds.
|
||||
Generated
Vendored
+1055
File diff suppressed because it is too large.
Load diff
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
# Authors, sorted by whether or not they are me
|
||||
Isaac Z. Schlueter <i@izs.me>
|
||||
Brian Cottingham <spiffytech@gmail.com>
|
||||
Carlos Brito Lage <carlos@carloslage.net>
|
||||
Jesse Dailey <jesse.dailey@gmail.com>
|
||||
Kevin O'Hara <kevinohara80@gmail.com>
|
||||
Marco Rogers <marco.rogers@gmail.com>
|
||||
Mark Cavage <mcavage@gmail.com>
|
||||
Marko Mikulicic <marko.mikulicic@isti.cnr.it>
|
||||
Nathan Rajlich <nathan@tootallnate.net>
|
||||
Satheesh Natesan <snateshan@myspace-inc.com>
|
||||
Trent Mick <trentm@gmail.com>
|
||||
ashleybrener <ashley@starlogik.com>
|
||||
n4kz <n4kz@n4kz.com>
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
# lru cache
|
||||
|
||||
A cache object that deletes the least-recently-used items.
|
||||
|
||||
## Usage:
|
||||
|
||||
```javascript
|
||||
var LRU = require("lru-cache")
|
||||
, options = { max: 500
|
||||
, length: function (n) { return n * 2 }
|
||||
, dispose: function (key, n) { n.close() }
|
||||
, maxAge: 1000 * 60 * 60 }
|
||||
, cache = LRU(options)
|
||||
, otherCache = LRU(50) // sets just the max size
|
||||
|
||||
cache.set("key", "value")
|
||||
cache.get("key") // "value"
|
||||
|
||||
cache.reset() // empty the cache
|
||||
```
|
||||
|
||||
If you put more stuff in it, then items will fall out.
|
||||
|
||||
If you try to put an oversized thing in it, then it'll fall out right
|
||||
away.
|
||||
|
||||
## Options
|
||||
|
||||
* `max` The maximum size of the cache, checked by applying the length
|
||||
function to all values in the cache. Not setting this is kind of
|
||||
silly, since that's the whole purpose of this lib, but it defaults
|
||||
to `Infinity`.
|
||||
* `maxAge` Maximum age in ms. Items are not pro-actively pruned out
|
||||
as they age, but if you try to get an item that is too old, it'll
|
||||
drop it and return undefined instead of giving it to you.
|
||||
* `length` Function that is used to calculate the length of stored
|
||||
items. If you're storing strings or buffers, then you probably want
|
||||
to do something like `function(n){return n.length}`. The default is
|
||||
`function(n){return 1}`, which is fine if you want to store `n`
|
||||
like-sized things.
|
||||
* `dispose` Function that is called on items when they are dropped
|
||||
from the cache. This can be handy if you want to close file
|
||||
descriptors or do other cleanup tasks when items are no longer
|
||||
accessible. Called with `key, value`. It's called *before*
|
||||
actually removing the item from the internal cache, so if you want
|
||||
to immediately put it back in, you'll have to do that in a
|
||||
`nextTick` or `setTimeout` callback or it won't do anything.
|
||||
* `stale` By default, if you set a `maxAge`, it'll only actually pull
|
||||
stale items out of the cache when you `get(key)`. (That is, it's
|
||||
not pre-emptively doing a `setTimeout` or anything.) If you set
|
||||
`stale:true`, it'll return the stale value before deleting it. If
|
||||
you don't set this, then it'll return `undefined` when you try to
|
||||
get a stale entry, as if it had already been deleted.
|
||||
|
||||
## API
|
||||
|
||||
* `set(key, value)`
|
||||
* `get(key) => value`
|
||||
|
||||
Both of these will update the "recently used"-ness of the key.
|
||||
They do what you think.
|
||||
|
||||
* `peek(key)`
|
||||
|
||||
Returns the key value (or `undefined` if not found) without
|
||||
updating the "recently used"-ness of the key.
|
||||
|
||||
(If you find yourself using this a lot, you *might* be using the
|
||||
wrong sort of data structure, but there are some use cases where
|
||||
it's handy.)
|
||||
|
||||
* `del(key)`
|
||||
|
||||
Deletes a key out of the cache.
|
||||
|
||||
* `reset()`
|
||||
|
||||
Clear the cache entirely, throwing away all values.
|
||||
|
||||
* `has(key)`
|
||||
|
||||
Check if a key is in the cache, without updating the recent-ness
|
||||
or deleting it for being stale.
|
||||
|
||||
* `forEach(function(value,key,cache), [thisp])`
|
||||
|
||||
Just like `Array.prototype.forEach`. Iterates over all the keys
|
||||
in the cache, in order of recent-ness. (Ie, more recently used
|
||||
items are iterated over first.)
|
||||
|
||||
* `keys()`
|
||||
|
||||
Return an array of the keys in the cache.
|
||||
|
||||
* `values()`
|
||||
|
||||
Return an array of the values in the cache.
|
||||
Generated
Vendored
+252
@@ -0,0 +1,252 @@
|
||||
;(function () { // closure for web browsers
|
||||
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = LRUCache
|
||||
} else {
|
||||
// just set the global for non-node platforms.
|
||||
this.LRUCache = LRUCache
|
||||
}
|
||||
|
||||
function hOP (obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key)
|
||||
}
|
||||
|
||||
function naiveLength () { return 1 }
|
||||
|
||||
function LRUCache (options) {
|
||||
if (!(this instanceof LRUCache))
|
||||
return new LRUCache(options)
|
||||
|
||||
if (typeof options === 'number')
|
||||
options = { max: options }
|
||||
|
||||
if (!options)
|
||||
options = {}
|
||||
|
||||
this._max = options.max
|
||||
// Kind of weird to have a default max of Infinity, but oh well.
|
||||
if (!this._max || !(typeof this._max === "number") || this._max <= 0 )
|
||||
this._max = Infinity
|
||||
|
||||
this._lengthCalculator = options.length || naiveLength
|
||||
if (typeof this._lengthCalculator !== "function")
|
||||
this._lengthCalculator = naiveLength
|
||||
|
||||
this._allowStale = options.stale || false
|
||||
this._maxAge = options.maxAge || null
|
||||
this._dispose = options.dispose
|
||||
this.reset()
|
||||
}
|
||||
|
||||
// resize the cache when the max changes.
|
||||
Object.defineProperty(LRUCache.prototype, "max",
|
||||
{ set : function (mL) {
|
||||
if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
|
||||
this._max = mL
|
||||
if (this._length > this._max) trim(this)
|
||||
}
|
||||
, get : function () { return this._max }
|
||||
, enumerable : true
|
||||
})
|
||||
|
||||
// resize the cache when the lengthCalculator changes.
|
||||
Object.defineProperty(LRUCache.prototype, "lengthCalculator",
|
||||
{ set : function (lC) {
|
||||
if (typeof lC !== "function") {
|
||||
this._lengthCalculator = naiveLength
|
||||
this._length = this._itemCount
|
||||
for (var key in this._cache) {
|
||||
this._cache[key].length = 1
|
||||
}
|
||||
} else {
|
||||
this._lengthCalculator = lC
|
||||
this._length = 0
|
||||
for (var key in this._cache) {
|
||||
this._cache[key].length = this._lengthCalculator(this._cache[key].value)
|
||||
this._length += this._cache[key].length
|
||||
}
|
||||
}
|
||||
|
||||
if (this._length > this._max) trim(this)
|
||||
}
|
||||
, get : function () { return this._lengthCalculator }
|
||||
, enumerable : true
|
||||
})
|
||||
|
||||
Object.defineProperty(LRUCache.prototype, "length",
|
||||
{ get : function () { return this._length }
|
||||
, enumerable : true
|
||||
})
|
||||
|
||||
|
||||
Object.defineProperty(LRUCache.prototype, "itemCount",
|
||||
{ get : function () { return this._itemCount }
|
||||
, enumerable : true
|
||||
})
|
||||
|
||||
LRUCache.prototype.forEach = function (fn, thisp) {
|
||||
thisp = thisp || this
|
||||
var i = 0;
|
||||
for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
|
||||
i++
|
||||
var hit = this._lruList[k]
|
||||
if (this._maxAge && (Date.now() - hit.now > this._maxAge)) {
|
||||
del(this, hit)
|
||||
if (!this._allowStale) hit = undefined
|
||||
}
|
||||
if (hit) {
|
||||
fn.call(thisp, hit.value, hit.key, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRUCache.prototype.keys = function () {
|
||||
var keys = new Array(this._itemCount)
|
||||
var i = 0
|
||||
for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
|
||||
var hit = this._lruList[k]
|
||||
keys[i++] = hit.key
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
LRUCache.prototype.values = function () {
|
||||
var values = new Array(this._itemCount)
|
||||
var i = 0
|
||||
for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
|
||||
var hit = this._lruList[k]
|
||||
values[i++] = hit.value
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
LRUCache.prototype.reset = function () {
|
||||
if (this._dispose && this._cache) {
|
||||
for (var k in this._cache) {
|
||||
this._dispose(k, this._cache[k].value)
|
||||
}
|
||||
}
|
||||
|
||||
this._cache = Object.create(null) // hash of items by key
|
||||
this._lruList = Object.create(null) // list of items in order of use recency
|
||||
this._mru = 0 // most recently used
|
||||
this._lru = 0 // least recently used
|
||||
this._length = 0 // number of items in the list
|
||||
this._itemCount = 0
|
||||
}
|
||||
|
||||
// Provided for debugging/dev purposes only. No promises whatsoever that
|
||||
// this API stays stable.
|
||||
LRUCache.prototype.dump = function () {
|
||||
return this._cache
|
||||
}
|
||||
|
||||
LRUCache.prototype.dumpLru = function () {
|
||||
return this._lruList
|
||||
}
|
||||
|
||||
LRUCache.prototype.set = function (key, value) {
|
||||
if (hOP(this._cache, key)) {
|
||||
// dispose of the old one before overwriting
|
||||
if (this._dispose) this._dispose(key, this._cache[key].value)
|
||||
if (this._maxAge) this._cache[key].now = Date.now()
|
||||
this._cache[key].value = value
|
||||
this.get(key)
|
||||
return true
|
||||
}
|
||||
|
||||
var len = this._lengthCalculator(value)
|
||||
var age = this._maxAge ? Date.now() : 0
|
||||
var hit = new Entry(key, value, this._mru++, len, age)
|
||||
|
||||
// oversized objects fall out of cache automatically.
|
||||
if (hit.length > this._max) {
|
||||
if (this._dispose) this._dispose(key, value)
|
||||
return false
|
||||
}
|
||||
|
||||
this._length += hit.length
|
||||
this._lruList[hit.lu] = this._cache[key] = hit
|
||||
this._itemCount ++
|
||||
|
||||
if (this._length > this._max) trim(this)
|
||||
return true
|
||||
}
|
||||
|
||||
LRUCache.prototype.has = function (key) {
|
||||
if (!hOP(this._cache, key)) return false
|
||||
var hit = this._cache[key]
|
||||
if (this._maxAge && (Date.now() - hit.now > this._maxAge)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
LRUCache.prototype.get = function (key) {
|
||||
return get(this, key, true)
|
||||
}
|
||||
|
||||
LRUCache.prototype.peek = function (key) {
|
||||
return get(this, key, false)
|
||||
}
|
||||
|
||||
LRUCache.prototype.pop = function () {
|
||||
var hit = this._lruList[this._lru]
|
||||
del(this, hit)
|
||||
return hit || null
|
||||
}
|
||||
|
||||
LRUCache.prototype.del = function (key) {
|
||||
del(this, this._cache[key])
|
||||
}
|
||||
|
||||
function get (self, key, doUse) {
|
||||
var hit = self._cache[key]
|
||||
if (hit) {
|
||||
if (self._maxAge && (Date.now() - hit.now > self._maxAge)) {
|
||||
del(self, hit)
|
||||
if (!self._allowStale) hit = undefined
|
||||
} else {
|
||||
if (doUse) use(self, hit)
|
||||
}
|
||||
if (hit) hit = hit.value
|
||||
}
|
||||
return hit
|
||||
}
|
||||
|
||||
function use (self, hit) {
|
||||
shiftLU(self, hit)
|
||||
hit.lu = self._mru ++
|
||||
self._lruList[hit.lu] = hit
|
||||
}
|
||||
|
||||
function trim (self) {
|
||||
while (self._lru < self._mru && self._length > self._max)
|
||||
del(self, self._lruList[self._lru])
|
||||
}
|
||||
|
||||
function shiftLU (self, hit) {
|
||||
delete self._lruList[ hit.lu ]
|
||||
while (self._lru < self._mru && !self._lruList[self._lru]) self._lru ++
|
||||
}
|
||||
|
||||
function del (self, hit) {
|
||||
if (hit) {
|
||||
if (self._dispose) self._dispose(hit.key, hit.value)
|
||||
self._length -= hit.length
|
||||
self._itemCount --
|
||||
delete self._cache[ hit.key ]
|
||||
shiftLU(self, hit)
|
||||
}
|
||||
}
|
||||
|
||||
// classy, since V8 prefers predictable objects.
|
||||
function Entry (key, value, lu, length, now) {
|
||||
this.key = key
|
||||
this.value = value
|
||||
this.lu = lu
|
||||
this.length = length
|
||||
this.now = now
|
||||
}
|
||||
|
||||
})()
|
||||
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "lru-cache",
|
||||
"description": "A cache object that deletes the least-recently-used items.",
|
||||
"version": "2.5.0",
|
||||
"author": {
|
||||
"name": "Isaac Z. Schlueter",
|
||||
"email": "i@izs.me"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test --gc"
|
||||
},
|
||||
"main": "lib/lru-cache.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/node-lru-cache.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "",
|
||||
"weak": ""
|
||||
},
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/isaacs/node-lru-cache/raw/master/LICENSE"
|
||||
},
|
||||
"readme": "# lru cache\n\nA cache object that deletes the least-recently-used items.\n\n## Usage:\n\n```javascript\nvar LRU = require(\"lru-cache\")\n , options = { max: 500\n , length: function (n) { return n * 2 }\n , dispose: function (key, n) { n.close() }\n , maxAge: 1000 * 60 * 60 }\n , cache = LRU(options)\n , otherCache = LRU(50) // sets just the max size\n\ncache.set(\"key\", \"value\")\ncache.get(\"key\") // \"value\"\n\ncache.reset() // empty the cache\n```\n\nIf you put more stuff in it, then items will fall out.\n\nIf you try to put an oversized thing in it, then it'll fall out right\naway.\n\n## Options\n\n* `max` The maximum size of the cache, checked by applying the length\n function to all values in the cache. Not setting this is kind of\n silly, since that's the whole purpose of this lib, but it defaults\n to `Infinity`.\n* `maxAge` Maximum age in ms. Items are not pro-actively pruned out\n as they age, but if you try to get an item that is too old, it'll\n drop it and return undefined instead of giving it to you.\n* `length` Function that is used to calculate the length of stored\n items. If you're storing strings or buffers, then you probably want\n to do something like `function(n){return n.length}`. The default is\n `function(n){return 1}`, which is fine if you want to store `n`\n like-sized things.\n* `dispose` Function that is called on items when they are dropped\n from the cache. This can be handy if you want to close file\n descriptors or do other cleanup tasks when items are no longer\n accessible. Called with `key, value`. It's called *before*\n actually removing the item from the internal cache, so if you want\n to immediately put it back in, you'll have to do that in a\n `nextTick` or `setTimeout` callback or it won't do anything.\n* `stale` By default, if you set a `maxAge`, it'll only actually pull\n stale items out of the cache when you `get(key)`. (That is, it's\n not pre-emptively doing a `setTimeout` or anything.) If you set\n `stale:true`, it'll return the stale value before deleting it. If\n you don't set this, then it'll return `undefined` when you try to\n get a stale entry, as if it had already been deleted.\n\n## API\n\n* `set(key, value)`\n* `get(key) => value`\n\n Both of these will update the \"recently used\"-ness of the key.\n They do what you think.\n\n* `peek(key)`\n\n Returns the key value (or `undefined` if not found) without\n updating the \"recently used\"-ness of the key.\n\n (If you find yourself using this a lot, you *might* be using the\n wrong sort of data structure, but there are some use cases where\n it's handy.)\n\n* `del(key)`\n\n Deletes a key out of the cache.\n\n* `reset()`\n\n Clear the cache entirely, throwing away all values.\n\n* `has(key)`\n\n Check if a key is in the cache, without updating the recent-ness\n or deleting it for being stale.\n\n* `forEach(function(value,key,cache), [thisp])`\n\n Just like `Array.prototype.forEach`. Iterates over all the keys\n in the cache, in order of recent-ness. (Ie, more recently used\n items are iterated over first.)\n\n* `keys()`\n\n Return an array of the keys in the cache.\n\n* `values()`\n\n Return an array of the values in the cache.\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/node-lru-cache/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/node-lru-cache",
|
||||
"_id": "lru-cache@2.5.0",
|
||||
"_from": "lru-cache@2"
|
||||
}
|
||||
Generated
Vendored
+369
@@ -0,0 +1,369 @@
|
||||
var test = require("tap").test
|
||||
, LRU = require("../")
|
||||
|
||||
test("basic", function (t) {
|
||||
var cache = new LRU({max: 10})
|
||||
cache.set("key", "value")
|
||||
t.equal(cache.get("key"), "value")
|
||||
t.equal(cache.get("nada"), undefined)
|
||||
t.equal(cache.length, 1)
|
||||
t.equal(cache.max, 10)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("least recently set", function (t) {
|
||||
var cache = new LRU(2)
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "B")
|
||||
cache.set("c", "C")
|
||||
t.equal(cache.get("c"), "C")
|
||||
t.equal(cache.get("b"), "B")
|
||||
t.equal(cache.get("a"), undefined)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("lru recently gotten", function (t) {
|
||||
var cache = new LRU(2)
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "B")
|
||||
cache.get("a")
|
||||
cache.set("c", "C")
|
||||
t.equal(cache.get("c"), "C")
|
||||
t.equal(cache.get("b"), undefined)
|
||||
t.equal(cache.get("a"), "A")
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("del", function (t) {
|
||||
var cache = new LRU(2)
|
||||
cache.set("a", "A")
|
||||
cache.del("a")
|
||||
t.equal(cache.get("a"), undefined)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("max", function (t) {
|
||||
var cache = new LRU(3)
|
||||
|
||||
// test changing the max, verify that the LRU items get dropped.
|
||||
cache.max = 100
|
||||
for (var i = 0; i < 100; i ++) cache.set(i, i)
|
||||
t.equal(cache.length, 100)
|
||||
for (var i = 0; i < 100; i ++) {
|
||||
t.equal(cache.get(i), i)
|
||||
}
|
||||
cache.max = 3
|
||||
t.equal(cache.length, 3)
|
||||
for (var i = 0; i < 97; i ++) {
|
||||
t.equal(cache.get(i), undefined)
|
||||
}
|
||||
for (var i = 98; i < 100; i ++) {
|
||||
t.equal(cache.get(i), i)
|
||||
}
|
||||
|
||||
// now remove the max restriction, and try again.
|
||||
cache.max = "hello"
|
||||
for (var i = 0; i < 100; i ++) cache.set(i, i)
|
||||
t.equal(cache.length, 100)
|
||||
for (var i = 0; i < 100; i ++) {
|
||||
t.equal(cache.get(i), i)
|
||||
}
|
||||
// should trigger an immediate resize
|
||||
cache.max = 3
|
||||
t.equal(cache.length, 3)
|
||||
for (var i = 0; i < 97; i ++) {
|
||||
t.equal(cache.get(i), undefined)
|
||||
}
|
||||
for (var i = 98; i < 100; i ++) {
|
||||
t.equal(cache.get(i), i)
|
||||
}
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("reset", function (t) {
|
||||
var cache = new LRU(10)
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "B")
|
||||
cache.reset()
|
||||
t.equal(cache.length, 0)
|
||||
t.equal(cache.max, 10)
|
||||
t.equal(cache.get("a"), undefined)
|
||||
t.equal(cache.get("b"), undefined)
|
||||
t.end()
|
||||
})
|
||||
|
||||
|
||||
// Note: `<cache>.dump()` is a debugging tool only. No guarantees are made
|
||||
// about the format/layout of the response.
|
||||
test("dump", function (t) {
|
||||
var cache = new LRU(10)
|
||||
var d = cache.dump();
|
||||
t.equal(Object.keys(d).length, 0, "nothing in dump for empty cache")
|
||||
cache.set("a", "A")
|
||||
var d = cache.dump() // { a: { key: "a", value: "A", lu: 0 } }
|
||||
t.ok(d.a)
|
||||
t.equal(d.a.key, "a")
|
||||
t.equal(d.a.value, "A")
|
||||
t.equal(d.a.lu, 0)
|
||||
|
||||
cache.set("b", "B")
|
||||
cache.get("b")
|
||||
d = cache.dump()
|
||||
t.ok(d.b)
|
||||
t.equal(d.b.key, "b")
|
||||
t.equal(d.b.value, "B")
|
||||
t.equal(d.b.lu, 2)
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
|
||||
test("basic with weighed length", function (t) {
|
||||
var cache = new LRU({
|
||||
max: 100,
|
||||
length: function (item) { return item.size }
|
||||
})
|
||||
cache.set("key", {val: "value", size: 50})
|
||||
t.equal(cache.get("key").val, "value")
|
||||
t.equal(cache.get("nada"), undefined)
|
||||
t.equal(cache.lengthCalculator(cache.get("key")), 50)
|
||||
t.equal(cache.length, 50)
|
||||
t.equal(cache.max, 100)
|
||||
t.end()
|
||||
})
|
||||
|
||||
|
||||
test("weighed length item too large", function (t) {
|
||||
var cache = new LRU({
|
||||
max: 10,
|
||||
length: function (item) { return item.size }
|
||||
})
|
||||
t.equal(cache.max, 10)
|
||||
|
||||
// should fall out immediately
|
||||
cache.set("key", {val: "value", size: 50})
|
||||
|
||||
t.equal(cache.length, 0)
|
||||
t.equal(cache.get("key"), undefined)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("least recently set with weighed length", function (t) {
|
||||
var cache = new LRU({
|
||||
max:8,
|
||||
length: function (item) { return item.length }
|
||||
})
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "BB")
|
||||
cache.set("c", "CCC")
|
||||
cache.set("d", "DDDD")
|
||||
t.equal(cache.get("d"), "DDDD")
|
||||
t.equal(cache.get("c"), "CCC")
|
||||
t.equal(cache.get("b"), undefined)
|
||||
t.equal(cache.get("a"), undefined)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("lru recently gotten with weighed length", function (t) {
|
||||
var cache = new LRU({
|
||||
max: 8,
|
||||
length: function (item) { return item.length }
|
||||
})
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "BB")
|
||||
cache.set("c", "CCC")
|
||||
cache.get("a")
|
||||
cache.get("b")
|
||||
cache.set("d", "DDDD")
|
||||
t.equal(cache.get("c"), undefined)
|
||||
t.equal(cache.get("d"), "DDDD")
|
||||
t.equal(cache.get("b"), "BB")
|
||||
t.equal(cache.get("a"), "A")
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("set returns proper booleans", function(t) {
|
||||
var cache = new LRU({
|
||||
max: 5,
|
||||
length: function (item) { return item.length }
|
||||
})
|
||||
|
||||
t.equal(cache.set("a", "A"), true)
|
||||
|
||||
// should return false for max exceeded
|
||||
t.equal(cache.set("b", "donuts"), false)
|
||||
|
||||
t.equal(cache.set("b", "B"), true)
|
||||
t.equal(cache.set("c", "CCCC"), true)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("drop the old items", function(t) {
|
||||
var cache = new LRU({
|
||||
max: 5,
|
||||
maxAge: 50
|
||||
})
|
||||
|
||||
cache.set("a", "A")
|
||||
|
||||
setTimeout(function () {
|
||||
cache.set("b", "b")
|
||||
t.equal(cache.get("a"), "A")
|
||||
}, 25)
|
||||
|
||||
setTimeout(function () {
|
||||
cache.set("c", "C")
|
||||
// timed out
|
||||
t.notOk(cache.get("a"))
|
||||
}, 60)
|
||||
|
||||
setTimeout(function () {
|
||||
t.notOk(cache.get("b"))
|
||||
t.equal(cache.get("c"), "C")
|
||||
}, 90)
|
||||
|
||||
setTimeout(function () {
|
||||
t.notOk(cache.get("c"))
|
||||
t.end()
|
||||
}, 155)
|
||||
})
|
||||
|
||||
test("disposal function", function(t) {
|
||||
var disposed = false
|
||||
var cache = new LRU({
|
||||
max: 1,
|
||||
dispose: function (k, n) {
|
||||
disposed = n
|
||||
}
|
||||
})
|
||||
|
||||
cache.set(1, 1)
|
||||
cache.set(2, 2)
|
||||
t.equal(disposed, 1)
|
||||
cache.set(3, 3)
|
||||
t.equal(disposed, 2)
|
||||
cache.reset()
|
||||
t.equal(disposed, 3)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("disposal function on too big of item", function(t) {
|
||||
var disposed = false
|
||||
var cache = new LRU({
|
||||
max: 1,
|
||||
length: function (k) {
|
||||
return k.length
|
||||
},
|
||||
dispose: function (k, n) {
|
||||
disposed = n
|
||||
}
|
||||
})
|
||||
var obj = [ 1, 2 ]
|
||||
|
||||
t.equal(disposed, false)
|
||||
cache.set("obj", obj)
|
||||
t.equal(disposed, obj)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("has()", function(t) {
|
||||
var cache = new LRU({
|
||||
max: 1,
|
||||
maxAge: 10
|
||||
})
|
||||
|
||||
cache.set('foo', 'bar')
|
||||
t.equal(cache.has('foo'), true)
|
||||
cache.set('blu', 'baz')
|
||||
t.equal(cache.has('foo'), false)
|
||||
t.equal(cache.has('blu'), true)
|
||||
setTimeout(function() {
|
||||
t.equal(cache.has('blu'), false)
|
||||
t.end()
|
||||
}, 15)
|
||||
})
|
||||
|
||||
test("stale", function(t) {
|
||||
var cache = new LRU({
|
||||
maxAge: 10,
|
||||
stale: true
|
||||
})
|
||||
|
||||
cache.set('foo', 'bar')
|
||||
t.equal(cache.get('foo'), 'bar')
|
||||
t.equal(cache.has('foo'), true)
|
||||
setTimeout(function() {
|
||||
t.equal(cache.has('foo'), false)
|
||||
t.equal(cache.get('foo'), 'bar')
|
||||
t.equal(cache.get('foo'), undefined)
|
||||
t.end()
|
||||
}, 15)
|
||||
})
|
||||
|
||||
test("lru update via set", function(t) {
|
||||
var cache = LRU({ max: 2 });
|
||||
|
||||
cache.set('foo', 1);
|
||||
cache.set('bar', 2);
|
||||
cache.del('bar');
|
||||
cache.set('baz', 3);
|
||||
cache.set('qux', 4);
|
||||
|
||||
t.equal(cache.get('foo'), undefined)
|
||||
t.equal(cache.get('bar'), undefined)
|
||||
t.equal(cache.get('baz'), 3)
|
||||
t.equal(cache.get('qux'), 4)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("least recently set w/ peek", function (t) {
|
||||
var cache = new LRU(2)
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "B")
|
||||
t.equal(cache.peek("a"), "A")
|
||||
cache.set("c", "C")
|
||||
t.equal(cache.get("c"), "C")
|
||||
t.equal(cache.get("b"), "B")
|
||||
t.equal(cache.get("a"), undefined)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test("pop the least used item", function (t) {
|
||||
var cache = new LRU(3)
|
||||
, last
|
||||
|
||||
cache.set("a", "A")
|
||||
cache.set("b", "B")
|
||||
cache.set("c", "C")
|
||||
|
||||
t.equal(cache.length, 3)
|
||||
t.equal(cache.max, 3)
|
||||
|
||||
// Ensure we pop a, c, b
|
||||
cache.get("b", "B")
|
||||
|
||||
last = cache.pop()
|
||||
t.equal(last.key, "a")
|
||||
t.equal(last.value, "A")
|
||||
t.equal(cache.length, 2)
|
||||
t.equal(cache.max, 3)
|
||||
|
||||
last = cache.pop()
|
||||
t.equal(last.key, "c")
|
||||
t.equal(last.value, "C")
|
||||
t.equal(cache.length, 1)
|
||||
t.equal(cache.max, 3)
|
||||
|
||||
last = cache.pop()
|
||||
t.equal(last.key, "b")
|
||||
t.equal(last.value, "B")
|
||||
t.equal(cache.length, 0)
|
||||
t.equal(cache.max, 3)
|
||||
|
||||
last = cache.pop()
|
||||
t.equal(last, null)
|
||||
t.equal(cache.length, 0)
|
||||
t.equal(cache.max, 3)
|
||||
|
||||
t.end()
|
||||
})
|
||||
Generated
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
var test = require('tap').test
|
||||
var LRU = require('../')
|
||||
|
||||
test('forEach', function (t) {
|
||||
var l = new LRU(5)
|
||||
for (var i = 0; i < 10; i ++) {
|
||||
l.set(i.toString(), i.toString(2))
|
||||
}
|
||||
|
||||
var i = 9
|
||||
l.forEach(function (val, key, cache) {
|
||||
t.equal(cache, l)
|
||||
t.equal(key, i.toString())
|
||||
t.equal(val, i.toString(2))
|
||||
i -= 1
|
||||
})
|
||||
|
||||
// get in order of most recently used
|
||||
l.get(6)
|
||||
l.get(8)
|
||||
|
||||
var order = [ 8, 6, 9, 7, 5 ]
|
||||
var i = 0
|
||||
|
||||
l.forEach(function (val, key, cache) {
|
||||
var j = order[i ++]
|
||||
t.equal(cache, l)
|
||||
t.equal(key, j.toString())
|
||||
t.equal(val, j.toString(2))
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('keys() and values()', function (t) {
|
||||
var l = new LRU(5)
|
||||
for (var i = 0; i < 10; i ++) {
|
||||
l.set(i.toString(), i.toString(2))
|
||||
}
|
||||
|
||||
t.similar(l.keys(), ['9', '8', '7', '6', '5'])
|
||||
t.similar(l.values(), ['1001', '1000', '111', '110', '101'])
|
||||
|
||||
// get in order of most recently used
|
||||
l.get(6)
|
||||
l.get(8)
|
||||
|
||||
t.similar(l.keys(), ['8', '6', '9', '7', '5'])
|
||||
t.similar(l.values(), ['1000', '110', '1001', '111', '101'])
|
||||
|
||||
t.end()
|
||||
})
|
||||
Generated
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node --expose_gc
|
||||
|
||||
var weak = require('weak');
|
||||
var test = require('tap').test
|
||||
var LRU = require('../')
|
||||
var l = new LRU({ max: 10 })
|
||||
var refs = 0
|
||||
function X() {
|
||||
refs ++
|
||||
weak(this, deref)
|
||||
}
|
||||
|
||||
function deref() {
|
||||
refs --
|
||||
}
|
||||
|
||||
test('no leaks', function (t) {
|
||||
// fill up the cache
|
||||
for (var i = 0; i < 100; i++) {
|
||||
l.set(i, new X);
|
||||
// throw some gets in there, too.
|
||||
if (i % 2 === 0)
|
||||
l.get(i / 2)
|
||||
}
|
||||
|
||||
gc()
|
||||
|
||||
var start = process.memoryUsage()
|
||||
|
||||
// capture the memory
|
||||
var startRefs = refs
|
||||
|
||||
// do it again, but more
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
l.set(i, new X);
|
||||
// throw some gets in there, too.
|
||||
if (i % 2 === 0)
|
||||
l.get(i / 2)
|
||||
}
|
||||
|
||||
gc()
|
||||
|
||||
var end = process.memoryUsage()
|
||||
t.equal(refs, startRefs, 'no leaky refs')
|
||||
|
||||
console.error('start: %j\n' +
|
||||
'end: %j', start, end);
|
||||
t.pass();
|
||||
t.end();
|
||||
})
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) Isaac Z. Schlueter ("Author")
|
||||
All rights reserved.
|
||||
|
||||
The BSD License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Generated
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
# sigmund
|
||||
|
||||
Quick and dirty signatures for Objects.
|
||||
|
||||
This is like a much faster `deepEquals` comparison, which returns a
|
||||
string key suitable for caches and the like.
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
function doSomething (someObj) {
|
||||
var key = sigmund(someObj, maxDepth) // max depth defaults to 10
|
||||
var cached = cache.get(key)
|
||||
if (cached) return cached)
|
||||
|
||||
var result = expensiveCalculation(someObj)
|
||||
cache.set(key, result)
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
The resulting key will be as unique and reproducible as calling
|
||||
`JSON.stringify` or `util.inspect` on the object, but is much faster.
|
||||
In order to achieve this speed, some differences are glossed over.
|
||||
For example, the object `{0:'foo'}` will be treated identically to the
|
||||
array `['foo']`.
|
||||
|
||||
Also, just as there is no way to summon the soul from the scribblings
|
||||
of a cocain-addled psychoanalyst, there is no way to revive the object
|
||||
from the signature string that sigmund gives you. In fact, it's
|
||||
barely even readable.
|
||||
|
||||
As with `sys.inspect` and `JSON.stringify`, larger objects will
|
||||
produce larger signature strings.
|
||||
|
||||
Because sigmund is a bit less strict than the more thorough
|
||||
alternatives, the strings will be shorter, and also there is a
|
||||
slightly higher chance for collisions. For example, these objects
|
||||
have the same signature:
|
||||
|
||||
var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
|
||||
var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}
|
||||
|
||||
Like a good Freudian, sigmund is most effective when you already have
|
||||
some understanding of what you're looking for. It can help you help
|
||||
yourself, but you must be willing to do some work as well.
|
||||
|
||||
Cycles are handled, and cyclical objects are silently omitted (though
|
||||
the key is included in the signature output.)
|
||||
|
||||
The second argument is the maximum depth, which defaults to 10,
|
||||
because that is the maximum object traversal depth covered by most
|
||||
insurance carriers.
|
||||
Generated
Vendored
+283
@@ -0,0 +1,283 @@
|
||||
// different ways to id objects
|
||||
// use a req/res pair, since it's crazy deep and cyclical
|
||||
|
||||
// sparseFE10 and sigmund are usually pretty close, which is to be expected,
|
||||
// since they are essentially the same algorithm, except that sigmund handles
|
||||
// regular expression objects properly.
|
||||
|
||||
|
||||
var http = require('http')
|
||||
var util = require('util')
|
||||
var sigmund = require('./sigmund.js')
|
||||
var sreq, sres, creq, cres, test
|
||||
|
||||
http.createServer(function (q, s) {
|
||||
sreq = q
|
||||
sres = s
|
||||
sres.end('ok')
|
||||
this.close(function () { setTimeout(function () {
|
||||
start()
|
||||
}, 200) })
|
||||
}).listen(1337, function () {
|
||||
creq = http.get({ port: 1337 })
|
||||
creq.on('response', function (s) { cres = s })
|
||||
})
|
||||
|
||||
function start () {
|
||||
test = [sreq, sres, creq, cres]
|
||||
// test = sreq
|
||||
// sreq.sres = sres
|
||||
// sreq.creq = creq
|
||||
// sreq.cres = cres
|
||||
|
||||
for (var i in exports.compare) {
|
||||
console.log(i)
|
||||
var hash = exports.compare[i]()
|
||||
console.log(hash)
|
||||
console.log(hash.length)
|
||||
console.log('')
|
||||
}
|
||||
|
||||
require('bench').runMain()
|
||||
}
|
||||
|
||||
function customWs (obj, md, d) {
|
||||
d = d || 0
|
||||
var to = typeof obj
|
||||
if (to === 'undefined' || to === 'function' || to === null) return ''
|
||||
if (d > md || !obj || to !== 'object') return ('' + obj).replace(/[\n ]+/g, '')
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(function (i, _, __) {
|
||||
return customWs(i, md, d + 1)
|
||||
}).reduce(function (a, b) { return a + b }, '')
|
||||
}
|
||||
|
||||
var keys = Object.keys(obj)
|
||||
return keys.map(function (k, _, __) {
|
||||
return k + ':' + customWs(obj[k], md, d + 1)
|
||||
}).reduce(function (a, b) { return a + b }, '')
|
||||
}
|
||||
|
||||
function custom (obj, md, d) {
|
||||
d = d || 0
|
||||
var to = typeof obj
|
||||
if (to === 'undefined' || to === 'function' || to === null) return ''
|
||||
if (d > md || !obj || to !== 'object') return '' + obj
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(function (i, _, __) {
|
||||
return custom(i, md, d + 1)
|
||||
}).reduce(function (a, b) { return a + b }, '')
|
||||
}
|
||||
|
||||
var keys = Object.keys(obj)
|
||||
return keys.map(function (k, _, __) {
|
||||
return k + ':' + custom(obj[k], md, d + 1)
|
||||
}).reduce(function (a, b) { return a + b }, '')
|
||||
}
|
||||
|
||||
function sparseFE2 (obj, maxDepth) {
|
||||
var seen = []
|
||||
var soFar = ''
|
||||
function ch (v, depth) {
|
||||
if (depth > maxDepth) return
|
||||
if (typeof v === 'function' || typeof v === 'undefined') return
|
||||
if (typeof v !== 'object' || !v) {
|
||||
soFar += v
|
||||
return
|
||||
}
|
||||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return
|
||||
seen.push(v)
|
||||
soFar += '{'
|
||||
Object.keys(v).forEach(function (k, _, __) {
|
||||
// pseudo-private values. skip those.
|
||||
if (k.charAt(0) === '_') return
|
||||
var to = typeof v[k]
|
||||
if (to === 'function' || to === 'undefined') return
|
||||
soFar += k + ':'
|
||||
ch(v[k], depth + 1)
|
||||
})
|
||||
soFar += '}'
|
||||
}
|
||||
ch(obj, 0)
|
||||
return soFar
|
||||
}
|
||||
|
||||
function sparseFE (obj, maxDepth) {
|
||||
var seen = []
|
||||
var soFar = ''
|
||||
function ch (v, depth) {
|
||||
if (depth > maxDepth) return
|
||||
if (typeof v === 'function' || typeof v === 'undefined') return
|
||||
if (typeof v !== 'object' || !v) {
|
||||
soFar += v
|
||||
return
|
||||
}
|
||||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return
|
||||
seen.push(v)
|
||||
soFar += '{'
|
||||
Object.keys(v).forEach(function (k, _, __) {
|
||||
// pseudo-private values. skip those.
|
||||
if (k.charAt(0) === '_') return
|
||||
var to = typeof v[k]
|
||||
if (to === 'function' || to === 'undefined') return
|
||||
soFar += k
|
||||
ch(v[k], depth + 1)
|
||||
})
|
||||
}
|
||||
ch(obj, 0)
|
||||
return soFar
|
||||
}
|
||||
|
||||
function sparse (obj, maxDepth) {
|
||||
var seen = []
|
||||
var soFar = ''
|
||||
function ch (v, depth) {
|
||||
if (depth > maxDepth) return
|
||||
if (typeof v === 'function' || typeof v === 'undefined') return
|
||||
if (typeof v !== 'object' || !v) {
|
||||
soFar += v
|
||||
return
|
||||
}
|
||||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return
|
||||
seen.push(v)
|
||||
soFar += '{'
|
||||
for (var k in v) {
|
||||
// pseudo-private values. skip those.
|
||||
if (k.charAt(0) === '_') continue
|
||||
var to = typeof v[k]
|
||||
if (to === 'function' || to === 'undefined') continue
|
||||
soFar += k
|
||||
ch(v[k], depth + 1)
|
||||
}
|
||||
}
|
||||
ch(obj, 0)
|
||||
return soFar
|
||||
}
|
||||
|
||||
function noCommas (obj, maxDepth) {
|
||||
var seen = []
|
||||
var soFar = ''
|
||||
function ch (v, depth) {
|
||||
if (depth > maxDepth) return
|
||||
if (typeof v === 'function' || typeof v === 'undefined') return
|
||||
if (typeof v !== 'object' || !v) {
|
||||
soFar += v
|
||||
return
|
||||
}
|
||||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return
|
||||
seen.push(v)
|
||||
soFar += '{'
|
||||
for (var k in v) {
|
||||
// pseudo-private values. skip those.
|
||||
if (k.charAt(0) === '_') continue
|
||||
var to = typeof v[k]
|
||||
if (to === 'function' || to === 'undefined') continue
|
||||
soFar += k + ':'
|
||||
ch(v[k], depth + 1)
|
||||
}
|
||||
soFar += '}'
|
||||
}
|
||||
ch(obj, 0)
|
||||
return soFar
|
||||
}
|
||||
|
||||
|
||||
function flatten (obj, maxDepth) {
|
||||
var seen = []
|
||||
var soFar = ''
|
||||
function ch (v, depth) {
|
||||
if (depth > maxDepth) return
|
||||
if (typeof v === 'function' || typeof v === 'undefined') return
|
||||
if (typeof v !== 'object' || !v) {
|
||||
soFar += v
|
||||
return
|
||||
}
|
||||
if (seen.indexOf(v) !== -1 || depth === maxDepth) return
|
||||
seen.push(v)
|
||||
soFar += '{'
|
||||
for (var k in v) {
|
||||
// pseudo-private values. skip those.
|
||||
if (k.charAt(0) === '_') continue
|
||||
var to = typeof v[k]
|
||||
if (to === 'function' || to === 'undefined') continue
|
||||
soFar += k + ':'
|
||||
ch(v[k], depth + 1)
|
||||
soFar += ','
|
||||
}
|
||||
soFar += '}'
|
||||
}
|
||||
ch(obj, 0)
|
||||
return soFar
|
||||
}
|
||||
|
||||
exports.compare =
|
||||
{
|
||||
// 'custom 2': function () {
|
||||
// return custom(test, 2, 0)
|
||||
// },
|
||||
// 'customWs 2': function () {
|
||||
// return customWs(test, 2, 0)
|
||||
// },
|
||||
'JSON.stringify (guarded)': function () {
|
||||
var seen = []
|
||||
return JSON.stringify(test, function (k, v) {
|
||||
if (typeof v !== 'object' || !v) return v
|
||||
if (seen.indexOf(v) !== -1) return undefined
|
||||
seen.push(v)
|
||||
return v
|
||||
})
|
||||
},
|
||||
|
||||
'flatten 10': function () {
|
||||
return flatten(test, 10)
|
||||
},
|
||||
|
||||
// 'flattenFE 10': function () {
|
||||
// return flattenFE(test, 10)
|
||||
// },
|
||||
|
||||
'noCommas 10': function () {
|
||||
return noCommas(test, 10)
|
||||
},
|
||||
|
||||
'sparse 10': function () {
|
||||
return sparse(test, 10)
|
||||
},
|
||||
|
||||
'sparseFE 10': function () {
|
||||
return sparseFE(test, 10)
|
||||
},
|
||||
|
||||
'sparseFE2 10': function () {
|
||||
return sparseFE2(test, 10)
|
||||
},
|
||||
|
||||
sigmund: function() {
|
||||
return sigmund(test, 10)
|
||||
},
|
||||
|
||||
|
||||
// 'util.inspect 1': function () {
|
||||
// return util.inspect(test, false, 1, false)
|
||||
// },
|
||||
// 'util.inspect undefined': function () {
|
||||
// util.inspect(test)
|
||||
// },
|
||||
// 'util.inspect 2': function () {
|
||||
// util.inspect(test, false, 2, false)
|
||||
// },
|
||||
// 'util.inspect 3': function () {
|
||||
// util.inspect(test, false, 3, false)
|
||||
// },
|
||||
// 'util.inspect 4': function () {
|
||||
// util.inspect(test, false, 4, false)
|
||||
// },
|
||||
// 'util.inspect Infinity': function () {
|
||||
// util.inspect(test, false, Infinity, false)
|
||||
// }
|
||||
}
|
||||
|
||||
/** results
|
||||
**/
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "sigmund",
|
||||
"version": "1.0.0",
|
||||
"description": "Quick and dirty signatures for Objects.",
|
||||
"main": "sigmund.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"tap": "~0.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test/*.js",
|
||||
"bench": "node bench.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/sigmund"
|
||||
},
|
||||
"keywords": [
|
||||
"object",
|
||||
"signature",
|
||||
"key",
|
||||
"data",
|
||||
"psychoanalysis"
|
||||
],
|
||||
"author": {
|
||||
"name": "Isaac Z. Schlueter",
|
||||
"email": "i@izs.me",
|
||||
"url": "http://blog.izs.me/"
|
||||
},
|
||||
"license": "BSD",
|
||||
"readme": "# sigmund\n\nQuick and dirty signatures for Objects.\n\nThis is like a much faster `deepEquals` comparison, which returns a\nstring key suitable for caches and the like.\n\n## Usage\n\n```javascript\nfunction doSomething (someObj) {\n var key = sigmund(someObj, maxDepth) // max depth defaults to 10\n var cached = cache.get(key)\n if (cached) return cached)\n\n var result = expensiveCalculation(someObj)\n cache.set(key, result)\n return result\n}\n```\n\nThe resulting key will be as unique and reproducible as calling\n`JSON.stringify` or `util.inspect` on the object, but is much faster.\nIn order to achieve this speed, some differences are glossed over.\nFor example, the object `{0:'foo'}` will be treated identically to the\narray `['foo']`.\n\nAlso, just as there is no way to summon the soul from the scribblings\nof a cocain-addled psychoanalyst, there is no way to revive the object\nfrom the signature string that sigmund gives you. In fact, it's\nbarely even readable.\n\nAs with `sys.inspect` and `JSON.stringify`, larger objects will\nproduce larger signature strings.\n\nBecause sigmund is a bit less strict than the more thorough\nalternatives, the strings will be shorter, and also there is a\nslightly higher chance for collisions. For example, these objects\nhave the same signature:\n\n var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}\n var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}\n\nLike a good Freudian, sigmund is most effective when you already have\nsome understanding of what you're looking for. It can help you help\nyourself, but you must be willing to do some work as well.\n\nCycles are handled, and cyclical objects are silently omitted (though\nthe key is included in the signature output.)\n\nThe second argument is the maximum depth, which defaults to 10,\nbecause that is the maximum object traversal depth covered by most\ninsurance carriers.\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/sigmund/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/sigmund",
|
||||
"_id": "sigmund@1.0.0",
|
||||
"_from": "sigmund@~1.0.0"
|
||||
}
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
module.exports = sigmund
|
||||
function sigmund (subject, maxSessions) {
|
||||
maxSessions = maxSessions || 10;
|
||||
var notes = [];
|
||||
var analysis = '';
|
||||
var RE = RegExp;
|
||||
|
||||
function psychoAnalyze (subject, session) {
|
||||
if (session > maxSessions) return;
|
||||
|
||||
if (typeof subject === 'function' ||
|
||||
typeof subject === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof subject !== 'object' || !subject ||
|
||||
(subject instanceof RE)) {
|
||||
analysis += subject;
|
||||
return;
|
||||
}
|
||||
|
||||
if (notes.indexOf(subject) !== -1 || session === maxSessions) return;
|
||||
|
||||
notes.push(subject);
|
||||
analysis += '{';
|
||||
Object.keys(subject).forEach(function (issue, _, __) {
|
||||
// pseudo-private values. skip those.
|
||||
if (issue.charAt(0) === '_') return;
|
||||
var to = typeof subject[issue];
|
||||
if (to === 'function' || to === 'undefined') return;
|
||||
analysis += issue;
|
||||
psychoAnalyze(subject[issue], session + 1);
|
||||
});
|
||||
}
|
||||
psychoAnalyze(subject, 0);
|
||||
return analysis;
|
||||
}
|
||||
|
||||
// vim: set softtabstop=4 shiftwidth=4:
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
var test = require('tap').test
|
||||
var sigmund = require('../sigmund.js')
|
||||
|
||||
|
||||
// occasionally there are duplicates
|
||||
// that's an acceptable edge-case. JSON.stringify and util.inspect
|
||||
// have some collision potential as well, though less, and collision
|
||||
// detection is expensive.
|
||||
var hash = '{abc/def/g{0h1i2{jkl'
|
||||
var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
|
||||
var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}
|
||||
|
||||
var obj3 = JSON.parse(JSON.stringify(obj1))
|
||||
obj3.c = /def/
|
||||
obj3.g[2].cycle = obj3
|
||||
var cycleHash = '{abc/def/g{0h1i2{jklcycle'
|
||||
|
||||
test('basic', function (t) {
|
||||
t.equal(sigmund(obj1), hash)
|
||||
t.equal(sigmund(obj2), hash)
|
||||
t.equal(sigmund(obj3), cycleHash)
|
||||
t.end()
|
||||
})
|
||||
|
||||
Generated
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"author": {
|
||||
"name": "Isaac Z. Schlueter",
|
||||
"email": "i@izs.me",
|
||||
"url": "http://blog.izs.me"
|
||||
},
|
||||
"name": "minimatch",
|
||||
"description": "a glob matcher in javascript",
|
||||
"version": "0.2.14",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/minimatch.git"
|
||||
},
|
||||
"main": "minimatch.js",
|
||||
"scripts": {
|
||||
"test": "tap test/*.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": "2",
|
||||
"sigmund": "~1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": ""
|
||||
},
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/isaacs/minimatch/raw/master/LICENSE"
|
||||
},
|
||||
"readme": "# minimatch\n\nA minimal matching utility.\n\n[](http://travis-ci.org/isaacs/minimatch)\n\n\nThis is the matching library used internally by npm.\n\nEventually, it will replace the C binding in node-glob.\n\nIt works by converting glob expressions into JavaScript `RegExp`\nobjects.\n\n## Usage\n\n```javascript\nvar minimatch = require(\"minimatch\")\n\nminimatch(\"bar.foo\", \"*.foo\") // true!\nminimatch(\"bar.foo\", \"*.bar\") // false!\nminimatch(\"bar.foo\", \"*.+(bar|foo)\", { debug: true }) // true, and noisy!\n```\n\n## Features\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n\n## Minimatch Class\n\nCreate a minimatch object by instanting the `minimatch.Minimatch` class.\n\n```javascript\nvar Minimatch = require(\"minimatch\").Minimatch\nvar mm = new Minimatch(pattern, options)\n```\n\n### Properties\n\n* `pattern` The original pattern the minimatch object represents.\n* `options` The options supplied to the constructor.\n* `set` A 2-dimensional array of regexp or string expressions.\n Each row in the\n array corresponds to a brace-expanded pattern. Each item in the row\n corresponds to a single path-part. For example, the pattern\n `{a,b/c}/d` would expand to a set of patterns like:\n\n [ [ a, d ]\n , [ b, c, d ] ]\n\n If a portion of the pattern doesn't have any \"magic\" in it\n (that is, it's something like `\"foo\"` rather than `fo*o?`), then it\n will be left as a string rather than converted to a regular\n expression.\n\n* `regexp` Created by the `makeRe` method. A single regular expression\n expressing the entire pattern. This is useful in cases where you wish\n to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.\n* `negate` True if the pattern is negated.\n* `comment` True if the pattern is a comment.\n* `empty` True if the pattern is `\"\"`.\n\n### Methods\n\n* `makeRe` Generate the `regexp` member if necessary, and return it.\n Will return `false` if the pattern is invalid.\n* `match(fname)` Return true if the filename matches the pattern, or\n false otherwise.\n* `matchOne(fileArray, patternArray, partial)` Take a `/`-split\n filename, and match it against a single row in the `regExpSet`. This\n method is mainly for internal use, but is exposed so that it can be\n used by a glob-walker that needs to avoid excessive filesystem calls.\n\nAll other methods are internal, and will be called as necessary.\n\n## Functions\n\nThe top-level exported function has a `cache` property, which is an LRU\ncache set to store 100 items. So, calling these methods repeatedly\nwith the same pattern and options will use the same Minimatch object,\nsaving the cost of parsing it multiple times.\n\n### minimatch(path, pattern, options)\n\nMain export. Tests a path against the pattern using the options.\n\n```javascript\nvar isJS = minimatch(file, \"*.js\", { matchBase: true })\n```\n\n### minimatch.filter(pattern, options)\n\nReturns a function that tests its\nsupplied argument, suitable for use with `Array.filter`. Example:\n\n```javascript\nvar javascripts = fileList.filter(minimatch.filter(\"*.js\", {matchBase: true}))\n```\n\n### minimatch.match(list, pattern, options)\n\nMatch against the list of\nfiles, in the style of fnmatch or glob. If nothing is matched, and\noptions.nonull is set, then return a list containing the pattern itself.\n\n```javascript\nvar javascripts = minimatch.match(fileList, \"*.js\", {matchBase: true}))\n```\n\n### minimatch.makeRe(pattern, options)\n\nMake a regular expression object from the pattern.\n\n## Options\n\nAll options are `false` by default.\n\n### debug\n\nDump a ton of stuff to stderr.\n\n### nobrace\n\nDo not expand `{a,b}` and `{1..3}` brace sets.\n\n### noglobstar\n\nDisable `**` matching against multiple folder names.\n\n### dot\n\nAllow patterns to match filenames starting with a period, even if\nthe pattern does not explicitly have a period in that spot.\n\nNote that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`\nis set.\n\n### noext\n\nDisable \"extglob\" style patterns like `+(a|b)`.\n\n### nocase\n\nPerform a case-insensitive match.\n\n### nonull\n\nWhen a match is not found by `minimatch.match`, return a list containing\nthe pattern itself. When set, an empty list is returned if there are\nno matches.\n\n### matchBase\n\nIf set, then patterns without slashes will be matched\nagainst the basename of the path if it contains slashes. For example,\n`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.\n\n### nocomment\n\nSuppress the behavior of treating `#` at the start of a pattern as a\ncomment.\n\n### nonegate\n\nSuppress the behavior of treating a leading `!` character as negation.\n\n### flipNegate\n\nReturns from negate expressions the same as if tLine truncated
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/minimatch/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/minimatch",
|
||||
"_id": "minimatch@0.2.14",
|
||||
"_from": "minimatch@~0.2.11"
|
||||
}
|
||||
Generated
Vendored
+399
@@ -0,0 +1,399 @@
|
||||
// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
|
||||
//
|
||||
// TODO: Some of these tests do very bad things with backslashes, and will
|
||||
// most likely fail badly on windows. They should probably be skipped.
|
||||
|
||||
var tap = require("tap")
|
||||
, globalBefore = Object.keys(global)
|
||||
, mm = require("../")
|
||||
, files = [ "a", "b", "c", "d", "abc"
|
||||
, "abd", "abe", "bb", "bcd"
|
||||
, "ca", "cb", "dd", "de"
|
||||
, "bdir/", "bdir/cfile"]
|
||||
, next = files.concat([ "a-b", "aXb"
|
||||
, ".x", ".y" ])
|
||||
|
||||
|
||||
var patterns =
|
||||
[ "http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test"
|
||||
, ["a*", ["a", "abc", "abd", "abe"]]
|
||||
, ["X*", ["X*"], {nonull: true}]
|
||||
|
||||
// allow null glob expansion
|
||||
, ["X*", []]
|
||||
|
||||
// isaacs: Slightly different than bash/sh/ksh
|
||||
// \\* is not un-escaped to literal "*" in a failed match,
|
||||
// but it does make it get treated as a literal star
|
||||
, ["\\*", ["\\*"], {nonull: true}]
|
||||
, ["\\**", ["\\**"], {nonull: true}]
|
||||
, ["\\*\\*", ["\\*\\*"], {nonull: true}]
|
||||
|
||||
, ["b*/", ["bdir/"]]
|
||||
, ["c*", ["c", "ca", "cb"]]
|
||||
, ["**", files]
|
||||
|
||||
, ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
|
||||
, ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
|
||||
|
||||
, "legendary larry crashes bashes"
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
|
||||
|
||||
, "character classes"
|
||||
, ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
|
||||
, ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
|
||||
"bdir/", "ca", "cb", "dd", "de"]]
|
||||
, ["a*[^c]", ["abd", "abe"]]
|
||||
, function () { files.push("a-b", "aXb") }
|
||||
, ["a[X-]b", ["a-b", "aXb"]]
|
||||
, function () { files.push(".x", ".y") }
|
||||
, ["[^a-c]*", ["d", "dd", "de"]]
|
||||
, function () { files.push("a*b/", "a*b/ooo") }
|
||||
, ["a\\*b/*", ["a*b/ooo"]]
|
||||
, ["a\\*?/*", ["a*b/ooo"]]
|
||||
, ["*\\\\!*", [], {null: true}, ["echo !7"]]
|
||||
, ["*\\!*", ["echo !7"], null, ["echo !7"]]
|
||||
, ["*.\\*", ["r.*"], null, ["r.*"]]
|
||||
, ["a[b]c", ["abc"]]
|
||||
, ["a[\\b]c", ["abc"]]
|
||||
, ["a?c", ["abc"]]
|
||||
, ["a\\*c", [], {null: true}, ["abc"]]
|
||||
, ["", [""], { null: true }, [""]]
|
||||
|
||||
, "http://www.opensource.apple.com/source/bash/bash-23/" +
|
||||
"bash/tests/glob-test"
|
||||
, function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
|
||||
, ["*/man*/bash.*", ["man/man1/bash.1"]]
|
||||
, ["man/man1/bash.1", ["man/man1/bash.1"]]
|
||||
, ["a***c", ["abc"], null, ["abc"]]
|
||||
, ["a*****?c", ["abc"], null, ["abc"]]
|
||||
, ["?*****??", ["abc"], null, ["abc"]]
|
||||
, ["*****??", ["abc"], null, ["abc"]]
|
||||
, ["?*****?c", ["abc"], null, ["abc"]]
|
||||
, ["?***?****c", ["abc"], null, ["abc"]]
|
||||
, ["?***?****?", ["abc"], null, ["abc"]]
|
||||
, ["?***?****", ["abc"], null, ["abc"]]
|
||||
, ["*******c", ["abc"], null, ["abc"]]
|
||||
, ["*******?", ["abc"], null, ["abc"]]
|
||||
, ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["[-abc]", ["-"], null, ["-"]]
|
||||
, ["[abc-]", ["-"], null, ["-"]]
|
||||
, ["\\", ["\\"], null, ["\\"]]
|
||||
, ["[\\\\]", ["\\"], null, ["\\"]]
|
||||
, ["[[]", ["["], null, ["["]]
|
||||
, ["[", ["["], null, ["["]]
|
||||
, ["[*", ["[abc"], null, ["[abc"]]
|
||||
, "a right bracket shall lose its special meaning and\n" +
|
||||
"represent itself in a bracket expression if it occurs\n" +
|
||||
"first in the list. -- POSIX.2 2.8.3.2"
|
||||
, ["[]]", ["]"], null, ["]"]]
|
||||
, ["[]-]", ["]"], null, ["]"]]
|
||||
, ["[a-\z]", ["p"], null, ["p"]]
|
||||
, ["??**********?****?", [], { null: true }, ["abc"]]
|
||||
, ["??**********?****c", [], { null: true }, ["abc"]]
|
||||
, ["?************c****?****", [], { null: true }, ["abc"]]
|
||||
, ["*c*?**", [], { null: true }, ["abc"]]
|
||||
, ["a*****c*?**", [], { null: true }, ["abc"]]
|
||||
, ["a********???*******", [], { null: true }, ["abc"]]
|
||||
, ["[]", [], { null: true }, ["a"]]
|
||||
, ["[abc", [], { null: true }, ["["]]
|
||||
|
||||
, "nocase tests"
|
||||
, ["XYZ", ["xYz"], { nocase: true, null: true }
|
||||
, ["xYz", "ABC", "IjK"]]
|
||||
, ["ab*", ["ABC"], { nocase: true, null: true }
|
||||
, ["xYz", "ABC", "IjK"]]
|
||||
, ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
|
||||
, ["xYz", "ABC", "IjK"]]
|
||||
|
||||
// [ pattern, [matches], MM opts, files, TAP opts]
|
||||
, "onestar/twostar"
|
||||
, ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
|
||||
, ["{/?,*}", ["/a", "bb"], {null: true}
|
||||
, ["/a", "/b/b", "/a/b/c", "bb"]]
|
||||
|
||||
, "dots should not match unless requested"
|
||||
, ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
|
||||
|
||||
// .. and . can only match patterns starting with .,
|
||||
// even when options.dot is set.
|
||||
, function () {
|
||||
files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
|
||||
}
|
||||
, ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
|
||||
, ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
|
||||
, ["a/*/b", ["a/c/b"], {dot:false}]
|
||||
, ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
|
||||
|
||||
|
||||
// this also tests that changing the options needs
|
||||
// to change the cache key, even if the pattern is
|
||||
// the same!
|
||||
, ["**", ["a/b","a/.d",".a/.d"], { dot: true }
|
||||
, [ ".a/.d", "a/.d", "a/b"]]
|
||||
|
||||
, "paren sets cannot contain slashes"
|
||||
, ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
|
||||
|
||||
// brace sets trump all else.
|
||||
//
|
||||
// invalid glob pattern. fails on bash4 and bsdglob.
|
||||
// however, in this implementation, it's easier just
|
||||
// to do the intuitive thing, and let brace-expansion
|
||||
// actually come before parsing any extglob patterns,
|
||||
// like the documentation seems to say.
|
||||
//
|
||||
// XXX: if anyone complains about this, either fix it
|
||||
// or tell them to grow up and stop complaining.
|
||||
//
|
||||
// bash/bsdglob says this:
|
||||
// , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
|
||||
// but we do this instead:
|
||||
, ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
|
||||
|
||||
// test partial parsing in the presence of comment/negation chars
|
||||
, ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
|
||||
, ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
|
||||
|
||||
// like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
|
||||
, ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
|
||||
, ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
|
||||
, {}
|
||||
, ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
|
||||
|
||||
|
||||
// crazy nested {,,} and *(||) tests.
|
||||
, function () {
|
||||
files = [ "a", "b", "c", "d"
|
||||
, "ab", "ac", "ad"
|
||||
, "bc", "cb"
|
||||
, "bc,d", "c,db", "c,d"
|
||||
, "d)", "(b|c", "*(b|c"
|
||||
, "b|c", "b|cc", "cb|c"
|
||||
, "x(a|b|c)", "x(a|c)"
|
||||
, "(a|b|c)", "(a|c)"]
|
||||
}
|
||||
, ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
|
||||
, ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
|
||||
// a
|
||||
// *(b|c)
|
||||
// *(b|d)
|
||||
, ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
|
||||
, ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
|
||||
|
||||
|
||||
// test various flag settings.
|
||||
, [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
|
||||
, { noext: true } ]
|
||||
, ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
|
||||
, ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
|
||||
, ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
|
||||
|
||||
|
||||
// begin channelling Boole and deMorgan...
|
||||
, "negation tests"
|
||||
, function () {
|
||||
files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
|
||||
}
|
||||
|
||||
// anything that is NOT a* matches.
|
||||
, ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
|
||||
|
||||
// anything that IS !a* matches.
|
||||
, ["!a*", ["!ab", "!abc"], {nonegate: true}]
|
||||
|
||||
// anything that IS a* matches
|
||||
, ["!!a*", ["a!b"]]
|
||||
|
||||
// anything that is NOT !a* matches
|
||||
, ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
|
||||
|
||||
// negation nestled within a pattern
|
||||
, function () {
|
||||
files = [ "foo.js"
|
||||
, "foo.bar"
|
||||
// can't match this one without negative lookbehind.
|
||||
, "foo.js.js"
|
||||
, "blar.js"
|
||||
, "foo."
|
||||
, "boo.js.boo" ]
|
||||
}
|
||||
, ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
|
||||
|
||||
// https://github.com/isaacs/minimatch/issues/5
|
||||
, function () {
|
||||
files = [ 'a/b/.x/c'
|
||||
, 'a/b/.x/c/d'
|
||||
, 'a/b/.x/c/d/e'
|
||||
, 'a/b/.x'
|
||||
, 'a/b/.x/'
|
||||
, 'a/.x/b'
|
||||
, '.x'
|
||||
, '.x/'
|
||||
, '.x/a'
|
||||
, '.x/a/b'
|
||||
, 'a/.x/b/.x/c'
|
||||
, '.x/.x' ]
|
||||
}
|
||||
, ["**/.x/**", [ '.x/'
|
||||
, '.x/a'
|
||||
, '.x/a/b'
|
||||
, 'a/.x/b'
|
||||
, 'a/b/.x/'
|
||||
, 'a/b/.x/c'
|
||||
, 'a/b/.x/c/d'
|
||||
, 'a/b/.x/c/d/e' ] ]
|
||||
|
||||
]
|
||||
|
||||
var regexps =
|
||||
[ '/^(?:(?=.)a[^/]*?)$/',
|
||||
'/^(?:(?=.)X[^/]*?)$/',
|
||||
'/^(?:(?=.)X[^/]*?)$/',
|
||||
'/^(?:\\*)$/',
|
||||
'/^(?:(?=.)\\*[^/]*?)$/',
|
||||
'/^(?:\\*\\*)$/',
|
||||
'/^(?:(?=.)b[^/]*?\\/)$/',
|
||||
'/^(?:(?=.)c[^/]*?)$/',
|
||||
'/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
|
||||
'/^(?:\\.\\.\\/(?!\\.)(?=.)[^/]*?\\/)$/',
|
||||
'/^(?:s\\/(?=.)\\.\\.[^/]*?\\/)$/',
|
||||
'/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/1\\/)$/',
|
||||
'/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/\u0001\\/)$/',
|
||||
'/^(?:(?!\\.)(?=.)[a-c]b[^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[a-y][^/]*?[^c])$/',
|
||||
'/^(?:(?=.)a[^/]*?[^c])$/',
|
||||
'/^(?:(?=.)a[X-]b)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^a-c][^/]*?)$/',
|
||||
'/^(?:a\\*b\\/(?!\\.)(?=.)[^/]*?)$/',
|
||||
'/^(?:(?=.)a\\*[^/]\\/(?!\\.)(?=.)[^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\\\\\![^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\![^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\.\\*)$/',
|
||||
'/^(?:(?=.)a[b]c)$/',
|
||||
'/^(?:(?=.)a[b]c)$/',
|
||||
'/^(?:(?=.)a[^/]c)$/',
|
||||
'/^(?:a\\*c)$/',
|
||||
'false',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\/(?=.)man[^/]*?\\/(?=.)bash\\.[^/]*?)$/',
|
||||
'/^(?:man\\/man1\\/bash\\.1)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/]*?c)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
|
||||
'/^(?:(?=.)a[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k[^/]*?[^/]*?[^/]*?)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k[^/]*?[^/]*?)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[-abc])$/',
|
||||
'/^(?:(?!\\.)(?=.)[abc-])$/',
|
||||
'/^(?:\\\\)$/',
|
||||
'/^(?:(?!\\.)(?=.)[\\\\])$/',
|
||||
'/^(?:(?!\\.)(?=.)[\\[])$/',
|
||||
'/^(?:\\[)$/',
|
||||
'/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[\\]])$/',
|
||||
'/^(?:(?!\\.)(?=.)[\\]-])$/',
|
||||
'/^(?:(?!\\.)(?=.)[a-z])$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
|
||||
'/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
|
||||
'/^(?:\\[\\])$/',
|
||||
'/^(?:\\[abc)$/',
|
||||
'/^(?:(?=.)XYZ)$/i',
|
||||
'/^(?:(?=.)ab[^/]*?)$/i',
|
||||
'/^(?:(?!\\.)(?=.)[ia][^/][ck])$/i',
|
||||
'/^(?:\\/(?!\\.)(?=.)[^/]*?|(?!\\.)(?=.)[^/]*?)$/',
|
||||
'/^(?:\\/(?!\\.)(?=.)[^/]|(?!\\.)(?=.)[^/]*?)$/',
|
||||
'/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
|
||||
'/^(?:a\\/(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)[^/]*?\\/b)$/',
|
||||
'/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
|
||||
'/^(?:a\\/(?!\\.)(?=.)[^/]*?\\/b)$/',
|
||||
'/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
|
||||
'/^(?:(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\(a\\/b\\))$/',
|
||||
'/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
|
||||
'/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/',
|
||||
'/^(?:(?=.)\\[(?=.)#a[^/]*?)$/',
|
||||
'/^(?:(?=.)\\+\\(a\\|[^/]*?\\|c\\\\\\\\\\|d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|f\\\\\\\\\\\\\\\\\\|g)$/',
|
||||
'/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
|
||||
'/^(?:a|(?!\\.)(?=.)[^/]*?\\(b\\|c|d\\))$/',
|
||||
'/^(?:a|(?!\\.)(?=.)(?:b|c)*|(?!\\.)(?=.)(?:b|d)*)$/',
|
||||
'/^(?:(?!\\.)(?=.)(?:a|b|c)*|(?!\\.)(?=.)(?:a|c)*)$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\(a\\|b\\|c\\)|(?!\\.)(?=.)[^/]*?\\(a\\|c\\))$/',
|
||||
'/^(?:(?=.)a[^/]b)$/',
|
||||
'/^(?:(?=.)#[^/]*?)$/',
|
||||
'/^(?!^(?:(?=.)a[^/]*?)$).*$/',
|
||||
'/^(?:(?=.)\\!a[^/]*?)$/',
|
||||
'/^(?:(?=.)a[^/]*?)$/',
|
||||
'/^(?!^(?:(?=.)\\!a[^/]*?)$).*$/',
|
||||
'/^(?:(?!\\.)(?=.)[^/]*?\\.(?:(?!js)[^/]*?))$/',
|
||||
'/^(?:(?:(?!(?:\\/|^)\\.).)*?\\/\\.x\\/(?:(?!(?:\\/|^)\\.).)*?)$/' ]
|
||||
var re = 0;
|
||||
|
||||
tap.test("basic tests", function (t) {
|
||||
var start = Date.now()
|
||||
|
||||
// [ pattern, [matches], MM opts, files, TAP opts]
|
||||
patterns.forEach(function (c) {
|
||||
if (typeof c === "function") return c()
|
||||
if (typeof c === "string") return t.comment(c)
|
||||
|
||||
var pattern = c[0]
|
||||
, expect = c[1].sort(alpha)
|
||||
, options = c[2] || {}
|
||||
, f = c[3] || files
|
||||
, tapOpts = c[4] || {}
|
||||
|
||||
// options.debug = true
|
||||
var m = new mm.Minimatch(pattern, options)
|
||||
var r = m.makeRe()
|
||||
var expectRe = regexps[re++]
|
||||
tapOpts.re = String(r) || JSON.stringify(r)
|
||||
tapOpts.files = JSON.stringify(f)
|
||||
tapOpts.pattern = pattern
|
||||
tapOpts.set = m.set
|
||||
tapOpts.negated = m.negate
|
||||
|
||||
var actual = mm.match(f, pattern, options)
|
||||
actual.sort(alpha)
|
||||
|
||||
t.equivalent( actual, expect
|
||||
, JSON.stringify(pattern) + " " + JSON.stringify(expect)
|
||||
, tapOpts )
|
||||
|
||||
t.equal(tapOpts.re, expectRe, tapOpts)
|
||||
})
|
||||
|
||||
t.comment("time=" + (Date.now() - start) + "ms")
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test("global leak test", function (t) {
|
||||
var globalAfter = Object.keys(global)
|
||||
t.equivalent(globalAfter, globalBefore, "no new globals, please")
|
||||
t.end()
|
||||
})
|
||||
|
||||
function alpha (a, b) {
|
||||
return a > b ? 1 : -1
|
||||
}
|
||||
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
var tap = require("tap")
|
||||
, minimatch = require("../")
|
||||
|
||||
tap.test("brace expansion", function (t) {
|
||||
// [ pattern, [expanded] ]
|
||||
; [ [ "a{b,c{d,e},{f,g}h}x{y,z}"
|
||||
, [ "abxy"
|
||||
, "abxz"
|
||||
, "acdxy"
|
||||
, "acdxz"
|
||||
, "acexy"
|
||||
, "acexz"
|
||||
, "afhxy"
|
||||
, "afhxz"
|
||||
, "aghxy"
|
||||
, "aghxz" ] ]
|
||||
, [ "a{1..5}b"
|
||||
, [ "a1b"
|
||||
, "a2b"
|
||||
, "a3b"
|
||||
, "a4b"
|
||||
, "a5b" ] ]
|
||||
, [ "a{b}c", ["a{b}c"] ]
|
||||
].forEach(function (tc) {
|
||||
var p = tc[0]
|
||||
, expect = tc[1]
|
||||
t.equivalent(minimatch.braceExpand(p), expect, p)
|
||||
})
|
||||
console.error("ending")
|
||||
t.end()
|
||||
})
|
||||
|
||||
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
var Minimatch = require("../minimatch.js").Minimatch
|
||||
var tap = require("tap")
|
||||
tap.test("cache test", function (t) {
|
||||
var mm1 = new Minimatch("a?b")
|
||||
var mm2 = new Minimatch("a?b")
|
||||
t.equal(mm1, mm2, "should get the same object")
|
||||
// the lru should drop it after 100 entries
|
||||
for (var i = 0; i < 100; i ++) {
|
||||
new Minimatch("a"+i)
|
||||
}
|
||||
mm2 = new Minimatch("a?b")
|
||||
t.notEqual(mm1, mm2, "cache should have dropped")
|
||||
t.end()
|
||||
})
|
||||
node_modules/gulp/node_modules/findup-sync/node_modules/glob/node_modules/minimatch/test/defaults.js
Generated
Vendored
+274
@@ -0,0 +1,274 @@
|
||||
// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test
|
||||
//
|
||||
// TODO: Some of these tests do very bad things with backslashes, and will
|
||||
// most likely fail badly on windows. They should probably be skipped.
|
||||
|
||||
var tap = require("tap")
|
||||
, globalBefore = Object.keys(global)
|
||||
, mm = require("../")
|
||||
, files = [ "a", "b", "c", "d", "abc"
|
||||
, "abd", "abe", "bb", "bcd"
|
||||
, "ca", "cb", "dd", "de"
|
||||
, "bdir/", "bdir/cfile"]
|
||||
, next = files.concat([ "a-b", "aXb"
|
||||
, ".x", ".y" ])
|
||||
|
||||
tap.test("basic tests", function (t) {
|
||||
var start = Date.now()
|
||||
|
||||
// [ pattern, [matches], MM opts, files, TAP opts]
|
||||
; [ "http://www.bashcookbook.com/bashinfo" +
|
||||
"/source/bash-1.14.7/tests/glob-test"
|
||||
, ["a*", ["a", "abc", "abd", "abe"]]
|
||||
, ["X*", ["X*"], {nonull: true}]
|
||||
|
||||
// allow null glob expansion
|
||||
, ["X*", []]
|
||||
|
||||
// isaacs: Slightly different than bash/sh/ksh
|
||||
// \\* is not un-escaped to literal "*" in a failed match,
|
||||
// but it does make it get treated as a literal star
|
||||
, ["\\*", ["\\*"], {nonull: true}]
|
||||
, ["\\**", ["\\**"], {nonull: true}]
|
||||
, ["\\*\\*", ["\\*\\*"], {nonull: true}]
|
||||
|
||||
, ["b*/", ["bdir/"]]
|
||||
, ["c*", ["c", "ca", "cb"]]
|
||||
, ["**", files]
|
||||
|
||||
, ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
|
||||
, ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
|
||||
|
||||
, "legendary larry crashes bashes"
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
|
||||
, ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
|
||||
|
||||
, "character classes"
|
||||
, ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
|
||||
, ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
|
||||
"bdir/", "ca", "cb", "dd", "de"]]
|
||||
, ["a*[^c]", ["abd", "abe"]]
|
||||
, function () { files.push("a-b", "aXb") }
|
||||
, ["a[X-]b", ["a-b", "aXb"]]
|
||||
, function () { files.push(".x", ".y") }
|
||||
, ["[^a-c]*", ["d", "dd", "de"]]
|
||||
, function () { files.push("a*b/", "a*b/ooo") }
|
||||
, ["a\\*b/*", ["a*b/ooo"]]
|
||||
, ["a\\*?/*", ["a*b/ooo"]]
|
||||
, ["*\\\\!*", [], {null: true}, ["echo !7"]]
|
||||
, ["*\\!*", ["echo !7"], null, ["echo !7"]]
|
||||
, ["*.\\*", ["r.*"], null, ["r.*"]]
|
||||
, ["a[b]c", ["abc"]]
|
||||
, ["a[\\b]c", ["abc"]]
|
||||
, ["a?c", ["abc"]]
|
||||
, ["a\\*c", [], {null: true}, ["abc"]]
|
||||
, ["", [""], { null: true }, [""]]
|
||||
|
||||
, "http://www.opensource.apple.com/source/bash/bash-23/" +
|
||||
"bash/tests/glob-test"
|
||||
, function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
|
||||
, ["*/man*/bash.*", ["man/man1/bash.1"]]
|
||||
, ["man/man1/bash.1", ["man/man1/bash.1"]]
|
||||
, ["a***c", ["abc"], null, ["abc"]]
|
||||
, ["a*****?c", ["abc"], null, ["abc"]]
|
||||
, ["?*****??", ["abc"], null, ["abc"]]
|
||||
, ["*****??", ["abc"], null, ["abc"]]
|
||||
, ["?*****?c", ["abc"], null, ["abc"]]
|
||||
, ["?***?****c", ["abc"], null, ["abc"]]
|
||||
, ["?***?****?", ["abc"], null, ["abc"]]
|
||||
, ["?***?****", ["abc"], null, ["abc"]]
|
||||
, ["*******c", ["abc"], null, ["abc"]]
|
||||
, ["*******?", ["abc"], null, ["abc"]]
|
||||
, ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
|
||||
, ["[-abc]", ["-"], null, ["-"]]
|
||||
, ["[abc-]", ["-"], null, ["-"]]
|
||||
, ["\\", ["\\"], null, ["\\"]]
|
||||
, ["[\\\\]", ["\\"], null, ["\\"]]
|
||||
, ["[[]", ["["], null, ["["]]
|
||||
, ["[", ["["], null, ["["]]
|
||||
, ["[*", ["[abc"], null, ["[abc"]]
|
||||
, "a right bracket shall lose its special meaning and\n" +
|
||||
"represent itself in a bracket expression if it occurs\n" +
|
||||
"first in the list. -- POSIX.2 2.8.3.2"
|
||||
, ["[]]", ["]"], null, ["]"]]
|
||||
, ["[]-]", ["]"], null, ["]"]]
|
||||
, ["[a-\z]", ["p"], null, ["p"]]
|
||||
, ["??**********?****?", [], { null: true }, ["abc"]]
|
||||
, ["??**********?****c", [], { null: true }, ["abc"]]
|
||||
, ["?************c****?****", [], { null: true }, ["abc"]]
|
||||
, ["*c*?**", [], { null: true }, ["abc"]]
|
||||
, ["a*****c*?**", [], { null: true }, ["abc"]]
|
||||
, ["a********???*******", [], { null: true }, ["abc"]]
|
||||
, ["[]", [], { null: true }, ["a"]]
|
||||
, ["[abc", [], { null: true }, ["["]]
|
||||
|
||||
, "nocase tests"
|
||||
, ["XYZ", ["xYz"], { nocase: true, null: true }
|
||||
, ["xYz", "ABC", "IjK"]]
|
||||
, ["ab*", ["ABC"], { nocase: true, null: true }
|
||||
, ["xYz", "ABC", "IjK"]]
|
||||
, ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
|
||||
, ["xYz", "ABC", "IjK"]]
|
||||
|
||||
// [ pattern, [matches], MM opts, files, TAP opts]
|
||||
, "onestar/twostar"
|
||||
, ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
|
||||
, ["{/?,*}", ["/a", "bb"], {null: true}
|
||||
, ["/a", "/b/b", "/a/b/c", "bb"]]
|
||||
|
||||
, "dots should not match unless requested"
|
||||
, ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
|
||||
|
||||
// .. and . can only match patterns starting with .,
|
||||
// even when options.dot is set.
|
||||
, function () {
|
||||
files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
|
||||
}
|
||||
, ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
|
||||
, ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
|
||||
, ["a/*/b", ["a/c/b"], {dot:false}]
|
||||
, ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
|
||||
|
||||
|
||||
// this also tests that changing the options needs
|
||||
// to change the cache key, even if the pattern is
|
||||
// the same!
|
||||
, ["**", ["a/b","a/.d",".a/.d"], { dot: true }
|
||||
, [ ".a/.d", "a/.d", "a/b"]]
|
||||
|
||||
, "paren sets cannot contain slashes"
|
||||
, ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
|
||||
|
||||
// brace sets trump all else.
|
||||
//
|
||||
// invalid glob pattern. fails on bash4 and bsdglob.
|
||||
// however, in this implementation, it's easier just
|
||||
// to do the intuitive thing, and let brace-expansion
|
||||
// actually come before parsing any extglob patterns,
|
||||
// like the documentation seems to say.
|
||||
//
|
||||
// XXX: if anyone complains about this, either fix it
|
||||
// or tell them to grow up and stop complaining.
|
||||
//
|
||||
// bash/bsdglob says this:
|
||||
// , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
|
||||
// but we do this instead:
|
||||
, ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
|
||||
|
||||
// test partial parsing in the presence of comment/negation chars
|
||||
, ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
|
||||
, ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
|
||||
|
||||
// like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
|
||||
, ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
|
||||
, ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
|
||||
, {}
|
||||
, ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
|
||||
|
||||
|
||||
// crazy nested {,,} and *(||) tests.
|
||||
, function () {
|
||||
files = [ "a", "b", "c", "d"
|
||||
, "ab", "ac", "ad"
|
||||
, "bc", "cb"
|
||||
, "bc,d", "c,db", "c,d"
|
||||
, "d)", "(b|c", "*(b|c"
|
||||
, "b|c", "b|cc", "cb|c"
|
||||
, "x(a|b|c)", "x(a|c)"
|
||||
, "(a|b|c)", "(a|c)"]
|
||||
}
|
||||
, ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
|
||||
, ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
|
||||
// a
|
||||
// *(b|c)
|
||||
// *(b|d)
|
||||
, ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
|
||||
, ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
|
||||
|
||||
|
||||
// test various flag settings.
|
||||
, [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
|
||||
, { noext: true } ]
|
||||
, ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
|
||||
, ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
|
||||
, ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
|
||||
|
||||
|
||||
// begin channelling Boole and deMorgan...
|
||||
, "negation tests"
|
||||
, function () {
|
||||
files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
|
||||
}
|
||||
|
||||
// anything that is NOT a* matches.
|
||||
, ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
|
||||
|
||||
// anything that IS !a* matches.
|
||||
, ["!a*", ["!ab", "!abc"], {nonegate: true}]
|
||||
|
||||
// anything that IS a* matches
|
||||
, ["!!a*", ["a!b"]]
|
||||
|
||||
// anything that is NOT !a* matches
|
||||
, ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
|
||||
|
||||
// negation nestled within a pattern
|
||||
, function () {
|
||||
files = [ "foo.js"
|
||||
, "foo.bar"
|
||||
// can't match this one without negative lookbehind.
|
||||
, "foo.js.js"
|
||||
, "blar.js"
|
||||
, "foo."
|
||||
, "boo.js.boo" ]
|
||||
}
|
||||
, ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
|
||||
|
||||
].forEach(function (c) {
|
||||
if (typeof c === "function") return c()
|
||||
if (typeof c === "string") return t.comment(c)
|
||||
|
||||
var pattern = c[0]
|
||||
, expect = c[1].sort(alpha)
|
||||
, options = c[2] || {}
|
||||
, f = c[3] || files
|
||||
, tapOpts = c[4] || {}
|
||||
|
||||
// options.debug = true
|
||||
var Class = mm.defaults(options).Minimatch
|
||||
var m = new Class(pattern, {})
|
||||
var r = m.makeRe()
|
||||
tapOpts.re = String(r) || JSON.stringify(r)
|
||||
tapOpts.files = JSON.stringify(f)
|
||||
tapOpts.pattern = pattern
|
||||
tapOpts.set = m.set
|
||||
tapOpts.negated = m.negate
|
||||
|
||||
var actual = mm.match(f, pattern, options)
|
||||
actual.sort(alpha)
|
||||
|
||||
t.equivalent( actual, expect
|
||||
, JSON.stringify(pattern) + " " + JSON.stringify(expect)
|
||||
, tapOpts )
|
||||
})
|
||||
|
||||
t.comment("time=" + (Date.now() - start) + "ms")
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test("global leak test", function (t) {
|
||||
var globalAfter = Object.keys(global)
|
||||
t.equivalent(globalAfter, globalBefore, "no new globals, please")
|
||||
t.end()
|
||||
})
|
||||
|
||||
function alpha (a, b) {
|
||||
return a > b ? 1 : -1
|
||||
}
|
||||
Loaded 100 of 3159 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user