diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c0696500..3613734f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -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: { diff --git a/.vscode/settings.json b/.vscode/settings.json index 9257dfa4..06f5e8bc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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, diff --git a/package.json b/package.json index da9f0b15..2774617a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/internal/__tests__/err-invalid-arg-type.spec.ts b/src/internal/__tests__/err-invalid-arg-type.spec.ts index d896339f..241511bf 100644 --- a/src/internal/__tests__/err-invalid-arg-type.spec.ts +++ b/src/internal/__tests__/err-invalid-arg-type.spec.ts @@ -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 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) }) }) diff --git a/src/internal/__tests__/node-error.spec.ts b/src/internal/__tests__/node-error.spec.ts deleted file mode 100644 index 8dd9c142..00000000 --- a/src/internal/__tests__/node-error.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file Units Tests - NodeError - * @module pathe/internal/tests/unit/NodeError - */ - -import TestSubject from '../node-error' - -describe('unit:internal/NodeError', () => { - describe('constructor', () => { - let code: string - let name: string - let subject: TestSubject - - beforeEach(() => { - code = 'ERR_INVALID_ARG_TYPE' - name = 'TypeError' - subject = new TestSubject(name, code) - }) - - it('should override error name', () => { - expect(subject).to.have.property('name').equal(`${name} [${code}]`) - }) - - it('should set error code', () => { - expect(subject).to.have.property('code').equal(code) - }) - }) -}) diff --git a/src/internal/__tests__/validate-object.spec.ts b/src/internal/__tests__/validate-object.spec.ts index abc8a7dd..38fe6310 100644 --- a/src/internal/__tests__/validate-object.spec.ts +++ b/src/internal/__tests__/validate-object.spec.ts @@ -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', () => { @@ -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[0][] = [ faker.datatype.array(), faker.datatype.bigInt(), @@ -38,7 +39,7 @@ describe('unit:internal/validateObject', () => { // Act + Expect cases.forEach(value => { - let error: ERR_INVALID_ARG_TYPE + let error: NodeError try { testSubject(value, name) @@ -46,7 +47,8 @@ describe('unit:internal/validateObject', () => { 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) }) }) }) diff --git a/src/internal/__tests__/validate-string.spec.ts b/src/internal/__tests__/validate-string.spec.ts index 036cb801..bc91c43f 100644 --- a/src/internal/__tests__/validate-string.spec.ts +++ b/src/internal/__tests__/validate-string.spec.ts @@ -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', () => { @@ -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 // Act try { @@ -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) }) }) diff --git a/src/internal/err-invalid-arg-type.ts b/src/internal/err-invalid-arg-type.ts index b4b061a3..7d3f90ed 100644 --- a/src/internal/err-invalid-arg-type.ts +++ b/src/internal/err-invalid-arg-type.ts @@ -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} + */ +const ERR_INVALID_ARG_TYPE: NodeErrorConstructor< + TypeErrorConstructor, + typeof msg +> = createNodeError(ErrorCode.ERR_INVALID_ARG_TYPE, TypeError, msg) + export default ERR_INVALID_ARG_TYPE diff --git a/src/internal/node-error.ts b/src/internal/node-error.ts deleted file mode 100644 index adbd8c93..00000000 --- a/src/internal/node-error.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @file Internal - NodeError - * @module pathe/internal/NodeError - */ - -/** - * Node.js error model. - * - * @class - * @extends {Error} - */ -class NodeError extends Error { - /** - * Error code. - * - * @see https://nodejs.org/api/errors.html#nodejs-error-codes - * - * @public - * @readonly - * @member {string} code - */ - public readonly code: string - - /** - * @public - * @override - * @member {string} name - Error name - */ - public override name: string - - /** - * Creates a new Node.js error. - * - * @param {string} name - Error class name - * @param {string} code - Error code - * @param {string} [message] - Error message - */ - constructor(name: string, code: string, message?: string) { - super(message) - this.code = code - this.name = `${name} [${this.code}]` - Error.captureStackTrace(this) - } -} - -export default NodeError diff --git a/src/internal/validate-object.ts b/src/internal/validate-object.ts index 783dca4f..c614c87c 100644 --- a/src/internal/validate-object.ts +++ b/src/internal/validate-object.ts @@ -8,16 +8,14 @@ import ERR_INVALID_ARG_TYPE from './err-invalid-arg-type' /** * Checks if `value` is an object. * - * Throws {@linkcode ERR_INVALID_ARG_TYPE} if `value` is not an object. - * * **Note**: Array values are not considered objects. * * [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type * - * @param {unknown} value - Possible object value - * @param {string} name - `value` label + * @param {unknown} value - Value provided by user + * @param {string} name - Name of invalid argument * @return {boolean} `true` if `value` is an object - * @throws {ERR_INVALID_ARG_TYPE} + * @throws {TypeError} If `value` is not an object */ const validateObject = (value: unknown, name: string): boolean => { if (typeof value === 'object' && !Array.isArray(value) && value !== null) { diff --git a/src/internal/validate-string.ts b/src/internal/validate-string.ts index ac5a8b1d..873fed6d 100644 --- a/src/internal/validate-string.ts +++ b/src/internal/validate-string.ts @@ -8,14 +8,12 @@ import ERR_INVALID_ARG_TYPE from './err-invalid-arg-type' /** * Checks if `value` is a string. * - * Throws {@linkcode ERR_INVALID_ARG_TYPE} if `value` is not a string. - * * [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type * - * @param {unknown} value - Possible string value - * @param {string} name - `value` label + * @param {unknown} value - Value provided by user + * @param {string} name - Name of invalid argument * @return {string} `value` if `value` is a string - * @throws {ERR_INVALID_ARG_TYPE} + * @throws {TypeError} If `value` is not an object */ const validateString = (value: unknown, name: string): string => { if (typeof value === 'string') return value diff --git a/yarn.lock b/yarn.lock index b5b15ddc..2a6ed2f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -972,6 +972,16 @@ __metadata: languageName: node linkType: hard +"@flex-development/errnode@npm:1.0.1": + version: 1.0.1 + resolution: "@flex-development/errnode@npm:1.0.1::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Ferrnode%2F1.0.1%2F3314864efc1693b9103051546d4e4e16a21267b2" + dependencies: + "@flex-development/tutils": "npm:6.0.0-alpha.7" + node-inspect-extracted: "npm:2.0.0" + checksum: 41af0fa70b37d9c8e53b652f63c96183145134d6caf694d04b85afc18c58693263e05b10c89ef38e6d7093bf83fe5e8b8ca250d247d2ff68ceff2df876336242 + languageName: node + linkType: hard + "@flex-development/is-builtin@npm:1.0.1": version: 1.0.1 resolution: "@flex-development/is-builtin@npm:1.0.1::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Fis-builtin%2F1.0.1%2Fa67edcfa3b5642f100fa7a1647f62e78641ca015" @@ -1029,6 +1039,7 @@ __metadata: "@commitlint/cli": "npm:17.3.0" "@commitlint/config-conventional": "npm:17.3.0" "@faker-js/faker": "npm:7.6.0" + "@flex-development/errnode": "npm:1.0.1" "@flex-development/mkbuild": "npm:1.0.0-alpha.9" "@flex-development/tutils": "npm:6.0.0-alpha.7" "@graphql-eslint/eslint-plugin": "npm:3.14.3" @@ -1074,7 +1085,6 @@ __metadata: is-ci: "npm:3.0.1" jsonc-eslint-parser: "npm:2.1.0" lint-staged: "npm:13.1.0" - node-inspect-extracted: "npm:2.0.0" node-notifier: "npm:10.0.1" prettier: "npm:2.8.1" prettier-plugin-sh: "npm:0.12.8"