Skip to content

Commit 8046e0e

Browse files
daeyeonRafaelGSS
authored andcommitted
errors: refactor to use a method that formats a list string
Signed-off-by: Daeyeon Jeong <[email protected]> PR-URL: #45793 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 98fc94a commit 8046e0e

File tree

3 files changed

+41
-38
lines changed

3 files changed

+41
-38
lines changed

lib/internal/errors.js

+20-37
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const {
1919
ArrayPrototypeIndexOf,
2020
ArrayPrototypeJoin,
2121
ArrayPrototypeMap,
22-
ArrayPrototypePop,
2322
ArrayPrototypePush,
2423
ArrayPrototypeSlice,
2524
ArrayPrototypeSplice,
@@ -895,6 +894,19 @@ function determineSpecificType(value) {
895894
return `type ${typeof value} (${inspected})`;
896895
}
897896

897+
/**
898+
* Create a list string in the form like 'A and B' or 'A, B, ..., and Z'.
899+
* We cannot use Intl.ListFormat because it's not available in
900+
* --without-intl builds.
901+
* @param {string[]} array An array of strings.
902+
* @param {string} [type] The list type to be inserted before the last element.
903+
* @returns {string}
904+
*/
905+
function formatList(array, type = 'and') {
906+
return array.length < 3 ? ArrayPrototypeJoin(array, ` ${type} `) :
907+
`${ArrayPrototypeJoin(ArrayPrototypeSlice(array, 0, -1), ', ')}, ${type} ${array[array.length - 1]}`;
908+
}
909+
898910
module.exports = {
899911
AbortError,
900912
aggregateTwoErrors,
@@ -909,6 +921,7 @@ module.exports = {
909921
errnoException,
910922
exceptionWithHostPort,
911923
fatalExceptionStackEnhancers,
924+
formatList,
912925
genericNodeError,
913926
getMessage,
914927
hideInternalStackFrames,
@@ -1240,39 +1253,20 @@ E('ERR_INVALID_ARG_TYPE',
12401253
}
12411254

12421255
if (types.length > 0) {
1243-
if (types.length > 2) {
1244-
const last = ArrayPrototypePop(types);
1245-
msg += `one of type ${ArrayPrototypeJoin(types, ', ')}, or ${last}`;
1246-
} else if (types.length === 2) {
1247-
msg += `one of type ${types[0]} or ${types[1]}`;
1248-
} else {
1249-
msg += `of type ${types[0]}`;
1250-
}
1256+
msg += `${types.length > 1 ? 'one of type' : 'of type'} ${formatList(types, 'or')}`;
12511257
if (instances.length > 0 || other.length > 0)
12521258
msg += ' or ';
12531259
}
12541260

12551261
if (instances.length > 0) {
1256-
if (instances.length > 2) {
1257-
const last = ArrayPrototypePop(instances);
1258-
msg +=
1259-
`an instance of ${ArrayPrototypeJoin(instances, ', ')}, or ${last}`;
1260-
} else {
1261-
msg += `an instance of ${instances[0]}`;
1262-
if (instances.length === 2) {
1263-
msg += ` or ${instances[1]}`;
1264-
}
1265-
}
1262+
msg += `an instance of ${formatList(instances, 'or')}`;
12661263
if (other.length > 0)
12671264
msg += ' or ';
12681265
}
12691266

12701267
if (other.length > 0) {
1271-
if (other.length > 2) {
1272-
const last = ArrayPrototypePop(other);
1273-
msg += `one of ${ArrayPrototypeJoin(other, ', ')}, or ${last}`;
1274-
} else if (other.length === 2) {
1275-
msg += `one of ${other[0]} or ${other[1]}`;
1268+
if (other.length > 1) {
1269+
msg += `one of ${formatList(other, 'or')}`;
12761270
} else {
12771271
if (StringPrototypeToLowerCase(other[0]) !== other[0])
12781272
msg += 'an ';
@@ -1452,18 +1446,7 @@ E('ERR_MISSING_ARGS',
14521446
ArrayPrototypeJoin(ArrayPrototypeMap(a, wrap), ' or ') :
14531447
wrap(a))
14541448
);
1455-
switch (len) {
1456-
case 1:
1457-
msg += `${args[0]} argument`;
1458-
break;
1459-
case 2:
1460-
msg += `${args[0]} and ${args[1]} arguments`;
1461-
break;
1462-
default:
1463-
msg += ArrayPrototypeJoin(ArrayPrototypeSlice(args, 0, len - 1), ', ');
1464-
msg += `, and ${args[len - 1]} arguments`;
1465-
break;
1466-
}
1449+
msg += `${formatList(args)} argument${len > 1 ? 's' : ''}`;
14671450
return `${msg} must be specified`;
14681451
}, TypeError);
14691452
E('ERR_MISSING_OPTION', '%s is required', TypeError);
@@ -1696,7 +1679,7 @@ E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError);
16961679
E('ERR_UNSUPPORTED_DIR_IMPORT', "Directory import '%s' is not supported " +
16971680
'resolving ES modules imported from %s', Error);
16981681
E('ERR_UNSUPPORTED_ESM_URL_SCHEME', (url, supported) => {
1699-
let msg = `Only URLs with a scheme in: ${ArrayPrototypeJoin(supported, ', ')} are supported by the default ESM loader`;
1682+
let msg = `Only URLs with a scheme in: ${formatList(supported)} are supported by the default ESM loader`;
17001683
if (isWindows && url.protocol.length === 2) {
17011684
msg +=
17021685
'. On Windows, absolute paths must be valid file:// URLs';

test/es-module/test-esm-dynamic-import.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function expectFsNamespace(result) {
5959
'ERR_UNSUPPORTED_ESM_URL_SCHEME');
6060
if (common.isWindows) {
6161
const msg =
62-
'Only URLs with a scheme in: file, data are supported by the default ' +
62+
'Only URLs with a scheme in: file and data are supported by the default ' +
6363
'ESM loader. On Windows, absolute paths must be valid file:// URLs. ' +
6464
"Received protocol 'c:'";
6565
expectModuleError(import('C:\\example\\foo.mjs'),
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
const { strictEqual } = require('node:assert');
6+
const { formatList } = require('internal/errors');
7+
8+
if (!common.hasIntl) common.skip('missing Intl');
9+
10+
{
11+
const and = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
12+
const or = new Intl.ListFormat('en', { style: 'long', type: 'disjunction' });
13+
14+
const input = ['apple', 'banana', 'orange'];
15+
for (let i = 0; i < input.length; i++) {
16+
const slicedInput = input.slice(0, i);
17+
strictEqual(formatList(slicedInput), and.format(slicedInput));
18+
strictEqual(formatList(slicedInput, 'or'), or.format(slicedInput));
19+
}
20+
}

0 commit comments

Comments
 (0)