Skip to content

Commit e80e1c3

Browse files
committed
docs: use
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent a5e0903 commit e80e1c3

File tree

2 files changed

+220
-1
lines changed

2 files changed

+220
-1
lines changed

.dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fpnv
2222
gpgsign
2323
hmarr
2424
iife
25+
instanceof
2526
keyid
2627
larsgw
2728
lcov

README.md

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,225 @@ yarn add @flex-development/errnode@flex-development/errnode
5858

5959
## Use
6060

61-
**TODO**: Update documentation.
61+
```typescript
62+
import {
63+
createNodeError,
64+
determineSpecificType,
65+
ErrorCode,
66+
type MessageFn,
67+
type NodeError,
68+
type NodeErrorConstructor
69+
} from '@flex-development/errnode'
70+
import type { OneOrMany } from '@flex-development/tutils'
71+
import assert from 'node:assert'
72+
73+
/**
74+
* Creates an [`ERR_INVALID_ARG_TYPE`][1] message.
75+
*
76+
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
77+
*
78+
* @see https://github.com/nodejs/node/blob/v19.3.0/lib/internal/errors.js#L1197-L1286
79+
*
80+
* @param {string} name - Name of invalid argument or property
81+
* @param {OneOrMany<string>} expected - Expected type(s)
82+
* @param {unknown} actual - Value supplied by user
83+
* @return {string} Error message
84+
*/
85+
const msg: MessageFn<[string, OneOrMany<string>, unknown]> = (
86+
name: string,
87+
expected: OneOrMany<string>,
88+
actual: unknown
89+
): string => {
90+
// ensure name is a string
91+
assert(typeof name === 'string', "'name' must be a string")
92+
93+
// ensure expected is an array
94+
if (!Array.isArray(expected)) expected = [expected]
95+
96+
/**
97+
* Primitive value names.
98+
*
99+
* Sorted by a rough estimate on most frequently used entries.
100+
*
101+
* @const {Set<string>} kTypes
102+
*/
103+
const kTypes: Set<string> = new Set([
104+
'string',
105+
'function',
106+
'number',
107+
'object',
108+
'Function',
109+
'Object',
110+
'boolean',
111+
'bigint',
112+
'symbol'
113+
])
114+
115+
/**
116+
* Error message.
117+
*
118+
* @var {string} msg
119+
*/
120+
let msg: string = 'The '
121+
122+
// stylize invalid argument name
123+
msg += name.endsWith(' argument')
124+
? name
125+
: `"${name}" ${name.includes('.') ? 'property' : 'argument'}`
126+
127+
// continue building error message
128+
msg += ' must be '
129+
130+
/**
131+
* Names of expected class instances.
132+
*
133+
* @const {string[]} instances
134+
*/
135+
const instances: string[] = []
136+
137+
/**
138+
* Names of other expected types.
139+
*/
140+
const other: string[] = []
141+
142+
/**
143+
* Names of expected primitive types.
144+
*/
145+
const types: string[] = []
146+
147+
// get expected types
148+
for (const value of expected) {
149+
assert(typeof value === 'string', '`expected` should be of type string[]')
150+
151+
if (kTypes.has(value)) types.push(value.toLowerCase())
152+
else if (/^([A-Z][\da-z]*)+$/.exec(value)) instances.push(value)
153+
else other.push(value === 'object' ? 'Object' : value)
154+
}
155+
156+
// special case: handle `object` in case other instances are allowed to
157+
// outline the differences between each other
158+
if (instances.length > 0) {
159+
/**
160+
* Position of `'object'` in {@linkcode types}.
161+
*
162+
* @const {number} pos
163+
*/
164+
const pos: number = types.indexOf('object')
165+
166+
// replace 'object' in types with "Object" in instances
167+
if (pos !== -1) {
168+
types.splice(pos, 1)
169+
instances.push('Object')
170+
}
171+
}
172+
173+
// add expected primitive types to error message
174+
if (types.length > 0) {
175+
if (types.length > 2) {
176+
/**
177+
* Last primitive type name in {@linkcode types}.
178+
*
179+
* @const {string} last_type
180+
*/
181+
const last_type: string = types.pop()!
182+
183+
msg += `one of type ${types.join(', ')}, or ${last_type}`
184+
} else if (types.length === 2) {
185+
msg += `one of type ${types[0]} or ${types[1]}`
186+
} else {
187+
msg += `of type ${types[0]}`
188+
}
189+
190+
if (instances.length > 0 || other.length > 0) msg += ' or '
191+
}
192+
193+
// add expected class instances to error message
194+
if (instances.length > 0) {
195+
if (instances.length > 2) {
196+
/**
197+
* Last instance type name in {@linkcode instances}.
198+
*
199+
* @const {string} last_instance
200+
*/
201+
const last_instance: string = types.pop()!
202+
203+
msg += `an instance of ${instances.join(', ')}, or ${last_instance}`
204+
} else {
205+
msg += `an instance of ${instances[0]}`
206+
if (instances.length === 2) msg += ` or ${instances[1]}`
207+
}
208+
209+
if (other.length > 0) msg += ' or '
210+
}
211+
212+
// add other expected types to error message
213+
if (other.length > 0) {
214+
if (other.length > 2) {
215+
/**
216+
* Last other type name in {@linkcode other}.
217+
*
218+
* @const {string} last_other
219+
*/
220+
const last_other: string = other.pop()!
221+
222+
msg += `one of ${other.join(', ')}, or ${last_other}`
223+
} else if (other.length === 2) {
224+
msg += `one of ${other[0]} or ${other[1]}`
225+
} else {
226+
if (other[0]?.toLowerCase() !== other[0]) msg += 'an '
227+
msg += `${other[0]}`
228+
}
229+
}
230+
231+
// end error message
232+
msg += `. Received ${determineSpecificType(actual)}`
233+
234+
return msg
235+
}
236+
237+
/**
238+
* [`ERR_INVALID_ARG_TYPE`][1] model.
239+
*
240+
* Thrown when an argument of the wrong type was passed to a Node.js API.
241+
*
242+
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
243+
*
244+
* @class
245+
* @implements {NodeError<TypeError>}
246+
*/
247+
const ERR_INVALID_ARG_TYPE: NodeErrorConstructor<
248+
TypeErrorConstructor,
249+
typeof msg
250+
> = createNodeError(ErrorCode.ERR_INVALID_ARG_TYPE, TypeError, msg)
251+
252+
/**
253+
* {@linkcode ERR_INVALID_ARG_TYPE} instance.
254+
*
255+
* @const {NodeError<TypeError>} err
256+
*/
257+
const err: NodeError<TypeError> = new ERR_INVALID_ARG_TYPE(
258+
'path',
259+
'string',
260+
null
261+
)
262+
263+
console.error(err)
264+
console.debug('err instanceof TypeError:', err instanceof TypeError)
265+
```
266+
267+
...running that yields:
268+
269+
```zsh
270+
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received null
271+
at new NodeError (file:////home/runner/work/errnode/errnode/src/create-node-error.ts:94:5)
272+
at file:////home/runner/work/errnode/errnode/scratch.ts:201:35
273+
at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
274+
at async Promise.all (index 0)
275+
at async ESMLoader.import (node:internal/modules/esm/loader:533:24)
276+
at async loadESM (node:internal/process/esm_loader:91:5)
277+
at async handleMainPromise (node:internal/modules/run_main:65:12)
278+
err instanceof TypeError: true
279+
```
62280

63281
## API
64282

0 commit comments

Comments
 (0)