makepromise
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -8,2 +8,3 @@ module.exports = { | ||
"parserOptions": { | ||
"ecmaVersion": 2017, | ||
"sourceType": "module" | ||
@@ -10,0 +11,0 @@ }, |
@@ -7,12 +7,16 @@ { | ||
"configurations": [ | ||
{ | ||
"type": "node", | ||
"request": "launch", | ||
"name": "Launch Program", | ||
"name": "Launch Zoroaster", | ||
"program": "${workspaceRoot}/node_modules/.bin/zoroaster", | ||
"args": [ | ||
"test/spec/integration.js" | ||
] | ||
], | ||
"env": { | ||
"ZOROASTER_TIMEOUT": 99999999 | ||
} | ||
} | ||
] | ||
} |
@@ -1,22 +0,28 @@ | ||
const fs = require('fs') | ||
const wrote = require('wrote') | ||
const makePromise = require('../.') | ||
const { unlink } = require('fs') | ||
const { createWritable } = require('wrote') | ||
const makePromise = require('../') | ||
let file | ||
wrote() // create a temp file and open a writable stream | ||
.then((ws) => { | ||
file = ws.path | ||
return new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
// here, just close the stream without makepromise, because it's a different | ||
// kind of constructor, ie. resolve-reject and not error callback handlers | ||
// todo: can close stream with writing null | ||
async function closeWritable(ws) { | ||
await new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
.then(() => { | ||
const promise = makePromise(fs.unlink, file, file) | ||
return promise | ||
}) | ||
.then( | ||
(res) => { console.log(res === file) }, // true | ||
console.error | ||
) | ||
} | ||
(async () => { | ||
// create a temp file and open a writable stream | ||
const ws = await createWritable() | ||
await closeWritable(ws) | ||
const { path: file } = ws | ||
console.log(file) // /var/folders/s0/tmp/T/wrote-69822.data | ||
// pass 3rd argument as the return value | ||
const resolvedFile = await makePromise(unlink, file, file) | ||
console.log(resolvedFile === file) // true | ||
})() |
@@ -1,23 +0,26 @@ | ||
const fs = require('fs') | ||
const wrote = require('wrote') | ||
const makePromise = require('../.') | ||
const { unlink } = require('fs') | ||
const { createWritable } = require('wrote') | ||
const makePromise = require('../') | ||
let file | ||
wrote() // create a temp file and open a writable stream | ||
.then((ws) => { | ||
file = ws.path | ||
console.log(ws.path) // /var/folders/s0/tmp/T/wrote-69822.data | ||
return new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
// here, just close the stream without makepromise, because it's a different | ||
// kind of constructor, ie. resolve-reject and not error callback handlers | ||
// todo: can close stream with writing null | ||
async function closeWritable(ws) { | ||
await new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
.then(() => { | ||
const promise = makePromise(fs.unlink, file) | ||
return promise | ||
}) | ||
.then( | ||
console.log, // undefined | ||
console.error | ||
) | ||
} | ||
(async () => { | ||
// create a temp file and open a writable stream | ||
const ws = await createWritable() | ||
await closeWritable(ws) | ||
const { path: file } = ws | ||
console.log(file) // /var/folders/s0/tmp/T/wrote-69822.data | ||
await makePromise(unlink, file) | ||
})() |
{ | ||
"name": "makepromise", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Make a native Promise from a function with a callback", | ||
"main": "src/index", | ||
"scripts": { | ||
"test": "zoroaster test/spec" | ||
"test": "zoroaster test/spec", | ||
"test-es5": "zoroaster es5/test/spec", | ||
"build-src": "babel src --out-dir es5/src", | ||
"build-test": "babel test --out-dir es5/test", | ||
"build": "run-s build-src build-test" | ||
}, | ||
@@ -27,6 +31,15 @@ "repository": { | ||
"devDependencies": { | ||
"catchment": "^1.0.0", | ||
"lockfile": "^1.0.3", | ||
"wrote": "^0.1.0" | ||
"babel-cli": "6.26.0", | ||
"babel-plugin-transform-rename-import": "2.1.1", | ||
"babel-preset-env": "1.6.1", | ||
"catchment": "1.0.0", | ||
"fast-async": "6.3.0", | ||
"lockfile": "1.0.3", | ||
"npm-run-all": "4.1.2", | ||
"wrote": "1.0.0", | ||
"zoroaster": "0.4.6" | ||
}, | ||
"dependencies": { | ||
"erotic": "0.2.0" | ||
} | ||
} |
134
README.md
@@ -5,38 +5,70 @@ # makepromise | ||
Make native Promise from function with callback | ||
Make native `Promise` from function with callback. | ||
## ES5 | ||
The package uses some newer language features. For your convenience, it's been | ||
transpiled to be compatible with Node 4. You can use the following snippet. | ||
```js | ||
const makePromise = require('makepromise/es5/src/') | ||
``` | ||
## `makePromise(fn:Function(...args, cb:Function(err:Error, res:any))) => Promise<any>` | ||
Create a promise from a function which accepts callback as the last argument, | ||
and where callback will be called with (error, result). | ||
Create a promise from a function which accepts a callback as the last argument, | ||
and where the callback will be called with 2 arguments: `(error, result)`. | ||
For example, you can unlink a closed file: | ||
For example, you can unlink a file (here, a temp file is created with | ||
[`wrote` package][1]): | ||
```js | ||
'use strict' | ||
const fs = require('fs') | ||
const wrote = require('wrote') | ||
const { unlink } = require('fs') | ||
const { createWritable } = require('wrote') | ||
const makePromise = require('makepromise') | ||
let file | ||
wrote() // create a temp file and open a writable stream | ||
.then((ws) => { | ||
file = ws.path | ||
console.log(ws.path) // /var/folders/s0/tmp/T/wrote-69822.data | ||
return new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
(async () => { | ||
// create a temp file and open a writable stream | ||
const ws = await createWritable() | ||
const { path: file } = ws // /var/folders/s0/tmp/T/wrote-69822.data | ||
// here, just close the stream without makepromise, because it's a different | ||
// kind of constructor, ie. resolve-reject and not error callback handlers | ||
await new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
.then(() => { | ||
const promise = makePromise(fs.unlink, file) | ||
return promise | ||
}) | ||
.then( | ||
console.log, // undefined | ||
console.error | ||
) | ||
await makePromise(unlink, file) | ||
})() | ||
``` | ||
In addition, errors will be updated to include the stack trace of when | ||
`makePromise` was called, rather than have node internal error stack, or no | ||
stack at all: | ||
```js | ||
const fs = require('fs') | ||
const makePromise = require('makepromise'); | ||
(async () => { | ||
try { | ||
await makePromise(fs.unlink, 'error-test-file') | ||
} catch ({ stack }){ | ||
console.log(stack) | ||
/* | ||
Error: ENOENT: no such file or directory, unlink 'error-test-file' | ||
at cb (makepromise/src/index.js:28:31) | ||
at makePromise (makepromise/src/index.js:18:16) | ||
at Object.<anonymous> (makepromise/examples/error-stack.js:4:1) | ||
*/ | ||
// without this feature: | ||
/* | ||
Error: ENOENT: no such file or directory, unlink 'error-test-file' | ||
*/ | ||
} | ||
})() | ||
``` | ||
@@ -49,27 +81,19 @@ | ||
```js | ||
'use strict' | ||
const { unlink } = require('fs') | ||
const { createWritable } = require('wrote') | ||
const makePromise = require('makepromise'); | ||
const fs = require('fs') | ||
const wrote = require('wrote') | ||
const makePromise = require('makepromise') | ||
(async () => { | ||
// create a temp file and open a writable stream | ||
const ws = await createWritable() | ||
await closeWritable(ws) | ||
let file | ||
wrote() // create a temp file and open a writable stream | ||
.then((ws) => { | ||
file = ws.path | ||
console.log(ws.path) // /var/folders/s0/tmp/T/wrote-69822.data | ||
return new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
}) | ||
.then(() => { | ||
const promise = makePromise(fs.unlink, file, file) | ||
return promise | ||
}) | ||
.then( | ||
(res) => { console.log(res === file) }, // true | ||
console.error | ||
) | ||
const { path: file } = ws | ||
console.log(file) // /var/folders/s0/tmp/T/wrote-69822.data | ||
// pass 3rd argument as the return value | ||
const resolvedFile = await makePromise(unlink, file, file) | ||
console.log(resolvedFile === file) // true | ||
})() | ||
``` | ||
@@ -81,8 +105,8 @@ | ||
* How to [unlock a file with](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L15) | ||
[`lockfile`](https://www.npmjs.com/package/lockfile) and close write stream | ||
* [How to write to a file stream](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L45) open with `fs.createWriteStream()` and end the stream | ||
* How to [unlink a file](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L73) with `fs.unlink` | ||
* How to [read stat](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L99) with `fs.stat` | ||
* How to catch errors (example with trying to [call `fs.stat` with non-existent file](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L114)) | ||
* How to [unlock a file with](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L12) | ||
[`lockfile` package][2] | ||
* [How to write to a writable stream](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L29) | ||
* How to [unlink a file with `fs.unlink`](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L38) | ||
* How to [read stat with `fs.stat`](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L58) | ||
* How to catch errors after [call `fs.stat` with non-existent file](https://github.com/Sobesednik/makepromise/blob/master/test/spec/integration.js#L66)) | ||
@@ -95,1 +119,3 @@ --- | ||
[1]: https://www.npmjs.com/package/wrote | ||
[2]: https://www.npmjs.com/package/lockfile |
@@ -0,1 +1,9 @@ | ||
const erotic = require('erotic') | ||
function checkArgumentIndex(length, i) { | ||
if (i > length - 2) { | ||
throw new Error('Function does not accept that many arguments') | ||
} | ||
} | ||
/** | ||
@@ -9,22 +17,35 @@ * Create a promise from a function. | ||
*/ | ||
function makePromise(fn, args, resolveValue) { | ||
async function makePromise(fn, args, resolveValue) { | ||
const er = erotic() | ||
if (typeof fn !== 'function') { | ||
return Promise.reject(new Error('function must be passed')) | ||
throw new Error('function must be passed') | ||
} | ||
return new Promise((resolve, reject)=> { | ||
if (!fn.length) { | ||
throw new Error('Function does not accept any arguments') | ||
} | ||
const res = await new Promise((resolve, reject)=> { | ||
const cb = (err, res) => { | ||
if (err) return reject(err) | ||
if (err) { | ||
const error = er(err) | ||
return reject(error) | ||
} | ||
return resolve(resolveValue || res) | ||
} | ||
const allArgs = [] | ||
const allArgs = Array.from({ length: fn.length }) | ||
allArgs[allArgs.length - 1] = cb | ||
if (Array.isArray(args)) { | ||
args.forEach(arg => allArgs.push(arg)) | ||
} else if (args) { | ||
allArgs.push(args) | ||
args.forEach((arg, i) => { | ||
checkArgumentIndex(allArgs.length, i) | ||
allArgs[i] = arg | ||
}) | ||
} else { | ||
checkArgumentIndex(allArgs.length, 0) | ||
allArgs[0] = args | ||
} | ||
allArgs.push(cb) | ||
fn.apply(fn.this, allArgs) | ||
}) | ||
return res | ||
} | ||
module.exports = makePromise |
const assert = require('assert') | ||
const makePromise = require('../../.') | ||
const context = require('../context/MakePromise') | ||
const makePromise = require('../..') | ||
function Context() { | ||
Object.defineProperties(this, { | ||
testArg: { | ||
value: 'test-arg', | ||
}, | ||
nullErrFn: { | ||
get: () => (arg, cb) => { | ||
if (typeof arg === 'function') { // assume no argument is expected | ||
return arg(null) | ||
} | ||
return cb(null, arg) | ||
}, | ||
}, | ||
errFn: { | ||
get: () => (err, cb) => cb(err, null), | ||
}, | ||
}) | ||
} | ||
const { equal } = assert | ||
const makePromiseTestSuite = { | ||
context: Context, | ||
'should create a promise': (ctx) => { | ||
const res = makePromise(ctx.nullErrFn) | ||
context, | ||
async 'should create a promise'({ nullErrFn }) { | ||
const res =makePromise(nullErrFn) | ||
assert(res instanceof Promise) | ||
return res.catch(() => {}) | ||
try { | ||
await res | ||
} catch (err) { /* test-error */ } | ||
}, | ||
'should resolve with undefined': (ctx) => { | ||
const res = makePromise(ctx.nullErrFn) | ||
return res | ||
.then((res) => { | ||
assert.equal(res, undefined) | ||
}) | ||
async 'should resolve with undefined'({ nullErrFn }) { | ||
const res = await makePromise(nullErrFn) | ||
equal(res, undefined) | ||
}, | ||
'should resolve with function result': (ctx) => { | ||
const res = makePromise(ctx.nullErrFn, ctx.testArg) | ||
return res | ||
.then((res) => { | ||
assert.equal(res, ctx.testArg) | ||
}) | ||
async 'should resolve with function result'({ nullErrFn, testArg }) { | ||
const res = await makePromise(nullErrFn, testArg) | ||
equal(res, testArg) | ||
}, | ||
'should reject if not a function': () => { | ||
async 'should reject if not a function'() { | ||
const notAFunction = 'this is not a function' | ||
const res = makePromise(notAFunction) | ||
return res | ||
.then(() => { | ||
throw new Error('should have been rejected') | ||
}, (err) => { | ||
assert(/function must be passed/.test(err.message)) | ||
}) | ||
try { | ||
await makePromise(notAFunction) | ||
throw new Error('should have been rejected') | ||
} catch ({ message }) { | ||
assert(/function must be passed/.test(message)) | ||
} | ||
}, | ||
'should resolve with supplied argument': (ctx) => { | ||
async 'should resolve with supplied argument'(ctx) { | ||
const testValue = 'test-value' | ||
const res = makePromise(ctx.nullErrFn, ctx.testArg, testValue) | ||
return res | ||
.then((res) => { | ||
assert.equal(res, testValue) | ||
}) | ||
const res = await makePromise(ctx.nullErrFn, ctx.testArg, testValue) | ||
equal(res, testValue) | ||
}, | ||
'should reject with error': (ctx) => { | ||
async 'should reject with error'({ errFn }) { | ||
const testError = new Error('test-error') | ||
const res = makePromise(ctx.errFn, testError) | ||
return res | ||
.then(() => { | ||
throw new Error('expected to have been rejected') | ||
}, (err) => { | ||
assert.equal(err, testError) | ||
}) | ||
try { | ||
await makePromise(errFn, testError) | ||
throw new Error('expected to have been rejected') | ||
} catch({ message }) { | ||
equal(message, testError.message) | ||
} | ||
}, | ||
// test context | ||
async 'should reject with error with a correct stack'(ctx) { | ||
const testError = new Error('test-error') | ||
delete testError.stack | ||
try { | ||
await makePromise(ctx.errFn, testError) | ||
throw new Error('expected to have been rejected') | ||
} catch ({ stack }) { | ||
assert(stack.indexOf('should reject with error with a correct stack') != -1) | ||
} | ||
}, | ||
} | ||
module.exports = makePromiseTestSuite |
@@ -1,130 +0,75 @@ | ||
'use strict' | ||
const assert = require('assert') | ||
const wrote = require('wrote') | ||
const fs = require('fs') | ||
const makePromise = require('../../.') | ||
const Stats = fs.Stats | ||
const Catchment = require('catchment') | ||
const lockfile = require('lockfile') | ||
const path = require('path') | ||
const { stat, unlink, Stats } = require('fs') | ||
const { unlock } = require('lockfile') | ||
const { erase, read } = require('wrote') | ||
const context = require('../context/MakePromise') | ||
const makePromise = require('../..') | ||
const filepath = path.join(__dirname, '/fixtures/some-file.lock') | ||
const { equal } = assert | ||
const IntegrationTestSuite = { | ||
'should be able to unlock a file': () => { | ||
let filepath | ||
let writeStream | ||
return wrote() | ||
.then((ws) => { | ||
writeStream = ws | ||
assert(writeStream.writable) | ||
filepath = writeStream.path | ||
return makePromise(fs.stat, filepath) | ||
}) | ||
.then(() => { | ||
return makePromise(lockfile.unlock, filepath) | ||
}) | ||
.then(() => { | ||
assert(writeStream.writable) // stream not closed | ||
return makePromise(fs.stat, filepath) | ||
}) | ||
.then(() => { | ||
throw new Error('should have been rejected') | ||
}, (err) => { | ||
assert(err instanceof Error) | ||
// console.log(Date.now(), 'no file') | ||
assert(/ENOENT: no such file or directory, stat/.test(err.message)) | ||
return makePromise(writeStream.end.bind(writeStream)) | ||
}) | ||
.then(() => { | ||
// console.log(Date.now(), 'write stream closed') | ||
assert(!writeStream.writable) | ||
}) | ||
context, | ||
async 'should be able to unlock a file'({ getWs }) { | ||
const ws = await getWs() | ||
assert(ws.writable) | ||
await makePromise(stat, ws.path) | ||
await makePromise(unlock, ws.path) | ||
assert(!ws.closed) // stream not closed | ||
assert(!ws._writableState.ended) | ||
try { | ||
await makePromise(stat, ws.path) | ||
throw new Error('should have been rejected') | ||
} catch ({ code }) { | ||
assert(code, 'ENOENT') | ||
} | ||
await makePromise(ws.end.bind(ws)) | ||
assert(ws.closed) | ||
assert(ws._writableState.ended) | ||
}, | ||
'should write to a file stream': () => { | ||
async 'should write to a file stream'({ getWs }) { | ||
const testData = 'some-test-data' | ||
let catchmentPromise | ||
let writeStream | ||
return wrote() | ||
.then((ws) => { | ||
writeStream = ws | ||
return makePromise(ws.write.bind(ws), testData, ws) | ||
}) | ||
.then(() => { | ||
// read file | ||
return new Promise((resolve, reject) => { | ||
const rs = fs.createReadStream(writeStream.path) | ||
rs.once('open', () => resolve(rs)) | ||
rs.once('error', reject) | ||
}) | ||
.then((rs) => { | ||
const catchment = new Catchment() | ||
rs.pipe(catchment) | ||
catchmentPromise = catchment.promise | ||
return makePromise(writeStream.end.bind(writeStream)) | ||
}) | ||
.then(() => catchmentPromise) | ||
.then((res) => { | ||
assert.equal(res, testData) | ||
}) | ||
}) | ||
const ws = await getWs() | ||
await makePromise(ws.write.bind(ws), testData) | ||
// read file | ||
const actual = await read(ws.path) | ||
equal(actual, testData) | ||
}, | ||
'should unlink a file': () => { | ||
let file | ||
return wrote() | ||
.then((ws) => { | ||
file = ws.path | ||
return new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
}) | ||
.then(() => { | ||
const promise = makePromise(fs.unlink, file, file) | ||
return promise | ||
}) | ||
async 'should unlink a file'({ getWs }) { | ||
const ws = await getWs() | ||
await new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
await makePromise(unlink, ws.path) | ||
try { | ||
await read(ws.path) | ||
throw new Error('should have thrown') | ||
} catch ({ code }) { | ||
equal(code, 'ENOENT') | ||
} | ||
}, | ||
'should end stream': () => { | ||
return wrote() | ||
.then((ws) => { | ||
const promise = makePromise(ws.end.bind(ws), null, ws) | ||
return promise | ||
}) | ||
.then((ws) => { | ||
assert(!ws.writable) | ||
}) | ||
async 'should end stream'({ getWs }) { | ||
const ws = await getWs() | ||
await makePromise(ws.end.bind(ws)) | ||
assert(ws._writableState.ended) | ||
}, | ||
'should read stats': () => { | ||
return wrote() | ||
.then((ws) => { | ||
assert(ws.writable) | ||
const promise = makePromise(fs.stat, ws.path) | ||
.then((res) => { | ||
assert(res instanceof Stats) | ||
return ws | ||
}) | ||
return promise | ||
}) | ||
.then((ws) => { | ||
return wrote.erase(ws) | ||
}) | ||
async 'should read stats'({ getWs }) { | ||
const ws = await getWs() | ||
assert(!ws._writableState.ended) | ||
const res = await makePromise(stat, ws.path) | ||
assert(res instanceof Stats) | ||
await erase(ws) // move to context | ||
}, | ||
'should not read stats of non-existent file': () => { | ||
return wrote() | ||
.then((ws) => { | ||
return wrote.erase(ws).then(() => ws) | ||
}) | ||
.then((ws) => { | ||
assert(!ws.writable) | ||
const promise = makePromise(fs.stat, ws.path) | ||
return promise | ||
}) | ||
.then(() => { | ||
throw new Error('should have been rejected') | ||
}, (err) => { | ||
assert(err instanceof Error) | ||
assert(/ENOENT: no such file or directory, stat/.test(err.message)) | ||
}) | ||
async 'should not read stats of non-existent file'({ getWs }) { | ||
const ws = await getWs() | ||
await erase(ws) | ||
try { | ||
await makePromise(stat, ws.path) | ||
throw new Error('should have been rejected') | ||
} catch ({ code }) { | ||
equal(code, 'ENOENT') | ||
} | ||
}, | ||
@@ -131,0 +76,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50863
20
1134
118
1
9
5
1
+ Addederotic@0.2.0
+ Addedclean-stack@1.3.0(transitive)
+ Addederotic@0.2.0(transitive)