From 186937665318e508ce388ea03f114960368a7856 Mon Sep 17 00:00:00 2001 From: Gavin Aiken Date: Fri, 28 Feb 2025 10:24:09 +0000 Subject: [PATCH] Update to use ESM throughout - all files changed from CJS / require to ESM / import - update dev dependencies to latest versions, except for AVA (latest version of that removes callback support) - update tests to be compatible with AVA changes - switch to sinon instead of mockery as mockery was not working with ESM imports - switch to c8 instead of nyc - drop in replacement which handles ESM correctly --- bin/pidtree.js | 6 +- index.js | 12 ++-- lib/bin.js | 10 +--- lib/get.js | 14 ++--- lib/pidtree.js | 10 +--- lib/ps.js | 11 ++-- lib/wmic.js | 11 ++-- package.json | 38 ++++++------- test/bench.js | 2 +- test/helpers/exec/child.js | 2 - test/helpers/exec/parent.js | 6 +- test/integration.js | 22 +++++--- test/ps.js | 110 +++++++++++------------------------- test/wmic.js | 41 ++++++-------- 14 files changed, 107 insertions(+), 188 deletions(-) diff --git a/bin/pidtree.js b/bin/pidtree.js index 542876e..cb9513b 100755 --- a/bin/pidtree.js +++ b/bin/pidtree.js @@ -1,9 +1,7 @@ #!/usr/bin/env node -'use strict'; - -var os = require('os'); -var pidtree = require('..'); +import os from 'os'; +import { pidtree } from '../index.js'; // The method startsWith is not defined on string objects in node 0.10 // eslint-disable-next-line no-extend-native diff --git a/index.js b/index.js index b38d08f..49a5a35 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -'use strict'; +import { pidtreeCallback } from './lib/pidtree.js'; function pify(fn, arg1, arg2) { return new Promise(function(resolve, reject) { @@ -18,8 +18,6 @@ if (!String.prototype.startsWith) { }; } -var pidtree = require('./lib/pidtree'); - /** * Get the list of children pids of the given pid. * @public @@ -32,18 +30,16 @@ var pidtree = require('./lib/pidtree'); * provided a promise is returned instead. * @returns {Promise.} Only when the callback is not provided. */ -function list(pid, options, callback) { +export function pidtree(pid, options, callback) { if (typeof options === 'function') { callback = options; options = undefined; } if (typeof callback === 'function') { - pidtree(pid, options, callback); + pidtreeCallback(pid, options, callback); return; } - return pify(pidtree, pid, options); + return pify(pidtreeCallback, pid, options); } - -module.exports = list; diff --git a/lib/bin.js b/lib/bin.js index e82f868..f5b4efa 100644 --- a/lib/bin.js +++ b/lib/bin.js @@ -1,6 +1,4 @@ -'use strict'; - -var spawn = require('child_process').spawn; +import child_process from 'child_process'; function stripStderr(stderr) { if (!stderr) return; @@ -20,14 +18,14 @@ function stripStderr(stderr) { * @param {Object} [options] Optional option for the spawn function. * @param {Function} done(err, stdout) */ -function run(cmd, args, options, done) { +export function run(cmd, args, options, done) { if (typeof options === 'function') { done = options; options = undefined; } var executed = false; - var ch = spawn(cmd, args, options); + var ch = child_process.spawn(cmd, args, options); var stdout = ''; var stderr = ''; @@ -57,5 +55,3 @@ function run(cmd, args, options, done) { done(null, stdout, code); }); } - -module.exports = run; diff --git a/lib/get.js b/lib/get.js index 7f94efd..1e2d56d 100644 --- a/lib/get.js +++ b/lib/get.js @@ -1,6 +1,4 @@ -'use strict'; - -var os = require('os'); +import os from 'os'; var platformToMethod = { darwin: 'ps', @@ -13,8 +11,8 @@ var platformToMethod = { }; var methodToRequireFn = { - ps: () => require('./ps'), - wmic: () => require('./wmic'), + ps: async () => (await import('./ps.js')).ps, + wmic: async () => (await import('./wmic.js')).wmic, }; var platform = os.platform(); @@ -28,7 +26,7 @@ var method = platformToMethod[platform]; * Gets the list of all the pids of the system. * @param {Function} callback Called when the list is ready. */ -function get(callback) { +export async function get(callback) { if (method === undefined) { callback( new Error( @@ -38,8 +36,6 @@ function get(callback) { ); } - var list = methodToRequireFn[method](); + var list = await methodToRequireFn[method](); list(callback); } - -module.exports = get; diff --git a/lib/pidtree.js b/lib/pidtree.js index f0c8e78..7b764ee 100644 --- a/lib/pidtree.js +++ b/lib/pidtree.js @@ -1,6 +1,4 @@ -'use strict'; - -var getAll = require('./get'); +import { get } from './get.js'; /** * Get the list of children and grandchildren pids of the given PID. @@ -11,7 +9,7 @@ var getAll = require('./get'); * format {pid: X, ppid: Y}. * @param {Function} callback(err, list) Called when the list is ready. */ -function list(PID, options, callback) { +export function pidtreeCallback(PID, options, callback) { if (typeof options === 'function') { callback = options; options = {}; @@ -27,7 +25,7 @@ function list(PID, options, callback) { return; } - getAll(function(err, list) { + get(function(err, list) { if (err) { callback(err); return; @@ -100,5 +98,3 @@ function list(PID, options, callback) { callback(null, pids); }); } - -module.exports = list; diff --git a/lib/ps.js b/lib/ps.js index 6d9bb5f..6a7012b 100644 --- a/lib/ps.js +++ b/lib/ps.js @@ -1,16 +1,15 @@ -'use strict'; +import os from 'os'; -var os = require('os'); -var bin = require('./bin'); +import { run } from './bin.js'; /** * Gets the list of all the pids of the system through the ps command. * @param {Function} callback(err, list) */ -function ps(callback) { +export function ps(callback) { var args = ['-A', '-o', 'ppid,pid']; - bin('ps', args, function(err, stdout, code) { + run('ps', args, function(err, stdout, code) { if (err) return callback(err); if (code !== 0) { return callback(new Error('pidtree ps command exited with code ' + code)); @@ -43,5 +42,3 @@ function ps(callback) { } }); } - -module.exports = ps; diff --git a/lib/wmic.js b/lib/wmic.js index 0361728..237c954 100644 --- a/lib/wmic.js +++ b/lib/wmic.js @@ -1,16 +1,15 @@ -'use strict'; +import os from 'os'; -var os = require('os'); -var bin = require('./bin'); +import { run } from './bin.js'; /** * Gets the list of all the pids of the system through the wmic command. * @param {Function} callback(err, list) */ -function wmic(callback) { +export function wmic(callback) { var args = ['PROCESS', 'get', 'ParentProcessId,ProcessId']; var options = {windowsHide: true, windowsVerbatimArguments: true}; - bin('wmic', args, options, function(err, stdout, code) { + run('wmic', args, options, function(err, stdout, code) { if (err) { callback(err); return; @@ -45,5 +44,3 @@ function wmic(callback) { } }); } - -module.exports = wmic; diff --git a/package.json b/package.json index 6e1e672..0d2b148 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pidtree", - "version": "0.6.0", + "version": "1.0.0", "description": "Cross platform children list of a PID", "license": "MIT", "homepage": "http://github.com/simonepri/pidtree#readme", @@ -11,7 +11,8 @@ }, "author": "Simone Primarosa (https://github.com/simonepri)", "contributors": [ - "Simone Primarosa (https://github.com/simonepri)" + "Simone Primarosa (https://github.com/simonepri)", + "Gavin Aiken (https://github.com/gavinaiken)" ], "keywords": [ "ps-tree", @@ -28,6 +29,7 @@ "processes" ], "main": "index.js", + "type": "module", "types": "index.d.ts", "bin": { "pidtree": "./bin/pidtree.js" @@ -39,41 +41,35 @@ "index.d.ts" ], "engines": { - "node": ">=0.10" + "node": ">=16.0.0" }, "scripts": { "start": "node ./bin/pidtree.js", "update": "npm-check -u", "release": "np", "lint": "xo", - "test": "nyc ava -m \"!*benchmark*\"", + "test": "c8 ava -m \"!*benchmark*\"", "test:windows": "ava -m \"!*benchmark*\"", "types": "tsd", "bench": "ava -m \"*benchmark*\"" }, "devDependencies": { - "ava": "~0.25.0", - "mockery": "^2.1.0", - "np": "^2.20.1", - "npm-check": "^5.9.2", - "nyc": "^11.6.0", - "pify": "^3.0.0", - "string-to-stream": "^1.1.0", + "ava": "~3.15.0", + "c8": "^10.1.3", + "np": "^10.2.0", + "npm-check": "^6.0.1", + "pify": "^6.1.0", + "sinon": "^19.0.2", + "string-to-stream": "^3.0.1", "through": "^2.3.8", - "time-span": "^2.0.0", - "tree-kill": "^1.1.0", - "tsd": "^0.11.0", - "xo": "~0.20.3" + "time-span": "^5.1.0", + "tree-kill": "^1.2.2", + "tsd": "^0.31.2", + "xo": "~0.60.0" }, "ava": { "verbose": true }, - "nyc": { - "reporter": [ - "lcovonly", - "text" - ] - }, "xo": { "prettier": true, "space": true, diff --git a/test/bench.js b/test/bench.js index b1f1123..d25f098 100644 --- a/test/bench.js +++ b/test/bench.js @@ -2,7 +2,7 @@ import test from 'ava'; import tspan from 'time-span'; -import pidtree from '..'; +import { pidtree } from '../index.js'; async function execute(pid, times) { const end = tspan(); diff --git a/test/helpers/exec/child.js b/test/helpers/exec/child.js index 9a5a307..e10d7c3 100644 --- a/test/helpers/exec/child.js +++ b/test/helpers/exec/child.js @@ -1,5 +1,3 @@ -'use strict'; - var started = false; setInterval(function() { if (started) return; diff --git a/test/helpers/exec/parent.js b/test/helpers/exec/parent.js index 9e31bf1..98cbe06 100644 --- a/test/helpers/exec/parent.js +++ b/test/helpers/exec/parent.js @@ -1,7 +1,5 @@ -'use strict'; - -var path = require('path'); -var cp = require('child_process'); +import cp from 'child_process'; +import path from 'path'; var started = false; var spawned = {}; diff --git a/test/integration.js b/test/integration.js index 6950add..e9cbe81 100644 --- a/test/integration.js +++ b/test/integration.js @@ -6,19 +6,22 @@ import test from 'ava'; import pify from 'pify'; import treek from 'tree-kill'; -import pidtree from '..'; +import { pidtree } from '../index.js'; + +const dirname = path.dirname(new URL(import.meta.url).pathname); const scripts = { - parent: path.join(__dirname, 'helpers', 'exec', 'parent.js'), - child: path.join(__dirname, 'helpers', 'exec', 'child.js'), + parent: path.join(dirname, 'helpers', 'exec', 'parent.js'), + child: path.join(dirname, 'helpers', 'exec', 'child.js'), }; test('should work with a single pid', async t => { let result = await pidtree(-1, {advanced: true}); - t.log(result); + // t.log(result); t.true(Array.isArray(result)); result.forEach((p, i) => { + i = i.toString(); t.is(typeof p, 'object', i); t.is(typeof p.ppid, 'number', i); t.false(isNaN(p.ppid), i); @@ -30,6 +33,7 @@ test('should work with a single pid', async t => { t.true(Array.isArray(result)); result.forEach((p, i) => { + i = i.toString(); t.is(typeof p, 'number', i); t.false(isNaN(p), i); }); @@ -109,18 +113,18 @@ test('show include the root if the root option is passsed', async t => { }); test('should throw an error if an invalid pid is provided', async t => { - let err = await t.throws(pidtree(null)); + let err = await t.throwsAsync(pidtree(null)); t.is(err.message, 'The pid provided is invalid'); - err = await t.throws(pidtree([])); + err = await t.throwsAsync(pidtree([])); t.is(err.message, 'The pid provided is invalid'); - err = await t.throws(pidtree('invalid')); + err = await t.throwsAsync(pidtree('invalid')); t.is(err.message, 'The pid provided is invalid'); - err = await t.throws(pidtree(-2)); + err = await t.throwsAsync(pidtree(-2)); t.is(err.message, 'The pid provided is invalid'); }); test('should throw an error if the pid does not exists', async t => { - const err = await t.throws(pidtree(65535)); + const err = await t.throwsAsync(pidtree(65535)); t.is(err.message, 'No matching pid found'); }); diff --git a/test/ps.js b/test/ps.js index a1be0d2..0733695 100644 --- a/test/ps.js +++ b/test/ps.js @@ -1,27 +1,28 @@ import test from 'ava'; -import mockery from 'mockery'; +import sinon from 'sinon'; import pify from 'pify'; -import mocks from './helpers/mocks'; +import mocks from './helpers/mocks.js'; -test.before(() => { - mockery.enable({ - warnOnReplace: false, - warnOnUnregistered: false, - useCleanCache: true, - }); -}); +// to be mocked by sinon: +import child_process from 'child_process'; +import os from 'os'; + +let sandbox; test.beforeEach(() => { - mockery.resetCache(); + sandbox = sinon.createSandbox(); + sandbox.stub(os, 'EOL').returns('\n'); + sandbox.stub(os, 'type').returns('type'); + sandbox.stub(os, 'release').returns('release'); }); -test.after(() => { - mockery.disable(); +test.afterEach.always(t => { + sandbox.restore(); }); -test('should parse ps output on Darwin', async t => { +test.serial('should parse ps output on Darwin', async t => { const stdout = 'PPID PID\n' + ' 1 430\n' + @@ -29,26 +30,16 @@ test('should parse ps output on Darwin', async t => { ' 1 727\n' + ' 1 7166\n'; - mockery.registerMock('child_process', { - spawn: () => mocks.spawn(stdout, '', null, 0, null), - }); - mockery.registerMock('os', { - EOL: '\n', - platform: () => 'darwin', - type: () => 'type', - release: () => 'release', - }); + sandbox.stub(child_process, 'spawn').callsFake(() => mocks.spawn(stdout, '', null, 0, null)); + sandbox.stub(os, 'platform').returns('darwin'); - const ps = require('../lib/ps'); + const ps = (await import('../lib/ps.js')).ps; const result = await pify(ps)(); t.deepEqual(result, [[1, 430], [430, 432], [1, 727], [1, 7166]]); - - mockery.deregisterMock('child_process'); - mockery.deregisterMock('os'); }); -test('should parse ps output on *nix', async t => { +test.serial('should parse ps output on *nix', async t => { const stdout = 'PPID PID\n' + ' 1 430\n' + @@ -56,26 +47,16 @@ test('should parse ps output on *nix', async t => { ' 1 727\n' + ' 1 7166\n'; - mockery.registerMock('child_process', { - spawn: () => mocks.spawn(stdout, '', null, 0, null), - }); - mockery.registerMock('os', { - EOL: '\n', - platform: () => 'linux', - type: () => 'type', - release: () => 'release', - }); + sandbox.stub(child_process, 'spawn').callsFake(() => mocks.spawn(stdout, '', null, 0, null)); + sandbox.stub(os, 'platform').returns('linux'); - const ps = require('../lib/ps'); + const ps = (await import('../lib/ps.js')).ps; const result = await pify(ps)(); t.deepEqual(result, [[1, 430], [430, 432], [1, 727], [1, 7166]]); - - mockery.deregisterMock('child_process'); - mockery.deregisterMock('os'); }); -test('should throw if stderr contains an error', async t => { +test.serial('should throw if stderr contains an error', async t => { const stdout = 'PPID PID\n' + ' 1 430\n' + @@ -83,25 +64,15 @@ test('should throw if stderr contains an error', async t => { ' 1 727\n' + ' 1 7166\n'; - mockery.registerMock('child_process', { - spawn: () => mocks.spawn(stdout, 'Some error', null, 0, null), - }); - mockery.registerMock('os', { - EOL: '\n', - platform: () => 'linux', - type: () => 'type', - release: () => 'release', - }); + sandbox.stub(child_process, 'spawn').callsFake(() => mocks.spawn(stdout, 'Some error', null, 0, null)); + sandbox.stub(os, 'platform').returns('linux'); - const ps = require('../lib/ps'); + const ps = (await import('../lib/ps.js')).ps; - await t.throws(pify(ps)()); - - mockery.deregisterMock('child_process'); - mockery.deregisterMock('os'); + await t.throwsAsync(pify(ps)); }); -test('should not throw if stderr contains the "bogus screen" error message', async t => { +test.serial('should not throw if stderr contains the "bogus screen" error message', async t => { const stdout = 'PPID PID\n' + ' 1 430\n' + @@ -109,28 +80,13 @@ test('should not throw if stderr contains the "bogus screen" error message', asy ' 1 727\n' + ' 1 7166\n'; - mockery.registerMock('child_process', { - spawn: () => - mocks.spawn( - stdout, - 'your 131072x1 screen size is bogus. expect trouble', - null, - 0, - null - ), - }); - mockery.registerMock('os', { - EOL: '\n', - platform: () => 'linux', - type: () => 'type', - release: () => 'release', - }); - - const ps = require('../lib/ps'); + sandbox.stub(child_process, 'spawn').callsFake( + () => mocks.spawn(stdout, 'your 131072x1 screen size is bogus. expect trouble', null, 0, null) + ); + sandbox.stub(os, 'platform').returns('linux'); + + const ps = (await import('../lib/ps.js')).ps; const result = await pify(ps)(); t.deepEqual(result, [[1, 430], [430, 432], [1, 727], [1, 7166]]); - - mockery.deregisterMock('child_process'); - mockery.deregisterMock('os'); }); diff --git a/test/wmic.js b/test/wmic.js index 506cedb..1c68315 100644 --- a/test/wmic.js +++ b/test/wmic.js @@ -1,24 +1,22 @@ import test from 'ava'; -import mockery from 'mockery'; +import sinon from 'sinon'; import pify from 'pify'; -import mocks from './helpers/mocks'; +import mocks from './helpers/mocks.js'; -test.before(() => { - mockery.enable({ - warnOnReplace: false, - warnOnUnregistered: false, - useCleanCache: true, - }); -}); +// to be mocked by sinon: +import child_process from 'child_process'; +import os from 'os'; + +let sandbox; test.beforeEach(() => { - mockery.resetCache(); + sandbox = sinon.createSandbox(); }); -test.after(() => { - mockery.disable(); +test.afterEach.always(t => { + sandbox.restore(); }); test('should parse wmic output on Windows', async t => { @@ -28,21 +26,14 @@ test('should parse wmic output on Windows', async t => { `777 778 \r\r\n` + `0 779 \r\r\n\r\r\n`; - mockery.registerMock('child_process', { - spawn: () => mocks.spawn(stdout, '', null, 0, null), - }); - mockery.registerMock('os', { - EOL: '\r\n', - platform: () => 'linux', - type: () => 'type', - release: () => 'release', - }); + sandbox.stub(child_process, 'spawn').callsFake(() => mocks.spawn(stdout, '', null, 0, null)); + sandbox.stub(os, 'EOL').returns('\n'); + sandbox.stub(os, 'platform').returns('linux'); + sandbox.stub(os, 'type').returns('type'); + sandbox.stub(os, 'release').returns('release'); - const wmic = require('../lib/wmic'); + const wmic = (await import('../lib/wmic.js')).wmic; const result = await pify(wmic)(); t.deepEqual(result, [[0, 777], [777, 778], [0, 779]]); - - mockery.deregisterMock('child_process'); - mockery.deregisterMock('os'); });