Skip to content

Commit e377ef4

Browse files
committed
Add support for promise if w/o callback
Closes GH-80.
1 parent 10bf51d commit e377ef4

19 files changed

+345
-299
lines changed

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* @typedef {import('./lib/file-set.js').Completer} Completer
44
* @typedef {import('./lib/index.js').Callback} Callback
55
* @typedef {import('./lib/configuration.js').ConfigTransform} ConfigTransform
6+
* @typedef {import('./lib/index.js').ContextWithCode} ContextWithCode
67
* @typedef {import('./lib/index.js').Context} Context
78
* @typedef {import('./lib/file-set.js').FileSet} FileSet
89
* @typedef {import('./lib/index.js').Options} Options

lib/index.js

+65-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
* @import {ConfigTransform, PresetSupportingSpecifiers} from './configuration.js'
66
* @import {FileSet} from './file-set.js'
77
* @import {ResolveFrom} from './ignore.js'
8-
* @import {Context} from './index.js'
98
*/
109

1110
/**
@@ -24,14 +23,23 @@
2423
* Nothing.
2524
*
2625
* Note: `void` included because `promisify` otherwise fails.
27-
*
26+
*/
27+
28+
/**
29+
* @typedef {Context & {code: 0 | 1}} ContextWithCode
30+
* Processing context with code.
31+
*/
32+
33+
/**
2834
* @typedef Context
2935
* Processing context.
3036
* @property {FileSet} fileSet
3137
* Internally used info.
3238
* @property {Array<VFile>} files
3339
* Processed files.
34-
*
40+
*/
41+
42+
/**
3543
* @typedef Options
3644
* Configuration.
3745
*
@@ -140,7 +148,9 @@
140148
* Whether to output as a syntax tree (default: `options.tree`).
141149
* @property {boolean | undefined} [verbose=false]
142150
* Report extra info (default: `false`); given to the reporter.
143-
*
151+
*/
152+
153+
/**
144154
* @typedef Settings
145155
* Resolved {@link Options `Options`} passed around.
146156
* @property {Options['processor']} processor
@@ -181,7 +191,9 @@
181191
* @property {Options['quiet']} quiet
182192
* @property {Options['frail']} frail
183193
* @property {Options['verbose']} verbose
184-
*
194+
*/
195+
196+
/**
185197
* @callback VFileReporter
186198
* Reporter.
187199
*
@@ -193,13 +205,16 @@
193205
* Configuration.
194206
* @returns {Promise<string> | string}
195207
* Report.
196-
*
208+
*/
209+
210+
/**
197211
* @typedef {{[Key in keyof VFileReporterKnownFields]: VFileReporterKnownFields[Key]} & Record<string, unknown>} VFileReporterOptions
198212
* Configuration.
199213
*
200214
* Note: this weird type fixes TSC:
201215
*/
202216

217+
import assert from 'node:assert/strict'
203218
import process from 'node:process'
204219
import {PassThrough} from 'node:stream'
205220
import {fileURLToPath} from 'node:url'
@@ -209,14 +224,57 @@ import {fileSetPipeline} from './file-set-pipeline/index.js'
209224
/**
210225
* Process.
211226
*
227+
* @overload
228+
* @param {Options} options
229+
* @param {Callback} callback
230+
* @returns {undefined}
231+
*
232+
* @overload
233+
* @param {Options} options
234+
* @returns {Promise<ContextWithCode>}
235+
*
236+
* @overload
237+
* @param {Options} options
238+
* @param {Callback | null | undefined} [callback]
239+
* @returns {Promise<ContextWithCode> | undefined}
240+
*
212241
* @param {Options} options
213242
* Configuration (required).
243+
* @param {Callback | null | undefined} [callback]
244+
* Callback.
245+
* @returns {Promise<ContextWithCode> | undefined}
246+
* Nothing.
247+
*/
248+
export function engine(options, callback) {
249+
if (callback) {
250+
return engineCallback(options, callback)
251+
}
252+
253+
return new Promise(function (resolve, reject) {
254+
engineCallback(options, function (error, code, context) {
255+
if (error) {
256+
reject(error)
257+
} else {
258+
assert(code !== undefined)
259+
assert(context !== undefined)
260+
resolve({code, fileSet: context.fileSet, files: context.files})
261+
}
262+
})
263+
})
264+
}
265+
266+
/**
267+
* Process,
268+
* always using callbacks.
269+
*
270+
* @param {Options} options
271+
* Configuration.
214272
* @param {Callback} callback
215273
* Callback.
216274
* @returns {undefined}
217275
* Nothing.
218276
*/
219-
export function engine(options, callback) {
277+
function engineCallback(options, callback) {
220278
/** @type {Settings} */
221279
const settings = {}
222280
/** @type {NodeJS.ReadStream | PassThrough} */
@@ -231,10 +289,6 @@ export function engine(options, callback) {
231289
// Empty.
232290
}
233291

234-
if (!callback) {
235-
throw new Error('Missing `callback`')
236-
}
237-
238292
if (!options || !options.processor) {
239293
return next(new Error('Missing `processor`'))
240294
}

readme.md

+24-5
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
* [Install](#install)
1818
* [Use](#use)
1919
* [API](#api)
20-
* [`engine(options, callback)`](#engineoptions-callback)
20+
* [`engine(options, callback?)`](#engineoptions-callback)
2121
* [`Configuration`](#configuration)
2222
* [`Completer`](#completer)
2323
* [`Callback`](#callback)
2424
* [`ConfigResult`](#configresult)
2525
* [`ConfigTransform`](#configtransform)
26+
* [`ContextWithCode`](#contextwithcode)
2627
* [`Context`](#context)
2728
* [`FileSet`](#fileset)
2829
* [`Options`](#options)
@@ -154,20 +155,21 @@ This package exports the identifiers [`Configuration`][api-configuration] and
154155
[`engine`][api-engine].
155156
There is no default export.
156157

157-
### `engine(options, callback)`
158+
### `engine(options, callback?)`
158159

159160
Process.
160161

161162
###### Parameters
162163

163164
* `options` ([`Options`][api-options], required)
164165
— configuration
165-
* `callback` ([`Callback`][api-callback], required)
166-
configuration
166+
* `callback` ([`Callback`][api-callback], optional)
167+
callback
167168

168169
###### Returns
169170

170-
Nothing (`undefined`).
171+
If a callback is given, nothing (`undefined`).
172+
Otherwise [`Promise<ContextWithCode>`][api-context-with-code].
171173

172174
### `Configuration`
173175

@@ -250,6 +252,20 @@ Transform arbitrary configs to our format (TypeScript type).
250252
251253
Our config format ([`Preset`][api-preset]).
252254
255+
### `ContextWithCode`
256+
257+
Processing context with code (TypeScript type).
258+
259+
###### Extends
260+
261+
* [`Context`][api-context]
262+
263+
###### Fields
264+
265+
* `code` (`0` or `1`)
266+
— exit code,
267+
`0` if successful or `1` if unsuccessful
268+
253269
### `Context`
254270
255271
Processing context (TypeScript type).
@@ -1538,6 +1554,7 @@ It exports the additional types
15381554
[`Callback`][api-callback],
15391555
[`ConfigResult`][api-config-result],
15401556
[`ConfigTransform`][api-config-transform],
1557+
[`ContextWithCode`][api-context-with-code],
15411558
[`Context`][api-context],
15421559
[`FileSet`][api-file-set],
15431560
[`Options`][api-options],
@@ -1591,6 +1608,8 @@ abide by its terms.
15911608
15921609
[api-context]: #context
15931610
1611+
[api-context-with-code]: #contextwithcode
1612+
15941613
[api-engine]: #engineoptions-callback
15951614
15961615
[api-file-set]: #fileset

test/color.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import assert from 'node:assert/strict'
22
import fs from 'node:fs/promises'
33
import test from 'node:test'
4-
import {promisify} from 'node:util'
54
import {engine} from 'unified-engine'
65
import {cleanError} from './util/clean-error.js'
76
import {noop} from './util/noop-processor.js'
87
import {spy} from './util/spy.js'
98

10-
const run = promisify(engine)
119
const fixtures = new URL('fixtures/', import.meta.url)
1210

1311
test('color', async function (t) {
@@ -17,15 +15,15 @@ test('color', async function (t) {
1715

1816
await fs.mkdir(cwd, {recursive: true})
1917

20-
const code = await run({
18+
const result = await engine({
2119
color: true,
2220
cwd,
2321
files: ['readme.md'],
2422
processor: noop,
2523
streamError: stderr.stream
2624
})
2725

28-
assert.equal(code, 1)
26+
assert.equal(result.code, 1)
2927
assert.equal(
3028
cleanError(stderr()),
3129
[

test/completers.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import assert from 'node:assert/strict'
66
import fs from 'node:fs/promises'
77
import path from 'node:path'
88
import test from 'node:test'
9-
import {promisify} from 'node:util'
109
import {engine} from 'unified-engine'
1110
import {noop} from './util/noop-processor.js'
1211
import {spy} from './util/spy.js'
1312

14-
const run = promisify(engine)
1513
const fixtures = new URL('fixtures/', import.meta.url)
1614

1715
test('completers', async function (t) {
@@ -23,7 +21,7 @@ test('completers', async function (t) {
2321
// `pluginId` can be used for those to ensure the completer runs once.
2422
otherCompleter.pluginId = 'foo'
2523

26-
const code = await run({
24+
const result = await engine({
2725
cwd: new URL('two-files/', fixtures),
2826
files: ['one.txt'],
2927
plugins: [
@@ -61,7 +59,7 @@ test('completers', async function (t) {
6159
streamError: stderr.stream
6260
})
6361

64-
assert.equal(code, 0)
62+
assert.equal(result.code, 0)
6563
assert.equal(stderr(), 'one.txt: no issues found\n')
6664

6765
/**
@@ -108,7 +106,7 @@ test('completers', async function (t) {
108106
await t.test('should support `fileSet.add` from plugins', async function () {
109107
const cwd = new URL('extensions/', fixtures)
110108
const stderr = spy()
111-
const code = await run({
109+
const result = await engine({
112110
cwd,
113111
files: ['foo.txt'],
114112
output: 'nested/',
@@ -127,7 +125,7 @@ test('completers', async function (t) {
127125

128126
await fs.unlink(url)
129127

130-
assert.equal(code, 0)
128+
assert.equal(result.code, 0)
131129
assert.equal(document, '')
132130
assert.equal(stderr(), 'foo.txt > nested' + path.sep + 'foo.txt: written\n')
133131
})

test/configuration-default.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import assert from 'node:assert/strict'
22
import test from 'node:test'
3-
import {promisify} from 'node:util'
43
import {engine} from 'unified-engine'
54
import {noop} from './util/noop-processor.js'
65
import {spy} from './util/spy.js'
76

87
const fixtures = new URL('fixtures/', import.meta.url)
9-
const run = promisify(engine)
108

119
test('`defaultConfig`', async function (t) {
1210
const defaultConfig = {
@@ -22,7 +20,7 @@ test('`defaultConfig`', async function (t) {
2220
globalThis.unifiedEngineTestCalls = 0
2321
globalThis.unifiedEngineTestValues = {}
2422

25-
const code = await run({
23+
const result = await engine({
2624
cwd: new URL('config-default/', fixtures),
2725
defaultConfig,
2826
extensions: ['txt'],
@@ -32,7 +30,7 @@ test('`defaultConfig`', async function (t) {
3230
streamError: stderr.stream
3331
})
3432

35-
assert.equal(code, 0)
33+
assert.equal(result.code, 0)
3634
assert.equal(stderr(), 'one.txt: no issues found\n')
3735
assert.equal(globalThis.unifiedEngineTestCalls, 1)
3836
assert.deepEqual(globalThis.unifiedEngineTestValues, {
@@ -50,7 +48,7 @@ test('`defaultConfig`', async function (t) {
5048
globalThis.unifiedEngineTestCalls = 0
5149
globalThis.unifiedEngineTestValues = {}
5250

53-
const code = await run({
51+
const result = await engine({
5452
cwd: new URL('config-default/', fixtures),
5553
defaultConfig,
5654
extensions: ['txt'],
@@ -60,7 +58,7 @@ test('`defaultConfig`', async function (t) {
6058
streamError: stderr.stream
6159
})
6260

63-
assert.equal(code, 0)
61+
assert.equal(result.code, 0)
6462
assert.equal(stderr(), 'one.txt: no issues found\n')
6563
assert.equal(globalThis.unifiedEngineTestCalls, 1)
6664
assert.deepEqual(

0 commit comments

Comments
 (0)