Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add exclude option #53

Merged
merged 36 commits into from Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9a5bd68
exclusion logic
Jul 19, 2021
aa5b82f
bumped.
Jul 19, 2021
cb7b8e6
test.
Jul 19, 2021
4a58851
added
Jul 19, 2021
39e736a
maintainer should do this.
Jul 19, 2021
92b78fb
updated.
Jul 20, 2021
08ee1f7
.
Jul 20, 2021
b106ca2
fix readme!
Jul 30, 2021
a60d014
rename exclusions -> exclude
Jul 30, 2021
6318107
updated.
Jul 30, 2021
3a89378
modified exclude option to support Iterable
Sep 15, 2021
0a37f9f
added iterable handling + UT
Sep 15, 2021
5900b0e
Update index.d.ts wording.
mastrzyz Oct 6, 2021
3550ba0
Update readme.md typing information
mastrzyz Oct 6, 2021
4e885a6
Merge remote-tracking branch 'upstream/main' into main
Oct 6, 2021
e92c79b
.
Oct 6, 2021
2ac007b
redundant.
Oct 6, 2021
ff49cda
exclude non number types from iterable.
Jan 25, 2022
00adfeb
remove test code.
Jan 25, 2022
cb28fb9
formatting issues
Jan 25, 2022
270289a
updated test.
Jan 25, 2022
3003d4e
updated comment.
Jan 25, 2022
ab2fdc6
Update index.js
sindresorhus Jan 26, 2022
63de20b
Update index.d.ts
sindresorhus Jan 26, 2022
3c1a062
added handling for NaN type numbers.
Jan 26, 2022
64d0f3d
Merge branch 'main' of https://github.com/mastrzyz/get-port
Jan 26, 2022
8ac75bf
.
Jan 26, 2022
6e023b5
updated readme.
Jan 26, 2022
6ba70df
updated concerns.
Feb 1, 2022
7be0ff2
Merge branch 'main' of https://github.com/mastrzyz/get-port into main
Feb 1, 2022
a4b0f53
,
Feb 7, 2022
fffa525
sync the two
Feb 7, 2022
736b38a
Update test.js
sindresorhus Feb 16, 2022
e6feab7
Update readme.md
sindresorhus Feb 16, 2022
f1da1f3
Update index.d.ts
sindresorhus Feb 16, 2022
859fb18
Update test.js
sindresorhus Feb 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions index.d.ts
Expand Up @@ -6,6 +6,13 @@ export interface Options extends Omit<ListenOptions, 'port'> {
*/
readonly port?: number | Iterable<number>;

/**
Ports that should not be returned.

You could, for example, pass it the return value of the `portNumbers()` function.
*/
readonly exclude?: Iterable<number>;

/**
The host on which port resolution should be performed. Can be either an IPv4 or IPv6 address.

Expand Down
40 changes: 35 additions & 5 deletions index.js
Expand Up @@ -17,6 +17,9 @@ const lockedPorts = {
// and a new young set for locked ports are created.
const releaseOldLockedPortsIntervalMs = 1000 * 15;

const minPort = 1024;
const maxPort = 65_535;

// Lazily create interval on first use
let interval;

Expand Down Expand Up @@ -78,9 +81,32 @@ const portCheckSequence = function * (ports) {

export default async function getPorts(options) {
let ports;
let exclude = new Set();

if (options) {
ports = typeof options.port === 'number' ? [options.port] : options.port;
if (options.port) {
ports = typeof options.port === 'number' ? [options.port] : options.port;
}

if (options.exclude) {
const excludeIterable = options.exclude;

if (typeof excludeIterable[Symbol.iterator] !== 'function') {
throw new TypeError('The `exclude` option must be an iterable.');
}

mastrzyz marked this conversation as resolved.
Show resolved Hide resolved
for (const element of excludeIterable) {
if (typeof element !== 'number') {
mastrzyz marked this conversation as resolved.
Show resolved Hide resolved
throw new TypeError('Each item in the `exclude` option must be a number corresponding to the port you want excluded.');
}

if (!Number.isSafeInteger(element)) {
throw new TypeError(`Number ${element} in the exclude option is not a safe integer and can't be used`);
}
}

exclude = new Set(excludeIterable);
}
}

if (interval === undefined) {
Expand All @@ -99,6 +125,10 @@ export default async function getPorts(options) {

for (const port of portCheckSequence(ports)) {
try {
if (exclude.has(port)) {
continue;
}

let availablePort = await getAvailablePort({...options, port}, hosts); // eslint-disable-line no-await-in-loop
while (lockedPorts.old.has(availablePort) || lockedPorts.young.has(availablePort)) {
if (port !== 0) {
Expand Down Expand Up @@ -126,12 +156,12 @@ export function portNumbers(from, to) {
throw new TypeError('`from` and `to` must be integer numbers');
}

if (from < 1024 || from > 65_535) {
throw new RangeError('`from` must be between 1024 and 65535');
if (from < minPort || from > maxPort) {
throw new RangeError(`'from' must be between ${minPort} and ${maxPort}`);
}

if (to < 1024 || to > 65_536) {
throw new RangeError('`to` must be between 1024 and 65536');
if (to < minPort || to > maxPort + 1) {
throw new RangeError(`'to' must be between ${minPort} and ${maxPort + 1}`);
}

if (to < from) {
Expand Down
2 changes: 2 additions & 0 deletions index.test-d.ts
Expand Up @@ -3,6 +3,8 @@ import getPort, {portNumbers} from './index.js';

expectType<Promise<number>>(getPort());
expectType<Promise<number>>(getPort({port: 3000}));
expectType<Promise<number>>(getPort({exclude: [3000]}));
expectType<Promise<number>>(getPort({exclude: [3000, 3001]}));
expectType<Promise<number>>(getPort({port: [3000, 3001, 3002]}));
expectType<Promise<number>>(getPort({host: 'https://localhost'}));
expectType<Promise<number>>(getPort({ipv6Only: true}));
Expand Down
8 changes: 8 additions & 0 deletions readme.md
Expand Up @@ -60,6 +60,14 @@ Type: `number | Iterable<number>`

A preferred port or an iterable of preferred ports to use.

##### exclude

Type: `Iterable<number>`

Ports that should not be returned.

You could, for example, pass it the return value of the `portNumbers()` function.

##### host

Type: `string`
Expand Down
23 changes: 23 additions & 0 deletions test.js
Expand Up @@ -139,6 +139,29 @@ test('makeRange produces valid ranges', t => {
t.deepEqual([...portNumbers(1024, 1027)], [1024, 1025, 1026, 1027]);
});

test('exclude produces ranges that exclude provided exclude list', async t => {
const exclude = [1024, 1026];
const foundPorts = await getPort({exclude, port: portNumbers(1024, 1026)});

// We should not find any of the exclusions in `foundPorts`.
t.is(foundPorts, 1025);
});

test('exclude throws error if not provided with a valid iterator', async t => {
const exclude = 42;
await t.throwsAsync(getPort({exclude}));
});

test('exclude throws error if provided iterator contains items which are non number', async t => {
const exclude = ['foo'];
await t.throwsAsync(getPort({exclude}));
});

test('exclude throws error if provided iterator contains items which are unsafe numbers', async t => {
const exclude = [Number.NaN];
await t.throwsAsync(getPort({exclude}));
});

// TODO: Re-enable this test when ESM supports import hooks.
// test('ports are locked for up to 30 seconds', async t => {
// // Speed up the test by overriding `setInterval`.
Expand Down