io-ts-reporters
Advanced tools
Comparing version 1.2.2 to 2.0.0-rc1
{ | ||
"name": "io-ts-reporters", | ||
"version": "1.2.2", | ||
"version": "2.0.0-rc1", | ||
"description": "Formatting of io-ts validation errors", | ||
@@ -9,2 +9,4 @@ "main": "./target/src/index.js", | ||
"scripts": { | ||
"fmt": "prettier --write '**/*.{json,md,ts,js}'", | ||
"fmt:check": "prettier --check '**/*.{json,md,ts,js}'", | ||
"docs": "yarn docs-ts", | ||
@@ -15,3 +17,3 @@ "lint": "npm run typecheck && npm run lint:only", | ||
"compile": "rm -rf ./target/* && tsc", | ||
"test": "npm run lint && npm run test:unit && yarn docs", | ||
"test": "npm run fmt:check && npm run lint && npm run test:unit && yarn docs", | ||
"test:unit": "ava", | ||
@@ -35,15 +37,19 @@ "prepublishOnly": "npm run compile && npm run lint" | ||
"peerDependencies": { | ||
"fp-ts": "^2.0.2", | ||
"io-ts": "^2.0.0" | ||
"fp-ts": "^2.10.5", | ||
"io-ts": "^2.2.16" | ||
}, | ||
"devDependencies": { | ||
"@types/tape": "^4.2.34", | ||
"@typescript-eslint/eslint-plugin": "^4.28.3", | ||
"@typescript-eslint/parser": "^4.28.3", | ||
"ava": "^3.8.2", | ||
"docs-ts": "^0.5.1", | ||
"fp-ts": "^2.0.2", | ||
"io-ts": "^2.0.0", | ||
"io-ts-types": "^0.5.6", | ||
"ts-node": "^8.6.2", | ||
"typescript": "^3.5.3", | ||
"xo": "^0.30.0" | ||
"docs-ts": "^0.6.10", | ||
"eslint-config-xo-typescript": "^0.43.0", | ||
"fp-ts": "^2.10.5", | ||
"io-ts": "^2.2.16", | ||
"io-ts-types": "^0.5.16", | ||
"prettier": "^2.3.2", | ||
"ts-node": "^10.1.0", | ||
"typescript": "^4.3.5", | ||
"xo": "^0.41.0" | ||
}, | ||
@@ -50,0 +56,0 @@ "tags": [ |
@@ -6,4 +6,4 @@ # io-ts-reporters | ||
Currently this package only includes one reporter. The output is an array of | ||
strings in the format of: | ||
Currently this package only includes one reporter. The output is an array of strings in the format | ||
of: | ||
@@ -34,30 +34,30 @@ ``` | ||
```ts | ||
import * as t from 'io-ts'; | ||
import reporter from 'io-ts-reporters'; | ||
import * as t from 'io-ts' | ||
import reporter from 'io-ts-reporters' | ||
const User = t.interface({ name: t.string }); | ||
const User = t.interface({name: t.string}) | ||
// When decoding fails, the errors are reported | ||
reporter.report(User.decode({ nam: 'Jane' })); | ||
reporter.report(User.decode({nam: 'Jane'})) | ||
//=> ['Expecting string at name but instead got: undefined'] | ||
// Nothing gets reported on success | ||
reporter.report(User.decode({ name: 'Jane' })); | ||
reporter.report(User.decode({name: 'Jane'})) | ||
//=> [] | ||
``` | ||
To only format the validation errors in case the validation failed (ie. | ||
`mapLeft`) use `formatValidationErrors` instead. | ||
To only format the validation errors in case the validation failed (ie. `mapLeft`) use | ||
`formatValidationErrors` instead. | ||
```ts | ||
import * as t from 'io-ts'; | ||
import { formatValidationErrors } from 'io-ts-reporters'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { pipe } from 'fp-ts/lib/pipeable'; | ||
import * as t from 'io-ts' | ||
import {formatValidationErrors} from 'io-ts-reporters' | ||
import * as E from 'fp-ts/Either' | ||
import {pipe} from 'fp-ts/pipeable' | ||
const User = t.interface({ name: t.string }); | ||
const User = t.interface({name: t.string}) | ||
const result = User.decode({ nam: 'Jane' }); // Either<t.Errors, User> | ||
const result = User.decode({nam: 'Jane'}) // Either<t.Errors, User> | ||
E.mapLeft(formatValidationErrors)(result); // Either<string[], User> | ||
E.mapLeft(formatValidationErrors)(result) // Either<string[], User> | ||
``` | ||
@@ -64,0 +64,0 @@ |
138
src/index.ts
@@ -19,17 +19,16 @@ /** | ||
*/ | ||
import * as A from 'fp-ts/lib/Array'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import * as NEA from 'fp-ts/lib/NonEmptyArray'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import * as R from 'fp-ts/lib/Record'; | ||
import { pipe } from 'fp-ts/lib/pipeable'; | ||
import * as t from 'io-ts'; | ||
import { Reporter } from 'io-ts/lib/Reporter'; | ||
import * as A from 'fp-ts/Array' | ||
import * as E from 'fp-ts/Either' | ||
import * as NEA from 'fp-ts/NonEmptyArray' | ||
import * as O from 'fp-ts/Option' | ||
import * as R from 'fp-ts/Record' | ||
import {pipe} from 'fp-ts/pipeable' | ||
import * as t from 'io-ts' | ||
import {Reporter} from 'io-ts/lib/Reporter' | ||
import { takeUntil } from './utils'; | ||
import {takeUntil} from './utils' | ||
const isUnionType = ({ type }: t.ContextEntry) => type instanceof t.UnionType; | ||
const isUnionType = ({type}: t.ContextEntry) => type instanceof t.UnionType | ||
const jsToString = (value: t.mixed) => | ||
value === undefined ? 'undefined' : JSON.stringify(value); | ||
const jsToString = (value: t.mixed) => (value === undefined ? 'undefined' : JSON.stringify(value)) | ||
@@ -40,5 +39,5 @@ const keyPath = (ctx: t.Context) => | ||
ctx | ||
.map(c => c.key) | ||
.map((c) => c.key) | ||
.filter(Boolean) | ||
.join('.'); | ||
.join('.') | ||
@@ -48,7 +47,7 @@ // The actual error is last in context | ||
// https://github.com/gcanti/fp-ts/pull/544/files | ||
A.last(validation.context as t.ContextEntry[]); | ||
A.last(validation.context as t.ContextEntry[]) | ||
const getValidationContext = (validation: t.ValidationError) => | ||
// https://github.com/gcanti/fp-ts/pull/544/files | ||
validation.context as t.ContextEntry[]; | ||
validation.context as t.ContextEntry[] | ||
@@ -59,12 +58,12 @@ /** | ||
*/ | ||
export const TYPE_MAX_LEN = 160; // Two lines of 80-col text | ||
export const TYPE_MAX_LEN = 160 // Two lines of 80-col text | ||
const truncateType = (type: string, options: ReporterOptions = {}): string => { | ||
const { truncateLongTypes = true } = options; | ||
const {truncateLongTypes = true} = options | ||
if (truncateLongTypes && type.length > TYPE_MAX_LEN) { | ||
return `${type.slice(0, TYPE_MAX_LEN - 3)}...`; | ||
return `${type.slice(0, TYPE_MAX_LEN - 3)}...` | ||
} | ||
return type; | ||
}; | ||
return type | ||
} | ||
@@ -75,3 +74,3 @@ const errorMessageSimple = ( | ||
error: t.ValidationError, | ||
options?: ReporterOptions | ||
options?: ReporterOptions, | ||
) => | ||
@@ -83,6 +82,6 @@ // https://github.com/elm-lang/core/blob/18c9e84e975ed22649888bfad15d1efdb0128ab2/src/Native/Json.js#L199 | ||
`but instead got: ${jsToString(error.value)}`, | ||
error.message ? `(${error.message})` : '' | ||
error.message ? `(${error.message})` : '', | ||
] | ||
.filter(Boolean) | ||
.join(' '); | ||
.join(' ') | ||
@@ -93,3 +92,3 @@ const errorMessageUnion = ( | ||
value: unknown, | ||
options?: ReporterOptions | ||
options?: ReporterOptions, | ||
) => | ||
@@ -99,8 +98,8 @@ // https://github.com/elm-lang/core/blob/18c9e84e975ed22649888bfad15d1efdb0128ab2/src/Native/Json.js#L199 | ||
'Expecting one of:\n', | ||
expectedTypes.map(type => ` ${truncateType(type, options)}`).join('\n'), | ||
expectedTypes.map((type) => ` ${truncateType(type, options)}`).join('\n'), | ||
path === '' ? '\n' : `\nat ${path} `, | ||
`but instead got: ${jsToString(value)}` | ||
`but instead got: ${jsToString(value)}`, | ||
] | ||
.filter(Boolean) | ||
.join(''); | ||
.join('') | ||
@@ -113,4 +112,4 @@ // Find the union type in the list of ContextEntry | ||
A.findIndex(isUnionType), | ||
O.chain(n => A.lookup(n + 1, ctx)) | ||
); | ||
O.chain((n) => A.lookup(n + 1, ctx)), | ||
) | ||
@@ -120,3 +119,3 @@ const formatValidationErrorOfUnion = ( | ||
errors: NEA.NonEmptyArray<t.ValidationError>, | ||
options?: ReporterOptions | ||
options?: ReporterOptions, | ||
) => { | ||
@@ -127,4 +126,4 @@ const expectedTypes = pipe( | ||
A.map(findExpectedType), | ||
A.compact | ||
); | ||
A.compact, | ||
) | ||
@@ -134,12 +133,10 @@ const value = pipe( | ||
A.head, | ||
O.map(v => v.actual), | ||
O.getOrElse((): unknown => undefined) | ||
); | ||
O.map((v) => v.actual), | ||
O.getOrElse((): unknown => undefined), | ||
) | ||
const expected = expectedTypes.map(({ type }) => type.name); | ||
const expected = expectedTypes.map(({type}) => type.name) | ||
return expected.length > 0 | ||
? O.some(errorMessageUnion(expected, path, value, options)) | ||
: O.none; | ||
}; | ||
return expected.length > 0 ? O.some(errorMessageUnion(expected, path, value, options)) : O.none | ||
} | ||
@@ -149,3 +146,3 @@ const formatValidationCommonError = ( | ||
error: t.ValidationError, | ||
options?: ReporterOptions | ||
options?: ReporterOptions, | ||
) => | ||
@@ -155,10 +152,8 @@ pipe( | ||
getErrorFromCtx, | ||
O.map(errorContext => | ||
errorMessageSimple(errorContext.type.name, path, error, options) | ||
) | ||
); | ||
O.map((errorContext) => errorMessageSimple(errorContext.type.name, path, error, options)), | ||
) | ||
const groupByKey = NEA.groupBy((error: t.ValidationError) => | ||
pipe(error.context, takeUntil(isUnionType), keyPath) | ||
); | ||
pipe(error.context, takeUntil(isUnionType), keyPath), | ||
) | ||
@@ -168,7 +163,7 @@ const format = ( | ||
errors: NEA.NonEmptyArray<t.ValidationError>, | ||
options?: ReporterOptions | ||
options?: ReporterOptions, | ||
) => | ||
NEA.tail(errors).length > 0 | ||
? formatValidationErrorOfUnion(path, errors, options) | ||
: formatValidationCommonError(path, NEA.head(errors), options); | ||
: formatValidationCommonError(path, NEA.head(errors), options) | ||
@@ -181,6 +176,4 @@ /** | ||
*/ | ||
export const formatValidationError = ( | ||
error: t.ValidationError, | ||
options?: ReporterOptions | ||
) => formatValidationCommonError(keyPath(error.context), error, options); | ||
export const formatValidationError = (error: t.ValidationError, options?: ReporterOptions) => | ||
formatValidationCommonError(keyPath(error.context), error, options) | ||
@@ -191,3 +184,3 @@ /** | ||
* @example | ||
* import * as E from 'fp-ts/lib/Either' | ||
* import * as E from 'fp-ts/Either' | ||
* import * as t from 'io-ts' | ||
@@ -206,6 +199,3 @@ * import { formatValidationErrors } from 'io-ts-reporters' | ||
*/ | ||
export const formatValidationErrors = ( | ||
errors: t.Errors, | ||
options?: ReporterOptions | ||
) => | ||
export const formatValidationErrors = (errors: t.Errors, options?: ReporterOptions) => | ||
pipe( | ||
@@ -217,4 +207,4 @@ errors, | ||
R.toArray, | ||
A.map(([_key, error]) => error) | ||
); | ||
A.map(([_key, error]) => error), | ||
) | ||
@@ -226,3 +216,3 @@ /** | ||
export interface ReporterOptions { | ||
truncateLongTypes?: boolean; | ||
truncateLongTypes?: boolean | ||
} | ||
@@ -237,23 +227,17 @@ | ||
*/ | ||
export const reporter = <T>( | ||
validation: t.Validation<T>, | ||
options?: ReporterOptions | ||
) => | ||
export const reporter = <T>(validation: t.Validation<T>, options?: ReporterOptions) => | ||
pipe( | ||
validation, | ||
E.mapLeft(errors => formatValidationErrors(errors, options)), | ||
E.mapLeft((errors) => formatValidationErrors(errors, options)), | ||
E.fold( | ||
errors => errors, | ||
() => [] | ||
) | ||
); | ||
(errors) => errors, | ||
() => [], | ||
), | ||
) | ||
interface PrettyReporter extends Reporter<string[]> { | ||
report: <T>( | ||
validation: t.Validation<T>, | ||
options?: ReporterOptions | ||
) => string[]; | ||
report: <T>(validation: t.Validation<T>, options?: ReporterOptions) => string[] | ||
} | ||
const prettyReporter: PrettyReporter = { report: reporter }; | ||
export default prettyReporter; | ||
const prettyReporter: PrettyReporter = {report: reporter} | ||
export default prettyReporter |
/** | ||
* @since 1.1.0 | ||
*/ | ||
import { Predicate } from 'fp-ts/lib/function'; | ||
import {Predicate} from 'fp-ts/function' | ||
@@ -10,17 +10,17 @@ /** | ||
/* eslint-disable @typescript-eslint/array-type */ | ||
export const takeUntil = <A = unknown>(predicate: Predicate<A>) => ( | ||
as: ReadonlyArray<A> | ||
): ReadonlyArray<A> => { | ||
const init = []; | ||
export const takeUntil = | ||
<A = unknown>(predicate: Predicate<A>) => | ||
(as: ReadonlyArray<A>): ReadonlyArray<A> => { | ||
const init = [] | ||
// eslint-disable-next-line unicorn/no-for-loop | ||
for (let i = 0; i < as.length; i++) { | ||
init[i] = as[i]; | ||
if (predicate(as[i])) { | ||
return init; | ||
// eslint-disable-next-line unicorn/no-for-loop | ||
for (let i = 0; i < as.length; i++) { | ||
init[i] = as[i] | ||
if (predicate(as[i])) { | ||
return init | ||
} | ||
} | ||
return init | ||
} | ||
return init; | ||
}; | ||
/* eslint-enable @typescript-eslint/array-type */ |
@@ -1,3 +0,2 @@ | ||
import * as E from 'fp-ts/lib/Either'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import * as O from 'fp-ts/Option'; | ||
import * as t from 'io-ts'; | ||
@@ -21,3 +20,3 @@ import { Reporter } from 'io-ts/lib/Reporter'; | ||
* @example | ||
* import * as E from 'fp-ts/lib/Either' | ||
* import * as E from 'fp-ts/Either' | ||
* import * as t from 'io-ts' | ||
@@ -51,3 +50,3 @@ * import { formatValidationErrors } from 'io-ts-reporters' | ||
*/ | ||
export declare const reporter: <T>(validation: E.Either<t.Errors, T>, options?: ReporterOptions | undefined) => string[]; | ||
export declare const reporter: <T>(validation: t.Validation<T>, options?: ReporterOptions | undefined) => string[]; | ||
interface PrettyReporter extends Reporter<string[]> { | ||
@@ -54,0 +53,0 @@ report: <T>(validation: t.Validation<T>, options?: ReporterOptions) => string[]; |
@@ -22,8 +22,8 @@ "use strict"; | ||
*/ | ||
var A = require("fp-ts/lib/Array"); | ||
var E = require("fp-ts/lib/Either"); | ||
var NEA = require("fp-ts/lib/NonEmptyArray"); | ||
var O = require("fp-ts/lib/Option"); | ||
var R = require("fp-ts/lib/Record"); | ||
var pipeable_1 = require("fp-ts/lib/pipeable"); | ||
var A = require("fp-ts/Array"); | ||
var E = require("fp-ts/Either"); | ||
var NEA = require("fp-ts/NonEmptyArray"); | ||
var O = require("fp-ts/Option"); | ||
var R = require("fp-ts/Record"); | ||
var pipeable_1 = require("fp-ts/pipeable"); | ||
var t = require("io-ts"); | ||
@@ -35,5 +35,3 @@ var utils_1 = require("./utils"); | ||
}; | ||
var jsToString = function (value) { | ||
return value === undefined ? 'undefined' : JSON.stringify(value); | ||
}; | ||
var jsToString = function (value) { return (value === undefined ? 'undefined' : JSON.stringify(value)); }; | ||
var keyPath = function (ctx) { | ||
@@ -75,3 +73,3 @@ // The context entry with an empty key is the original | ||
"but instead got: " + jsToString(error.value), | ||
error.message ? "(" + error.message + ")" : '' | ||
error.message ? "(" + error.message + ")" : '', | ||
] | ||
@@ -87,3 +85,3 @@ .filter(Boolean) | ||
path === '' ? '\n' : "\nat " + path + " ", | ||
"but instead got: " + jsToString(value) | ||
"but instead got: " + jsToString(value), | ||
] | ||
@@ -105,10 +103,6 @@ .filter(Boolean) | ||
}); | ||
return expected.length > 0 | ||
? O.some(errorMessageUnion(expected, path, value, options)) | ||
: O.none; | ||
return expected.length > 0 ? O.some(errorMessageUnion(expected, path, value, options)) : O.none; | ||
}; | ||
var formatValidationCommonError = function (path, error, options) { | ||
return pipeable_1.pipe(error, getErrorFromCtx, O.map(function (errorContext) { | ||
return errorMessageSimple(errorContext.type.name, path, error, options); | ||
})); | ||
return pipeable_1.pipe(error, getErrorFromCtx, O.map(function (errorContext) { return errorMessageSimple(errorContext.type.name, path, error, options); })); | ||
}; | ||
@@ -129,3 +123,6 @@ var groupByKey = NEA.groupBy(function (error) { | ||
*/ | ||
exports.formatValidationError = function (error, options) { return formatValidationCommonError(keyPath(error.context), error, options); }; | ||
var formatValidationError = function (error, options) { | ||
return formatValidationCommonError(keyPath(error.context), error, options); | ||
}; | ||
exports.formatValidationError = formatValidationError; | ||
/** | ||
@@ -135,3 +132,3 @@ * Format validation errors (`t.Errors`). | ||
* @example | ||
* import * as E from 'fp-ts/lib/Either' | ||
* import * as E from 'fp-ts/Either' | ||
* import * as t from 'io-ts' | ||
@@ -150,3 +147,3 @@ * import { formatValidationErrors } from 'io-ts-reporters' | ||
*/ | ||
exports.formatValidationErrors = function (errors, options) { | ||
var formatValidationErrors = function (errors, options) { | ||
return pipeable_1.pipe(errors, groupByKey, R.mapWithIndex(function (path, errors) { return format(path, errors, options); }), R.compact, R.toArray, A.map(function (_a) { | ||
@@ -157,2 +154,3 @@ var _key = _a[0], error = _a[1]; | ||
}; | ||
exports.formatValidationErrors = formatValidationErrors; | ||
/** | ||
@@ -165,7 +163,8 @@ * Deprecated, use the default export instead. | ||
*/ | ||
exports.reporter = function (validation, options) { | ||
var reporter = function (validation, options) { | ||
return pipeable_1.pipe(validation, E.mapLeft(function (errors) { return exports.formatValidationErrors(errors, options); }), E.fold(function (errors) { return errors; }, function () { return []; })); | ||
}; | ||
exports.reporter = reporter; | ||
var prettyReporter = { report: exports.reporter }; | ||
exports.default = prettyReporter; | ||
//# sourceMappingURL=index.js.map |
/** | ||
* @since 1.1.0 | ||
*/ | ||
import { Predicate } from 'fp-ts/lib/function'; | ||
import { Predicate } from 'fp-ts/function'; | ||
/** | ||
@@ -6,0 +6,0 @@ * @since 1.1.0 |
@@ -8,14 +8,17 @@ "use strict"; | ||
/* eslint-disable @typescript-eslint/array-type */ | ||
exports.takeUntil = function (predicate) { return function (as) { | ||
var init = []; | ||
// eslint-disable-next-line unicorn/no-for-loop | ||
for (var i = 0; i < as.length; i++) { | ||
init[i] = as[i]; | ||
if (predicate(as[i])) { | ||
return init; | ||
var takeUntil = function (predicate) { | ||
return function (as) { | ||
var init = []; | ||
// eslint-disable-next-line unicorn/no-for-loop | ||
for (var i = 0; i < as.length; i++) { | ||
init[i] = as[i]; | ||
if (predicate(as[i])) { | ||
return init; | ||
} | ||
} | ||
} | ||
return init; | ||
}; }; | ||
return init; | ||
}; | ||
}; | ||
exports.takeUntil = takeUntil; | ||
/* eslint-enable @typescript-eslint/array-type */ | ||
//# sourceMappingURL=utils.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
24778
13
462
1