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

Refactor, inner infrastructure updates #1

Merged
merged 6 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions .eslintrc.js

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
coverage/
dist/
node_modules/
reports/

.npmrc
11 changes: 11 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
coverage:
status:
patch:
default:
target: 90%
threshold: 5%

project:
default:
target: 90%
threshold: 1%
34 changes: 34 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import globals from 'globals'
import pluginJs from '@eslint/js'
import pluginTs from 'typescript-eslint'

export default [
{ files: ['**/*.{js,mjs,cjs,ts,mts}'] },
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
pluginJs.configs.recommended,
...pluginTs.configs.recommended,
{
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
'comma-dangle': ['error', {
arrays: 'always-multiline',
exports: 'always-multiline',
functions: 'never',
imports: 'always-multiline',
objects: 'always-multiline',
}],
'no-debugger': 'error',
'object-curly-spacing': ['error', 'always'],
'quotes': ['error', 'single'],
'semi': ['error', 'never'],
},
},
]
9 changes: 0 additions & 9 deletions jest.config.ts

This file was deleted.

24 changes: 14 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,39 @@
"types": "types/index.d.ts",
"scripts": {
"build": "rollup --config rollup.config.ts --configPlugin typescript",
"lint": "eslint --ext .js,.mjs,.ts src tests types",
"lint": "eslint src tests types",
"prepare": "husky",
"release": "standard-version",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch",
"release:major": "standard-version --release-as major",
"stats": "gzip -c ./dist/index.mjs | wc -c",
"test": "jest --config jest.config.ts",
"test:coverage": "jest --config jest.config.ts --coverage --coverageReporters=lcov"
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:coverage:html": "vitest run --coverage --reporter=html --outputFile.html=./reports/html/report.html"
},
"devDependencies": {
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@jest/types": "^29.6.3",
"@eslint/js": "^9.13.0",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-typescript": "^11.1.6",
"@types/node": "^18.15 || ^20.11",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"eslint": "^8.56.0",
"@vitest/coverage-istanbul": "2.1.3",
"@vitest/ui": "2.1.3",
"eslint": "^9.13.0",
"globals": "^15.11.0",
"husky": "^9.0.10",
"jest": "^29.7.0",
"rollup": "^4.9.6",
"rollup-plugin-delete": "^2.0.0",
"standard-version": "^9.5.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3"
"typescript": "^5.3.3",
"typescript-eslint": "^8.10.0",
"vite": "^5.4.9",
"vite-plugin-dts": "^4.2.4",
"vitest": "^2.1.3"
},
"publishConfig": {
"access": "public"
Expand Down
15 changes: 8 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type {
Constraint,
ConstraintViolation,
Key,
Provider,
Recursive,
Validator,
Expand All @@ -18,14 +17,16 @@
import {
arraify,
flatten,
isRecord,
} from '@/utils'

import isArray from '@/predicates/isArray'
import isRecord from '@/predicates/isRecord'

const validateAsynchronously = async <Value> (
provider: Provider,
value: Value,
constraints: Constraint<Value> | Constraint<Value>[],
path: Key[] = []
path: PropertyKey[] = []

Check warning on line 29 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L29

Added line #L29 was not covered by tests
): Promise<ConstraintViolation[]> => {
const validations: Promise<ConstraintViolation[]>[] = []

Expand All @@ -42,7 +43,7 @@
}

if (c instanceof Each) {
if (Array.isArray(value)) {
if (isArray(value)) {
value.forEach((value, index) => {
validations.push(validateAsynchronously(provider, value, c.constraints, [...path, index]))
})
Expand Down Expand Up @@ -91,7 +92,7 @@
provider: Provider,
value: Value,
constraints: Constraint<Value> | Constraint<Value>[],
path: Key[] = []
path: PropertyKey[] = []

Check warning on line 95 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L95

Added line #L95 was not covered by tests
): ConstraintViolation[] => {
const violations: Recursive<ConstraintViolation>[] = []

Expand All @@ -108,7 +109,7 @@
}

if (c instanceof Each) {
if (Array.isArray(value)) {
if (isArray(value)) {
value.forEach((value, index) => {
violations.push(...validateSynchronously(provider, value, c.constraints, [...path, index]))
})
Expand Down Expand Up @@ -149,7 +150,7 @@
provider: Provider,
value: Value,
constraints: Constraint<Value> | Constraint<Value>[],
path: Key[] = [],
path: PropertyKey[] = [],

Check warning on line 153 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L153

Added line #L153 was not covered by tests
asynchronously: Asynchronously = true as Asynchronously
): MaybePromise<ConstraintViolation[], Asynchronously> => {
return asynchronously
Expand Down
7 changes: 7 additions & 0 deletions src/predicates/hasProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import isNull from './isNull'
import isObject from './isObject'

/** Checks if a value has a property */
export default (value: unknown, key: PropertyKey): boolean => {
return isObject(value) && !isNull(value) && Object.prototype.hasOwnProperty.call(value, key)
}
2 changes: 2 additions & 0 deletions src/predicates/isArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Checks if a value is an array */
export default (value: unknown): value is unknown[] => Array.isArray(value)
6 changes: 6 additions & 0 deletions src/predicates/isEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import isString from '@/predicates/isString'

const pattern = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i

/** Checks if a value is an email */
export default (value: unknown): value is string => isString(value) && pattern.test(value)
2 changes: 2 additions & 0 deletions src/predicates/isNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Checks if value is null */
export default (value: unknown): value is null => value === null
2 changes: 2 additions & 0 deletions src/predicates/isNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Checks if a value is a number */
export default (value: unknown): value is number => typeof value === 'number' && !isNaN(value)
11 changes: 11 additions & 0 deletions src/predicates/isNumeric.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import isNumber from '@/predicates/isNumber'
import isString from '@/predicates/isString'

/** Checks if a value is a number or a numeric string */
export default (value: unknown, integer = false): value is number | string => {
const valid = (value: number) => {
return !isNaN(value) && (!integer || Number.isInteger(value))
}

return (isNumber(value) || isString(value)) && valid(Number(value))
}
2 changes: 2 additions & 0 deletions src/predicates/isObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Checks if a value is an object */
export default (value: unknown): value is object => typeof value === 'object'
12 changes: 12 additions & 0 deletions src/predicates/isRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import isNull from './isNull'
import isObject from './isObject'

const prototypeOf = (value: object) => Object.getPrototypeOf(value)
const constructorOf = (value: object): unknown => {
return prototypeOf(value).constructor
}

/** Check if a value is a record like Record<PropertyKey, unknown> */
export default (value: unknown): value is Record<PropertyKey, unknown> => {
return isObject(value) && !isNull(value) && constructorOf(value) === Object && Object.keys(prototypeOf(value)).length === 0
}
2 changes: 2 additions & 0 deletions src/predicates/isString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Checks if a value is a string */
export default (value: unknown): value is string => typeof value === 'string'
2 changes: 2 additions & 0 deletions src/predicates/isUndefined.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Checks if value is undefined */
export default (value: unknown): value is undefined => value === undefined
8 changes: 0 additions & 8 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,4 @@ export const flatten = <T>(recursive: Recursive<T>[]): T[] => {
})

return flattened
}

const constructorOf = (value: object): unknown => {
return Object.getPrototypeOf(value).constructor
}

export const isRecord = (value: object): boolean => {
return constructorOf(value) === Object && Object.keys(Object.getPrototypeOf(value)).length === 0
}
8 changes: 5 additions & 3 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
describe,
expect,
test,
} from '@jest/globals'
} from 'vitest'

import Collection from '@/constraints/Collection'
import Each from '@/constraints/Each'
Expand All @@ -23,6 +23,8 @@ import {
createValidator,
} from '@/index'

import isEmail from '@/predicates/isEmail'

describe('validates synchronously', () => {
const validator = createValidator()

Expand Down Expand Up @@ -277,7 +279,7 @@ describe('override', () => {
}

validate (value: unknown, path?: Key[]): ConstraintViolation | null {
if (!(typeof value === 'string') || !/\S+@\S+\.\S+/.test(value)) {
if (!isEmail(value)) {
return this._constraint.toViolation(value, path)
}

Expand All @@ -293,7 +295,7 @@ describe('override', () => {
}

validate (value: unknown, path?: Key[]): Promise<ConstraintViolation | null> {
if (!(typeof value === 'string') || !/\S+@\S+\.\S+/.test(value)) {
if (!isEmail(value)) {
return Promise.resolve(this._constraint.toViolation(value, path))
}

Expand Down
26 changes: 26 additions & 0 deletions tests/predicates/hasProperty.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
describe,
expect,
test,
} from 'vitest'

import hasProperty from '@/predicates/hasProperty'

const property = Symbol('property')

describe('predicates/hasProperty', () => {
test('returns true when property exists', () => {
expect(hasProperty({ a: 1 }, 'a')).toBe(true)
expect(hasProperty({ 0: 1 }, 0)).toBe(true)
expect(hasProperty({ [property]: 1 }, property)).toBe(true)
expect(hasProperty([], 'length')).toBe(true)
})

test('returns false when property does not exist', () => {
expect(hasProperty({}, 'a')).toBe(false)
expect(hasProperty({}, 0)).toBe(false)
expect(hasProperty({}, property)).toBe(false)
expect(hasProperty(null, property)).toBe(false)
expect(hasProperty([], 'does_not_exists')).toBe(false)
})
})
19 changes: 19 additions & 0 deletions tests/predicates/isNull.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
describe,
expect,
test,
} from 'vitest'

import isNull from '@/predicates/isNull'

describe('predicates/isNull', () => {
test('returns true when a value is equal to undefined', () => {
expect(isNull(null)).toBe(true)
})

test('returns false when a value is not equal to undefined', () => {
expect(isNull({})).toBe(false)
expect(isNull(2)).toBe(false)
expect(isNull(Symbol('2'))).toBe(false)
})
})
20 changes: 20 additions & 0 deletions tests/predicates/isNumber.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {
describe,
expect,
test,
} from 'vitest'

import isNumber from '@/predicates/isNumber'

describe('predicates/isNumber', () => {
test('returns true when a value\'s type is a number', () => {
expect(isNumber(1)).toBe(true)
expect(isNumber(1.5)).toBe(true)
})

test('returns false when a value\'s type is not a number', () => {
expect(isNumber({})).toBe(false)
expect(isNumber('1')).toBe(false)
expect(isNumber('1.5')).toBe(false)
})
})
Loading