From 93f854ba73393c4ac7d70baade314d631de0eab8 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Fri, 3 Nov 2023 20:44:10 +0700 Subject: [PATCH] Move to ESM --- .github/workflows/main.yml | 2 +- gulpfile.js | 14 ++--- index.js | 77 ++++++++++++------------ package.json | 19 +++--- readme.md | 44 ++++++-------- test/fixtures/fixture-async.js | 3 +- test/fixtures/fixture-fail.js | 3 +- test/fixtures/fixture-pass.js | 3 +- test/fixtures/fixture-throws-uncaught.js | 3 +- test/fixtures/fixture-throws.js | 3 +- test/test.js | 28 +++++---- test/utils.js | 9 --- utils.js | 13 ---- 13 files changed, 98 insertions(+), 123 deletions(-) delete mode 100644 test/utils.js delete mode 100644 utils.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a1f7a82..346585c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: - 18 steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/gulpfile.js b/gulpfile.js index 9d5d98a..eb32609 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,8 +1,8 @@ -'use strict'; -const gulp = require('gulp'); -const mocha = require('./index.js'); +import gulp from 'gulp'; +import mocha from './index.js'; + +export default function main() { + return gulp.src('test/fixtures/fixture-pass.js', {read: false}) + .pipe(mocha()); +} -exports.default = () => ( - gulp.src('test/fixtures/fixture-pass.js', {read: false}) - .pipe(mocha()) -); diff --git a/index.js b/index.js index 5bee792..7623575 100644 --- a/index.js +++ b/index.js @@ -1,58 +1,57 @@ -'use strict'; -const dargs = require('dargs'); -const execa = require('execa'); -const PluginError = require('plugin-error'); -const supportsColor = require('supports-color'); -const through = require('through2'); -const utils = require('./utils.js'); +import process from 'node:process'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; +import dargs from 'dargs'; +import {execa} from 'execa'; +import supportsColor from 'supports-color'; +import {gulpPlugin} from 'gulp-plugin-extras'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); // Mocha options that can be specified multiple times -const MULTIPLE_OPTS = new Set([ - 'require' +const MULTIPLE_OPTIONS = new Set([ + 'require', ]); -module.exports = options => { +function convertObjectToList(object) { + return Object.entries(object) + .map(([key, value]) => `${key}=${value}`) + .join(','); +} + +export default function gulpMocha(options) { options = { colors: Boolean(supportsColor.stdout), suppress: false, - ...options + ...options, }; for (const [key, value] of Object.entries(options)) { if (Array.isArray(value)) { - if (!MULTIPLE_OPTS.has(key)) { + if (!MULTIPLE_OPTIONS.has(key)) { // Convert arrays into comma separated lists options[key] = value.join(','); } } else if (typeof value === 'object') { // Convert an object into comma separated list - options[key] = utils.convertObjectToList(value); + options[key] = convertObjectToList(value); } } - const args = dargs(options, { + const arguments_ = dargs(options, { excludes: ['suppress'], - ignoreFalse: true + ignoreFalse: true, }); const files = []; - function aggregate(file, encoding, done) { - if (file.isStream()) { - done(new PluginError('gulp-mocha', 'Streaming not supported')); - return; - } - + return gulpPlugin('gulp-mocha', file => { files.push(file.path); - - done(); - } - - function flush(done) { - (async () => { - const subprocess = execa('mocha', files.concat(args), { + }, { + async * onFinish(stream) { // eslint-disable-line require-yield + const subprocess = execa('mocha', [...files, ...arguments_], { localDir: __dirname, - preferLocal: true + preferLocal: true, }); if (!options.suppress) { @@ -62,14 +61,16 @@ module.exports = options => { try { const result = await subprocess; - this.emit('_result', result); + stream.emit('_result', result); } catch (error) { - this.emit('error', new PluginError('gulp-mocha', error.exitCode > 0 ? 'There were test failures' : error)); - } - - done(); - })(); - } + if (error.exitCode > 0) { + const error = new Error('There were test failures'); + error.isPresentable = true; + throw error; + } - return through.obj(aggregate, flush); -}; + throw error; + } + }, + }); +} diff --git a/package.json b/package.json index 18aca62..41a3228 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", "exports": "./index.js", "engines": { "node": ">=18" @@ -18,8 +19,7 @@ "test": "xo && ava" }, "files": [ - "index.js", - "utils.js" + "index.js" ], "keywords": [ "gulpplugin", @@ -36,19 +36,18 @@ "tap" ], "dependencies": { - "dargs": "^7.0.0", - "execa": "^5.0.0", + "dargs": "^8.1.0", + "execa": "^8.0.1", + "gulp-plugin-extras": "^0.3.0", "mocha": "^10.2.0", - "plugin-error": "^1.0.1", - "supports-color": "^8.1.1", - "through2": "^4.0.2" + "supports-color": "^9.4.0" }, "devDependencies": { - "ava": "^2.3.0", + "ava": "^5.3.1", "gulp": "^4.0.2", - "p-event": "^4.2.0", + "p-event": "^6.0.0", "vinyl": "^3.0.0", - "xo": "^0.37.1" + "xo": "^0.56.0" }, "peerDependencies": { "gulp": ">=4" diff --git a/readme.md b/readme.md index b998b27..6f443e0 100644 --- a/readme.md +++ b/readme.md @@ -4,28 +4,25 @@ *Keep in mind that this is just a thin wrapper around Mocha and your issue is most likely with Mocha.* - ## Install +```sh +npm install --save-dev gulp-mocha ``` -$ npm install --save-dev gulp-mocha -``` - ## Usage ```js -const gulp = require('gulp'); -const mocha = require('gulp-mocha'); +import gulp from 'gulp'; +import mocha from 'gulp-mocha'; -exports.default = () => ( +export default () => ( gulp.src('test.js', {read: false}) - // `gulp-mocha` needs filepaths so you can't have any plugins before it + // `gulp-mocha` needs file paths so you cannot have any plugins before it. .pipe(mocha({reporter: 'nyan'})) ); ``` - ## API ### mocha(options?) @@ -38,25 +35,25 @@ Options are passed directly to the `mocha` binary, so you can use any its [comma ##### ui -Type: `string`
-Default: `bdd`
-Values: `bdd` `tdd` `qunit` `exports` +Type: `string`\ +Default: `'bdd'`\ +Values: `'bdd' | 'tdd' | 'qunit' | 'exports'` -Interface to use. +The interface to use. ##### reporter -Type: `string`
-Default: `spec` +Type: `string`\ +Default: `spec`\ Values: [Reporters](https://github.com/mochajs/mocha/tree/master/lib/reporters) -Reporter that will be used. +The reporter that will be used. This option can also be used to utilize third-party reporters. For example, if you `npm install mocha-lcov-reporter` you can then do use `mocha-lcov-reporter` as value. ##### reporterOptions -Type: `object`
+Type: `object`\ Example: `{reportFilename: 'index.html'}` Reporter specific options. @@ -69,21 +66,21 @@ List of accepted global variable names, example `['YUI']`. Accepts wildcards to ##### timeout -Type: `number`
+Type: `number`\ Default: `2000` Test-case timeout in milliseconds. ##### bail -Type: `boolean`
+Type: `boolean`\ Default: `false` Bail on the first test failure. ##### checkLeaks -Type: `boolean`
+Type: `boolean`\ Default: `false` Check for global variable leaks. @@ -102,12 +99,11 @@ Require custom modules before tests are run. ##### compilers -Type: `string`
+Type: `string`\ Example: `js:babel-core/register` Specify a compiler. - ## FAQ ### Test suite not exiting @@ -115,7 +111,7 @@ Specify a compiler. If your test suite is not exiting it might be because you still have a lingering callback, most often caused by an open database connection. You should close this connection or do the following: ```js -exports.default = () => ( +export default () => ( gulp.src('test.js') .pipe(mocha()) .once('error', err => { @@ -131,7 +127,7 @@ exports.default = () => ( Or you might just need to pass the `exit` option: ```js -exports.test = () => ( +export const test = () => ( gulp.src(['test/**/*.js'], {read: false}) .pipe(mocha({reporter: 'list', exit: true})) .on('error', console.error) diff --git a/test/fixtures/fixture-async.js b/test/fixtures/fixture-async.js index 00fac80..0258548 100644 --- a/test/fixtures/fixture-async.js +++ b/test/fixtures/fixture-async.js @@ -1,5 +1,4 @@ -'use strict'; -const assert = require('assert'); +import assert from 'node:assert'; it('should fail after timeout', done => { setTimeout(() => { diff --git a/test/fixtures/fixture-fail.js b/test/fixtures/fixture-fail.js index 08d9aac..5126811 100644 --- a/test/fixtures/fixture-fail.js +++ b/test/fixtures/fixture-fail.js @@ -1,5 +1,4 @@ -'use strict'; -const assert = require('assert'); +import assert from 'node:assert'; it('should fail', () => { assert(false); diff --git a/test/fixtures/fixture-pass.js b/test/fixtures/fixture-pass.js index d9bbd07..168a4e0 100644 --- a/test/fixtures/fixture-pass.js +++ b/test/fixtures/fixture-pass.js @@ -1,5 +1,4 @@ -'use strict'; -const assert = require('assert'); +import assert from 'node:assert'; it('should pass', () => { assert(true); diff --git a/test/fixtures/fixture-throws-uncaught.js b/test/fixtures/fixture-throws-uncaught.js index 79439f9..3753b6a 100644 --- a/test/fixtures/fixture-throws-uncaught.js +++ b/test/fixtures/fixture-throws-uncaught.js @@ -1,5 +1,4 @@ -'use strict'; -const assert = require('assert'); +import assert from 'node:assert'; it('throws after timeout', () => { setTimeout(() => { diff --git a/test/fixtures/fixture-throws.js b/test/fixtures/fixture-throws.js index 841f8a1..5939eb3 100644 --- a/test/fixtures/fixture-throws.js +++ b/test/fixtures/fixture-throws.js @@ -1,5 +1,4 @@ -'use strict'; -const assert = require('assert'); +import assert from 'node:assert'; it('contains syntax errors', () => { assert false; diff --git a/test/test.js b/test/test.js index d9a3e42..b27e568 100644 --- a/test/test.js +++ b/test/test.js @@ -1,16 +1,19 @@ -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; import test from 'ava'; import Vinyl from 'vinyl'; -import pEvent from 'p-event'; +import {pEvent} from 'p-event'; import mocha from '../index.js'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + function fixture(name) { - const fileName = path.join(__dirname, 'fixtures', name); + const filename = path.join(__dirname, 'fixtures', name); return new Vinyl({ - path: fileName, - contents: fs.existsSync(fileName) ? fs.readFileSync(fileName) : null + path: filename, + contents: fs.existsSync(filename) ? fs.readFileSync(filename) : null, }); } @@ -18,14 +21,16 @@ test('run unit test and pass', async t => { const stream = mocha({suppress: true}); const result = pEvent(stream, '_result'); stream.end(fixture('fixture-pass.js')); - t.regex((await result).stdout, /1 passing/); + const {stdout} = await result; + t.regex(stdout, /1 passing/); }); test('run unit test and fail', async t => { const stream = mocha({suppress: true}); const error = pEvent(stream, 'error'); stream.end(fixture('fixture-fail.js')); - t.regex((await error).message, /There were test failures/); + const {message} = await error; + t.regex(message, /There were test failures/); }); test('pass async AssertionError to mocha', async t => { @@ -41,10 +46,11 @@ test('require two files', async t => { suppress: true, require: [ 'test/fixtures/fixture-require1.js', - 'test/fixtures/fixture-require2.js' - ] + 'test/fixtures/fixture-require2.js', + ], }); const result = pEvent(stream, '_result'); stream.end(fixture('fixture-pass.js')); - t.regex((await result).stdout, /1 passing/); + const {stdout} = await result; + t.regex(stdout, /1 passing/); }); diff --git a/test/utils.js b/test/utils.js deleted file mode 100644 index 002678b..0000000 --- a/test/utils.js +++ /dev/null @@ -1,9 +0,0 @@ -import test from 'ava'; -import utils from '../utils.js'; - -test('convertObjectToList produces a comma separated string of k=v', t => { - t.is( - utils.convertObjectToList({key1: 'value1', key2: 'value2', key99: 'value99'}), - 'key1=value1,key2=value2,key99=value99' - ); -}); diff --git a/utils.js b/utils.js deleted file mode 100644 index afd1ce3..0000000 --- a/utils.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -function convertObjectToList(object) { - return Object.entries(object) - // TODO: Stop using `.reduce` - // eslint-disable-next-line unicorn/no-array-reduce - .reduce((result, current) => result.concat(`${current[0]}=${current[1]}`), []) - .join(','); -} - -module.exports = { - convertObjectToList -};