Skip to content

Commit

Permalink
refactor(internal): ERR_INVALID_ARG_TYPE model
Browse files Browse the repository at this point in the history
- uses `errnode` to create errors consistent with node.js api
- ensures errors are `TypeError` instances
- https://github.com/flex-development/errnode

Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Dec 29, 2022
1 parent e7b0f99 commit 43bc3ba
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 272 deletions.
9 changes: 0 additions & 9 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@ const config = {
extends: ['./.eslintrc.base.cjs'],
overrides: [
...require('./.eslintrc.base.cjs').overrides,
{
files: [
'./src/internal/err-invalid-arg-type.ts',
'./src/internal/node-error.ts'
],
rules: {
'unicorn/custom-error-definition': 0
}
},
{
files: ['./src/lib/format.ts', './src/lib/resolve.ts'],
rules: {
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.detectIndentation": false,
"editor.renderFinalNewline": false,
"editor.renderFinalNewline": "off",
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnType": true,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@
"typecheck:watch": "vitest typecheck"
},
"dependencies": {
"@flex-development/tutils": "6.0.0-alpha.7",
"node-inspect-extracted": "2.0.0"
"@flex-development/errnode": "1.0.1",
"@flex-development/tutils": "6.0.0-alpha.7"
},
"devDependencies": {
"@commitlint/cli": "17.3.0",
Expand Down
118 changes: 17 additions & 101 deletions src/internal/__tests__/err-invalid-arg-type.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,118 +3,34 @@
* @module pathe/internal/tests/unit/ERR_INVALID_ARG_TYPE
*/

import { inspect } from 'node-inspect-extracted'
import { ErrorCode, type NodeError } from '@flex-development/errnode'
import TestSubject from '../err-invalid-arg-type'

describe('unit:internal/ERR_INVALID_ARG_TYPE', () => {
let code: string
let subject: TestSubject
let result: NodeError<TypeError>

beforeEach(() => {
code = 'ERR_INVALID_ARG_TYPE'
subject = new TestSubject('path', 'string', null)
result = new TestSubject('path', 'string', null)
})

describe('constructor', () => {
it('should set error code', () => {
expect(subject).to.have.property('code').equal(code)
})

it('should set error message', () => {
expect(subject)
.to.have.property('message')
.match(/^The "path" argument must be of type string\. Received .+?$/s)
})

it('should set error name', () => {
expect(subject).to.have.property('name').equal(`TypeError [${code}]`)
})
it('should return TypeError instance', () => {
expect(result).to.be.instanceof(TypeError)
})

describe('#determineSpecificType', () => {
it('should detect bigint', () => {
// Arrange
const value: bigint = faker.datatype.bigInt(13)
const expected: string = `type bigint (${inspect(value, {
colors: false
})})`

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})

it('should detect boolean', () => {
// Arrange
const value: unknown = faker.datatype.boolean()
const expected: string = `type boolean (${value})`

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})

it('should detect function', () => {
// Arrange
const value: unknown = vi.fn()
const expected: string = 'function spy'

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})

it('should detect instance object', () => {
// Arrange
const value: unknown = faker.datatype.datetime()
const expected: string = 'an instance of Date'
it('should set error code', () => {
// Arrange
const code: ErrorCode = ErrorCode.ERR_INVALID_ARG_TYPE

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})

it('should detect null', () => {
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(null)).to.equal('null')
})

it('should detect number', () => {
// Arrange
const value: unknown = faker.datatype.number(13)
const expected: string = `type number (${value})`

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})

it('should detect string', () => {
// Arrange
const value: unknown = faker.datatype.string(30)
const inspected: string = inspect(value, { colors: false })
const expected: string = `type string (${inspected.slice(0, 25)}...)`

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})

it('should detect symbol', () => {
// Arrange
const value: unknown = Symbol('pathe')
const expected: string = `type symbol (${inspect(value, {
colors: false
})})`
// Expect
expect(result).to.have.property('code').equal(code)
})

// Act + Expect
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(value)).to.equal(expected)
})
it('should set error message', () => {
// Arrange
const message: string =
'The "path" argument must be of type string. Received null'

it('should detect undefined', () => {
// @ts-expect-error ts(2445)
expect(subject.determineSpecificType(undefined)).to.equal('undefined')
})
// Expect
expect(result).to.have.property('message').equal(message)
})
})
28 changes: 0 additions & 28 deletions src/internal/__tests__/node-error.spec.ts

This file was deleted.

8 changes: 5 additions & 3 deletions src/internal/__tests__/validate-object.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @module pathe/internal/tests/unit/validateObject
*/

import ERR_INVALID_ARG_TYPE from '../err-invalid-arg-type'
import { ErrorCode, type NodeError } from '@flex-development/errnode'
import testSubject from '../validate-object'

describe('unit:internal/validateObject', () => {
Expand All @@ -27,6 +27,7 @@ describe('unit:internal/validateObject', () => {

it('should throw if value is not an object', () => {
// Arrange
const code: ErrorCode = ErrorCode.ERR_INVALID_ARG_TYPE
const cases: Parameters<typeof testSubject>[0][] = [
faker.datatype.array(),
faker.datatype.bigInt(),
Expand All @@ -38,15 +39,16 @@ describe('unit:internal/validateObject', () => {

// Act + Expect
cases.forEach(value => {
let error: ERR_INVALID_ARG_TYPE
let error: NodeError<TypeError>

try {
testSubject(value, name)
} catch (e: unknown) {
error = e as typeof error
}

expect(error!).to.be.instanceOf(ERR_INVALID_ARG_TYPE)
expect(error!).to.be.instanceof(TypeError)
expect(error!).to.have.property('code').equal(code)
})
})
})
8 changes: 5 additions & 3 deletions src/internal/__tests__/validate-string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @module pathe/internal/tests/unit/validateString
*/

import ERR_INVALID_ARG_TYPE from '../err-invalid-arg-type'
import { ErrorCode, type NodeError } from '@flex-development/errnode'
import testSubject from '../validate-string'

describe('unit:internal/validateString', () => {
Expand All @@ -19,7 +19,8 @@ describe('unit:internal/validateString', () => {

it('should throw if value is not typeof string', () => {
// Arrange
let error: ERR_INVALID_ARG_TYPE
const code: ErrorCode = ErrorCode.ERR_INVALID_ARG_TYPE
let error: NodeError<TypeError>

// Act
try {
Expand All @@ -29,6 +30,7 @@ describe('unit:internal/validateString', () => {
}

// Expect
expect(error!).to.be.instanceOf(ERR_INVALID_ARG_TYPE)
expect(error!).to.be.instanceof(TypeError)
expect(error!).to.have.property('code').equal(code)
})
})
106 changes: 38 additions & 68 deletions src/internal/err-invalid-arg-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,49 @@
* @module pathe/internal/ERR_INVALID_ARG_TYPE
*/

import { inspect } from 'node-inspect-extracted'
import NodeError from './node-error'
import {
createNodeError,
determineSpecificType,
ErrorCode,
type MessageFn,
type NodeError,
type NodeErrorConstructor
} from '@flex-development/errnode'

/**
* Invalid argument type error model.
* Creates an [`ERR_INVALID_ARG_TYPE`][1] message.
*
* @see https://nodejs.org/api/errors.html#err_invalid_arg_type
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
*
* @class
* @extends {NodeError}
* @param {string} name - Name of invalid argument or property
* @param {string} expected - Expected type
* @param {unknown} actual - Value supplied by user
* @return {string} Error message
*/
class ERR_INVALID_ARG_TYPE extends NodeError {
/**
* Creates an [invalid argument type error][1].
*
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
*
* @param {string} name - Name of invalid argument
* @param {string} type - Expected argument type
* @param {unknown} value - Value passed by user
*/
constructor(name: string, type: string, value: unknown) {
super('TypeError', 'ERR_INVALID_ARG_TYPE')

this.message = `The "${name}" argument must be of type ${type}.`
this.message += ` Received ${this.determineSpecificType(value)}`
}

/**
* Determines the specific type of a value.
*
* @protected
*
* @param {unknown} value - Value to detect type of
* @return {string} Specific type of `value`
*/
protected determineSpecificType(value: unknown): string {
/**
* Specific type of `value`.
*
* @var {string} type
*/
let type: string = ''

switch (true) {
case typeof value === 'function':
type = `function ${(value as FunctionConstructor).name}`
break
case typeof value === 'object':
type = value?.constructor?.name
? `an instance of ${value.constructor.name}`
: inspect(value, { depth: -1 })
break
case typeof value === 'undefined':
type = typeof value
break
default:
/**
* String representation of {@linkcode value}.
*
* @var {string} inspected
*/
let inspected: string = inspect(value, { colors: false })

// trim string representation of value
if (inspected.length > 28) inspected = inspected.slice(0, 25) + '...'

type = `type ${typeof value} (${inspected})`
break
}

return type
}
const msg: MessageFn<[string, string, unknown]> = (
name: string,
expected: string,
actual: unknown
): string => {
return [
`The "${name}" argument must be of type ${expected}.`,
`Received ${determineSpecificType(actual)}`
].join(' ')
}

/**
* [`ERR_INVALID_ARG_TYPE`][1] model.
*
* Thrown when an argument of the wrong type was passed to a Node.js API.
*
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
*
* @class
* @implements {NodeError<TypeError>}
*/
const ERR_INVALID_ARG_TYPE: NodeErrorConstructor<
TypeErrorConstructor,
typeof msg
> = createNodeError(ErrorCode.ERR_INVALID_ARG_TYPE, TypeError, msg)

export default ERR_INVALID_ARG_TYPE
Loading

0 comments on commit 43bc3ba

Please sign in to comment.