付智勇

no message

正在显示 51 个修改的文件 包含 4413 行增加2 行删除

要显示太多修改。

为保证性能只显示 51 of 51+ 个文件。

... ... @@ -6,12 +6,23 @@ const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger')
const cors = require('koa-cors');
const koaBody = require('koa-body');
const route = require('koa-router');
// app.use(route.post('/profile', uploads.single('avatar')));
const index = require('./routes/index')
const users = require('./routes/users')
const meeting = require('./routes/meeting')
const studentMeeting = require('./routes/studentMeeting')
const upload = require('./routes/upload')
const email = require('./routes/email')
const filterUrl = require(__dirname+'/util/filterUrl')
var tokenUtil = require('./util/tokenUtil');
const _ = require('lodash');
... ... @@ -20,6 +31,8 @@ var status = require('./util/resTemplate')
// error handler
onerror(app)
app.use(koaBody({ multipart: true }));
// middlewares
app.use(cors());
app.use(bodyparser({
... ... @@ -30,9 +43,11 @@ app.use(logger())
app.use(require('koa-static')(__dirname + '/public'))
app.use(views(__dirname + '/views', {
extension: 'pug'
extension: 'html'
}))
// logger
app.use(async (ctx, next) => {
try{
... ... @@ -60,6 +75,10 @@ app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())
app.use(meeting.routes(), meeting.allowedMethods())
app.use(studentMeeting.routes(), studentMeeting.allowedMethods())
app.use(upload.routes(), upload.allowedMethods())
app.use(email.routes(), email.allowedMethods())
... ...

32 字节

../which/bin/which
\ No newline at end of file
... ...
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store*
ehthumbs.db
Icon?
Thumbs.db
# Node.js #
###########
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
# Components #
##############
/build
/components
\ No newline at end of file
... ...
language: node_js
node_js:
- "0.4"
- "0.6"
- "0.8"
- "0.10"
\ No newline at end of file
... ...
# Array Series [![Build Status](https://travis-ci.org/component/array-parallel.png)](https://travis-ci.org/component/array-parallel)
Call an array of asynchronous functions in parallel
### API
#### parallel(fns[, context[, callback]])
```js
var parallel = require('array-parallel')
parallel([
function (done) {
done()
}
], this, function (err) {
})
```
#### fns
`fns` is an array of functions to call in parallel.
The argument signature should be:
```js
function (done) {
done(new Error())
// or
done(null, result)
}
```
That is, each function should only take a `done` as an argument.
Each callback should only take an `Error` as the first argument,
or a value as the second.
#### context
Optional context to pass to each `fn`.
Basically `fn.call(context, done)`.
#### callback(err, results)
```js
function (err, results) {
}
```
Only argument is an `Error` argument.
It will be the first error retrieved from all the `fns`.
`results` will be an array of results from each `fn`,
thus this could be considered an asynchronous version of `[].map`.
### License
The MIT License (MIT)
Copyright (c) 2013 Jonathan Ong me@jongleberry.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.
... ...
{
"name": "array-parallel",
"description": "Call an array of asynchronous functions in parallel",
"repo": "array-parallel",
"version": "0.1.3",
"main": "index.js",
"scripts": [
"index.js"
],
"license": "MIT"
}
\ No newline at end of file
... ...
module.exports = function parallel(fns, context, callback) {
if (!callback) {
if (typeof context === 'function') {
callback = context
context = null
} else {
callback = noop
}
}
var pending = fns && fns.length
if (!pending) return callback(null, []);
var finished = false
var results = new Array(pending)
fns.forEach(context ? function (fn, i) {
fn.call(context, maybeDone(i))
} : function (fn, i) {
fn(maybeDone(i))
})
function maybeDone(i) {
return function (err, result) {
if (finished) return;
if (err) {
callback(err, results)
finished = true
return
}
results[i] = result
if (!--pending) callback(null, results);
}
}
}
function noop() {}
... ...
{
"_args": [
[
{
"raw": "array-parallel@~0.1.3",
"scope": null,
"escapedName": "array-parallel",
"name": "array-parallel",
"rawSpec": "~0.1.3",
"spec": ">=0.1.3 <0.2.0",
"type": "range"
},
"/Users/fzy/project/koa2_Sequelize_project/node_modules/gm"
]
],
"_from": "array-parallel@>=0.1.3 <0.2.0",
"_id": "array-parallel@0.1.3",
"_inCache": true,
"_location": "/array-parallel",
"_npmUser": {
"name": "jongleberry",
"email": "jonathanrichardong@gmail.com"
},
"_npmVersion": "1.3.17",
"_phantomChildren": {},
"_requested": {
"raw": "array-parallel@~0.1.3",
"scope": null,
"escapedName": "array-parallel",
"name": "array-parallel",
"rawSpec": "~0.1.3",
"spec": ">=0.1.3 <0.2.0",
"type": "range"
},
"_requiredBy": [
"/gm"
],
"_resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz",
"_shasum": "8f785308926ed5aa478c47e64d1b334b6c0c947d",
"_shrinkwrap": null,
"_spec": "array-parallel@~0.1.3",
"_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/gm",
"author": {
"name": "Jonathan Ong",
"email": "me@jongleberry.com",
"url": "http://jongleberry.com"
},
"bugs": {
"url": "https://github.com/component/array-parallel/issues",
"email": "me@jongleberry.com"
},
"dependencies": {},
"description": "Call an array of asynchronous functions in parallel",
"devDependencies": {},
"directories": {},
"dist": {
"shasum": "8f785308926ed5aa478c47e64d1b334b6c0c947d",
"tarball": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz"
},
"homepage": "https://github.com/component/array-parallel#readme",
"license": "MIT",
"maintainers": [
{
"name": "jongleberry",
"email": "jonathanrichardong@gmail.com"
}
],
"name": "array-parallel",
"optionalDependencies": {},
"readme": "# Array Series [![Build Status](https://travis-ci.org/component/array-parallel.png)](https://travis-ci.org/component/array-parallel)\n\nCall an array of asynchronous functions in parallel\n\n### API\n\n#### parallel(fns[, context[, callback]])\n\n```js\nvar parallel = require('array-parallel')\n\nparallel([\n function (done) {\n done()\n }\n], this, function (err) {\n\n})\n```\n\n#### fns\n\n`fns` is an array of functions to call in parallel.\nThe argument signature should be:\n\n```js\nfunction (done) {\n done(new Error())\n // or\n done(null, result)\n}\n```\n\nThat is, each function should only take a `done` as an argument.\nEach callback should only take an `Error` as the first argument,\nor a value as the second.\n\n#### context\n\nOptional context to pass to each `fn`.\nBasically `fn.call(context, done)`.\n\n#### callback(err, results)\n\n```js\nfunction (err, results) {\n\n}\n```\n\nOnly argument is an `Error` argument.\nIt will be the first error retrieved from all the `fns`.\n`results` will be an array of results from each `fn`,\nthus this could be considered an asynchronous version of `[].map`.\n\n### License\n\nThe MIT License (MIT)\n\nCopyright (c) 2013 Jonathan Ong me@jongleberry.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "git+https://github.com/component/array-parallel.git"
},
"scripts": {
"test": "node test"
},
"version": "0.1.3"
}
... ...
var assert = require('assert')
var parallel = require('./')
var a, b, c
parallel([
function (done) {
setTimeout(function () {
done(null, a = 0)
}, 5)
},
function (done) {
setTimeout(function () {
done(null, b = 1)
}, 10)
},
function (done) {
setTimeout(function () {
done(null, c = 2)
}, 15)
}
], function (err, results) {
assert.equal(a, 0)
assert.equal(b, 1)
assert.equal(c, 2)
assert.deepEqual(results, [0, 1, 2])
})
var d, e
parallel([
function (done) {
setTimeout(function () {
d = 1
done(new Error('message'))
}, 5)
},
function (done) {
setTimeout(function () {
e = 2
done()
}, 10)
}
], function (err) {
assert.equal(err.message, 'message')
assert.equal(d, 1)
assert.equal(e, undefined)
})
var context = 'hello'
parallel([function (done) {
assert.equal(this, context)
}], context)
var f
parallel([function (done) {
f = true
done()
}])
process.nextTick(function () {
assert.equal(f, true)
})
\ No newline at end of file
... ...
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store*
ehthumbs.db
Icon?
Thumbs.db
# Node.js #
###########
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
# Components #
##############
/build
/components
\ No newline at end of file
... ...
language: node_js
node_js:
- "0.8"
- "0.10"
\ No newline at end of file
... ...
# Array Series [![Build Status](https://travis-ci.org/component/array-series.png)](https://travis-ci.org/component/array-series)
Call an array of asynchronous functions in series
### API
#### series(fns[, context[, callback]])
```js
var series = require('array-series')
series([
function (done) {
done()
}
], this, function (err) {
})
```
#### fns
`fns` is an array of functions to call in series.
The argument signature should be:
```js
function (done) {
done(new Error())
// or
done()
}
```
That is, each function should only take a `done` as an argument.
Each callback should only take an optional `Error` as an argument.
#### context
Optional context to pass to each `fn`.
Basically `fn.call(context, done)`.
#### callback(err)
```js
function (err) {
}
```
Only argument is an `Error` argument.
It will return the first error in the series of functions that returns an error,
and no function after will be called.
### License
The MIT License (MIT)
Copyright (c) 2013 Jonathan Ong me@jongleberry.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.
\ No newline at end of file
... ...
{
"name": "array-series",
"description": "Call an array of asynchronous functions in series",
"repo": "component/array-series",
"version": "0.1.5",
"main": "index.js",
"scripts": [
"index.js"
],
"license": "MIT"
}
\ No newline at end of file
... ...
module.exports = function series(fns, context, callback) {
if (!callback) {
if (typeof context === 'function') {
callback = context
context = null
} else {
callback = noop
}
}
if (!(fns && fns.length)) return callback();
fns = fns.slice(0)
var call = context
? function () {
fns.length
? fns.shift().call(context, next)
: callback()
}
: function () {
fns.length
? fns.shift()(next)
: callback()
}
call()
function next(err) {
err ? callback(err) : call()
}
}
function noop() {}
... ...
{
"_args": [
[
{
"raw": "array-series@~0.1.5",
"scope": null,
"escapedName": "array-series",
"name": "array-series",
"rawSpec": "~0.1.5",
"spec": ">=0.1.5 <0.2.0",
"type": "range"
},
"/Users/fzy/project/koa2_Sequelize_project/node_modules/gm"
]
],
"_from": "array-series@>=0.1.5 <0.2.0",
"_id": "array-series@0.1.5",
"_inCache": true,
"_location": "/array-series",
"_npmUser": {
"name": "jongleberry",
"email": "jonathanrichardong@gmail.com"
},
"_npmVersion": "1.3.17",
"_phantomChildren": {},
"_requested": {
"raw": "array-series@~0.1.5",
"scope": null,
"escapedName": "array-series",
"name": "array-series",
"rawSpec": "~0.1.5",
"spec": ">=0.1.5 <0.2.0",
"type": "range"
},
"_requiredBy": [
"/gm"
],
"_resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz",
"_shasum": "df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f",
"_shrinkwrap": null,
"_spec": "array-series@~0.1.5",
"_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/gm",
"author": {
"name": "Jonathan Ong",
"email": "me@jongleberry.com",
"url": "http://jongleberry.com"
},
"bugs": {
"url": "https://github.com/component/array-series/issues",
"email": "me@jongleberry.com"
},
"dependencies": {},
"description": "Call an array of asynchronous functions in series",
"devDependencies": {},
"directories": {},
"dist": {
"shasum": "df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f",
"tarball": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz"
},
"homepage": "https://github.com/component/array-series#readme",
"license": "MIT",
"maintainers": [
{
"name": "jongleberry",
"email": "jonathanrichardong@gmail.com"
}
],
"name": "array-series",
"optionalDependencies": {},
"readme": "# Array Series [![Build Status](https://travis-ci.org/component/array-series.png)](https://travis-ci.org/component/array-series)\n\nCall an array of asynchronous functions in series\n\n### API\n\n#### series(fns[, context[, callback]])\n\n```js\nvar series = require('array-series')\n\nseries([\n function (done) {\n done()\n }\n], this, function (err) {\n\n})\n```\n\n#### fns\n\n`fns` is an array of functions to call in series.\nThe argument signature should be:\n\n```js\nfunction (done) {\n done(new Error())\n // or\n done()\n}\n```\n\nThat is, each function should only take a `done` as an argument.\nEach callback should only take an optional `Error` as an argument.\n\n#### context\n\nOptional context to pass to each `fn`.\nBasically `fn.call(context, done)`.\n\n#### callback(err)\n\n```js\nfunction (err) {\n\n}\n```\n\nOnly argument is an `Error` argument.\nIt will return the first error in the series of functions that returns an error,\nand no function after will be called.\n\n### License\n\nThe MIT License (MIT)\n\nCopyright (c) 2013 Jonathan Ong me@jongleberry.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "git+https://github.com/component/array-series.git"
},
"scripts": {
"test": "node test"
},
"version": "0.1.5"
}
... ...
var assert = require('assert')
var series = require('./')
var a, b, c
series([
function (done) {
a = 1
process.nextTick(done)
check('a')
},
function (done) {
b = 2
process.nextTick(done)
check('b')
},
function (done) {
c = 3
process.nextTick(done)
check('c')
}
], function (err) {
assert.ifError(err)
assert.equal(a, 1)
assert.equal(b, 2)
assert.equal(c, 3)
})
function check(x) {
switch (x) {
case 'a':
assert.equal(a, 1)
assert.equal(b, undefined)
assert.equal(c, undefined)
break
case 'b':
assert.equal(a, 1)
assert.equal(b, 2)
assert.equal(c, undefined)
break
case 'c':
assert.equal(a, 1)
assert.equal(b, 2)
assert.equal(c, 3)
break
}
}
var context = 'hello'
series([function (done) {
assert.equal(this, context)
done()
}], context)
var finished
series([], function (err) {
finished = true
})
process.nextTick(function () {
if (!finished)
throw new Error('Failed with no functions.');
})
var r, d, o
series([
function (done) {
r = 1
process.nextTick(done)
},
function (done) {
d = 0
process.nextTick(function () {
done(new Error('message'))
})
},
function (done) {
o = 0
process.nextTick(done)
}
], function (err) {
assert.equal(err.message, 'message')
assert.equal(r, 1)
assert.equal(d, 0)
assert.equal(o, undefined)
})
console.log('Array series tests pass!')
\ No newline at end of file
... ...
Copyright (c) 2014 IndigoUnited
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.
... ...
# cross-spawn
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build status][appveyor-image]][appveyor-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url]
[npm-url]:https://npmjs.org/package/cross-spawn
[downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg
[npm-image]:http://img.shields.io/npm/v/cross-spawn.svg
[travis-url]:https://travis-ci.org/IndigoUnited/node-cross-spawn
[travis-image]:http://img.shields.io/travis/IndigoUnited/node-cross-spawn/master.svg
[appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn
[appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg
[david-dm-url]:https://david-dm.org/IndigoUnited/node-cross-spawn
[david-dm-image]:https://img.shields.io/david/IndigoUnited/node-cross-spawn.svg
[david-dm-dev-url]:https://david-dm.org/IndigoUnited/node-cross-spawn#info=devDependencies
[david-dm-dev-image]:https://img.shields.io/david/dev/IndigoUnited/node-cross-spawn.svg
A cross platform solution to node's spawn and spawnSync.
## Installation
`$ npm install cross-spawn`
If you are using `spawnSync` on node 0.10 or older, you will also need to install `spawn-sync`:
`$ npm install spawn-sync`
## Why
Node has issues when using spawn on Windows:
- It ignores [PATHEXT](https://github.com/joyent/node/issues/2318)
- It does not support [shebangs](http://pt.wikipedia.org/wiki/Shebang)
- It does not allow you to run `del` or `dir`
- It does not properly escape arguments with spaces or special characters
All these issues are handled correctly by `cross-spawn`.
There are some known modules, such as [win-spawn](https://github.com/ForbesLindesay/win-spawn), that try to solve this but they are either broken or provide faulty escaping of shell arguments.
## Usage
Exactly the same way as node's [`spawn`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) or [`spawnSync`](https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options), so it's a drop in replacement.
```javascript
var spawn = require('cross-spawn');
// Spawn NPM asynchronously
var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
// Spawn NPM synchronously
var results = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
```
## Caveat
On Windows, cross-spawn will only spawn `cmd.exe` if necessary. If the extension
of the executable is `.exe` or `.com`, it will spawn it directly. If you wish
to override this behavior and *always* spawn a shell, pass the `{shell: true}`
option.
## Tests
`$ npm test`
## License
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
... ...
'use strict';
var cp = require('child_process');
var parse = require('./lib/parse');
var enoent = require('./lib/enoent');
var cpSpawnSync = cp.spawnSync;
function spawn(command, args, options) {
var parsed;
var spawned;
// Parse the arguments
parsed = parse(command, args, options);
// Spawn the child process
spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
// Hook into child process "exit" event to emit an error if the command
// does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
enoent.hookChildProcess(spawned, parsed);
return spawned;
}
function spawnSync(command, args, options) {
var parsed;
var result;
if (!cpSpawnSync) {
try {
cpSpawnSync = require('spawn-sync'); // eslint-disable-line global-require
} catch (ex) {
throw new Error(
'In order to use spawnSync on node 0.10 or older, you must ' +
'install spawn-sync:\n\n' +
' npm install spawn-sync --save'
);
}
}
// Parse the arguments
parsed = parse(command, args, options);
// Spawn the child process
result = cpSpawnSync(parsed.command, parsed.args, parsed.options);
// Analyze if the command does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
return result;
}
module.exports = spawn;
module.exports.spawn = spawn;
module.exports.sync = spawnSync;
module.exports._parse = parse;
module.exports._enoent = enoent;
... ...
'use strict';
var isWin = process.platform === 'win32';
var resolveCommand = require('./resolveCommand');
var isNode10 = process.version.indexOf('v0.10.') === 0;
function notFoundError(command, syscall) {
var err;
err = new Error(syscall + ' ' + command + ' ENOENT');
err.code = err.errno = 'ENOENT';
err.syscall = syscall + ' ' + command;
return err;
}
function hookChildProcess(cp, parsed) {
var originalEmit;
if (!isWin) {
return;
}
originalEmit = cp.emit;
cp.emit = function (name, arg1) {
var err;
// If emitting "exit" event and exit code is 1, we need to check if
// the command exists and emit an "error" instead
// See: https://github.com/IndigoUnited/node-cross-spawn/issues/16
if (name === 'exit') {
err = verifyENOENT(arg1, parsed, 'spawn');
if (err) {
return originalEmit.call(cp, 'error', err);
}
}
return originalEmit.apply(cp, arguments);
};
}
function verifyENOENT(status, parsed) {
if (isWin && status === 1 && !parsed.file) {
return notFoundError(parsed.original, 'spawn');
}
return null;
}
function verifyENOENTSync(status, parsed) {
if (isWin && status === 1 && !parsed.file) {
return notFoundError(parsed.original, 'spawnSync');
}
// If we are in node 10, then we are using spawn-sync; if it exited
// with -1 it probably means that the command does not exist
if (isNode10 && status === -1) {
parsed.file = isWin ? parsed.file : resolveCommand(parsed.original);
if (!parsed.file) {
return notFoundError(parsed.original, 'spawnSync');
}
}
return null;
}
module.exports.hookChildProcess = hookChildProcess;
module.exports.verifyENOENT = verifyENOENT;
module.exports.verifyENOENTSync = verifyENOENTSync;
module.exports.notFoundError = notFoundError;
... ...
'use strict';
module.exports = (function () {
if (process.platform !== 'win32') {
return false;
}
var nodeVer = process.version.substr(1).split('.').map(function (num) {
return parseInt(num, 10);
});
return (nodeVer[0] === 0 && nodeVer[1] < 12);
})();
... ...
'use strict';
var fs = require('fs');
var LRU = require('lru-cache');
var resolveCommand = require('./resolveCommand');
var hasBrokenSpawn = require('./hasBrokenSpawn');
var isWin = process.platform === 'win32';
var shebangCache = new LRU({ max: 50, maxAge: 30 * 1000 }); // Cache just for 30sec
function readShebang(command) {
var buffer;
var fd;
var match;
var shebang;
// Check if it is in the cache first
if (shebangCache.has(command)) {
return shebangCache.get(command);
}
// Read the first 150 bytes from the file
buffer = new Buffer(150);
try {
fd = fs.openSync(command, 'r');
fs.readSync(fd, buffer, 0, 150, 0);
fs.closeSync(fd);
} catch (e) { /* empty */ }
// Check if it is a shebang
match = buffer.toString().trim().match(/#!(.+)/i);
if (match) {
shebang = match[1].replace(/\/usr\/bin\/env\s+/i, ''); // Remove /usr/bin/env
}
// Store the shebang in the cache
shebangCache.set(command, shebang);
return shebang;
}
function escapeArg(arg, quote) {
// Convert to string
arg = '' + arg;
// If we are not going to quote the argument,
// escape shell metacharacters, including double and single quotes:
if (!quote) {
arg = arg.replace(/([\(\)%!\^<>&|;,"'\s])/g, '^$1');
} else {
// Sequence of backslashes followed by a double quote:
// double up all the backslashes and escape the double quote
arg = arg.replace(/(\\*)"/g, '$1$1\\"');
// Sequence of backslashes followed by the end of the string
// (which will become a double quote later):
// double up all the backslashes
arg = arg.replace(/(\\*)$/, '$1$1');
// All other backslashes occur literally
// Quote the whole thing:
arg = '"' + arg + '"';
}
return arg;
}
function escapeCommand(command) {
// Do not escape if this command is not dangerous..
// We do this so that commands like "echo" or "ifconfig" work
// Quoting them, will make them unaccessible
return /^[a-z0-9_-]+$/i.test(command) ? command : escapeArg(command, true);
}
function requiresShell(command) {
return !/\.(?:com|exe)$/i.test(command);
}
function parse(command, args, options) {
var shebang;
var applyQuotes;
var file;
var original;
var shell;
// Normalize arguments, similar to nodejs
if (args && !Array.isArray(args)) {
options = args;
args = null;
}
args = args ? args.slice(0) : []; // Clone array to avoid changing the original
options = options || {};
original = command;
if (isWin) {
// Detect & add support for shebangs
file = resolveCommand(command);
file = file || resolveCommand(command, true);
shebang = file && readShebang(file);
shell = options.shell || hasBrokenSpawn;
if (shebang) {
args.unshift(file);
command = shebang;
shell = shell || requiresShell(resolveCommand(shebang) || resolveCommand(shebang, true));
} else {
shell = shell || requiresShell(file);
}
if (shell) {
// Escape command & arguments
applyQuotes = (command !== 'echo'); // Do not quote arguments for the special "echo" command
command = escapeCommand(command);
args = args.map(function (arg) {
return escapeArg(arg, applyQuotes);
});
// Use cmd.exe
args = ['/s', '/c', '"' + command + (args.length ? ' ' + args.join(' ') : '') + '"'];
command = process.env.comspec || 'cmd.exe';
// Tell node's spawn that the arguments are already escaped
options.windowsVerbatimArguments = true;
}
}
return {
command: command,
args: args,
options: options,
file: file,
original: original,
};
}
module.exports = parse;
... ...
'use strict';
var path = require('path');
var which = require('which');
var LRU = require('lru-cache');
var commandCache = new LRU({ max: 50, maxAge: 30 * 1000 }); // Cache just for 30sec
function resolveCommand(command, noExtension) {
var resolved;
noExtension = !!noExtension;
resolved = commandCache.get(command + '!' + noExtension);
// Check if its resolved in the cache
if (commandCache.has(command)) {
return commandCache.get(command);
}
try {
resolved = !noExtension ?
which.sync(command) :
which.sync(command, { pathExt: path.delimiter + (process.env.PATHEXT || '') });
} catch (e) { /* empty */ }
commandCache.set(command + '!' + noExtension, resolved);
return resolved;
}
module.exports = resolveCommand;
... ...
{
"_args": [
[
{
"raw": "cross-spawn@^4.0.0",
"scope": null,
"escapedName": "cross-spawn",
"name": "cross-spawn",
"rawSpec": "^4.0.0",
"spec": ">=4.0.0 <5.0.0",
"type": "range"
},
"/Users/fzy/project/koa2_Sequelize_project/node_modules/gm"
]
],
"_from": "cross-spawn@>=4.0.0 <5.0.0",
"_id": "cross-spawn@4.0.2",
"_inCache": true,
"_location": "/cross-spawn",
"_nodeVersion": "4.5.0",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/cross-spawn-4.0.2.tgz_1474803799646_0.017929385183379054"
},
"_npmUser": {
"name": "satazor",
"email": "andremiguelcruz@msn.com"
},
"_npmVersion": "2.15.9",
"_phantomChildren": {},
"_requested": {
"raw": "cross-spawn@^4.0.0",
"scope": null,
"escapedName": "cross-spawn",
"name": "cross-spawn",
"rawSpec": "^4.0.0",
"spec": ">=4.0.0 <5.0.0",
"type": "range"
},
"_requiredBy": [
"/gm"
],
"_resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
"_shasum": "7b9247621c23adfdd3856004a823cbe397424d41",
"_shrinkwrap": null,
"_spec": "cross-spawn@^4.0.0",
"_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/gm",
"author": {
"name": "IndigoUnited",
"email": "hello@indigounited.com",
"url": "http://indigounited.com"
},
"bugs": {
"url": "https://github.com/IndigoUnited/node-cross-spawn/issues/"
},
"dependencies": {
"lru-cache": "^4.0.1",
"which": "^1.2.9"
},
"description": "Cross platform child_process#spawn and child_process#spawnSync",
"devDependencies": {
"@satazor/eslint-config": "^3.0.0",
"eslint": "^3.0.0",
"expect.js": "^0.3.0",
"glob": "^7.0.0",
"mkdirp": "^0.5.1",
"mocha": "^3.0.2",
"rimraf": "^2.5.0"
},
"directories": {},
"dist": {
"shasum": "7b9247621c23adfdd3856004a823cbe397424d41",
"tarball": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz"
},
"files": [
"index.js",
"lib"
],
"gitHead": "674ceb2f2b69ad64b5dcad661b9bf048d6ec4c22",
"homepage": "https://github.com/IndigoUnited/node-cross-spawn#readme",
"keywords": [
"spawn",
"spawnSync",
"windows",
"cross",
"platform",
"path",
"ext",
"path-ext",
"path_ext",
"shebang",
"hashbang",
"cmd",
"execute"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"name": "satazor",
"email": "andremiguelcruz@msn.com"
}
],
"name": "cross-spawn",
"optionalDependencies": {},
"readme": "# cross-spawn\n\n[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build status][appveyor-image]][appveyor-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url]\n\n[npm-url]:https://npmjs.org/package/cross-spawn\n[downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg\n[npm-image]:http://img.shields.io/npm/v/cross-spawn.svg\n[travis-url]:https://travis-ci.org/IndigoUnited/node-cross-spawn\n[travis-image]:http://img.shields.io/travis/IndigoUnited/node-cross-spawn/master.svg\n[appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn\n[appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg\n[david-dm-url]:https://david-dm.org/IndigoUnited/node-cross-spawn\n[david-dm-image]:https://img.shields.io/david/IndigoUnited/node-cross-spawn.svg\n[david-dm-dev-url]:https://david-dm.org/IndigoUnited/node-cross-spawn#info=devDependencies\n[david-dm-dev-image]:https://img.shields.io/david/dev/IndigoUnited/node-cross-spawn.svg\n\nA cross platform solution to node's spawn and spawnSync.\n\n\n## Installation\n\n`$ npm install cross-spawn`\n\nIf you are using `spawnSync` on node 0.10 or older, you will also need to install `spawn-sync`:\n\n`$ npm install spawn-sync`\n\n\n## Why\n\nNode has issues when using spawn on Windows:\n\n- It ignores [PATHEXT](https://github.com/joyent/node/issues/2318)\n- It does not support [shebangs](http://pt.wikipedia.org/wiki/Shebang)\n- It does not allow you to run `del` or `dir`\n- It does not properly escape arguments with spaces or special characters\n\nAll these issues are handled correctly by `cross-spawn`.\nThere are some known modules, such as [win-spawn](https://github.com/ForbesLindesay/win-spawn), that try to solve this but they are either broken or provide faulty escaping of shell arguments.\n\n\n## Usage\n\nExactly the same way as node's [`spawn`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) or [`spawnSync`](https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options), so it's a drop in replacement.\n\n```javascript\nvar spawn = require('cross-spawn');\n\n// Spawn NPM asynchronously\nvar child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });\n\n// Spawn NPM synchronously\nvar results = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });\n```\n\n## Caveat\n\nOn Windows, cross-spawn will only spawn `cmd.exe` if necessary. If the extension\nof the executable is `.exe` or `.com`, it will spawn it directly. If you wish\nto override this behavior and *always* spawn a shell, pass the `{shell: true}`\noption.\n\n\n## Tests\n\n`$ npm test`\n\n\n## License\n\nReleased under the [MIT License](http://www.opensource.org/licenses/mit-license.php).\n",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "git://github.com/IndigoUnited/node-cross-spawn.git"
},
"scripts": {
"lint": "eslint '{*.js,lib/**/*.js,test/**/*.js}'",
"test": "node test/prepare && mocha --bail test/test"
},
"version": "4.0.2"
}
... ...
Copyright (c) 2012-2014 Raynos.
The MIT License (MIT)
Copyright (c) 2014-2015, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
... ...
# extend-shallow [![NPM version](https://badge.fury.io/js/extend-shallow.svg)](http://badge.fury.io/js/extend-shallow) [![Build Status](https://travis-ci.org/jonschlinkert/extend-shallow.svg)](https://travis-ci.org/jonschlinkert/extend-shallow)
> Extend an object with the properties of additional objects. node.js/javascript util.
## Install
Install with [npm](https://www.npmjs.com/)
```sh
$ npm i extend-shallow --save
```
## Usage
```js
var extend = require('extend-shallow');
extend({a: 'b'}, {c: 'd'})
//=> {a: 'b', c: 'd'}
```
Pass an empty object to shallow clone:
```js
var obj = {};
extend(obj, {a: 'b'}, {c: 'd'})
//=> {a: 'b', c: 'd'}
```
## Related
* [extend-shallow](https://github.com/jonschlinkert/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util.
* [for-own](https://github.com/jonschlinkert/for-own): Iterate over the own enumerable properties of an object, and return an object with properties… [more](https://github.com/jonschlinkert/for-own)
* [for-in](https://github.com/jonschlinkert/for-in): Iterate over the own and inherited enumerable properties of an objecte, and return an object… [more](https://github.com/jonschlinkert/for-in)
* [is-plain-object](https://github.com/jonschlinkert/is-plain-object): Returns true if an object was created by the `Object` constructor.
* [isobject](https://github.com/jonschlinkert/isobject): Returns true if the value is an object and not an array or null.
* [kind-of](https://github.com/jonschlinkert/kind-of): Get the native type of a value.
## Running tests
Install dev dependencies:
```sh
$ npm i -d && npm test
```
## Author
**Jon Schlinkert**
+ [github/jonschlinkert](https://github.com/jonschlinkert)
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
## License
Copyright © 2015 Jon Schlinkert
Released under the MIT license.
***
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on June 29, 2015._
\ No newline at end of file
... ...
'use strict';
var isObject = require('is-extendable');
module.exports = function extend(o/*, objects*/) {
if (!isObject(o)) { o = {}; }
var len = arguments.length;
for (var i = 1; i < len; i++) {
var obj = arguments[i];
if (isObject(obj)) {
assign(o, obj);
}
}
return o;
};
function assign(a, b) {
for (var key in b) {
if (hasOwn(b, key)) {
a[key] = b[key];
}
}
}
/**
* Returns true if the given `key` is an own property of `obj`.
*/
function hasOwn(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
... ...
{
"_args": [
[
{
"raw": "extend-shallow@^2.0.1",
"scope": null,
"escapedName": "extend-shallow",
"name": "extend-shallow",
"rawSpec": "^2.0.1",
"spec": ">=2.0.1 <3.0.0",
"type": "range"
},
"/Users/fzy/project/koa2_Sequelize_project/node_modules/koa-better-body"
]
],
"_from": "extend-shallow@>=2.0.1 <3.0.0",
"_id": "extend-shallow@2.0.1",
"_inCache": true,
"_location": "/extend-shallow",
"_nodeVersion": "0.12.4",
"_npmUser": {
"name": "jonschlinkert",
"email": "github@sellside.com"
},
"_npmVersion": "2.10.1",
"_phantomChildren": {},
"_requested": {
"raw": "extend-shallow@^2.0.1",
"scope": null,
"escapedName": "extend-shallow",
"name": "extend-shallow",
"rawSpec": "^2.0.1",
"spec": ">=2.0.1 <3.0.0",
"type": "range"
},
"_requiredBy": [
"/koa-better-body"
],
"_resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"_shasum": "51af7d614ad9a9f610ea1bafbb989d6b1c56890f",
"_shrinkwrap": null,
"_spec": "extend-shallow@^2.0.1",
"_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/koa-better-body",
"author": {
"name": "Jon Schlinkert",
"url": "https://github.com/jonschlinkert"
},
"bugs": {
"url": "https://github.com/jonschlinkert/extend-shallow/issues"
},
"dependencies": {
"is-extendable": "^0.1.0"
},
"description": "Extend an object with the properties of additional objects. node.js/javascript util.",
"devDependencies": {
"array-slice": "^0.2.3",
"benchmarked": "^0.1.4",
"chalk": "^1.0.0",
"for-own": "^0.1.3",
"glob": "^5.0.12",
"is-plain-object": "^2.0.1",
"kind-of": "^2.0.0",
"minimist": "^1.1.1",
"mocha": "^2.2.5",
"should": "^7.0.1"
},
"directories": {},
"dist": {
"shasum": "51af7d614ad9a9f610ea1bafbb989d6b1c56890f",
"tarball": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
"gitHead": "e9b1f1d2ff9d2990ec4a127afa7c14732d1eec8a",
"homepage": "https://github.com/jonschlinkert/extend-shallow",
"keywords": [
"assign",
"extend",
"javascript",
"js",
"keys",
"merge",
"obj",
"object",
"prop",
"properties",
"property",
"props",
"shallow",
"util",
"utility",
"utils",
"value"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"name": "jonschlinkert",
"email": "github@sellside.com"
}
],
"name": "extend-shallow",
"optionalDependencies": {},
"readme": "# extend-shallow [![NPM version](https://badge.fury.io/js/extend-shallow.svg)](http://badge.fury.io/js/extend-shallow) [![Build Status](https://travis-ci.org/jonschlinkert/extend-shallow.svg)](https://travis-ci.org/jonschlinkert/extend-shallow)\n\n> Extend an object with the properties of additional objects. node.js/javascript util.\n\n## Install\n\nInstall with [npm](https://www.npmjs.com/)\n\n```sh\n$ npm i extend-shallow --save\n```\n\n## Usage\n\n```js\nvar extend = require('extend-shallow');\n\nextend({a: 'b'}, {c: 'd'})\n//=> {a: 'b', c: 'd'}\n```\n\nPass an empty object to shallow clone:\n\n```js\nvar obj = {};\nextend(obj, {a: 'b'}, {c: 'd'})\n//=> {a: 'b', c: 'd'}\n```\n\n## Related\n\n* [extend-shallow](https://github.com/jonschlinkert/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util.\n* [for-own](https://github.com/jonschlinkert/for-own): Iterate over the own enumerable properties of an object, and return an object with properties… [more](https://github.com/jonschlinkert/for-own)\n* [for-in](https://github.com/jonschlinkert/for-in): Iterate over the own and inherited enumerable properties of an objecte, and return an object… [more](https://github.com/jonschlinkert/for-in)\n* [is-plain-object](https://github.com/jonschlinkert/is-plain-object): Returns true if an object was created by the `Object` constructor.\n* [isobject](https://github.com/jonschlinkert/isobject): Returns true if the value is an object and not an array or null.\n* [kind-of](https://github.com/jonschlinkert/kind-of): Get the native type of a value.\n\n## Running tests\n\nInstall dev dependencies:\n\n```sh\n$ npm i -d && npm test\n```\n\n## Author\n\n**Jon Schlinkert**\n\n+ [github/jonschlinkert](https://github.com/jonschlinkert)\n+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)\n\n## License\n\nCopyright © 2015 Jon Schlinkert\nReleased under the MIT license.\n\n***\n\n_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on June 29, 2015._",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "git+https://github.com/jonschlinkert/extend-shallow.git"
},
"scripts": {
"test": "mocha"
},
"version": "2.0.1"
}
... ...
/test
/tool
/example
/benchmark
*.upload
*.un~
*.http
... ...
language: node_js
node_js:
- 4
- 6
- 7
... ...
Copyright (C) 2011 Felix Geisendörfer
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.
... ...
# Formidable
[![Build Status](https://travis-ci.org/felixge/node-formidable.svg?branch=master)](https://travis-ci.org/felixge/node-formidable)
## Purpose
A Node.js module for parsing form data, especially file uploads.
## Current status
**Maintainers Wanted:** Please see https://github.com/felixge/node-formidable/issues/412
This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading
and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from
a large variety of clients and is considered production-ready.
## Features
* Fast (~500mb/sec), non-buffering multipart parser
* Automatically writing file uploads to disk
* Low memory footprint
* Graceful error handling
* Very high test coverage
## Installation
```sh
npm i -S formidable
```
This is a low level package, and if you're using a high level framework such as Express, chances are it's already included in it. You can [read this discussion](http://stackoverflow.com/questions/11295554/how-to-disable-express-bodyparser-for-file-uploads-node-js) about how Formidable is integrated with Express.
Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.
## Example
Parse an incoming file upload.
```javascript
var formidable = require('formidable'),
http = require('http'),
util = require('util');
http.createServer(function(req, res) {
if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
// parse a file upload
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
return;
}
// show a file upload form
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="title"><br>'+
'<input type="file" name="upload" multiple="multiple"><br>'+
'<input type="submit" value="Upload">'+
'</form>'
);
}).listen(8080);
```
## API
### Formidable.IncomingForm
```javascript
var form = new formidable.IncomingForm()
```
Creates a new incoming form.
```javascript
form.encoding = 'utf-8';
```
Sets encoding for incoming form fields.
```javascript
form.uploadDir = "/my/dir";
```
Sets the directory for placing file uploads in. You can move them later on using
`fs.rename()`. The default is `os.tmpdir()`.
```javascript
form.keepExtensions = false;
```
If you want the files written to `form.uploadDir` to include the extensions of the original files, set this property to `true`.
```javascript
form.type
```
Either 'multipart' or 'urlencoded' depending on the incoming request.
```javascript
form.maxFieldsSize = 2 * 1024 * 1024;
```
Limits the amount of memory all fields together (except files) can allocate in bytes.
If this value is exceeded, an `'error'` event is emitted. The default
size is 2MB.
```javascript
form.maxFields = 1000;
```
Limits the number of fields that the querystring parser will decode. Defaults
to 1000 (0 for unlimited).
```javascript
form.hash = false;
```
If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.
```javascript
form.multiples = false;
```
If this option is enabled, when you call `form.parse`, the `files` argument will contain arrays of files for inputs which submit multiple files using the HTML5 `multiple` attribute.
```javascript
form.bytesReceived
```
The amount of bytes received for this form so far.
```javascript
form.bytesExpected
```
The expected number of bytes in this form.
```javascript
form.parse(request, [cb]);
```
Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields and files are collected and passed to the callback:
```javascript
form.parse(req, function(err, fields, files) {
// ...
});
form.onPart(part);
```
You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing.
```javascript
form.onPart = function(part) {
part.addListener('data', function() {
// ...
});
}
```
If you want to use formidable to only handle certain parts for you, you can do so:
```javascript
form.onPart = function(part) {
if (!part.filename) {
// let formidable handle all non-file parts
form.handlePart(part);
}
}
```
Check the code in this method for further inspiration.
### Formidable.File
```javascript
file.size = 0
```
The size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet.
```javascript
file.path = null
```
The path this file is being written to. You can modify this in the `'fileBegin'` event in
case you are unhappy with the way formidable generates a temporary path for your files.
```javascript
file.name = null
```
The name this file had according to the uploading client.
```javascript
file.type = null
```
The mime type of this file, according to the uploading client.
```javascript
file.lastModifiedDate = null
```
A date object (or `null`) containing the time this file was last written to. Mostly
here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
```javascript
file.hash = null
```
If hash calculation was set, you can read the hex digest out of this var.
#### Formidable.File#toJSON()
This method returns a JSON-representation of the file, allowing you to
`JSON.stringify()` the file which is useful for logging and responding
to requests.
### Events
#### 'progress'
Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.
```javascript
form.on('progress', function(bytesReceived, bytesExpected) {
});
```
#### 'field'
Emitted whenever a field / value pair has been received.
```javascript
form.on('field', function(name, value) {
});
```
#### 'fileBegin'
Emitted whenever a new file is detected in the upload stream. Use this event if
you want to stream the file to somewhere else while buffering the upload on
the file system.
```javascript
form.on('fileBegin', function(name, file) {
});
```
#### 'file'
Emitted whenever a field / file pair has been received. `file` is an instance of `File`.
```javascript
form.on('file', function(name, file) {
});
```
#### 'error'
Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.
```javascript
form.on('error', function(err) {
});
```
#### 'aborted'
Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. After this event is emitted, an `error` event will follow. In the future there will be a separate 'timeout' event (needs a change in the node core).
```javascript
form.on('aborted', function() {
});
```
##### 'end'
```javascript
form.on('end', function() {
});
```
Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.
## Changelog
### v1.1.1 (2017-01-15)
* Fix DeprecationWarning about os.tmpDir() (Christian)
* Update `buffer.write` order of arguments for Node 7 (Kornel Lesiński)
* JSON Parser emits error events to the IncomingForm (alessio.montagnani)
* Improved Content-Disposition parsing (Sebastien)
* Access WriteStream of fs during runtime instead of include time (Jonas Amundsen)
* Use built-in toString to convert buffer to hex (Charmander)
* Add hash to json if present (Nick Stamas)
* Add license to package.json (Simen Bekkhus)
### v1.0.14 (2013-05-03)
* Add failing hash tests. (Ben Trask)
* Enable hash calculation again (Eugene Girshov)
* Test for immediate data events (Tim Smart)
* Re-arrange IncomingForm#parse (Tim Smart)
### v1.0.13
* Only update hash if update method exists (Sven Lito)
* According to travis v0.10 needs to go quoted (Sven Lito)
* Bumping build node versions (Sven Lito)
* Additional fix for empty requests (Eugene Girshov)
* Change the default to 1000, to match the new Node behaviour. (OrangeDog)
* Add ability to control maxKeys in the querystring parser. (OrangeDog)
* Adjust test case to work with node 0.9.x (Eugene Girshov)
* Update package.json (Sven Lito)
* Path adjustment according to eb4468b (Markus Ast)
### v1.0.12
* Emit error on aborted connections (Eugene Girshov)
* Add support for empty requests (Eugene Girshov)
* Fix name/filename handling in Content-Disposition (jesperp)
* Tolerate malformed closing boundary in multipart (Eugene Girshov)
* Ignore preamble in multipart messages (Eugene Girshov)
* Add support for application/json (Mike Frey, Carlos Rodriguez)
* Add support for Base64 encoding (Elmer Bulthuis)
* Add File#toJSON (TJ Holowaychuk)
* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)
* Documentation improvements (Sven Lito, Andre Azevedo)
* Add support for application/octet-stream (Ion Lupascu, Chris Scribner)
* Use os.tmpdir() to get tmp directory (Andrew Kelley)
* Improve package.json (Andrew Kelley, Sven Lito)
* Fix benchmark script (Andrew Kelley)
* Fix scope issue in incoming_forms (Sven Lito)
* Fix file handle leak on error (OrangeDog)
## License
Formidable is licensed under the MIT license.
## Ports
* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable
## Credits
* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js
... ...
module.exports = require('./lib');
\ No newline at end of file
... ...
if (global.GENTLY) require = GENTLY.hijack(require);
var util = require('util'),
fs = require('fs'),
EventEmitter = require('events').EventEmitter,
crypto = require('crypto');
function File(properties) {
EventEmitter.call(this);
this.size = 0;
this.path = null;
this.name = null;
this.type = null;
this.hash = null;
this.lastModifiedDate = null;
this._writeStream = null;
for (var key in properties) {
this[key] = properties[key];
}
if(typeof this.hash === 'string') {
this.hash = crypto.createHash(properties.hash);
} else {
this.hash = null;
}
}
module.exports = File;
util.inherits(File, EventEmitter);
File.prototype.open = function() {
this._writeStream = new fs.WriteStream(this.path);
};
File.prototype.toJSON = function() {
var json = {
size: this.size,
path: this.path,
name: this.name,
type: this.type,
mtime: this.lastModifiedDate,
length: this.length,
filename: this.filename,
mime: this.mime
};
if (this.hash && this.hash != "") {
json.hash = this.hash;
}
return json;
};
File.prototype.write = function(buffer, cb) {
var self = this;
if (self.hash) {
self.hash.update(buffer);
}
this._writeStream.write(buffer, function() {
self.lastModifiedDate = new Date();
self.size += buffer.length;
self.emit('progress', self.size);
cb();
});
};
File.prototype.end = function(cb) {
var self = this;
if (self.hash) {
self.hash = self.hash.digest('hex');
}
this._writeStream.end(function() {
self.emit('end');
cb();
});
};
... ...
if (global.GENTLY) require = GENTLY.hijack(require);
var crypto = require('crypto');
var fs = require('fs');
var util = require('util'),
path = require('path'),
File = require('./file'),
MultipartParser = require('./multipart_parser').MultipartParser,
QuerystringParser = require('./querystring_parser').QuerystringParser,
OctetParser = require('./octet_parser').OctetParser,
JSONParser = require('./json_parser').JSONParser,
StringDecoder = require('string_decoder').StringDecoder,
EventEmitter = require('events').EventEmitter,
Stream = require('stream').Stream,
os = require('os');
function IncomingForm(opts) {
if (!(this instanceof IncomingForm)) return new IncomingForm(opts);
EventEmitter.call(this);
opts=opts||{};
this.error = null;
this.ended = false;
this.maxFields = opts.maxFields || 1000;
this.maxFieldsSize = opts.maxFieldsSize || 2 * 1024 * 1024;
this.keepExtensions = opts.keepExtensions || false;
this.uploadDir = opts.uploadDir || (os.tmpdir && os.tmpdir()) || os.tmpDir();
this.encoding = opts.encoding || 'utf-8';
this.headers = null;
this.type = null;
this.hash = opts.hash || false;
this.multiples = opts.multiples || false;
this.bytesReceived = null;
this.bytesExpected = null;
this._parser = null;
this._flushing = 0;
this._fieldsSize = 0;
this.openedFiles = [];
return this;
}
util.inherits(IncomingForm, EventEmitter);
exports.IncomingForm = IncomingForm;
IncomingForm.prototype.parse = function(req, cb) {
this.pause = function() {
try {
req.pause();
} catch (err) {
// the stream was destroyed
if (!this.ended) {
// before it was completed, crash & burn
this._error(err);
}
return false;
}
return true;
};
this.resume = function() {
try {
req.resume();
} catch (err) {
// the stream was destroyed
if (!this.ended) {
// before it was completed, crash & burn
this._error(err);
}
return false;
}
return true;
};
// Setup callback first, so we don't miss anything from data events emitted
// immediately.
if (cb) {
var fields = {}, files = {};
this
.on('field', function(name, value) {
fields[name] = value;
})
.on('file', function(name, file) {
if (this.multiples) {
if (files[name]) {
if (!Array.isArray(files[name])) {
files[name] = [files[name]];
}
files[name].push(file);
} else {
files[name] = file;
}
} else {
files[name] = file;
}
})
.on('error', function(err) {
cb(err, fields, files);
})
.on('end', function() {
cb(null, fields, files);
});
}
// Parse headers and setup the parser, ready to start listening for data.
this.writeHeaders(req.headers);
// Start listening for data.
var self = this;
req
.on('error', function(err) {
self._error(err);
})
.on('aborted', function() {
self.emit('aborted');
self._error(new Error('Request aborted'));
})
.on('data', function(buffer) {
self.write(buffer);
})
.on('end', function() {
if (self.error) {
return;
}
var err = self._parser.end();
if (err) {
self._error(err);
}
});
return this;
};
IncomingForm.prototype.writeHeaders = function(headers) {
this.headers = headers;
this._parseContentLength();
this._parseContentType();
};
IncomingForm.prototype.write = function(buffer) {
if (this.error) {
return;
}
if (!this._parser) {
this._error(new Error('uninitialized parser'));
return;
}
this.bytesReceived += buffer.length;
this.emit('progress', this.bytesReceived, this.bytesExpected);
var bytesParsed = this._parser.write(buffer);
if (bytesParsed !== buffer.length) {
this._error(new Error('parser error, '+bytesParsed+' of '+buffer.length+' bytes parsed'));
}
return bytesParsed;
};
IncomingForm.prototype.pause = function() {
// this does nothing, unless overwritten in IncomingForm.parse
return false;
};
IncomingForm.prototype.resume = function() {
// this does nothing, unless overwritten in IncomingForm.parse
return false;
};
IncomingForm.prototype.onPart = function(part) {
// this method can be overwritten by the user
this.handlePart(part);
};
IncomingForm.prototype.handlePart = function(part) {
var self = this;
if (part.filename === undefined) {
var value = ''
, decoder = new StringDecoder(this.encoding);
part.on('data', function(buffer) {
self._fieldsSize += buffer.length;
if (self._fieldsSize > self.maxFieldsSize) {
self._error(new Error('maxFieldsSize exceeded, received '+self._fieldsSize+' bytes of field data'));
return;
}
value += decoder.write(buffer);
});
part.on('end', function() {
self.emit('field', part.name, value);
});
return;
}
this._flushing++;
var file = new File({
path: this._uploadPath(part.filename),
name: part.filename,
type: part.mime,
hash: self.hash
});
this.emit('fileBegin', part.name, file);
file.open();
this.openedFiles.push(file);
part.on('data', function(buffer) {
if (buffer.length == 0) {
return;
}
self.pause();
file.write(buffer, function() {
self.resume();
});
});
part.on('end', function() {
file.end(function() {
self._flushing--;
self.emit('file', part.name, file);
self._maybeEnd();
});
});
};
function dummyParser(self) {
return {
end: function () {
self.ended = true;
self._maybeEnd();
return null;
}
};
}
IncomingForm.prototype._parseContentType = function() {
if (this.bytesExpected === 0) {
this._parser = dummyParser(this);
return;
}
if (!this.headers['content-type']) {
this._error(new Error('bad content-type header, no content-type'));
return;
}
if (this.headers['content-type'].match(/octet-stream/i)) {
this._initOctetStream();
return;
}
if (this.headers['content-type'].match(/urlencoded/i)) {
this._initUrlencoded();
return;
}
if (this.headers['content-type'].match(/multipart/i)) {
var m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (m) {
this._initMultipart(m[1] || m[2]);
} else {
this._error(new Error('bad content-type header, no multipart boundary'));
}
return;
}
if (this.headers['content-type'].match(/json/i)) {
this._initJSONencoded();
return;
}
this._error(new Error('bad content-type header, unknown content-type: '+this.headers['content-type']));
};
IncomingForm.prototype._error = function(err) {
if (this.error || this.ended) {
return;
}
this.error = err;
this.emit('error', err);
if (Array.isArray(this.openedFiles)) {
this.openedFiles.forEach(function(file) {
file._writeStream.destroy();
setTimeout(fs.unlink, 0, file.path, function(error) { });
});
}
};
IncomingForm.prototype._parseContentLength = function() {
this.bytesReceived = 0;
if (this.headers['content-length']) {
this.bytesExpected = parseInt(this.headers['content-length'], 10);
} else if (this.headers['transfer-encoding'] === undefined) {
this.bytesExpected = 0;
}
if (this.bytesExpected !== null) {
this.emit('progress', this.bytesReceived, this.bytesExpected);
}
};
IncomingForm.prototype._newParser = function() {
return new MultipartParser();
};
IncomingForm.prototype._initMultipart = function(boundary) {
this.type = 'multipart';
var parser = new MultipartParser(),
self = this,
headerField,
headerValue,
part;
parser.initWithBoundary(boundary);
parser.onPartBegin = function() {
part = new Stream();
part.readable = true;
part.headers = {};
part.name = null;
part.filename = null;
part.mime = null;
part.transferEncoding = 'binary';
part.transferBuffer = '';
headerField = '';
headerValue = '';
};
parser.onHeaderField = function(b, start, end) {
headerField += b.toString(self.encoding, start, end);
};
parser.onHeaderValue = function(b, start, end) {
headerValue += b.toString(self.encoding, start, end);
};
parser.onHeaderEnd = function() {
headerField = headerField.toLowerCase();
part.headers[headerField] = headerValue;
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
var m = headerValue.match(/\bname=("([^"]*)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))/i);
if (headerField == 'content-disposition') {
if (m) {
part.name = m[2] || m[3] || '';
}
part.filename = self._fileName(headerValue);
} else if (headerField == 'content-type') {
part.mime = headerValue;
} else if (headerField == 'content-transfer-encoding') {
part.transferEncoding = headerValue.toLowerCase();
}
headerField = '';
headerValue = '';
};
parser.onHeadersEnd = function() {
switch(part.transferEncoding){
case 'binary':
case '7bit':
case '8bit':
parser.onPartData = function(b, start, end) {
part.emit('data', b.slice(start, end));
};
parser.onPartEnd = function() {
part.emit('end');
};
break;
case 'base64':
parser.onPartData = function(b, start, end) {
part.transferBuffer += b.slice(start, end).toString('ascii');
/*
four bytes (chars) in base64 converts to three bytes in binary
encoding. So we should always work with a number of bytes that
can be divided by 4, it will result in a number of buytes that
can be divided vy 3.
*/
var offset = parseInt(part.transferBuffer.length / 4, 10) * 4;
part.emit('data', new Buffer(part.transferBuffer.substring(0, offset), 'base64'));
part.transferBuffer = part.transferBuffer.substring(offset);
};
parser.onPartEnd = function() {
part.emit('data', new Buffer(part.transferBuffer, 'base64'));
part.emit('end');
};
break;
default:
return self._error(new Error('unknown transfer-encoding'));
}
self.onPart(part);
};
parser.onEnd = function() {
self.ended = true;
self._maybeEnd();
};
this._parser = parser;
};
IncomingForm.prototype._fileName = function(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
var m = headerValue.match(/\bfilename=("(.*?)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))($|;\s)/i);
if (!m) return;
var match = m[2] || m[3] || '';
var filename = match.substr(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#([\d]{4});/g, function(m, code) {
return String.fromCharCode(code);
});
return filename;
};
IncomingForm.prototype._initUrlencoded = function() {
this.type = 'urlencoded';
var parser = new QuerystringParser(this.maxFields)
, self = this;
parser.onField = function(key, val) {
self.emit('field', key, val);
};
parser.onEnd = function() {
self.ended = true;
self._maybeEnd();
};
this._parser = parser;
};
IncomingForm.prototype._initOctetStream = function() {
this.type = 'octet-stream';
var filename = this.headers['x-file-name'];
var mime = this.headers['content-type'];
var file = new File({
path: this._uploadPath(filename),
name: filename,
type: mime
});
this.emit('fileBegin', filename, file);
file.open();
this._flushing++;
var self = this;
self._parser = new OctetParser();
//Keep track of writes that haven't finished so we don't emit the file before it's done being written
var outstandingWrites = 0;
self._parser.on('data', function(buffer){
self.pause();
outstandingWrites++;
file.write(buffer, function() {
outstandingWrites--;
self.resume();
if(self.ended){
self._parser.emit('doneWritingFile');
}
});
});
self._parser.on('end', function(){
self._flushing--;
self.ended = true;
var done = function(){
file.end(function() {
self.emit('file', 'file', file);
self._maybeEnd();
});
};
if(outstandingWrites === 0){
done();
} else {
self._parser.once('doneWritingFile', done);
}
});
};
IncomingForm.prototype._initJSONencoded = function() {
this.type = 'json';
var parser = new JSONParser(this)
, self = this;
if (this.bytesExpected) {
parser.initWithLength(this.bytesExpected);
}
parser.onField = function(key, val) {
self.emit('field', key, val);
};
parser.onEnd = function() {
self.ended = true;
self._maybeEnd();
};
this._parser = parser;
};
IncomingForm.prototype._uploadPath = function(filename) {
var buf = crypto.randomBytes(16);
var name = 'upload_' + buf.toString('hex');
if (this.keepExtensions) {
var ext = path.extname(filename);
ext = ext.replace(/(\.[a-z0-9]+).*/i, '$1');
name += ext;
}
return path.join(this.uploadDir, name);
};
IncomingForm.prototype._maybeEnd = function() {
if (!this.ended || this._flushing || this.error) {
return;
}
this.emit('end');
};
... ...
var IncomingForm = require('./incoming_form').IncomingForm;
IncomingForm.IncomingForm = IncomingForm;
module.exports = IncomingForm;
... ...
if (global.GENTLY) require = GENTLY.hijack(require);
var Buffer = require('buffer').Buffer;
function JSONParser(parent) {
this.parent = parent;
this.data = new Buffer('');
this.bytesWritten = 0;
}
exports.JSONParser = JSONParser;
JSONParser.prototype.initWithLength = function(length) {
this.data = new Buffer(length);
};
JSONParser.prototype.write = function(buffer) {
if (this.data.length >= this.bytesWritten + buffer.length) {
buffer.copy(this.data, this.bytesWritten);
} else {
this.data = Buffer.concat([this.data, buffer]);
}
this.bytesWritten += buffer.length;
return buffer.length;
};
JSONParser.prototype.end = function() {
try {
var fields = JSON.parse(this.data.toString('utf8'));
for (var field in fields) {
this.onField(field, fields[field]);
}
} catch (e) {
this.parent.emit('error', e);
}
this.data = null;
this.onEnd();
};
... ...
var Buffer = require('buffer').Buffer,
s = 0,
S =
{ PARSER_UNINITIALIZED: s++,
START: s++,
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
PART_END: s++,
END: s++
},
f = 1,
F =
{ PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
},
LF = 10,
CR = 13,
SPACE = 32,
HYPHEN = 45,
COLON = 58,
A = 97,
Z = 122,
lower = function(c) {
return c | 0x20;
};
for (s in S) {
exports[s] = S[s];
}
function MultipartParser() {
this.boundary = null;
this.boundaryChars = null;
this.lookbehind = null;
this.state = S.PARSER_UNINITIALIZED;
this.index = null;
this.flags = 0;
}
exports.MultipartParser = MultipartParser;
MultipartParser.stateToString = function(stateNumber) {
for (var state in S) {
var number = S[state];
if (number === stateNumber) return state;
}
};
MultipartParser.prototype.initWithBoundary = function(str) {
this.boundary = new Buffer(str.length+4);
this.boundary.write('\r\n--', 0);
this.boundary.write(str, 4);
this.lookbehind = new Buffer(this.boundary.length+8);
this.state = S.START;
this.boundaryChars = {};
for (var i = 0; i < this.boundary.length; i++) {
this.boundaryChars[this.boundary[i]] = true;
}
};
MultipartParser.prototype.write = function(buffer) {
var self = this,
i = 0,
len = buffer.length,
prevIndex = this.index,
index = this.index,
state = this.state,
flags = this.flags,
lookbehind = this.lookbehind,
boundary = this.boundary,
boundaryChars = this.boundaryChars,
boundaryLength = this.boundary.length,
boundaryEnd = boundaryLength - 1,
bufferLength = buffer.length,
c,
cl,
mark = function(name) {
self[name+'Mark'] = i;
},
clear = function(name) {
delete self[name+'Mark'];
},
callback = function(name, buffer, start, end) {
if (start !== undefined && start === end) {
return;
}
var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
if (callbackSymbol in self) {
self[callbackSymbol](buffer, start, end);
}
},
dataCallback = function(name, clear) {
var markSymbol = name+'Mark';
if (!(markSymbol in self)) {
return;
}
if (!clear) {
callback(name, buffer, self[markSymbol], buffer.length);
self[markSymbol] = 0;
} else {
callback(name, buffer, self[markSymbol], i);
delete self[markSymbol];
}
};
for (i = 0; i < len; i++) {
c = buffer[i];
switch (state) {
case S.PARSER_UNINITIALIZED:
return i;
case S.START:
index = 0;
state = S.START_BOUNDARY;
case S.START_BOUNDARY:
if (index == boundary.length - 2) {
if (c == HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c != CR) {
return i;
}
index++;
break;
} else if (index - 1 == boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c == HYPHEN){
callback('end');
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c == LF) {
index = 0;
callback('partBegin');
state = S.HEADER_FIELD_START;
} else {
return i;
}
break;
}
if (c != boundary[index+2]) {
index = -2;
}
if (c == boundary[index+2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('headerField');
index = 0;
case S.HEADER_FIELD:
if (c == CR) {
clear('headerField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c == HYPHEN) {
break;
}
if (c == COLON) {
if (index == 1) {
// empty header field
return i;
}
dataCallback('headerField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return i;
}
break;
case S.HEADER_VALUE_START:
if (c == SPACE) {
break;
}
mark('headerValue');
state = S.HEADER_VALUE;
case S.HEADER_VALUE:
if (c == CR) {
dataCallback('headerValue', true);
callback('headerEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c != LF) {
return i;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c != LF) {
return i;
}
callback('headersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('partData');
case S.PART_DATA:
prevIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(buffer[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = buffer[i];
}
if (index < boundary.length) {
if (boundary[index] == c) {
if (index === 0) {
dataCallback('partData', true);
}
index++;
} else {
index = 0;
}
} else if (index == boundary.length) {
index++;
if (c == CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c == HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 == boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c == LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('partEnd');
callback('partBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c == HYPHEN) {
callback('partEnd');
callback('end');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index-1] = c;
} else if (prevIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
callback('partData', lookbehind, 0, prevIndex);
prevIndex = 0;
mark('partData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
return i;
}
}
dataCallback('headerField');
dataCallback('headerValue');
dataCallback('partData');
this.index = index;
this.state = state;
this.flags = flags;
return len;
};
MultipartParser.prototype.end = function() {
var callback = function(self, name) {
var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
if (callbackSymbol in self) {
self[callbackSymbol]();
}
};
if ((this.state == S.HEADER_FIELD_START && this.index === 0) ||
(this.state == S.PART_DATA && this.index == this.boundary.length)) {
callback(this, 'partEnd');
callback(this, 'end');
} else if (this.state != S.END) {
return new Error('MultipartParser.end(): stream ended unexpectedly: ' + this.explain());
}
};
MultipartParser.prototype.explain = function() {
return 'state = ' + MultipartParser.stateToString(this.state);
};
... ...
var EventEmitter = require('events').EventEmitter
, util = require('util');
function OctetParser(options){
if(!(this instanceof OctetParser)) return new OctetParser(options);
EventEmitter.call(this);
}
util.inherits(OctetParser, EventEmitter);
exports.OctetParser = OctetParser;
OctetParser.prototype.write = function(buffer) {
this.emit('data', buffer);
return buffer.length;
};
OctetParser.prototype.end = function() {
this.emit('end');
};
... ...
if (global.GENTLY) require = GENTLY.hijack(require);
// This is a buffering parser, not quite as nice as the multipart one.
// If I find time I'll rewrite this to be fully streaming as well
var querystring = require('querystring');
function QuerystringParser(maxKeys) {
this.maxKeys = maxKeys;
this.buffer = '';
}
exports.QuerystringParser = QuerystringParser;
QuerystringParser.prototype.write = function(buffer) {
this.buffer += buffer.toString('ascii');
return buffer.length;
};
QuerystringParser.prototype.end = function() {
var fields = querystring.parse(this.buffer, '&', '=', { maxKeys: this.maxKeys });
for (var field in fields) {
this.onField(field, fields[field]);
}
this.buffer = '';
this.onEnd();
};
... ...
{
"_args": [
[
{
"raw": "formidable@1.1.1",
"scope": null,
"escapedName": "formidable",
"name": "formidable",
"rawSpec": "1.1.1",
"spec": "1.1.1",
"type": "version"
},
"/Users/fzy/project/koa2_Sequelize_project/node_modules/koa-body"
]
],
"_from": "formidable@1.1.1",
"_id": "formidable@1.1.1",
"_inCache": true,
"_location": "/formidable",
"_nodeVersion": "7.2.1",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/formidable-1.1.1.tgz_1484514649272_0.35996662196703255"
},
"_npmUser": {
"name": "kornel",
"email": "pornel@pornel.net"
},
"_npmVersion": "4.0.5",
"_phantomChildren": {},
"_requested": {
"raw": "formidable@1.1.1",
"scope": null,
"escapedName": "formidable",
"name": "formidable",
"rawSpec": "1.1.1",
"spec": "1.1.1",
"type": "version"
},
"_requiredBy": [
"/koa-body"
],
"_resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz",
"_shasum": "96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9",
"_shrinkwrap": null,
"_spec": "formidable@1.1.1",
"_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/koa-body",
"bugs": {
"url": "http://github.com/felixge/node-formidable/issues"
},
"dependencies": {},
"description": "A node.js module for parsing form data, especially file uploads.",
"devDependencies": {
"findit": "^0.1.2",
"gently": "^0.8.0",
"hashish": "^0.0.4",
"request": "^2.11.4",
"urun": "^0.0.6",
"utest": "^0.0.8"
},
"directories": {
"lib": "./lib"
},
"dist": {
"shasum": "96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9",
"tarball": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz"
},
"engines": {
"node": ">=0.8.0"
},
"gitHead": "7a36a8e932044252fe648c81dbd8cf837d0178d0",
"homepage": "https://github.com/felixge/node-formidable",
"license": "MIT",
"main": "./lib/index",
"maintainers": [
{
"name": "felixge",
"email": "felix@debuggable.com"
},
{
"name": "kornel",
"email": "pornel@pornel.net"
},
{
"name": "superjoe",
"email": "superjoe30@gmail.com"
},
{
"name": "svnlto",
"email": "me@svenlito.com"
},
{
"name": "tim-smart",
"email": "tim@fostle.com"
},
{
"name": "tunnckocore",
"email": "mameto_100@mail.bg"
}
],
"name": "formidable",
"optionalDependencies": {},
"readme": "# Formidable\n\n[![Build Status](https://travis-ci.org/felixge/node-formidable.svg?branch=master)](https://travis-ci.org/felixge/node-formidable)\n\n## Purpose\n\nA Node.js module for parsing form data, especially file uploads.\n\n## Current status\n\n**Maintainers Wanted:** Please see https://github.com/felixge/node-formidable/issues/412\n\nThis module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading\nand encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from\na large variety of clients and is considered production-ready.\n\n## Features\n\n* Fast (~500mb/sec), non-buffering multipart parser\n* Automatically writing file uploads to disk\n* Low memory footprint\n* Graceful error handling\n* Very high test coverage\n\n## Installation\n\n```sh\nnpm i -S formidable\n```\n\nThis is a low level package, and if you're using a high level framework such as Express, chances are it's already included in it. You can [read this discussion](http://stackoverflow.com/questions/11295554/how-to-disable-express-bodyparser-for-file-uploads-node-js) about how Formidable is integrated with Express.\n\nNote: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.\n\n## Example\n\nParse an incoming file upload.\n```javascript\nvar formidable = require('formidable'),\n http = require('http'),\n util = require('util');\n\nhttp.createServer(function(req, res) {\n if (req.url == '/upload' && req.method.toLowerCase() == 'post') {\n // parse a file upload\n var form = new formidable.IncomingForm();\n\n form.parse(req, function(err, fields, files) {\n res.writeHead(200, {'content-type': 'text/plain'});\n res.write('received upload:\\n\\n');\n res.end(util.inspect({fields: fields, files: files}));\n });\n\n return;\n }\n\n // show a file upload form\n res.writeHead(200, {'content-type': 'text/html'});\n res.end(\n '<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"post\">'+\n '<input type=\"text\" name=\"title\"><br>'+\n '<input type=\"file\" name=\"upload\" multiple=\"multiple\"><br>'+\n '<input type=\"submit\" value=\"Upload\">'+\n '</form>'\n );\n}).listen(8080);\n```\n## API\n\n### Formidable.IncomingForm\n```javascript\nvar form = new formidable.IncomingForm()\n```\nCreates a new incoming form.\n\n```javascript\nform.encoding = 'utf-8';\n```\nSets encoding for incoming form fields.\n\n```javascript\nform.uploadDir = \"/my/dir\";\n```\nSets the directory for placing file uploads in. You can move them later on using\n`fs.rename()`. The default is `os.tmpdir()`.\n\n```javascript\nform.keepExtensions = false;\n```\nIf you want the files written to `form.uploadDir` to include the extensions of the original files, set this property to `true`.\n\n```javascript\nform.type\n```\nEither 'multipart' or 'urlencoded' depending on the incoming request.\n\n```javascript\nform.maxFieldsSize = 2 * 1024 * 1024;\n```\nLimits the amount of memory all fields together (except files) can allocate in bytes.\nIf this value is exceeded, an `'error'` event is emitted. The default\nsize is 2MB.\n\n```javascript\nform.maxFields = 1000;\n```\nLimits the number of fields that the querystring parser will decode. Defaults\nto 1000 (0 for unlimited).\n\n```javascript\nform.hash = false;\n```\nIf you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.\n\n```javascript\nform.multiples = false;\n```\nIf this option is enabled, when you call `form.parse`, the `files` argument will contain arrays of files for inputs which submit multiple files using the HTML5 `multiple` attribute.\n\n```javascript\nform.bytesReceived\n```\nThe amount of bytes received for this form so far.\n\n```javascript\nform.bytesExpected\n```\nThe expected number of bytes in this form.\n\n```javascript\nform.parse(request, [cb]);\n```\nParses an incoming node.js `request` containing form data. If `cb` is provided, all fields and files are collected and passed to the callback:\n\n\n```javascript\nform.parse(req, function(err, fields, files) {\n // ...\n});\n\nform.onPart(part);\n```\nYou may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing.\n\n```javascript\nform.onPart = function(part) {\n part.addListener('data', function() {\n // ...\n });\n}\n```\nIf you want to use formidable to only handle certain parts for you, you can do so:\n```javascript\nform.onPart = function(part) {\n if (!part.filename) {\n // let formidable handle all non-file parts\n form.handlePart(part);\n }\n}\n```\nCheck the code in this method for further inspiration.\n\n\n### Formidable.File\n```javascript\nfile.size = 0\n```\nThe size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet.\n```javascript\nfile.path = null\n```\nThe path this file is being written to. You can modify this in the `'fileBegin'` event in\ncase you are unhappy with the way formidable generates a temporary path for your files.\n```javascript\nfile.name = null\n```\nThe name this file had according to the uploading client.\n```javascript\nfile.type = null\n```\nThe mime type of this file, according to the uploading client.\n```javascript\nfile.lastModifiedDate = null\n```\nA date object (or `null`) containing the time this file was last written to. Mostly\nhere for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).\n```javascript\nfile.hash = null\n```\nIf hash calculation was set, you can read the hex digest out of this var.\n\n#### Formidable.File#toJSON()\n\n This method returns a JSON-representation of the file, allowing you to\n `JSON.stringify()` the file which is useful for logging and responding\n to requests.\n\n### Events\n\n\n#### 'progress'\n\nEmitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.\n\n```javascript\nform.on('progress', function(bytesReceived, bytesExpected) {\n});\n```\n\n\n\n#### 'field'\n\nEmitted whenever a field / value pair has been received.\n\n```javascript\nform.on('field', function(name, value) {\n});\n```\n\n#### 'fileBegin'\n\nEmitted whenever a new file is detected in the upload stream. Use this event if\nyou want to stream the file to somewhere else while buffering the upload on\nthe file system.\n\n```javascript\nform.on('fileBegin', function(name, file) {\n});\n```\n\n#### 'file'\n\nEmitted whenever a field / file pair has been received. `file` is an instance of `File`.\n\n```javascript\nform.on('file', function(name, file) {\n});\n```\n\n#### 'error'\n\nEmitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.\n\n```javascript\nform.on('error', function(err) {\n});\n```\n\n#### 'aborted'\n\n\nEmitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. After this event is emitted, an `error` event will follow. In the future there will be a separate 'timeout' event (needs a change in the node core).\n```javascript\nform.on('aborted', function() {\n});\n```\n\n##### 'end'\n```javascript\nform.on('end', function() {\n});\n```\nEmitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.\n\n\n\n## Changelog\n\n### v1.1.1 (2017-01-15)\n\n * Fix DeprecationWarning about os.tmpDir() (Christian)\n * Update `buffer.write` order of arguments for Node 7 (Kornel Lesiński)\n * JSON Parser emits error events to the IncomingForm (alessio.montagnani)\n * Improved Content-Disposition parsing (Sebastien)\n * Access WriteStream of fs during runtime instead of include time (Jonas Amundsen)\n * Use built-in toString to convert buffer to hex (Charmander)\n * Add hash to json if present (Nick Stamas)\n * Add license to package.json (Simen Bekkhus)\n\n### v1.0.14 (2013-05-03)\n\n* Add failing hash tests. (Ben Trask)\n* Enable hash calculation again (Eugene Girshov)\n* Test for immediate data events (Tim Smart)\n* Re-arrange IncomingForm#parse (Tim Smart)\n\n### v1.0.13\n\n* Only update hash if update method exists (Sven Lito)\n* According to travis v0.10 needs to go quoted (Sven Lito)\n* Bumping build node versions (Sven Lito)\n* Additional fix for empty requests (Eugene Girshov)\n* Change the default to 1000, to match the new Node behaviour. (OrangeDog)\n* Add ability to control maxKeys in the querystring parser. (OrangeDog)\n* Adjust test case to work with node 0.9.x (Eugene Girshov)\n* Update package.json (Sven Lito)\n* Path adjustment according to eb4468b (Markus Ast)\n\n### v1.0.12\n\n* Emit error on aborted connections (Eugene Girshov)\n* Add support for empty requests (Eugene Girshov)\n* Fix name/filename handling in Content-Disposition (jesperp)\n* Tolerate malformed closing boundary in multipart (Eugene Girshov)\n* Ignore preamble in multipart messages (Eugene Girshov)\n* Add support for application/json (Mike Frey, Carlos Rodriguez)\n* Add support for Base64 encoding (Elmer Bulthuis)\n* Add File#toJSON (TJ Holowaychuk)\n* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)\n* Documentation improvements (Sven Lito, Andre Azevedo)\n* Add support for application/octet-stream (Ion Lupascu, Chris Scribner)\n* Use os.tmpdir() to get tmp directory (Andrew Kelley)\n* Improve package.json (Andrew Kelley, Sven Lito)\n* Fix benchmark script (Andrew Kelley)\n* Fix scope issue in incoming_forms (Sven Lito)\n* Fix file handle leak on error (OrangeDog)\n\n## License\n\nFormidable is licensed under the MIT license.\n\n## Ports\n\n* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable\n\n## Credits\n\n* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js\n",
"readmeFilename": "Readme.md",
"repository": {
"type": "git",
"url": "git://github.com/felixge/node-formidable.git"
},
"scripts": {
"clean": "rm test/tmp/*",
"test": "node test/run.js"
},
"version": "1.1.1"
}
... ...
# Security holding package
This package name is not currently in use, but was formerly occupied
by another package. To avoid malicious use, npm is hanging on to the
package name, but loosely, and we'll probably give it to you if you
want it.
You may adopt this package by contacting support@npmjs.com and
requesting the name.
... ...
{
"_args": [
[
{
"raw": "fs",
"scope": null,
"escapedName": "fs",
"name": "fs",
"rawSpec": "",
"spec": "latest",
"type": "tag"
},
"/Users/fzy/project/koa2_Sequelize_project/util/Agora_Recording_SDK_for_Linux_FULL/samples"
]
],
"_from": "fs@latest",
"_id": "fs@0.0.1-security",
"_inCache": true,
"_location": "/fs",
"_nodeVersion": "4.1.2",
"_npmUser": {
"name": "ehsalazar",
"email": "ernie@npmjs.com"
},
"_npmVersion": "3.10.5",
"_phantomChildren": {},
"_requested": {
"raw": "fs",
"scope": null,
"escapedName": "fs",
"name": "fs",
"rawSpec": "",
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"#DEV:/",
"#USER"
],
"_resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"_shasum": "8a7bd37186b6dddf3813f23858b57ecaaf5e41d4",
"_shrinkwrap": null,
"_spec": "fs",
"_where": "/Users/fzy/project/koa2_Sequelize_project/util/Agora_Recording_SDK_for_Linux_FULL/samples",
"author": "",
"bugs": {
"url": "https://github.com/npm/security-holder/issues"
},
"dependencies": {},
"description": "This package name is not currently in use, but was formerly occupied by another package. To avoid malicious use, npm is hanging on to the package name, but loosely, and we'll probably give it to you if you want it.",
"devDependencies": {},
"directories": {},
"dist": {
"shasum": "8a7bd37186b6dddf3813f23858b57ecaaf5e41d4",
"tarball": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz"
},
"homepage": "https://github.com/npm/security-holder#readme",
"keywords": [],
"license": "ISC",
"main": "index.js",
"maintainers": [
{
"name": "ehsalazar",
"email": "ernie@npmjs.com"
}
],
"name": "fs",
"optionalDependencies": {},
"readme": "# Security holding package\n\nThis package name is not currently in use, but was formerly occupied\nby another package. To avoid malicious use, npm is hanging on to the\npackage name, but loosely, and we'll probably give it to you if you\nwant it.\n\nYou may adopt this package by contacting support@npmjs.com and\nrequesting the name.\n",
"readmeFilename": "README.md",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/security-holder.git"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"version": "0.0.1-security"
}
... ...
test/
examples/
... ...
before_install:
- sudo apt-get update
- sudo apt-get install imagemagick graphicsmagick
language: node_js
node_js:
- "6"
- "4"
- "0.10"
- "0.12"
- "iojs-v2"
... ...
fatal: Invalid refspec '/dev/null'
... ...
1.23.0 / 2016-08-03
* fixed; webpack support #547 sean-shirazi
* fixed; windows support - use cross-spawn to spawn processes #537 bdukes
* added; allow thumbnail to accept the same options as resize #527 Sebmaster
* added; dispose support #487 dlwr
* docs; add example of loading image from URL #544 wahengchang
* docs; Fix a link in README.md #532 clbn
* travis; update travis versions #551 amilajack
1.22.0 / 2016-04-07
* fixed; identity parser: support multi-value keys by creating an array #508 #509 [emaniacs](https://github.com/emaniacs)
* fixed; error handling if gm is not installed #499 [aeo3](https://github.com/aeo3)
* fixed; highlightColor typo in compare #504 [DanielHudson](https://github.com/DanielHudson)
* docs; Fix typo #475 [rodrigoalviani](https://github.com/rodrigoalviani)
1.21.1 / 2015-10-26
* fixed: Fixed #465 hard coded gm binary, also fixed issues with compare and fixed tests so they will fail on subsequent runs when they should do [rwky](https://github.com/rwky)
1.21.0 / 2015-10-26 **contains security fix**
* fixed: gm.compare fails to escape arguments properly (Reported by Brendan Scarvell) [rwky](https://github.com/rwky)
1.20.0 / 2015-09-23
* changed: Reverted "Add format inference from filename for buffers/streams" due to errors #448
1.19.0 / 2015-09-16
* changed: Added error to notify about image magick not supporting minify [encima](https://github.com/encima)
* changed: Refactored orientation getter to use faster identify call [lbeschastny](https://github.com/lbeschastny)
* added: resizeExact function [DanMMX](https://github.com/DanMMX)
* added: thumbExact function [DanMMX](https://github.com/DanMMX)
* added: Add format inference from filename for buffers/streams [adurrive](https://github.com/adurrive)
* fixed: Hex values when passed to compare aren't quoted automatically [DanMMX](https://github.com/DanMMX)
* fixed: identify returning last frame size instead of the larges on animated gifs [preynal](https://github.com/preynal)
* docs: Updated docs [laurilehmijoki](https://github.com/laurilehmijoki)
1.18.1 / 2015-05-18
* changed: Added io.js support [rwky](https://github.com/rwky)
1.18.0 / 2015-05-18
* changed: Removed support for node 0.8 and added support for 0.12 [rwky](https://github.com/rwky)
* changed: Listen to stdin error event for spawn errors [kapouer](https://github.com/kapouer)
* changed: Improved error handling when gm isn't installed [FreshXOpenSource](https://github.com/FreshXOpenSource)
* changed: Allow append method to use an array of arguments [emohacker](https://github.com/emohacker)
* changed: appPath option now specifies full path to gm binary John Borkowski
* changed: Ignore warning messages for identify [asrail](https://github.com/asrail)
* added: Montage method [donaldpcook](https://github.com/donaldpcook)
* added: Progressive option to thumb [mohebifar](https://github.com/mohebifar)
* added: Native gm auto-orient for use with gm >= 1.3.18 [bog](https://github.com/bog)
* added: Timeout support by passing the timeout option in milliseconds [marcbachmann](https://github.com/marcbachmann)
* fixed: density when using ImageMagick [syzer](https://github.com/syzer)
* fixed: resize behaviour for falsy values [adius](https://github.com/adius)
1.17.0 / 2014-10-28
==================
* changed: extended compare callback also returns the file names #297 [mastix](https://github.com/mastix)
* changed: pass spawn crash to callback #306 [medikoo](https://github.com/medikoo)
* changed: geometry supports arbitary string as first argument #330 [jdiez17](https://github.com/jdiez17)
* added: support for repage+ option #275 [desigens](https://github.com/desigens)
* added: added the dissolve command #300 [microadm](https://github.com/microadam)
* added: composite method #332 [jdiez17](https://github.com/jdiez17)
* fixed: cannot set tolerance to 0 #302 [rwky](https://github.com/rwky)
* fixed: handle empty buffers #330 [alcidesv](https://github.com/alcidesv)
1.16.0 / 2014-05-09
==================
* fixed; dropped "+" when 0 passed as vertical roll amt #267 [dwtkns](https://github.com/dwtkns)
* added; highlight-style support #272 [fdecampredon](https://github.com/fdecampredon)
1.15.0 / 2014-05-03
===================
* changed; gm.compare logic to always run the mse comparison as expected #258 [Vokkim](https://github.com/Vokkim)
* added; `tolerance` to gm.compare options object #258 [Vokkim](https://github.com/Vokkim)
* added; option to set ImageMagick application path explicitly #250 (akreitals)
* fixed; gm.compare: support values like 9.51582e-05 #260 [normanrz](https://github.com/normanrz)
* README: add call for maintainers
1.14.2 / 2013-12-24
===================
* fixed; background is now a setting #246 (PEM--)
1.14.1 / 2013-12-09
===================
* fixed; identify -verbose colon behavior #240 ludow
1.14.0 / 2013-12-04
===================
* added; compare method for imagemagick (longlho)
1.13.3 / 2013-10-22
===================
* fixed; escape diffOptions.file in compare (dwabyick)
1.13.2 / 2013-10-18
===================
* fixed; density is a setting not an operator
1.13.1 / 2013-09-15
===================
* added; boolean for % crop
1.13.0 / 2013-09-07
===================
* added; morph more than two images (overra)
1.12.2 / 2013-08-29
===================
* fixed; fallback to through in node 0.8
1.12.1 / 2013-08-29 (unpublished)
===================
* refactor; replace through with stream.PassThrough
1.12.0 / 2013-08-27
===================
* added; diff image output file (chenglou)
1.11.1 / 2013-08-17
===================
* added; proto.selectFrame(#)
* fixed; getters should not ignore frame selection
1.11.0 / 2013-07-23
===================
* added; optional formatting string for gm().identify(format, callback) (tornillo)
* removed; error messages when gm/im binary is not installed
1.10.0 / 2013-06-27
===================
* refactor; use native `-auto-orient` for imagemagick
1.9.2 / 2013-06-12
==================
* refactor; move `streamToBuffer` to a separate module
* fixed; .stream(format) without a callback
1.9.1 / 2013-05-07
==================
* fixed; gm().resize(width) always only resizes width
* fixed; gm('img.gif').format() returns the format of the first frame
1.9.0 / 2013-04-21
==================
* added; node v0.10 support
* removed; node < v0.8 support - `Buffer.concat()`
* tests; all tests now run on Travis
* added; gm().stream() returns a stream when no callback is present
* added; gm().toBuffer(callback)
* fixed; gm().size() only returns the size of the first frame of a GIF
1.8.2 / 2013-03-07
==================
* include source path in identify data #126 [soupdiver](https://github.com/soupdiver)
1.8.1 / 2012-12-21
==================
* Avoid losing already set arguments on identify #105 #113 #109 [JNissi](https://github.com/JNissi)
* tests; add autoOrient + thumb() test
* tests; add test case for #113
* tests; added test for #109
* tests; add resize on buffer test
1.8.0 / 2012-12-14
==================
* added; geometry support to scale() #98
* removed; incorrect/broken dissolve() method (never worked)
* fixed; handle child_proc error when using Buffer input #109
* fixed; use of Buffers with identify() #109
* fixed; no longer include -size arg with resize() #98
* fixed; remove -size arg from extent() #103
* fixed; magnify support
* fixed; autoOrient to work with all types of exif orientations [dambalah](https://github.com/dambalah) #108
* tests; npm test runs unit only (now compatible with travis)
* tests; fix magnify test on imagemagick
* tests; added for cmd line args
1.7.0 / 2012-12-06
==================
* added; gm.compare support
* added; passing Buffers directly [danmilon](https://github.com/danmilon)
1.6.1 / 2012-11-13
==================
* fixed regression; only pass additional params on error #96
1.6.0 / 2012-11-10
==================
* changed; rename internal buffer to _buffer #88 [kof](https://github.com/kof)
* changed; optimized identify getters (format, depth, size, color, filesize). #83 please read this for details: https://github.com/aheckmann/gm/commit/8fcf3f8f84a02cc2001da874cbebb89bf7084409
* added; visionmedia/debug support
* added; `gm convert -thumbnail` support. _differs from thumb()._ [danmilon](https://github.com/danmilon)
* fixed; -rotate 0 support #90
* fixed; multi-execution of same gm instance arguments corruption
* fixed; gracefully handle parser errors #94 [eldilibra](https://github.com/eldilibra)
1.5.1 / 2012-10-02
==================
* fixed; passing multiple paths to append() #77
1.5.0 / 2012-09-15
==================
* fixed; callback scope
* fixed; append() usage #77
1.4.2 / 2012-08-17
==================
* fixed; identify parsing for ImageMagick exif data (#58)
* fixed; when in imageMagick mode, complain about missing imageMagick [bcherry](https://github.com/bcherry) (#73)
* added; tests
1.4.1 / 2012-07-31
==================
* fixed; scenes() args
* fixed; accept the left-to-right arg of append()
* added; _subCommand
## v1.4 - 07/28/2012
* added; adjoin() [Math-]
* added; affine() [Math-]
* added; append() [Math-]
* added; authenticate() [Math-]
* added; average() [Math-]
* added; backdrop() [Math-]
* added; blackThreshold() [Math-]
* added; bluePrimary() [Math-]
* added; border() [Math-]
* added; borderColor() [Math-]
* added; box() [Math-]
* added; channel() [Math-]
* added; clip() [Math-]
* added; coalesce() [Math-]
* added; colorMap() [Math-]
* added; compose() [Math-]
* added; compress() [Math-]
* added; convolve() [Math-]
* added; createDirectories() [Math-]
* added; deconstruct() [Math-]
* added; delay() [Math-]
* added; define() [Math-]
* added; displace() [Math-]
* added; display() [Math-]
* added; dispose() [Math-]
* added; disolve() [Math-]
* added; encoding() [Math-]
* added; endian() [Math-]
* added; file() [Math-]
* added; flatten() [Math-]
* added; foreground() [Math-]
* added; frame() [Math-]
* added; fuzz() [Math-]
* added; gaussian() [Math-]
* added; geometry() [Math-]
* added; greenPrimary() [Math-]
* added; highlightColor() [Math-]
* added; highlightStyle() [Math-]
* added; iconGeometry() [Math-]
* added; intent() [Math-]
* added; lat() [Math-]
* added; level() [Math-]
* added; list() [Math-]
* added; log() [Math-]
* added; map() [Math-]
* added; matte() [Math-]
* added; matteColor() [Math-]
* added; mask() [Math-]
* added; maximumError() [Math-]
* added; mode() [Math-]
* added; monitor() [Math-]
* added; mosaic() [Math-]
* added; motionBlur() [Math-]
* added; name() [Math-]
* added; noop() [Math-]
* added; normalize() [Math-]
* added; opaque() [Math-]
* added; operator() [Math-]
* added; orderedDither() [Math-]
* added; outputDirectory() [Math-]
* added; page() [Math-]
* added; pause() [Math-]
* added; pen() [Math-]
* added; ping() [Math-]
* added; pointSize() [Math-]
* added; preview() [Math-]
* added; process() [Math-]
* added; profile() [Math-]
* added; progress() [Math-]
* added; rawSize() [Math-]
* added; randomThreshold() [Math-]
* added; recolor() [Math-]
* added; redPrimary() [Math-]
* added; remote() [Math-]
* added; render() [Math-]
* added; repage() [Math-]
* added; sample() [Math-]
* added; samplingFactor() [Math-]
* added; scene() [Math-]
* added; scenes() [Math-]
* added; screen() [Math-]
* added; segment() [Math-]
* added; set() [Math-]
* added; shade() [Math-]
* added; shadow() [Math-]
* added; sharedMemory() [Math-]
* added; shave() [Math-]
* added; shear() [Math-]
* added; silent() [Math-]
* added; snaps() [Math-]
* added; stagano() [Math-]
* added; stereo() [Math-]
* added; textFont() [Math-]
* added; texture() [Math-]
* added; threshold() [Math-]
* added; tile() [Math-]
* added; transform() [Math-]
* added; transparent() [Math-]
* added; treeDepth() [Math-]
* added; update() [Math-]
* added; units() [Math-]
* added; unsharp() [Math-]
* added; usePixmap() [Math-]
* added; view() [Math-]
* added; virtualPixel() [Math-]
* added; visual() [Math-]
* added; watermark() [Math-]
* added; wave() [Math-]
* added; whitePoint() [Math-]
* added; whiteThreshold() [Math-]
* added; window() [Math-]
* added; windowGroup() [Math-]
## v1.3.2 - 06/22/2012
* added; node >= 0.7/0.8 compat
## v1.3.1 - 06/06/2012
* fixed; thumb() alignment and cropping [thomaschaaf]
* added; hint when graphicsmagick is not installed (#62)
* fixed; minify() (#59)
## v1.3.0 - 04/11/2012
* added; flatten support [jwarchol]
* added; background support [jwarchol]
* fixed; identify parser error [chriso]
## v1.2.0 - 03/30/2012
* added; extent and gravity support [jwarchol]
## v1.1.0 - 03/15/2012
* added; filter() support [travisbeck]
* added; density() [travisbeck]
* fixed; permit either width or height in resize [dambalah]
* updated; docs
## v1.0.5 - 02/15/2012
* added; strip() support [Math-]
* added; interlace() support [Math-]
* added; setFormat() support [Math-]
* fixed; regexps for image types [Math-]
## v1.0.4 - 02/09/2012
* expose utils
## v1.0.3 - 01/27/2012
* removed; console.log
## v1.0.2 - 01/24/2012
* added; debugging info on parser errors
* fixed; exports.version
## v1.0.1 - 01/12/2012
* fixed; use of reserved keyword `super` for node v0.5+
## v1.0.0 - 01/12/2012
* added; autoOrient support [kainosnoema] (#21)
* added; orientation support [kainosnoema] (#21)
* fixed; identify parser now properly JSON formats all data output by `gm identify` such as IPTC, GPS, Make, etc (#20)
* added; support for running as imagemagick (#23, #29)
* added; subclassing support; useful for setting default constructor options like one constructor for ImageMagick, the other for GM
* added; more tests
* changed; remove redundant `orientation`, `resolution`, and `filesize` from `this.data` in `indentify()`. Use their uppercase equivalents.
## v0.6.0 - 12/14/2011
* added; stream support [kainosnoema] (#22)
## v0.5.0 - 07/07/2011
* added; gm#trim() support [lepokle]
* added; gm#inputIs() support
* fixed; 'geometry does not contain image' error: gh-17
## v0.4.3 - 05/17/2011
* added; bunch of tests
* fixed; polygon, polyline, bezier drawing bug
## v0.4.2 - 05/10/2011
* added; resize options support
## v0.4.1 - 04/28/2011
* shell args are now escaped (thanks @visionmedia)
* added; gm.in()
* added; gm.out()
* various refactoring
## v0.4.0 - 9/21/2010
* removed deprecated `new` method
* added drawing docs
## v0.3.2 - 9/06/2010
* new images are now created using same gm() constructor
## v0.3.1 - 9/06/2010
* can now create images from scratch
* add type method
## v0.3.0 - 8/26/2010
* add drawing api
## v0.2.2 - 8/22/2010
* add quality option to thumb()
* add teropa to contributors
* added support for colorspace()
## v0.2.1 - 7/31/2010
* fixed naming conflict. depth() manipulation method renamed bitdepth()
* added better docs
## v0.2.0 - 7/29/2010
new methods
- swirl
- spread
- solarize
- sharpen
- roll
- sepia
- region
- raise
- lower
- paint
- noise
- negative
- morph
- median
- antialias
- limit
- label
- implode
- gamma
- enhance
- equalize
- emboss
- edge
- dither
- monochrome
- despeckle
- depth
- cycle
- contrast
- comment
- colors
added more default args to several methods
added more examples
## v0.1.2 - 7/28/2010
* refactor project into separate modules
## v0.1.1 - 7/27/2010
* add modulate method
* add colorize method
* add charcoal method
* add chop method
* bug fix in write without a callback
## v0.1.0 - 6/27/2010
* no longer supporting mogrify
* add image data getter methods
* size
* format
* color
* res
* depth
* filesize
* identify
* add new convert methods
* scale
* resample
* rotate
* flip
* flop
* crop
* magnify
* minify
* quality
* blur
* thumb
## v0.0.1 - 6/11/2010
Initial release
... ...
test:
@node test/ --integration $(TESTS)
test-unit:
@node test/ $(TESTS)
.PHONY: test test-unit
... ...
# gm [![Build Status](https://travis-ci.org/aheckmann/gm.png?branch=master)](https://travis-ci.org/aheckmann/gm) [![NPM Version](https://img.shields.io/npm/v/gm.svg?style=flat)](https://www.npmjs.org/package/gm)
GraphicsMagick and ImageMagick for node
## Bug Reports
When reporting bugs please include the version of graphicsmagick/imagemagick you're using (gm -version/convert -version) as well as the version of this module and copies of any images you're having problems with.
## Getting started
First download and install [GraphicsMagick](http://www.graphicsmagick.org/) or [ImageMagick](http://www.imagemagick.org/). In Mac OS X, you can simply use [Homebrew](http://mxcl.github.io/homebrew/) and do:
brew install imagemagick
brew install graphicsmagick
If you want WebP support with ImageMagick, you must add the WebP option:
brew install imagemagick --with-webp
then either use npm:
npm install gm
or clone the repo:
git clone git://github.com/aheckmann/gm.git
## Use ImageMagick instead of gm
Subclass `gm` to enable ImageMagick
```js
var fs = require('fs')
, gm = require('gm').subClass({imageMagick: true});
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
...
```
## Basic Usage
```js
var fs = require('fs')
, gm = require('gm');
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
.noProfile()
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
// some files would not be resized appropriately
// http://stackoverflow.com/questions/5870466/imagemagick-incorrect-dimensions
// you have two options:
// use the '!' flag to ignore aspect ratio
gm('/path/to/my/img.jpg')
.resize(240, 240, '!')
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
// use the .resizeExact with only width and/or height arguments
gm('/path/to/my/img.jpg')
.resizeExact(240, 240)
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
// obtain the size of an image
gm('/path/to/my/img.jpg')
.size(function (err, size) {
if (!err)
console.log(size.width > size.height ? 'wider' : 'taller than you');
});
// output all available image properties
gm('/path/to/img.png')
.identify(function (err, data) {
if (!err) console.log(data)
});
// pull out the first frame of an animated gif and save as png
gm('/path/to/animated.gif[0]')
.write('/path/to/firstframe.png', function (err) {
if (err) console.log('aaw, shucks');
});
// auto-orient an image
gm('/path/to/img.jpg')
.autoOrient()
.write('/path/to/oriented.jpg', function (err) {
if (err) ...
})
// crazytown
gm('/path/to/my/img.jpg')
.flip()
.magnify()
.rotate('green', 45)
.blur(7, 3)
.crop(300, 300, 150, 130)
.edge(3)
.write('/path/to/crazy.jpg', function (err) {
if (!err) console.log('crazytown has arrived');
})
// annotate an image
gm('/path/to/my/img.jpg')
.stroke("#ffffff")
.drawCircle(10, 10, 20, 10)
.font("Helvetica.ttf", 12)
.drawText(30, 20, "GMagick!")
.write("/path/to/drawing.png", function (err) {
if (!err) console.log('done');
});
// creating an image
gm(200, 400, "#ddff99f3")
.drawText(10, 50, "from scratch")
.write("/path/to/brandNewImg.jpg", function (err) {
// ...
});
```
## Streams
```js
// passing a stream
var readStream = fs.createReadStream('/path/to/my/img.jpg');
gm(readStream, 'img.jpg')
.write('/path/to/reformat.png', function (err) {
if (!err) console.log('done');
});
// passing a downloadable image by url
var request = require('request');
var url = "www.abc.com/pic.jpg"
gm(request(url))
.write('/path/to/reformat.png', function (err) {
if (!err) console.log('done');
});
// can also stream output to a ReadableStream
// (can be piped to a local file or remote server)
gm('/path/to/my/img.jpg')
.resize('200', '200')
.stream(function (err, stdout, stderr) {
var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
stdout.pipe(writeStream);
});
// without a callback, .stream() returns a stream
// this is just a convenience wrapper for above.
var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
gm('/path/to/my/img.jpg')
.resize('200', '200')
.stream()
.pipe(writeStream);
// pass a format or filename to stream() and
// gm will provide image data in that format
gm('/path/to/my/img.jpg')
.stream('png', function (err, stdout, stderr) {
var writeStream = fs.createWriteStream('/path/to/my/reformatted.png');
stdout.pipe(writeStream);
});
// or without the callback
var writeStream = fs.createWriteStream('/path/to/my/reformatted.png');
gm('/path/to/my/img.jpg')
.stream('png')
.pipe(writeStream);
// combine the two for true streaming image processing
var readStream = fs.createReadStream('/path/to/my/img.jpg');
gm(readStream)
.resize('200', '200')
.stream(function (err, stdout, stderr) {
var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
stdout.pipe(writeStream);
});
// GOTCHA:
// when working with input streams and any 'identify'
// operation (size, format, etc), you must pass "{bufferStream: true}" if
// you also need to convert (write() or stream()) the image afterwards
// NOTE: this buffers the readStream in memory!
var readStream = fs.createReadStream('/path/to/my/img.jpg');
gm(readStream)
.size({bufferStream: true}, function(err, size) {
this.resize(size.width / 2, size.height / 2)
this.write('/path/to/resized.jpg', function (err) {
if (!err) console.log('done');
});
});
```
## Buffers
```js
// A buffer can be passed instead of a filepath as well
var buf = require('fs').readFileSync('/path/to/image.jpg');
gm(buf, 'image.jpg')
.noise('laplacian')
.write('/path/to/out.jpg', function (err) {
if (err) return handle(err);
console.log('Created an image from a Buffer!');
});
/*
A buffer can also be returned instead of a stream
The first argument to toBuffer is optional, it specifies the image format
*/
gm('img.jpg')
.resize(100, 100)
.toBuffer('PNG',function (err, buffer) {
if (err) return handle(err);
console.log('done!');
})
```
## Custom Arguments
If `gm` does not supply you with a method you need or does not work as you'd like, you can simply use `gm().in()` or `gm().out()` to set your own arguments.
- `gm().command()` - Custom command such as `identify` or `convert`
- `gm().in()` - Custom input arguments
- `gm().out()` - Custom output arguments
The command will be formatted in the following order:
1. `command` - ie `convert`
2. `in` - the input arguments
3. `source` - stdin or an image file
4. `out` - the output arguments
5. `output` - stdout or the image file to write to
For example, suppose you want the following command:
```bash
gm "convert" "label:Offline" "PNG:-"
```
However, using `gm().label()` may not work as intended for you:
```js
gm()
.label('Offline')
.stream();
```
would yield:
```bash
gm "convert" "-label" "\"Offline\"" "PNG:-"
```
Instead, you can use `gm().out()`:
```js
gm()
.out('label:Offline')
.stream();
```
which correctly yields:
```bash
gm "convert" "label:Offline" "PNG:-"
```
### Custom Identify Format String
When identifying an image, you may want to use a custom formatting string instead of using `-verbose`, which is quite slow.
You can use your own [formatting string](http://www.imagemagick.org/script/escape.php) when using `gm().identify(format, callback)`.
For example,
```js
gm('img.png').format(function (err, format) {
})
// is equivalent to
gm('img.png').identify('%m', function (err, format) {
})
```
since `%m` is the format option for getting the image file format.
## Platform differences
Please document and refer to any [platform or ImageMagick/GraphicsMagick issues/differences here](https://github.com/aheckmann/gm/wiki/GraphicsMagick-and-ImageMagick-versions).
## Examples:
Check out the [examples](http://github.com/aheckmann/gm/tree/master/examples/) directory to play around.
Also take a look at the [extending gm](http://wiki.github.com/aheckmann/gm/extending-gm)
page to see how to customize gm to your own needs.
## Constructor:
There are a few ways you can use the `gm` image constructor.
- 1) `gm(path)` When you pass a string as the first argument it is interpreted as the path to an image you intend to manipulate.
- 2) `gm(stream || buffer, [filename])` You may also pass a ReadableStream or Buffer as the first argument, with an optional file name for format inference.
- 3) `gm(width, height, [color])` When you pass two integer arguments, gm will create a new image on the fly with the provided dimensions and an optional background color. And you can still chain just like you do with pre-existing images too. See [here](http://github.com/aheckmann/gm/blob/master/examples/new.js) for an example.
The links below refer to an older version of gm but everything should still work, if anyone feels like updating them please make a PR
## Methods
- getters
- [size](http://aheckmann.github.com/gm/docs.html#getters) - returns the size (WxH) of the image
- [orientation](http://aheckmann.github.com/gm/docs.html#getters) - returns the EXIF orientation of the image
- [format](http://aheckmann.github.com/gm/docs.html#getters) - returns the image format (gif, jpeg, png, etc)
- [depth](http://aheckmann.github.com/gm/docs.html#getters) - returns the image color depth
- [color](http://aheckmann.github.com/gm/docs.html#getters) - returns the number of colors
- [res](http://aheckmann.github.com/gm/docs.html#getters) - returns the image resolution
- [filesize](http://aheckmann.github.com/gm/docs.html#getters) - returns image filesize
- [identify](http://aheckmann.github.com/gm/docs.html#getters) - returns all image data available. Takes an optional format string.
- manipulation
- [adjoin](http://aheckmann.github.com/gm/docs.html#adjoin)
- [affine](http://aheckmann.github.com/gm/docs.html#affine)
- [antialias](http://aheckmann.github.com/gm/docs.html#antialias)
- [append](http://aheckmann.github.com/gm/docs.html#append)
- [authenticate](http://aheckmann.github.com/gm/docs.html#authenticate)
- [autoOrient](http://aheckmann.github.com/gm/docs.html#autoOrient)
- [average](http://aheckmann.github.com/gm/docs.html#average)
- [backdrop](http://aheckmann.github.com/gm/docs.html#backdrop)
- [bitdepth](http://aheckmann.github.com/gm/docs.html#bitdepth)
- [blackThreshold](http://aheckmann.github.com/gm/docs.html#blackThreshold)
- [bluePrimary](http://aheckmann.github.com/gm/docs.html#bluePrimary)
- [blur](http://aheckmann.github.com/gm/docs.html#blur)
- [border](http://aheckmann.github.com/gm/docs.html#border)
- [borderColor](http://aheckmann.github.com/gm/docs.html#borderColor)
- [box](http://aheckmann.github.com/gm/docs.html#box)
- [channel](http://aheckmann.github.com/gm/docs.html#channel)
- [charcoal](http://aheckmann.github.com/gm/docs.html#charcoal)
- [chop](http://aheckmann.github.com/gm/docs.html#chop)
- [clip](http://aheckmann.github.com/gm/docs.html#clip)
- [coalesce](http://aheckmann.github.com/gm/docs.html#coalesce)
- [colors](http://aheckmann.github.com/gm/docs.html#colors)
- [colorize](http://aheckmann.github.com/gm/docs.html#colorize)
- [colorMap](http://aheckmann.github.com/gm/docs.html#colorMap)
- [colorspace](http://aheckmann.github.com/gm/docs.html#colorspace)
- [comment](http://aheckmann.github.com/gm/docs.html#comment)
- [compose](http://aheckmann.github.com/gm/docs.html#compose)
- [compress](http://aheckmann.github.com/gm/docs.html#compress)
- [contrast](http://aheckmann.github.com/gm/docs.html#contrast)
- [convolve](http://aheckmann.github.com/gm/docs.html#convolve)
- [createDirectories](http://aheckmann.github.com/gm/docs.html#createDirectories)
- [crop](http://aheckmann.github.com/gm/docs.html#crop)
- [cycle](http://aheckmann.github.com/gm/docs.html#cycle)
- [deconstruct](http://aheckmann.github.com/gm/docs.html#deconstruct)
- [delay](http://aheckmann.github.com/gm/docs.html#delay)
- [define](http://aheckmann.github.com/gm/docs.html#define)
- [density](http://aheckmann.github.com/gm/docs.html#density)
- [despeckle](http://aheckmann.github.com/gm/docs.html#despeckle)
- [dither](http://aheckmann.github.com/gm/docs.html#dither)
- [displace](http://aheckmann.github.com/gm/docs.html#dither)
- [display](http://aheckmann.github.com/gm/docs.html#display)
- [dispose](http://aheckmann.github.com/gm/docs.html#dispose)
- [dissolve](http://aheckmann.github.com/gm/docs.html#dissolve)
- [edge](http://aheckmann.github.com/gm/docs.html#edge)
- [emboss](http://aheckmann.github.com/gm/docs.html#emboss)
- [encoding](http://aheckmann.github.com/gm/docs.html#encoding)
- [enhance](http://aheckmann.github.com/gm/docs.html#enhance)
- [endian](http://aheckmann.github.com/gm/docs.html#endian)
- [equalize](http://aheckmann.github.com/gm/docs.html#equalize)
- [extent](http://aheckmann.github.com/gm/docs.html#extent)
- [file](http://aheckmann.github.com/gm/docs.html#file)
- [filter](http://aheckmann.github.com/gm/docs.html#filter)
- [flatten](http://aheckmann.github.com/gm/docs.html#flatten)
- [flip](http://aheckmann.github.com/gm/docs.html#flip)
- [flop](http://aheckmann.github.com/gm/docs.html#flop)
- [foreground](http://aheckmann.github.com/gm/docs.html#foreground)
- [frame](http://aheckmann.github.com/gm/docs.html#frame)
- [fuzz](http://aheckmann.github.com/gm/docs.html#fuzz)
- [gamma](http://aheckmann.github.com/gm/docs.html#gamma)
- [gaussian](http://aheckmann.github.com/gm/docs.html#gaussian)
- [geometry](http://aheckmann.github.com/gm/docs.html#geometry)
- [gravity](http://aheckmann.github.com/gm/docs.html#gravity)
- [greenPrimary](http://aheckmann.github.com/gm/docs.html#greenPrimary)
- [highlightColor](http://aheckmann.github.com/gm/docs.html#highlightColor)
- [highlightStyle](http://aheckmann.github.com/gm/docs.html#highlightStyle)
- [iconGeometry](http://aheckmann.github.com/gm/docs.html#iconGeometry)
- [implode](http://aheckmann.github.com/gm/docs.html#implode)
- [intent](http://aheckmann.github.com/gm/docs.html#intent)
- [interlace](http://aheckmann.github.com/gm/docs.html#interlace)
- [label](http://aheckmann.github.com/gm/docs.html#label)
- [lat](http://aheckmann.github.com/gm/docs.html#lat)
- [level](http://aheckmann.github.com/gm/docs.html#level)
- [list](http://aheckmann.github.com/gm/docs.html#list)
- [limit](http://aheckmann.github.com/gm/docs.html#limit)
- [log](http://aheckmann.github.com/gm/docs.html#log)
- [loop](http://aheckmann.github.com/gm/docs.html#loop)
- [lower](http://aheckmann.github.com/gm/docs.html#lower)
- [magnify](http://aheckmann.github.com/gm/docs.html#magnify)
- [map](http://aheckmann.github.com/gm/docs.html#map)
- [matte](http://aheckmann.github.com/gm/docs.html#matte)
- [matteColor](http://aheckmann.github.com/gm/docs.html#matteColor)
- [mask](http://aheckmann.github.com/gm/docs.html#mask)
- [maximumError](http://aheckmann.github.com/gm/docs.html#maximumError)
- [median](http://aheckmann.github.com/gm/docs.html#median)
- [minify](http://aheckmann.github.com/gm/docs.html#minify)
- [mode](http://aheckmann.github.com/gm/docs.html#mode)
- [modulate](http://aheckmann.github.com/gm/docs.html#modulate)
- [monitor](http://aheckmann.github.com/gm/docs.html#monitor)
- [monochrome](http://aheckmann.github.com/gm/docs.html#monochrome)
- [morph](http://aheckmann.github.com/gm/docs.html#morph)
- [mosaic](http://aheckmann.github.com/gm/docs.html#mosaic)
- [motionBlur](http://aheckmann.github.com/gm/docs.html#motionBlur)
- [name](http://aheckmann.github.com/gm/docs.html#name)
- [negative](http://aheckmann.github.com/gm/docs.html#negative)
- [noise](http://aheckmann.github.com/gm/docs.html#noise)
- [noop](http://aheckmann.github.com/gm/docs.html#noop)
- [normalize](http://aheckmann.github.com/gm/docs.html#normalize)
- [noProfile](http://aheckmann.github.com/gm/docs.html#profile)
- [opaque](http://aheckmann.github.com/gm/docs.html#opaque)
- [operator](http://aheckmann.github.com/gm/docs.html#operator)
- [orderedDither](http://aheckmann.github.com/gm/docs.html#orderedDither)
- [outputDirectory](http://aheckmann.github.com/gm/docs.html#outputDirectory)
- [paint](http://aheckmann.github.com/gm/docs.html#paint)
- [page](http://aheckmann.github.com/gm/docs.html#page)
- [pause](http://aheckmann.github.com/gm/docs.html#pause)
- [pen](http://aheckmann.github.com/gm/docs.html#pen)
- [ping](http://aheckmann.github.com/gm/docs.html#ping)
- [pointSize](http://aheckmann.github.com/gm/docs.html#pointSize)
- [preview](http://aheckmann.github.com/gm/docs.html#preview)
- [process](http://aheckmann.github.com/gm/docs.html#process)
- [profile](http://aheckmann.github.com/gm/docs.html#profile)
- [progress](http://aheckmann.github.com/gm/docs.html#progress)
- [quality](http://aheckmann.github.com/gm/docs.html#quality)
- [raise](http://aheckmann.github.com/gm/docs.html#raise)
- [rawSize](http://aheckmann.github.com/gm/docs.html#rawSize)
- [randomThreshold](http://aheckmann.github.com/gm/docs.html#randomThreshold)
- [recolor](http://aheckmann.github.com/gm/docs.html#recolor)
- [redPrimary](http://aheckmann.github.com/gm/docs.html#redPrimary)
- [region](http://aheckmann.github.com/gm/docs.html#region)
- [remote](http://aheckmann.github.com/gm/docs.html#remote)
- [render](http://aheckmann.github.com/gm/docs.html#render)
- [repage](http://aheckmann.github.com/gm/docs.html#repage)
- [resample](http://aheckmann.github.com/gm/docs.html#resample)
- [resize](http://aheckmann.github.com/gm/docs.html#resize)
- [roll](http://aheckmann.github.com/gm/docs.html#roll)
- [rotate](http://aheckmann.github.com/gm/docs.html#rotate)
- [sample](http://aheckmann.github.com/gm/docs.html#sample)
- [samplingFactor](http://aheckmann.github.com/gm/docs.html#samplingFactor)
- [scale](http://aheckmann.github.com/gm/docs.html#scale)
- [scene](http://aheckmann.github.com/gm/docs.html#scene)
- [scenes](http://aheckmann.github.com/gm/docs.html#scenes)
- [screen](http://aheckmann.github.com/gm/docs.html#screen)
- [segment](http://aheckmann.github.com/gm/docs.html#segment)
- [sepia](http://aheckmann.github.com/gm/docs.html#sepia)
- [set](http://aheckmann.github.com/gm/docs.html#set)
- [setFormat](http://aheckmann.github.com/gm/docs.html#setformat)
- [shade](http://aheckmann.github.com/gm/docs.html#shade)
- [shadow](http://aheckmann.github.com/gm/docs.html#shadow)
- [sharedMemory](http://aheckmann.github.com/gm/docs.html#sharedMemory)
- [sharpen](http://aheckmann.github.com/gm/docs.html#sharpen)
- [shave](http://aheckmann.github.com/gm/docs.html#shave)
- [shear](http://aheckmann.github.com/gm/docs.html#shear)
- [silent](http://aheckmann.github.com/gm/docs.html#silent)
- [solarize](http://aheckmann.github.com/gm/docs.html#solarize)
- [snaps](http://aheckmann.github.com/gm/docs.html#snaps)
- [stegano](http://aheckmann.github.com/gm/docs.html#stegano)
- [stereo](http://aheckmann.github.com/gm/docs.html#stereo)
- [strip](http://aheckmann.github.com/gm/docs.html#strip) _imagemagick only_
- [spread](http://aheckmann.github.com/gm/docs.html#spread)
- [swirl](http://aheckmann.github.com/gm/docs.html#swirl)
- [textFont](http://aheckmann.github.com/gm/docs.html#textFont)
- [texture](http://aheckmann.github.com/gm/docs.html#texture)
- [threshold](http://aheckmann.github.com/gm/docs.html#threshold)
- [thumb](http://aheckmann.github.com/gm/docs.html#thumb)
- [tile](http://aheckmann.github.com/gm/docs.html#tile)
- [transform](http://aheckmann.github.com/gm/docs.html#transform)
- [transparent](http://aheckmann.github.com/gm/docs.html#transparent)
- [treeDepth](http://aheckmann.github.com/gm/docs.html#treeDepth)
- [trim](http://aheckmann.github.com/gm/docs.html#trim)
- [type](http://aheckmann.github.com/gm/docs.html#type)
- [update](http://aheckmann.github.com/gm/docs.html#update)
- [units](http://aheckmann.github.com/gm/docs.html#units)
- [unsharp](http://aheckmann.github.com/gm/docs.html#unsharp)
- [usePixmap](http://aheckmann.github.com/gm/docs.html#usePixmap)
- [view](http://aheckmann.github.com/gm/docs.html#view)
- [virtualPixel](http://aheckmann.github.com/gm/docs.html#virtualPixel)
- [visual](http://aheckmann.github.com/gm/docs.html#visual)
- [watermark](http://aheckmann.github.com/gm/docs.html#watermark)
- [wave](http://aheckmann.github.com/gm/docs.html#wave)
- [whitePoint](http://aheckmann.github.com/gm/docs.html#whitePoint)
- [whiteThreshold](http://aheckmann.github.com/gm/docs.html#whiteThreshold)
- [window](http://aheckmann.github.com/gm/docs.html#window)
- [windowGroup](http://aheckmann.github.com/gm/docs.html#windowGroup)
- drawing primitives
- [draw](http://aheckmann.github.com/gm/docs.html#draw)
- [drawArc](http://aheckmann.github.com/gm/docs.html#drawArc)
- [drawBezier](http://aheckmann.github.com/gm/docs.html#drawBezier)
- [drawCircle](http://aheckmann.github.com/gm/docs.html#drawCircle)
- [drawEllipse](http://aheckmann.github.com/gm/docs.html#drawEllipse)
- [drawLine](http://aheckmann.github.com/gm/docs.html#drawLine)
- [drawPoint](http://aheckmann.github.com/gm/docs.html#drawPoint)
- [drawPolygon](http://aheckmann.github.com/gm/docs.html#drawPolygon)
- [drawPolyline](http://aheckmann.github.com/gm/docs.html#drawPolyline)
- [drawRectangle](http://aheckmann.github.com/gm/docs.html#drawRectangle)
- [drawText](http://aheckmann.github.com/gm/docs.html#drawText)
- [fill](http://aheckmann.github.com/gm/docs.html#fill)
- [font](http://aheckmann.github.com/gm/docs.html#font)
- [fontSize](http://aheckmann.github.com/gm/docs.html#fontSize)
- [stroke](http://aheckmann.github.com/gm/docs.html#stroke)
- [strokeWidth](http://aheckmann.github.com/gm/docs.html#strokeWidth)
- [setDraw](http://aheckmann.github.com/gm/docs.html#setDraw)
- image output
- **write** - writes the processed image data to the specified filename
- **stream** - provides a `ReadableStream` with the processed image data
- **toBuffer** - returns the image as a `Buffer` instead of a stream
##compare
Graphicsmagicks `compare` command is exposed through `gm.compare()`. This allows us to determine if two images can be considered "equal".
Currently `gm.compare` only accepts file paths.
gm.compare(path1, path2 [, options], callback)
```js
gm.compare('/path/to/image1.jpg', '/path/to/another.png', function (err, isEqual, equality, raw, path1, path2) {
if (err) return handle(err);
// if the images were considered equal, `isEqual` will be true, otherwise, false.
console.log('The images were equal: %s', isEqual);
// to see the total equality returned by graphicsmagick we can inspect the `equality` argument.
console.log('Actual equality: %d', equality);
// inspect the raw output
console.log(raw);
// print file paths
console.log(path1, path2);
})
```
You may wish to pass a custom tolerance threshold to increase or decrease the default level of `0.4`.
```js
gm.compare('/path/to/image1.jpg', '/path/to/another.png', 1.2, function (err, isEqual) {
...
})
```
To output a diff image, pass a configuration object to define the diff options and tolerance.
```js
var options = {
file: '/path/to/diff.png',
highlightColor: 'yellow',
tolerance: 0.02
}
gm.compare('/path/to/image1.jpg', '/path/to/another.png', options, function (err, isEqual, equality, raw) {
...
})
```
##composite
GraphicsMagick supports compositing one image on top of another. This is exposed through `gm.composite()`. Its first argument is an image path with the changes to the base image, and an optional mask image.
Currently, `gm.composite()` only accepts file paths.
gm.composite(other [, mask])
```js
gm('/path/to/image.jpg')
.composite('/path/to/second_image.jpg')
.geometry('+100+150')
.write('/path/to/composite.png', function(err) {
if(!err) console.log("Written composite image.");
});
```
##montage
GraphicsMagick supports montage for combining images side by side. This is exposed through `gm.montage()`. Its only argument is an image path with the changes to the base image.
Currently, `gm.montage()` only accepts file paths.
gm.montage(other)
```js
gm('/path/to/image.jpg')
.montage('/path/to/second_image.jpg')
.geometry('+100+150')
.write('/path/to/montage.png', function(err) {
if(!err) console.log("Written montage image.");
});
```
## Contributors
[https://github.com/aheckmann/gm/contributors](https://github.com/aheckmann/gm/contributors)
## Inspiration
http://github.com/quiiver/magickal-node
## Plugins
[https://github.com/aheckmann/gm/wiki](https://github.com/aheckmann/gm/wiki)
## License
(The MIT License)
Copyright (c) 2010 [Aaron Heckmann](aaron.heckmann+github@gmail.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.
... ...
/**
* Module dependencies.
*/
var Stream = require('stream').Stream;
var EventEmitter = require('events').EventEmitter;
var util = require('util');
util.inherits(gm, EventEmitter);
/**
* Constructor.
*
* @param {String|Number} path - path to img source or ReadableStream or width of img to create
* @param {Number} [height] - optional filename of ReadableStream or height of img to create
* @param {String} [color] - optional hex background color of created img
*/
function gm (source, height, color) {
var width;
if (!(this instanceof gm)) {
return new gm(source, height, color);
}
EventEmitter.call(this);
this._options = {};
this.options(this.__proto__._options);
this.data = {};
this._in = [];
this._out = [];
this._outputFormat = null;
this._subCommand = 'convert';
if (source instanceof Stream) {
this.sourceStream = source;
source = height || 'unknown.jpg';
} else if (Buffer.isBuffer(source)) {
this.sourceBuffer = source;
source = height || 'unknown.jpg';
} else if (height) {
// new images
width = source;
source = "";
this.in("-size", width + "x" + height);
if (color) {
this.in("xc:"+ color);
}
}
if (typeof source === "string") {
// then source is a path
// parse out gif frame brackets from filename
// since stream doesn't use source path
// eg. "filename.gif[0]"
var frames = source.match(/(\[.+\])$/);
if (frames) {
this.sourceFrames = source.substr(frames.index, frames[0].length);
source = source.substr(0, frames.index);
}
}
this.source = source;
this.addSrcFormatter(function (src) {
// must be first source formatter
var inputFromStdin = this.sourceStream || this.sourceBuffer;
var ret = inputFromStdin ? '-' : this.source;
if (ret && this.sourceFrames) ret += this.sourceFrames;
src.length = 0;
src[0] = ret;
});
}
/**
* Subclasses the gm constructor with custom options.
*
* @param {options} options
* @return {gm} the subclasses gm constructor
*/
var parent = gm;
gm.subClass = function subClass (options) {
function gm (source, height, color) {
if (!(this instanceof parent)) {
return new gm(source, height, color);
}
parent.call(this, source, height, color);
}
gm.prototype.__proto__ = parent.prototype;
gm.prototype._options = {};
gm.prototype.options(options);
return gm;
}
/**
* Augment the prototype.
*/
require("./lib/options")(gm.prototype);
require("./lib/getters")(gm);
require("./lib/args")(gm.prototype);
require("./lib/drawing")(gm.prototype);
require("./lib/convenience")(gm.prototype);
require("./lib/command")(gm.prototype);
require("./lib/compare")(gm.prototype);
require("./lib/composite")(gm.prototype);
require("./lib/montage")(gm.prototype);
/**
* Expose.
*/
module.exports = exports = gm;
module.exports.utils = require('./lib/utils');
module.exports.compare = require('./lib/compare')();
module.exports.version = require('./package.json').version;
... ...