makepromise
Advanced tools
Comparing version 2.0.0 to 3.0.0
@@ -1,68 +0,52 @@ | ||
"use strict"; | ||
let erotic = require('erotic'); if (erotic && erotic.__esModule) erotic = erotic.default; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = makePromise; | ||
var _erotic = _interopRequireDefault(require("erotic")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function checkArgumentIndex(length, i) { | ||
if (i > length - 2) { | ||
throw new Error('Function does not accept that many arguments'); | ||
throw new Error('Function does not accept that many arguments.') | ||
} | ||
} | ||
/** | ||
* Create a promise from a function. | ||
* @param {function} fn Function to call | ||
* @param {any[]|any} [args] Array of arguments to use in call, or one argument, or none | ||
* @param {any} [resolveValue] Override function's return value with this | ||
* @returns {Promise<any>} A promise resolved on callback invocation w/out error, | ||
* and rejected on callback called w/ error. | ||
* Get a promise from a function which otherwise accepts a callback. | ||
* @param {function} fn A function to promisify. | ||
* @param {any[]|any} [args] An array of arguments to use in the call, or a single argument. | ||
* @param {any} [resolveValue] A value to override the value with which the promise will be resolved. | ||
* @returns {Promise<any>} A promise resolved on callback invocation without an error and rejected on callback called with an error. | ||
*/ | ||
async function makePromise(fn, args, resolveValue) { | ||
const er = (0, _erotic.default)(); | ||
async function makePromise(fn, args, resolveValue) { | ||
const er = erotic(true) | ||
if (typeof fn !== 'function') { | ||
throw new Error('function must be passed'); | ||
throw new Error('Function must be passed.') | ||
} | ||
const { | ||
length: fnLength | ||
} = fn; | ||
const { length: fnLength } = fn | ||
if (!fnLength) { | ||
throw new Error('Function does not accept any arguments'); | ||
throw new Error('Function does not accept any arguments.') | ||
} | ||
const res = await new Promise((resolve, reject) => { | ||
const res = await new Promise((resolve, reject)=> { | ||
const cb = (err, res) => { | ||
if (err) { | ||
const error = er(err); | ||
return reject(error); | ||
const error = er(err) | ||
return reject(error) | ||
} | ||
return resolve(resolveValue || res) | ||
} | ||
return resolve(resolveValue || res); | ||
}; | ||
let allArgs = [cb] | ||
let allArgs = [cb]; | ||
if (Array.isArray(args)) { | ||
args.forEach((arg, i) => { | ||
checkArgumentIndex(fnLength, i); | ||
}); | ||
allArgs = [...args, cb]; | ||
} else if (Array.from(arguments).length > 1) { | ||
// args passed as a single argument, not array | ||
checkArgumentIndex(fnLength, 0); | ||
allArgs = [args, cb]; | ||
checkArgumentIndex(fnLength, i) | ||
}) | ||
allArgs = [...args, cb] | ||
} else if (Array.from(arguments).length > 1) { // args passed as a single argument, not array | ||
checkArgumentIndex(fnLength, 0) | ||
allArgs = [args, cb] | ||
} | ||
fn.apply(fn.this, allArgs) | ||
}) | ||
return res | ||
} | ||
fn.apply(fn.this, allArgs); | ||
}); | ||
return res; | ||
} | ||
module.exports = makePromise | ||
//# sourceMappingURL=index.js.map |
@@ -0,1 +1,10 @@ | ||
## 31 August 2018 | ||
### 3.0.0 | ||
- [package] Remove `ES5` folder. | ||
- [build] Build w/ [`alamode`](https://alamode.cc). | ||
- [doc] Document with `documentary`, add bound methods example. | ||
- [feature] Transparent `erotic` error stack. | ||
## 12 May 2018 | ||
@@ -2,0 +11,0 @@ |
{ | ||
"name": "makepromise", | ||
"version": "2.0.0", | ||
"description": "Make a native Promise from a function with a callback", | ||
"version": "3.0.0", | ||
"description": "Make a Promise from a function with a callback and preserve its error stack.", | ||
"main": "build", | ||
"scripts": { | ||
"teb": "LIB_MAIN=../../es5 zoroaster --babel", | ||
"t": "zoroaster --babel", | ||
"test": "zoroaster test/spec --babel", | ||
"test-build": "LIB_MAIN=../../build zoroaster test/spec --babel", | ||
"test-es5-bin": "LIB_MAIN=../../es5 zoroaster test/spec --babel", | ||
"test-es5": "zoroaster es5/test/spec", | ||
"build": "babel src --out-dir build", | ||
"e": "node examples/run" | ||
"t": "zoroaster -a", | ||
"test": "zoroaster test/spec -a", | ||
"test-build": "ALAMODE_ENV=test-build zoroaster test/spec -a", | ||
"build": "yarn-s b doc", | ||
"b": "alamode src -o build", | ||
"doc": "NODE_DEBUG=doc doc documentary -o README.md", | ||
"e": "node example" | ||
}, | ||
@@ -22,6 +21,13 @@ "repository": { | ||
"promise", | ||
"async", | ||
"await", | ||
"ecmascript", | ||
"es6", | ||
"ecma", | ||
"promisify", | ||
"node", | ||
"callback", | ||
"cb" | ||
"cb", | ||
"error", | ||
"stack" | ||
], | ||
@@ -35,15 +41,12 @@ "author": "Anton <anton@adc.sh>", | ||
"devDependencies": { | ||
"@babel/cli": "7.0.0-beta.46", | ||
"@babel/core": "7.0.0-beta.46", | ||
"@babel/plugin-syntax-object-rest-spread": "7.0.0-beta.46", | ||
"@babel/plugin-transform-modules-commonjs": "7.0.0-beta.46", | ||
"@babel/register": "7.0.0-beta.46", | ||
"catchment": "2.0.1", | ||
"lockfile": "1.0.4", | ||
"alamode": "1.3.0", | ||
"catchment": "3.0.0", | ||
"documentary": "1.10.0", | ||
"wrote": "1.4.0", | ||
"zoroaster": "1.1.0" | ||
"yarn-s": "1.1.0", | ||
"zoroaster": "2.4.0" | ||
}, | ||
"dependencies": { | ||
"erotic": "1.0.1" | ||
"erotic": "1.2.1" | ||
} | ||
} |
209
README.md
# makepromise | ||
[![npm version](https://badge.fury.io/js/makepromise.svg)](https://badge.fury.io/js/makepromise) | ||
[![npm version](https://badge.fury.io/js/makepromise.svg)](https://npmjs.org/package/makepromise) | ||
Make native `Promise` from a function with a callback. | ||
`makepromise` can be used to get a _Promise_ from a function with a callback. It will also make sure that the error stack starts at the line where the `makepromise` was called. | ||
## `makePromise(fn:Function(...args, cb:Function(err:Error, res:any))) => Promise<any>` | ||
``` | ||
yarn add -E makepromise | ||
``` | ||
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)`. | ||
## Table Of Contents | ||
For example, you can unlink a file (here, a temp file is created with | ||
[`wrote` package][1]): | ||
- [Table Of Contents](#table-of-contents) | ||
- [API](#api) | ||
* [`async makePromise(fn: function(...args, cb), args: (*[]|*))`](#async-makepromisefn-functionargs-cbargs--void) | ||
* [`async makePromise(fn: function(...args, cb), args: (*[]|*), resolveValue: *)`](#async-makepromisefn-functionargs-cbargs-resolvevalue--void) | ||
* [Binding Methods](#binding-methods) | ||
- [Error Stack](#error-stack) | ||
- [TODO](#todo) | ||
- [Copyright](#copyright) | ||
## API | ||
The package exports a default `makepromise` function. | ||
```js | ||
import { unlink } from 'fs' | ||
import { createWritable } from 'wrote' | ||
import makePromise from 'makepromise' | ||
``` | ||
### `async makePromise(`<br/> `fn: function(...args, cb),`<br/> `args: (*[]|*),`<br/>`): void` | ||
let file | ||
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` and `result`. The arguments must be passed either as an array, or a single value. | ||
The example below shows how to use `makePromise` with an array of arguments (promisified `truncate`) and a single argument (promisified `unlink`). The context of the example is 2 methods from a lib to create a temp file, and read data from a file. | ||
```js | ||
import { truncate, unlink, existsSync } from 'fs' | ||
import makePromise from 'makepromise' | ||
import { createTempFile, readFile } from './lib' | ||
(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() | ||
}) | ||
await makePromise(unlink, file) | ||
try { | ||
// 0. SETUP: create a temp file. | ||
const path = await createTempFile('hello-world') | ||
const data = readFile(path) | ||
console.log('Created temp file %s', path) | ||
console.log('Exists: %s', existsSync(path)) | ||
console.log('Content: "%s"', data) | ||
// 1. TRUNCATE to 5 characters. | ||
await makePromise(truncate, [path, 5]) | ||
const data2 = await readFile(path) | ||
console.log('Content: "%s"', data2) | ||
// 2. ERASE the temp file. | ||
await makePromise(unlink, path) | ||
console.log('Exists: %s', existsSync(path)) | ||
} catch (err) { | ||
console.log(err) | ||
} | ||
})() | ||
``` | ||
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: | ||
``` | ||
Created temp file example/temp.data | ||
Exists: true | ||
Content: "hello-world" | ||
Content: "hello" | ||
Exists: false | ||
``` | ||
### `async makePromise(`<br/> `fn: function(...args, cb),`<br/> `args: (*[]|*),`<br/> `resolveValue: *,`<br/>`): void` | ||
When `resolveValue` is passed as the last argument to the `makePromise` function, the returned promise will be forced to resolve with it. | ||
```js | ||
import { unlink } from 'fs' | ||
import makePromise from 'makepromise' | ||
import { createTempFile } from './lib' | ||
(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' | ||
*/ | ||
// 0. SETUP: create a temp file. | ||
const path = await createTempFile() | ||
// 1. UNLINK and return the path to the temp file. | ||
const erasedPath = await makePromise(unlink, path, path) | ||
console.log('Erased: %s', erasedPath) | ||
} catch (err) { | ||
console.log(err) | ||
} | ||
@@ -62,51 +93,97 @@ })() | ||
## `makePromise(fn:Function(...args, cb:Function(err:Error, res:any), resolveValue:any)) => Promise<any>` | ||
``` | ||
Erased: example/temp.data | ||
``` | ||
Pass `resolveValue` as second argument to make promise be resolved with it. | ||
### Binding Methods | ||
Sometimes, it is important to bind methods of instances to their contexts, otherwise they will loose access to `this`. | ||
For example. when closing a _Writable_ stream, its `close` method must be bound to its instance. | ||
```js | ||
import { unlink } from 'fs' | ||
import { createWritable } from 'wrote' | ||
import { createWriteStream, unlink } from 'fs' | ||
import makePromise from 'makepromise' | ||
import { createTempFile, readFile } from './lib' | ||
(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 | ||
try { | ||
// 0. SETUP: create a temp file. | ||
const path = await createTempFile() | ||
// 1. CREATE a write stream, and end it with data. | ||
const ws = createWriteStream(path) | ||
await makePromise(ws.end.bind(ws), 'example-data') | ||
// 2. CHECK that data has been written. | ||
const data = await readFile(path) | ||
console.log('Read file: "%s"', data) | ||
// 3. TEAR-DOWN: remove file. | ||
await makePromise(unlink, path) | ||
} catch (err) { | ||
console.log(err) | ||
} | ||
})() | ||
``` | ||
## More examples | ||
``` | ||
Read file: "example-data" | ||
``` | ||
Check `test/spec/integration` for the following tests: | ||
## Error Stack | ||
* How to [unlock a file with](https://github.com/artdecocode/makepromise/blob/master/test/spec/integration.js#L12) | ||
[`lockfile` package][2] | ||
* [How to write to a writable stream](https://github.com/artdecocode/makepromise/blob/master/test/spec/integration.js#L29) | ||
* How to [unlink a file with `fs.unlink`](https://github.com/artdecocode/makepromise/blob/master/test/spec/integration.js#L38) | ||
* How to [read stat with `fs.stat`](https://github.com/artdecocode/makepromise/blob/master/test/spec/integration.js#L58) | ||
* How to catch errors after [call `fs.stat` with non-existent file](https://github.com/artdecocode/makepromise/blob/master/test/spec/integration.js#L66)) | ||
This modules will make sure that errors are updated to include the stack trace of when `makePromise` was called, rather than have the Node's internal error stack or no stack at all. | ||
## ES5 | ||
```js | ||
import { unlink } from 'fs' | ||
import makePromise from 'makepromise' | ||
> DO NOT USE THIS. USE NEWER NODE.JS | ||
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. | ||
(async () => { | ||
try { | ||
await makePromise(unlink, 'error-test-file') | ||
} catch ({ stack }) { | ||
console.log(stack) | ||
} | ||
})() | ||
``` | ||
``` | ||
Error: ENOENT: no such file or directory, unlink 'error-test-file' | ||
at /Users/zavr/adc/makepromise/example/error-stack.js:6:11 | ||
at Object.<anonymous> (/Users/zavr/adc/makepromise/example/error-stack.js:10:3) | ||
at Module._compile (/Users/zavr/adc/makepromise/node_modules/pirates/lib/index.js:83:24) | ||
at Object.newLoader [as .js] (/Users/zavr/adc/makepromise/node_modules/pirates/lib/index.js:88:7) | ||
``` | ||
Without this functionality, the error stack would not appear. | ||
```js | ||
const makePromise = require('makepromise/es5') | ||
import { unlink } from 'fs' | ||
(async () => { | ||
try { | ||
await new Promise((r, j) => { | ||
unlink('error-test-file', (err) => { | ||
if (err) return j(err) | ||
return r() | ||
}) | ||
}) | ||
} catch ({ stack }) { | ||
console.log(stack) | ||
} | ||
})() | ||
``` | ||
--- | ||
``` | ||
Error: ENOENT: no such file or directory, unlink 'error-test-file' | ||
``` | ||
## TODO | ||
Licence: MIT | ||
- [ ] fix missing `v2.0.0` commit. | ||
*(c) [Art Deco Code](https://artdeco.bz) 2018* | ||
## Copyright | ||
[1]: https://www.npmjs.com/package/wrote | ||
[2]: https://www.npmjs.com/package/lockfile | ||
(c) [Art Deco][1] 2018 | ||
[1]: https://artdeco.bz |
@@ -5,3 +5,3 @@ import erotic from 'erotic' | ||
if (i > length - 2) { | ||
throw new Error('Function does not accept that many arguments') | ||
throw new Error('Function does not accept that many arguments.') | ||
} | ||
@@ -11,17 +11,16 @@ } | ||
/** | ||
* Create a promise from a function. | ||
* @param {function} fn Function to call | ||
* @param {any[]|any} [args] Array of arguments to use in call, or one argument, or none | ||
* @param {any} [resolveValue] Override function's return value with this | ||
* @returns {Promise<any>} A promise resolved on callback invocation w/out error, | ||
* and rejected on callback called w/ error. | ||
* Get a promise from a function which otherwise accepts a callback. | ||
* @param {function} fn A function to promisify. | ||
* @param {any[]|any} [args] An array of arguments to use in the call, or a single argument. | ||
* @param {any} [resolveValue] A value to override the value with which the promise will be resolved. | ||
* @returns {Promise<any>} A promise resolved on callback invocation without an error and rejected on callback called with an error. | ||
*/ | ||
export default async function makePromise(fn, args, resolveValue) { | ||
const er = erotic() | ||
const er = erotic(true) | ||
if (typeof fn !== 'function') { | ||
throw new Error('function must be passed') | ||
throw new Error('Function must be passed.') | ||
} | ||
const { length: fnLength } = fn | ||
if (!fnLength) { | ||
throw new Error('Function does not accept any arguments') | ||
throw new Error('Function does not accept any arguments.') | ||
} | ||
@@ -52,3 +51,2 @@ | ||
return res | ||
} | ||
} |
@@ -1,35 +0,52 @@ | ||
import { createWritable, erase } from 'wrote' | ||
import { createWritable, erase, read } from 'wrote' | ||
export default async function context() { | ||
const t = this | ||
Object.assign(t, { | ||
async getWs() { | ||
const ws = await createWritable() | ||
t._ws = ws | ||
return ws | ||
}, | ||
async _destroy() { | ||
if (t._ws) { | ||
try { | ||
await erase(t._ws) | ||
} catch (err) { /* ignore error if deleted already */ } | ||
} | ||
}, | ||
}) | ||
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), | ||
}, | ||
}) | ||
} | ||
export default class Context { | ||
async _destroy() { | ||
if (this._ws) { | ||
try { | ||
await erase(this._ws) | ||
} catch (err) { /* ignore error if deleted already */ } | ||
} | ||
} | ||
get readFile() { | ||
return read | ||
} | ||
get testArg() { | ||
return 'test-arg' | ||
} | ||
get simple() { | ||
return cb => cb(null) | ||
} | ||
get withArg() { | ||
return (arg, cb) => cb(null, arg) | ||
} | ||
/** | ||
* Make a function which will call the callback with an error. | ||
* @param {Error} err Error which will be passed to the callback. | ||
*/ | ||
makeErrFn(err) { | ||
return cb => cb(err) | ||
} | ||
async getWs() { | ||
const ws = await createWritable() | ||
this._ws = ws | ||
return ws | ||
} | ||
async closeWs(ws) { | ||
await new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
}) | ||
} | ||
/** | ||
* Returns an array of arguments passed to a function, without the callback. | ||
* @param {*[]} args Arguments passed to a function, including a callback. | ||
*/ | ||
parseArguments(args) { | ||
const argArray = Array.from(args) | ||
const realArgs = argArray.splice(0, argArray.length - 1) | ||
return realArgs | ||
} | ||
} |
@@ -1,3 +0,4 @@ | ||
/* global makePromise */ | ||
import { equal, deepEqual } from 'assert' | ||
import { equal, deepEqual, throws } from 'zoroaster/assert' | ||
import makePromise from '../../src' | ||
import Context from '../context' | ||
@@ -7,67 +8,64 @@ const argCheck = { | ||
const fn = () => {} | ||
try { | ||
await makePromise(fn) | ||
throw new Error('should have thrown an error') | ||
} catch ({ message }) { | ||
equal(message, 'Function does not accept any arguments') | ||
} | ||
await throws({ | ||
fn: makePromise, | ||
args: [fn], | ||
message: 'Function does not accept any arguments.', | ||
}) | ||
}, | ||
async 'passing arguments when none allowed'() { | ||
const fn = cb => cb() | ||
try { | ||
await makePromise(fn, 'hello-world') | ||
throw new Error('should have thrown an error') | ||
} catch ({ message }) { | ||
equal(message, 'Function does not accept that many arguments') | ||
} | ||
await throws({ | ||
fn: makePromise, | ||
args: [fn, 'hello-world'], | ||
message: 'Function does not accept that many arguments.', | ||
}) | ||
}, | ||
async 'passing more arguments when allowed'() { | ||
const fn = (test, cb) => { cb(null, test) } | ||
try { | ||
await makePromise(fn, ['hello world', 'extra argument']) | ||
throw new Error('should have thrown an error') | ||
} catch ({ message, stack }) { | ||
equal(message, 'Function does not accept that many arguments') | ||
} | ||
const fn = (test, cb) => cb(null, test) | ||
await throws({ | ||
fn: makePromise, | ||
args: [fn, ['hello world', 'extra argument']], | ||
message: 'Function does not accept that many arguments.', | ||
}) | ||
}, | ||
} | ||
export const argumentCheckTestSuite = { | ||
'returns a rejected promise when': argCheck, | ||
async 'should call function with correct arguments'() { | ||
/** @type {Object.<string, (c: Context)>} */ | ||
const T = { | ||
context: Context, | ||
'throws when': argCheck, | ||
async 'calls function with correct arguments'({ parseArguments }) { | ||
const testArgs = ['test-arg-1', undefined, 'test-arg-2'] | ||
let args | ||
const fn = function TestFn(testArgA, testArgB, testArgC, cb) { | ||
args = arguments | ||
args = parseArguments(arguments) | ||
cb(null) | ||
} | ||
await makePromise(fn, testArgs) | ||
const argArray = Array.from(args) | ||
const realArgs = argArray.splice(0, argArray.length - 1) | ||
deepEqual(realArgs, testArgs) | ||
deepEqual(args, testArgs) | ||
}, | ||
async 'should call function with correct argument'() { | ||
async 'calls function with correct argument'({ parseArguments }) { | ||
const testArg = 'test-arg-1' | ||
let args | ||
const fn = function TestFn(testArgA, cb) { | ||
args = arguments | ||
args = parseArguments(arguments) | ||
cb(null) | ||
} | ||
await makePromise(fn, testArg) | ||
const argArray = Array.from(args) | ||
equal(argArray.length, 2) | ||
equal(argArray[0], testArg) | ||
equal(args.length, 1) | ||
equal(args[0], testArg) | ||
}, | ||
async 'should call function with correct undefined argument'() { | ||
async 'calls function with correct undefined argument'({ parseArguments }) { | ||
const testArg = undefined | ||
let args | ||
const fn = function TestFn(testArgA, cb) { | ||
args = arguments | ||
args = parseArguments(arguments) | ||
cb(null) | ||
} | ||
await makePromise(fn, testArg) | ||
const argArray = Array.from(args) | ||
equal(argArray.length, 2) | ||
equal(argArray[0], testArg) | ||
equal(args.length, 1) | ||
equal(args[0], testArg) | ||
}, | ||
} | ||
export default T |
@@ -1,10 +0,13 @@ | ||
/* global makePromise */ | ||
import context from '../context' | ||
import { ok } from 'zoroaster/assert' | ||
import makePromise from '../../src' | ||
import Context from '../context' | ||
/** @type {Object.<string, (c: Context)>} */ | ||
const T = { | ||
context, | ||
async 'should resolve with function result'({ getWs }) { | ||
context: Context, | ||
async 'resolves with function result'({ getWs }) { | ||
const ws = await getWs() | ||
const boundFn = ws.close.bind(ws) | ||
await makePromise(boundFn) | ||
ok(ws.closed) | ||
}, | ||
@@ -11,0 +14,0 @@ } |
@@ -1,28 +0,11 @@ | ||
/* global makePromise */ | ||
import { equal, ok } from 'assert' | ||
import { equal, ok, throws } from 'zoroaster/assert' | ||
import { stat, unlink, Stats } from 'fs' | ||
import { unlock } from 'lockfile' | ||
import { erase, read } from 'wrote' | ||
import context from '../context' | ||
import { erase } from 'wrote' | ||
import makePromise from '../../src' | ||
import Context from '../context' | ||
/** @type {Object.<string, (c: Context)>} */ | ||
const T = { | ||
context, | ||
async 'should be able to unlock a file'({ getWs }) { | ||
const ws = await getWs() | ||
ok(ws.writable) | ||
await makePromise(stat, ws.path) | ||
await makePromise(unlock, ws.path) | ||
ok(!ws.closed) // stream not closed | ||
ok(!ws._writableState.ended) | ||
try { | ||
await makePromise(stat, ws.path) | ||
throw new Error('should have been rejected') | ||
} catch ({ code }) { | ||
ok(code, 'ENOENT') | ||
} | ||
await makePromise(ws.end.bind(ws)) | ||
ok(ws.closed) | ||
ok(ws._writableState.ended) | ||
}, | ||
async 'should write to a file stream'({ getWs }) { | ||
context: Context, | ||
async 'writes to a file stream'({ getWs, readFile }) { | ||
const testData = 'some-test-data' | ||
@@ -33,21 +16,16 @@ const ws = await getWs() | ||
// read file | ||
const actual = await read(ws.path) | ||
const actual = await readFile(ws.path) | ||
equal(actual, testData) | ||
}, | ||
async 'should unlink a file'({ getWs }) { | ||
async 'unlinks a file'({ getWs, closeWs, readFile }) { | ||
const ws = await getWs() | ||
await new Promise((resolve, reject) => { | ||
ws.once('close', resolve) | ||
ws.once('error', reject) | ||
ws.close() | ||
await closeWs(ws) | ||
await makePromise(unlink, ws.path) | ||
await throws({ | ||
fn: readFile, | ||
args: [ws.path], | ||
code: 'ENOENT', | ||
}) | ||
await makePromise(unlink, ws.path) | ||
try { | ||
await read(ws.path) | ||
throw new Error('should have thrown') | ||
} catch ({ code }) { | ||
equal(code, 'ENOENT') | ||
} | ||
}, | ||
async 'should end stream'({ getWs }) { | ||
async 'ends stream'({ getWs }) { | ||
const ws = await getWs() | ||
@@ -57,22 +35,23 @@ await makePromise(ws.end.bind(ws)) | ||
}, | ||
async 'should read stats'({ getWs }) { | ||
async 'reads stats'({ getWs }) { | ||
const ws = await getWs() | ||
ok(!ws._writableState.ended) | ||
/** | ||
* @type {Stats} | ||
*/ | ||
const res = await makePromise(stat, ws.path) | ||
ok(res instanceof Stats) | ||
await erase(ws) // move to context | ||
ok(res.isFile()) | ||
}, | ||
async 'should not read stats of non-existent file'({ getWs }) { | ||
async 'throws when readings 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') | ||
} | ||
await throws({ | ||
fn: makePromise, | ||
args: [stat, ws.path], | ||
code: 'ENOENT', | ||
}) | ||
}, | ||
} | ||
export default T | ||
export default T |
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
6
30
188
8
28053
464
1
+ Addederotic@1.2.1(transitive)
- Removederotic@1.0.1(transitive)
Updatederotic@1.2.1