Skip to content

Commit

Permalink
Convert to type: module
Browse files Browse the repository at this point in the history
Convert to native ECMAScript Modules, supported since Node.js 12.17.
This gains the benefits of native `import`/`export` for faster
asynchronous loading, cycle detection, and tree shaking in bundlers.
Unfortunately, it will create some pain for dependencies, since ESM can
not be imported using `require()` and must be imported using `import()`,
which operates asynchronously and returns a `Promise`.

See discussion in sindresorhus/meta#15

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
  • Loading branch information
kevinoid committed Aug 5, 2021
1 parent df5bbac commit 1436733
Show file tree
Hide file tree
Showing 26 changed files with 143 additions and 191 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"bin/*.js"
],
"rules": {
// Executable scripts are not expected to have exports
"import/no-unused-modules": "off",

// Executable scripts should have a shebang
"node/shebang": "off"
}
Expand Down
4 changes: 1 addition & 3 deletions bin/hub-ci-status.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
* @license MIT
*/

'use strict';

const main = require('../cli.js');
import main from '../cli.js';

// This file was invoked directly.
// Note: Could pass process.exit as callback to force immediate exit.
Expand Down
14 changes: 5 additions & 9 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@
* @module hub-ci-status/cli.js
*/

'use strict';

const {
import {
Command,
InvalidOptionArgumentError,
Option,
} = require('commander');
} from 'commander';

const hubCiStatus = require('.');
const getPackageJson = require('./lib/get-package-json.js');
import getPackageJson from './lib/get-package-json.js';
import hubCiStatus from './index.js';

// Same --color options as hub(1)
const colorOptions = ['always', 'never', 'auto'];
Expand Down Expand Up @@ -72,7 +70,7 @@ function countOption(optarg, previous) {
* @returns {!Promise<number>} Promise for exit code. Only rejected for
* arguments with invalid type (or args.length < 2).
*/
async function hubCiStatusMain(args, options) {
export default async function hubCiStatusMain(args, options) {
if (!Array.isArray(args) || args.length < 2) {
throw new TypeError('args must be an Array with at least 2 items');
}
Expand Down Expand Up @@ -169,5 +167,3 @@ async function hubCiStatusMain(args, options) {
return 1;
}
}

module.exports = hubCiStatusMain;
17 changes: 7 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@
* @module hub-ci-status
*/

'use strict';

const fetchCiStatus = require('./lib/fetch-ci-status.js');
const { resolveCommit } = require('./lib/git-utils.js');
const { getProjectName } = require('./lib/github-utils.js');
const {
import fetchCiStatus from './lib/fetch-ci-status.js';
import { resolveCommit } from './lib/git-utils.js';
import { getProjectName } from './lib/github-utils.js';
import {
fetchCiStatusMockSymbol,
getProjectNameMockSymbol,
resolveCommitMockSymbol,
} = require('./lib/symbols.js');
} from './lib/symbols.js';

// Use same "severity" as hub(1) for determining state
// https://github.com/github/hub/blob/v2.14.2/commands/ci_status.go#L60-L69
Expand Down Expand Up @@ -164,8 +162,7 @@ function checkRunToStatus(checkRun) {
* printed. 0 if the status was printed, non-zero if the status could not
* be determined.
*/
module.exports =
async function hubCiStatus(
export default async function hubCiStatus(
rev = 'HEAD',
{
[fetchCiStatusMockSymbol]: fetchCiStatusMock,
Expand Down Expand Up @@ -218,4 +215,4 @@ async function hubCiStatus(
}

return stateToExitCode(state);
};
}
8 changes: 2 additions & 6 deletions lib/exec-file-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
* @license MIT
*/

'use strict';

const { execFile } = require('child_process');
import { execFile } from 'child_process';

/** Promisified <code>execFile</code> wrapper which only provides access to
* <code>stdout</code> and fails if <code>stderr</code> is non-empty.
Expand All @@ -19,7 +17,7 @@ const { execFile } = require('child_process');
* non-whitespace characters.
* @private
*/
function execFileOut(file, args, options) {
export default function execFileOut(file, args, options) {
return new Promise((resolve, reject) => {
const child = execFile(file, args, options, (err, stdout, stderr) => {
if (err) {
Expand Down Expand Up @@ -47,5 +45,3 @@ function execFileOut(file, args, options) {
child.stdin.end();
});
}

module.exports = execFileOut;
27 changes: 12 additions & 15 deletions lib/fetch-ci-status.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,24 @@
* @license MIT
*/

'use strict';

const { Octokit } = require('@octokit/rest');
const { Agent: HttpAgent } = require('http');
const { Agent: HttpsAgent } = require('https');
const timers = require('timers');
const { promisify } = require('util');

const getPackageJson = require('./get-package-json.js');
const retryAsync = require('./retry-async.js');
const {
import { Octokit } from '@octokit/rest';
import { Agent as HttpAgent } from 'http';
import { Agent as HttpsAgent } from 'https';
import timers from 'timers';
import { promisify } from 'util';

import getPackageJson from './get-package-json.js';
import retryAsync from './retry-async.js';
import {
HttpAgentMockSymbol,
HttpsAgentMockSymbol,
OctokitMockSymbol,
} = require('./symbols.js');
} from './symbols.js';

// TODO [engine:node@>=15]: import { setTimeout } from 'timers/promises';
const setTimeoutP = promisify(timers.setTimeout);

module.exports =
async function fetchCiStatus(apiArgs, options = {}) {
export default async function fetchCiStatus(apiArgs, options = {}) {
let agent;
let { octokit } = options;
if (octokit === undefined) {
Expand Down Expand Up @@ -157,4 +154,4 @@ async function fetchCiStatus(apiArgs, options = {}) {
agent.destroy();
}
}
};
}
14 changes: 6 additions & 8 deletions lib/get-package-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
// once JSON modules are supported non-experimentally:
// https://github.com/nodejs/node/issues/37141

'use strict';

const { readFile } = require('fs').promises;
const path = require('path');
// TODO [engine:node@>=14]: Use readFile from 'fs/promises'
import { promises as fsPromises } from 'fs';

const { readFile } = fsPromises;
let packageJsonP;

async function readJson(pathOrUrl, options) {
Expand All @@ -23,11 +22,10 @@ async function readJson(pathOrUrl, options) {
*
* @returns {!Promise<!object>} Parsed content of package.json.
*/
module.exports =
function getPackageJson() {
export default function getPackageJson() {
if (!packageJsonP) {
packageJsonP = readJson(path.join(__dirname, '..', 'package.json'));
packageJsonP = readJson(new URL('../package.json', import.meta.url));
}

return packageJsonP;
};
}
29 changes: 13 additions & 16 deletions lib/git-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
* @license MIT
*/

'use strict';

const { pathToFileURL } = require('url');
const execFileOut = require('./exec-file-out.js');
import { pathToFileURL } from 'url';
import execFileOut from './exec-file-out.js';

/** Is this process running on Windows?
*
Expand All @@ -22,7 +20,6 @@ const configScopes = Object.freeze(Object.assign(Object.create(null), {
system: 'system',
worktree: 'worktree',
}));
exports.configScopes = configScopes;

function trim(str) {
return String.prototype.trim.call(str);
Expand All @@ -36,13 +33,13 @@ function trim(str) {
* branch, not in a git repository, or another error occurs.
* @private
*/
exports.getBranch = function getBranch(options) {
export function getBranch(options) {
return execFileOut('git', ['symbolic-ref', '-q', '--short', 'HEAD'], options)
.then(trim)
.catch((err) => {
throw new Error(`Unable to determine current branch: ${err.message}`);
});
};
}

/** Parse output of `git config --null`.
*
Expand Down Expand Up @@ -92,7 +89,7 @@ function parseConfigOutput(configData) {
* @throws Error If an error occurs when parsing the git config output.
* @private
*/
exports.getConfig = async function getConfig(scope, options) {
export async function getConfig(scope, options) {
if (scope !== undefined && scope !== null && !configScopes[scope]) {
throw new RangeError(`Invalid scope "${scope}"`);
}
Expand All @@ -111,7 +108,7 @@ exports.getConfig = async function getConfig(scope, options) {
},
);
return parseConfigOutput(configData);
};
}

/** Is git URL a local path?
* From url_is_local_not_ssh in connect.c
Expand All @@ -121,10 +118,10 @@ exports.getConfig = async function getConfig(scope, options) {
* local path.
* @private
*/
exports.gitUrlIsLocalNotSsh = function gitUrlIsLocalNotSsh(gitUrl) {
export function gitUrlIsLocalNotSsh(gitUrl) {
return !/^[^/]*:/.test(gitUrl)
|| (isWindows && /^[A-Za-z]:/.test(gitUrl));
};
}

/** Parses a git URL string into a URL object like {@link url.parse} with
* support for git helpers, git's SCP-like URL syntax, and local file paths.
Expand All @@ -135,8 +132,8 @@ exports.gitUrlIsLocalNotSsh = function gitUrlIsLocalNotSsh(gitUrl) {
* @throws {TypeError} If gitUrl can not be parsed as a URL.
* @private
*/
exports.parseGitUrl = function parseGitUrl(gitUrl) {
if (exports.gitUrlIsLocalNotSsh(gitUrl)) {
export function parseGitUrl(gitUrl) {
if (gitUrlIsLocalNotSsh(gitUrl)) {
const fileUrlObj = pathToFileURL(gitUrl);
fileUrlObj.helper = undefined;
return fileUrlObj;
Expand All @@ -160,7 +157,7 @@ exports.parseGitUrl = function parseGitUrl(gitUrl) {
const gitUrlObj = new URL(gitUrl);
gitUrlObj.helper = helper;
return gitUrlObj;
};
}

/** Resolve a named commit to its hash.
*
Expand All @@ -171,7 +168,7 @@ exports.parseGitUrl = function parseGitUrl(gitUrl) {
* if <code>commitName</code> can not be resolved.
* @private
*/
exports.resolveCommit = async function resolveCommit(commitName, options) {
export async function resolveCommit(commitName, options) {
try {
const sha = await execFileOut(
'git',
Expand All @@ -184,4 +181,4 @@ exports.resolveCommit = async function resolveCommit(commitName, options) {
`Unable to resolve '${commitName}' to a commit hash: ${err.message}`;
throw err;
}
};
}
12 changes: 5 additions & 7 deletions lib/github-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
* @license MIT
*/

'use strict';
import { debuglog } from 'util';

const { debuglog } = require('util');

const { getBranch, getConfig, parseGitUrl } = require('./git-utils.js');
import { getBranch, getConfig, parseGitUrl } from './git-utils.js';

const debug = debuglog('hub-ci-status');

Expand Down Expand Up @@ -119,8 +117,8 @@ async function tryGetBranch(options) {
* @returns {!Promise<!Array<string>>} Promise for the owner and repo name,
* as Array elements, or a UnknownProjectError if they can not be determined.
*/
exports.getProjectName =
async function getProjectName(options) {
// eslint-disable-next-line import/prefer-default-export
export async function getProjectName(options) {
// Run getBranch() and getConfig() concurrently.
const [branch, config] = await Promise.all([
tryGetBranch(options),
Expand Down Expand Up @@ -157,4 +155,4 @@ async function getProjectName(options) {
}

throw new UnknownProjectError();
};
}
20 changes: 8 additions & 12 deletions lib/retry-async.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
* @license MIT
*/

'use strict';
import timers from 'timers';
import { promisify } from 'util';

const timers = require('timers');
const { promisify } = require('util');

const constant = require('./retry-async/constant.js');
const exponential = require('./retry-async/exponential.js');
import constant from './retry-async/constant.js';
import exponential from './retry-async/exponential.js';

// TODO [engine:node@>=15]: import { setTimeout } from 'timers/promises';
const setTimeoutP = promisify(timers.setTimeout);
Expand Down Expand Up @@ -48,7 +46,8 @@ function defaultShouldRetry(result) {
* @private
* @type {RetryAsyncOptions}
*/
const DEFAULT_OPTIONS = Object.freeze({
// eslint-disable-next-line import/no-unused-modules
export const DEFAULT_OPTIONS = Object.freeze({
maxTotalMs: Infinity,
minWaitMs: 4000,
now: Date.now,
Expand All @@ -69,8 +68,7 @@ const DEFAULT_OPTIONS = Object.freeze({
* @returns {!Promise<TReturn>} Promise for return value of last call to
* operation.
*/
module.exports =
async function retryAsync(
export default async function retryAsync(
operation,
{
maxTotalMs = DEFAULT_OPTIONS.maxTotalMs,
Expand Down Expand Up @@ -126,6 +124,4 @@ async function retryAsync(
waitIterator.return();
}
}
};

module.exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
}
Loading

0 comments on commit 1436733

Please sign in to comment.