Skip to content

Commit

Permalink
feat: refactor with typescript to support esm and cjs both
Browse files Browse the repository at this point in the history
  • Loading branch information
hanquliu authored and hanquliu committed Nov 20, 2023
1 parent 9804ad5 commit a4f7723
Show file tree
Hide file tree
Showing 18 changed files with 420 additions and 366 deletions.
9 changes: 9 additions & 0 deletions .eslintrc
@@ -0,0 +1,9 @@
{
"extends": [
"eslint-config-egg/typescript",
"eslint-config-egg/lib/rules/enforce-node-prefix"
],
"rules": {
"@typescript-eslint/ban-ts-comment": "off"
}
}
8 changes: 0 additions & 8 deletions .eslintrc.js

This file was deleted.

2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -2,3 +2,5 @@ node_modules
coverage
*.un~
*.sw*
.tshy*
dist/
13 changes: 5 additions & 8 deletions bin/detect-port.js → bin/detect-port.ts
@@ -1,14 +1,14 @@
#!/usr/bin/env node

'use strict';

const pkg = require('../package');
import pkg from '../package.json';
import detectPort from '../src/detect-port.js';

const args = process.argv.slice(2);
let arg_0 = args[0];

if (arg_0 && [ '-v', '--version' ].includes(arg_0.toLowerCase())) {
console.log(pkg.version);

process.exit(0);
}

Expand All @@ -21,18 +21,15 @@ const removeByValue = (arr, val) => {
}
};

const main = require('..');

const port = parseInt(arg_0, 10);
const isVerbose = args.includes('--verbose');

removeByValue(args, '--verbose');
arg_0 = args[0];

if (!arg_0) {
const random = Math.floor(9000 + Math.random() * (65535 - 9000));

main(random, (err, port) => {
detectPort(random, (err, port) => {
if (isVerbose) {
if (err) {
console.log(`get available port failed with ${err}`);
Expand Down Expand Up @@ -61,7 +58,7 @@ if (!arg_0) {
console.log(` ${pkg.homepage}`);
console.log();
} else {
main(port, (err, _port) => {
detectPort(port, (err, _port) => {
if (isVerbose) {
if (err) {
console.log(`get available port failed with ${err}`);
Expand Down
4 changes: 0 additions & 4 deletions index.js

This file was deleted.

32 changes: 0 additions & 32 deletions lib/wait-port.js

This file was deleted.

59 changes: 43 additions & 16 deletions package.json
Expand Up @@ -10,7 +10,7 @@
"detect": "./bin/detect-port.js",
"detect-port": "./bin/detect-port.js"
},
"main": "index.js",
"main": "./dist/commonjs/index.js",
"files": [
"bin",
"lib",
Expand All @@ -21,25 +21,52 @@
"url": "git://github.com/node-modules/detect-port.git"
},
"dependencies": {
"address": "^1.0.1",
"debug": "4"
"address": "^2.0.1"
},
"devDependencies": {
"command-line-test": "1",
"egg-bin": "^5.2.0",
"eslint": "^8.23.1",
"eslint-config-egg": "^12.0.0",
"git-contributor": "1",
"mm": "^2.1.0",
"pedding": "^1.1.0",
"power-assert": "^1.6.1"
"@eggjs/tsconfig": "^1.3.3",
"@types/node": "^20.9.0",
"c8": "^8.0.1",
"egg-bin": "^6.5.2",
"eslint": "^8.52.0",
"eslint-config-egg": "^13.0.0",
"execa": "^8.0.1",
"git-contributor": "^2.1.5",
"strip-ansi": "^7.1.0",
"ts-node": "^10.9.1",
"tshy": "^1.8.0",
"tshy-after": "^1.0.0",
"tsx": "^4.1.2",
"typescript": "^5.2.2"
},
"scripts": {
"test": "egg-bin test",
"ci": "npm run lint && egg-bin cov",
"lint": "eslint .",
"contributor": "git-contributor"
"test": "npm run lint -- --fix && tsx --test test/*.test.ts",
"lint": "eslint src test --ext ts",
"ci": "npm run lint && npm run cov && npm run prepublishOnly",
"contributor": "git-contributor",
"prepublishOnly": "npm run tsc && tshy && tshy-after",
"tsc": "tsc ./bin/detect-port.ts --resolveJsonModule --esModuleInterop",
"cov": "c8 npm test"
},
"homepage": "https://github.com/node-modules/detect-port",
"license": "MIT"
"license": "MIT",
"tshy": {
"exports": {
".": "./src/index.ts"
}
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
}
},
"types": "./dist/commonjs/index.d.ts",
"type": "module"
}
59 changes: 36 additions & 23 deletions lib/detect-port.js → src/detect-port.ts
@@ -1,24 +1,35 @@
'use strict';
import net from 'node:net';
import { ip } from 'address';
import { debuglog } from 'node:util';

const net = require('net');
const address = require('address');
const debug = require('debug')('detect-port');
const debug = debuglog('detect-port');

module.exports = (port, callback) => {
let hostname = '';
type DetectPortCallback = (err: Error | null, port: number) => void;

if (typeof port === 'object' && port) {
interface PortConfig {
port?: number | string;
hostname?: string | undefined;
callback?: DetectPortCallback;
}

export default function detectPort(port?: number | PortConfig | string): Promise<number>;
export default function detectPort(callback: DetectPortCallback): void;
export default function detectPort(port: number | PortConfig | string | undefined, callback: DetectPortCallback): void;
export default function detectPort(port?: number | string | PortConfig | DetectPortCallback, callback?: DetectPortCallback) {
let hostname: string | undefined = '';

if (port && typeof port === 'object') {
hostname = port.hostname;
callback = port.callback;
port = port.port;
} else {
if (typeof port === 'function') {
callback = port;
port = null;
port = void 0;
}
}

port = parseInt(port) || 0;
port = parseInt(port as unknown as string) || 0;
let maxPort = port + 10;
if (maxPort > 65535) {
maxPort = 65535;
Expand All @@ -29,13 +40,13 @@ module.exports = (port, callback) => {
}
// promise
return new Promise(resolve => {
tryListen(port, maxPort, hostname, (_, realPort) => {
tryListen(port as number, maxPort, hostname, (_, realPort) => {
resolve(realPort);
});
});
};
}

function tryListen(port, maxPort, hostname, callback) {
function tryListen(port: number, maxPort: number, hostname: string | undefined, callback: (...args: any[]) => void) {
function handleError() {
port++;
if (port >= maxPort) {
Expand All @@ -50,7 +61,7 @@ function tryListen(port, maxPort, hostname, callback) {
if (hostname) {
listen(port, hostname, (err, realPort) => {
if (err) {
if (err.code === 'EADDRNOTAVAIL') {
if ((err as any).code === 'EADDRNOTAVAIL') {
return callback(new Error('the ip that is not unknown on the machine'));
}
return handleError();
Expand All @@ -60,34 +71,34 @@ function tryListen(port, maxPort, hostname, callback) {
});
} else {
// 1. check null
listen(port, null, (err, realPort) => {
listen(port, void 0, (err, realPort) => {
// ignore random listening
if (port === 0) {
return callback(err, realPort);
}

if (err) {
return handleError(err);
return handleError();
}

// 2. check 0.0.0.0
listen(port, '0.0.0.0', err => {
if (err) {
return handleError(err);
return handleError();
}

// 3. check localhost
listen(port, 'localhost', err => {
// if localhost refer to the ip that is not unkonwn on the machine, you will see the error EADDRNOTAVAIL
// https://stackoverflow.com/questions/10809740/listen-eaddrnotavail-error-in-node-js
if (err && err.code !== 'EADDRNOTAVAIL') {
return handleError(err);
if (err && (err as any).code !== 'EADDRNOTAVAIL') {
return handleError();
}

// 4. check current ip
listen(port, address.ip(), (err, realPort) => {
listen(port, ip(), (err, realPort) => {
if (err) {
return handleError(err);
return handleError();
}

callback(null, realPort);
Expand All @@ -98,21 +109,23 @@ function tryListen(port, maxPort, hostname, callback) {
}
}

function listen(port, hostname, callback) {
function listen(port: number, hostname: string | undefined, callback: (...args: any[]) => void) {
const server = new net.Server();

server.on('error', err => {
debug('listen %s:%s error: %s', hostname, port, err);
server.close();
if (err.code === 'ENOTFOUND') {

if ((err as any).code === 'ENOTFOUND') {
debug('ignore dns ENOTFOUND error, get free %s:%s', hostname, port);
return callback(null, port);
}

return callback(err);
});

server.listen(port, hostname, () => {
port = server.address().port;
port = (server.address() as net.AddressInfo).port;
server.close();
debug('get free %s:%s', hostname, port);
return callback(null, port);
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
@@ -0,0 +1,3 @@
import detectPort from './detect-port.js';

export default detectPort;
35 changes: 35 additions & 0 deletions src/wait-port.ts
@@ -0,0 +1,35 @@
import { debuglog } from 'node:util';
import detectPort from './detect-port.js';

const debug = debuglog('wait-port');

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

export interface WaitPortOptions {
retryInterval?: number;
retries?: number;
}

export default async function waitPort(port: number, options: WaitPortOptions = {}) {
const { retryInterval = 1000, retries = Infinity } = options;
let count = 1;

async function loop() {
debug('retries', retries, 'count', count);
if (count > retries) {
const err = new Error('retries exceeded');
(err as any).retries = retries;
(err as any).count = count;
throw err;
}
count++;
const freePort = await detectPort(port);
if (freePort === port) {
await sleep(retryInterval);
return loop();
}
return true;
}

return await loop();
}

0 comments on commit a4f7723

Please sign in to comment.