Skip to content

Commit d13ac93

Browse files
authored
chore: move APIs to async/await (+ fix tests failing) (#270)
* Add editorconfig * BREAKING: Make Promise based Fixes #249 Changes the callback based API:s to instead be async and return Promises. * Keep cli() not returning a promise
1 parent 3849864 commit d13ac93

File tree

5 files changed

+91
-117
lines changed

5 files changed

+91
-117
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ Modify and return `eslintConfig`, or return a new object with the eslint config
299299

300300
## API Usage
301301

302-
### `engine.lintText(text, [opts], callback)`
302+
### `engine.lintText(text, [opts])`
303303

304304
Lint the provided source `text` to enforce your defined style. An `opts` object may
305305
be provided:
@@ -326,7 +326,7 @@ All options are optional, though some ESLint plugins require the `filename` opti
326326

327327
Additional options may be loaded from a `package.json` if it's found for the current working directory. See below for further details.
328328

329-
The `callback` will be called with an `Error` and `results` object.
329+
Returns a `Promise` resolving to the `results` or rejected with an `Error`.
330330

331331
The `results` object will contain the following properties:
332332

@@ -379,7 +379,7 @@ Additional options may be loaded from a `package.json` if it's found for the cur
379379

380380
Both `ignore` and `files` patterns are resolved relative to the current working directory.
381381

382-
The `callback` will be called with an `Error` and `results` object (same as above).
382+
Returns a `Promise` resolving to the `results` or rejected with an `Error` (same as above).
383383

384384
**NOTE: There is no synchronous version of `engine.lintFiles()`.**
385385

bin/cmd.js

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const getStdin = require('get-stdin')
1414

1515
/**
1616
* @param {Omit<import('../').LinterOptions, 'cmd'> & StandardCliOptions} rawOpts
17+
* @returns {void}
1718
*/
1819
function cli (rawOpts) {
1920
const opts = {
@@ -99,24 +100,50 @@ Flags (advanced):
99100
parser: argv.parser
100101
}
101102

102-
/** @type {string} */
103-
let stdinText
103+
const outputFixed = argv.stdin && argv.fix
104104

105-
if (argv.stdin) {
106-
getStdin().then(function (text) {
107-
stdinText = text
108-
standard.lintText(text, lintOpts, onResult)
109-
})
110-
} else {
111-
standard.lintFiles(argv._, lintOpts, onResult)
105+
/**
106+
* Print lint errors to stdout -- this is expected output from `standard-engine`.
107+
* Note: When fixing code from stdin (`standard --stdin --fix`), the transformed
108+
* code is printed to stdout, so print lint errors to stderr in this case.
109+
* @type {typeof console.log}
110+
*/
111+
const log = (...args) => {
112+
if (outputFixed) {
113+
args[0] = opts.cmd + ': ' + args[0]
114+
console.error.apply(console, args)
115+
} else {
116+
console.log.apply(console, args)
117+
}
112118
}
113119

114-
/** @type {import('../').LinterCallback} */
115-
function onResult (err, result) {
116-
if (err) return onError(err)
120+
Promise.resolve(argv.stdin ? getStdin() : '').then(async stdinText => {
121+
/** @type {import('eslint').CLIEngine.LintReport} */
122+
let result
123+
124+
try {
125+
result = argv.stdin
126+
? await standard.lintText(stdinText, lintOpts)
127+
: await standard.lintFiles(argv._, lintOpts)
128+
} catch (err) {
129+
console.error(opts.cmd + ': Unexpected linter output:\n')
130+
if (err instanceof Error) {
131+
console.error(err.stack || err.message)
132+
} else {
133+
console.error(err)
134+
}
135+
console.error(
136+
'\nIf you think this is a bug in `%s`, open an issue: %s',
137+
opts.cmd,
138+
opts.bugs
139+
)
140+
process.exitCode = 1
141+
return
142+
}
143+
117144
if (!result) throw new Error('expected a result')
118145

119-
if (argv.stdin && argv.fix) {
146+
if (outputFixed) {
120147
if (result.results[0] && result.results[0].output) {
121148
// Code contained fixable errors, so print the fixed code
122149
process.stdout.write(result.results[0].output)
@@ -134,11 +161,7 @@ Flags (advanced):
134161
console.error('%s: %s (%s)', opts.cmd, opts.tagline, opts.homepage)
135162

136163
// Are any warnings present?
137-
const isSomeWarnings = result.results.some(function (result) {
138-
return result.messages.some(function (message) {
139-
return message.severity === 1
140-
})
141-
})
164+
const isSomeWarnings = result.results.some(item => item.messages.some(message => message.severity === 1))
142165

143166
if (isSomeWarnings) {
144167
const homepage = opts.homepage != null ? ` (${opts.homepage})` : ''
@@ -150,11 +173,7 @@ Flags (advanced):
150173
}
151174

152175
// Are any fixable rules present?
153-
const isSomeFixable = result.results.some(function (result) {
154-
return result.messages.some(function (message) {
155-
return !!message.fix
156-
})
157-
})
176+
const isSomeFixable = result.results.some(item => item.messages.some(message => !!message.fix))
158177

159178
if (isSomeFixable) {
160179
console.error(
@@ -164,53 +183,23 @@ Flags (advanced):
164183
)
165184
}
166185

167-
result.results.forEach(function (result) {
168-
result.messages.forEach(function (message) {
186+
for (const item of result.results) {
187+
for (const message of item.messages) {
169188
log(
170189
' %s:%d:%d: %s%s%s',
171-
result.filePath,
190+
item.filePath,
172191
message.line || 0,
173192
message.column || 0,
174193
message.message,
175194
' (' + message.ruleId + ')',
176195
message.severity === 1 ? ' (warning)' : ''
177196
)
178-
})
179-
})
180-
181-
process.exitCode = result.errorCount ? 1 : 0
182-
}
183-
184-
/** @param {Error|unknown} err */
185-
function onError (err) {
186-
console.error(opts.cmd + ': Unexpected linter output:\n')
187-
if (err instanceof Error) {
188-
console.error(err.stack || err.message)
189-
} else {
190-
console.error(err)
197+
}
191198
}
192-
console.error(
193-
'\nIf you think this is a bug in `%s`, open an issue: %s',
194-
opts.cmd,
195-
opts.bugs
196-
)
197-
process.exitCode = 1
198-
}
199199

200-
/**
201-
* Print lint errors to stdout -- this is expected output from `standard-engine`.
202-
* Note: When fixing code from stdin (`standard --stdin --fix`), the transformed
203-
* code is printed to stdout, so print lint errors to stderr in this case.
204-
* @type {typeof console.log}
205-
*/
206-
function log (...args) {
207-
if (argv.stdin && argv.fix) {
208-
args[0] = opts.cmd + ': ' + args[0]
209-
console.error.apply(console, args)
210-
} else {
211-
console.log.apply(console, args)
212-
}
213-
}
200+
process.exitCode = result.errorCount ? 1 : 0
201+
})
202+
.catch(err => process.nextTick(() => { throw err }))
214203
}
215204

216205
module.exports = cli

index.js

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const { resolveEslintConfig } = require('./lib/resolve-eslint-config')
99

1010
/** @typedef {ConstructorParameters<typeof import('eslint').CLIEngine>[0]} CLIEngineOptions */
1111
/** @typedef {Omit<import('./lib/resolve-eslint-config').ResolveOptions, 'cmd'|'cwd'>} BaseLintOptions */
12-
/** @typedef {(err: Error|unknown, result?: import('eslint').CLIEngine.LintReport) => void} LinterCallback */
1312

1413
/**
1514
* @typedef LinterOptions
@@ -82,51 +81,32 @@ class Linter {
8281
*
8382
* @param {string} text file text to lint
8483
* @param {Omit<BaseLintOptions, 'ignore'|'noDefaultIgnore'> & { filename?: string }} [opts] base options + path of file containing the text being linted
85-
* @param {LinterCallback} [cb]
86-
* @returns {void}
84+
* @returns {Promise<import('eslint').CLIEngine.LintReport>}
8785
*/
88-
lintText (text, opts, cb) {
89-
if (typeof opts === 'function') return this.lintText(text, undefined, opts)
90-
if (!cb) throw new Error('callback is required')
91-
92-
let result
93-
try {
94-
result = this.lintTextSync(text, opts)
95-
} catch (err) {
96-
return process.nextTick(cb, err)
97-
}
98-
process.nextTick(cb, null, result)
86+
async lintText (text, opts) {
87+
return this.lintTextSync(text, opts)
9988
}
10089

10190
/**
10291
* Lint files to enforce JavaScript Style.
10392
*
104-
* @param {Array.<string>} files file globs to lint
93+
* @param {Array<string>} files file globs to lint
10594
* @param {BaseLintOptions & { cwd?: string }} [opts] base options + file globs to ignore (has sane defaults) + current working directory (default: process.cwd())
106-
* @param {LinterCallback} [cb]
107-
* @returns {void}
95+
* @returns {Promise<import('eslint').CLIEngine.LintReport>}
10896
*/
109-
lintFiles (files, opts, cb) {
110-
if (typeof opts === 'function') { return this.lintFiles(files, undefined, opts) }
111-
if (!cb) throw new Error('callback is required')
112-
97+
async lintFiles (files, opts) {
11398
const eslintConfig = this.resolveEslintConfig(opts)
11499

115100
if (typeof files === 'string') files = [files]
116101
if (files.length === 0) files = ['.']
117102

118-
let result
119-
try {
120-
result = new this.eslint.CLIEngine(eslintConfig).executeOnFiles(files)
121-
} catch (err) {
122-
return cb(err)
123-
}
103+
const result = new this.eslint.CLIEngine(eslintConfig).executeOnFiles(files)
124104

125105
if (eslintConfig.fix) {
126106
this.eslint.CLIEngine.outputFixes(result)
127107
}
128108

129-
return cb(null, result)
109+
return result
130110
}
131111

132112
/**

test/api.js

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,51 @@ const test = require('tape')
44

55
const { Linter } = require('../')
66

7-
function getStandard () {
7+
async function getStandard () {
88
return new Linter({
99
cmd: 'pocketlint',
1010
version: '0.0.0',
1111
eslint,
12-
eslintConfig: require('../tmp/standard/options').eslintConfig
12+
eslintConfig: (await import('../tmp/standard/options.js')).default.eslintConfig
1313
})
1414
}
1515

16-
test('api: lintFiles', function (t) {
17-
t.plan(3)
18-
const standard = getStandard()
19-
standard.lintFiles([], { cwd: path.join(__dirname, '../bin') }, function (err, result) {
20-
t.error(err, 'no error while linting')
21-
t.equal(typeof result, 'object', 'result is an object')
22-
t.equal(result.errorCount, 0)
23-
})
16+
test('api: lintFiles', async function (t) {
17+
t.plan(2)
18+
const standard = await getStandard()
19+
const result = await standard.lintFiles([], { cwd: path.join(__dirname, '../bin') })
20+
t.equal(typeof result, 'object', 'result is an object')
21+
t.equal(result.errorCount, 0)
2422
})
2523

26-
test('api: lintText', function (t) {
27-
t.plan(3)
28-
const standard = getStandard()
29-
standard.lintText('console.log("hi there")\n', function (err, result) {
30-
t.error(err, 'no error while linting')
31-
t.equal(typeof result, 'object', 'result is an object')
32-
t.equal(result.errorCount, 1, 'should have used single quotes')
33-
})
24+
test('api: lintText', async function (t) {
25+
t.plan(2)
26+
const standard = await getStandard()
27+
const result = await standard.lintText('console.log("hi there")\n')
28+
t.equal(typeof result, 'object', 'result is an object')
29+
t.equal(result.errorCount, 1, 'should have used single quotes')
3430
})
3531

36-
test('api: lintTextSync', function (t) {
32+
test('api: lintTextSync', async function (t) {
3733
t.plan(2)
38-
const standard = getStandard()
34+
const standard = await getStandard()
3935
const result = standard.lintTextSync('console.log("hi there")\n')
4036
t.equal(typeof result, 'object', 'result is an object')
4137
t.equal(result.errorCount, 1, 'should have used single quotes')
4238
})
4339

44-
test('api: resolveEslintConfig -- avoid this.eslintConfig parser mutation', function (t) {
40+
test('api: resolveEslintConfig -- avoid this.eslintConfig parser mutation', async function (t) {
4541
t.plan(2)
46-
const standard = getStandard()
47-
const opts = standard.resolveEslintConfig({ parser: 'blah' })
42+
const standard = await getStandard()
43+
const opts = await standard.resolveEslintConfig({ parser: 'blah' })
4844
t.equal(opts.parser, 'blah')
4945
t.equal(standard.eslintConfig.parser, undefined)
5046
})
5147

52-
test('api: resolveEslintConfig -- avoid this.eslintConfig global mutation', function (t) {
48+
test('api: resolveEslintConfig -- avoid this.eslintConfig global mutation', async function (t) {
5349
t.plan(2)
54-
const standard = getStandard()
55-
const opts = standard.resolveEslintConfig({ globals: ['what'] })
50+
const standard = await getStandard()
51+
const opts = await standard.resolveEslintConfig({ globals: ['what'] })
5652
t.deepEqual(opts.globals, ['what'])
5753
t.deepEqual(standard.eslintConfig.globals, [])
5854
})

0 commit comments

Comments
 (0)