Skip to content

Commit 3902889

Browse files
committed
fix(ono): remove dependency on ono
BREAKING CHANGE: going to make this a breaking change in case there are people depending on the stack traces / benefits of ono
1 parent bd9478d commit 3902889

File tree

9 files changed

+82
-39
lines changed

9 files changed

+82
-39
lines changed

lib/dereference.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import $Ref from "./ref.js";
22
import Pointer from "./pointer.js";
3-
import { ono } from "@jsdevtools/ono";
43
import * as url from "./util/url.js";
54
import type $Refs from "./refs.js";
65
import type { DereferenceOptions, ParserOptions } from "./options.js";
@@ -355,7 +354,8 @@ function foundCircularReference(keyPath: any, $refs: any, options: any) {
355354
options?.dereference?.onCircular?.(keyPath);
356355

357356
if (!options.dereference.circular) {
358-
throw ono.reference(`Circular $ref pointer found at ${keyPath}`);
357+
const error = new ReferenceError(`Circular $ref pointer found at ${keyPath}`);
358+
throw error;
359359
}
360360
return true;
361361
}

lib/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
isHandledError,
1717
JSONParserErrorGroup,
1818
} from "./util/errors.js";
19-
import { ono } from "@jsdevtools/ono";
2019
import maybe from "./util/maybe.js";
2120
import type { ParserOptions } from "./options.js";
2221
import { getJsonSchemaRefParserDefaultOptions } from "./options.js";
@@ -77,7 +76,7 @@ export class $RefParser<S extends object = JSONSchema, O extends ParserOptions<S
7776
let promise;
7877

7978
if (!args.path && !args.schema) {
80-
const err = ono(`Expected a file path, URL, or object. Got ${args.path || args.schema}`);
79+
const err = new Error(`Expected a file path, URL, or object. Got ${args.path || args.schema}`);
8180
return maybe(args.callback, Promise.reject(err));
8281
}
8382

@@ -129,7 +128,7 @@ export class $RefParser<S extends object = JSONSchema, O extends ParserOptions<S
129128
this.schema = null; // it's already set to null at line 79, but let's set it again for the sake of readability
130129
return maybe(args.callback, Promise.resolve(this.schema!));
131130
} else {
132-
throw ono.syntax(`"${this.$refs._root$Ref.path || result}" is not a valid JSON Schema`);
131+
throw new SyntaxError(`"${this.$refs._root$Ref.path || result}" is not a valid JSON Schema`);
133132
}
134133
} catch (err) {
135134
if (!args.options.continueOnError || !isHandledError(err)) {

lib/parse.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ono } from "@jsdevtools/ono";
21
import * as url from "./util/url.js";
32
import * as plugins from "./util/plugins.js";
43
import {
@@ -92,7 +91,7 @@ async function readFile<S extends object = JSONSchema, O extends ParserOptions<S
9291
throw new UnmatchedResolverError(file.url);
9392
} else if (!err || !("error" in err)) {
9493
// Throw a generic, friendly error.
95-
throw ono.syntax(`Unable to resolve $ref pointer "${file.url}"`);
94+
throw new SyntaxError(`Unable to resolve $ref pointer "${file.url}"`);
9695
}
9796
// Throw the original error, if it's one of our own (user-friendly) errors.
9897
else if (err.error instanceof ResolverError) {
@@ -133,7 +132,7 @@ async function parseFile<S extends object = JSONSchema, O extends ParserOptions<
133132
try {
134133
const parser = await plugins.run<S, O>(parsers, "parse", file, $refs);
135134
if (!parser.plugin.allowEmpty && isEmpty(parser.result)) {
136-
throw ono.syntax(`Error parsing "${file.url}" as ${parser.plugin.name}. \nParsed value is empty`);
135+
throw new SyntaxError(`Error parsing "${file.url}" as ${parser.plugin.name}. \nParsed value is empty`);
137136
} else {
138137
return parser;
139138
}
@@ -144,7 +143,7 @@ async function parseFile<S extends object = JSONSchema, O extends ParserOptions<
144143
} else if (err && err.message && err.message.startsWith("Error parsing")) {
145144
throw err;
146145
} else if (!err || !("error" in err)) {
147-
throw ono.syntax(`Unable to parse ${file.url}`);
146+
throw new SyntaxError(`Unable to parse ${file.url}`);
148147
} else if (err.error instanceof ParserError) {
149148
throw err.error;
150149
} else {

lib/refs.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ono } from "@jsdevtools/ono";
21
import $Ref from "./ref.js";
32
import * as url from "./util/url.js";
43
import type { JSONSchema4Type, JSONSchema6Type, JSONSchema7Type } from "json-schema";
@@ -100,7 +99,7 @@ export default class $Refs<S extends object = JSONSchema, O extends ParserOption
10099
const $ref = this._$refs[withoutHash];
101100

102101
if (!$ref) {
103-
throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
102+
throw new Error(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
104103
}
105104

106105
$ref.set(absPath, value);
@@ -150,7 +149,7 @@ export default class $Refs<S extends object = JSONSchema, O extends ParserOption
150149
const $ref = this._$refs[withoutHash];
151150

152151
if (!$ref) {
153-
throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
152+
throw new Error(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
154153
}
155154

156155
return $ref.resolve(absPath, options, path, pathFromRoot);

lib/resolvers/file.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import fs from "fs";
2-
import { ono } from "@jsdevtools/ono";
32
import * as url from "../util/url.js";
43
import { ResolverError } from "../util/errors.js";
54
import type { JSONSchema, ResolverOptions } from "../types/index.js";
@@ -28,12 +27,16 @@ export default {
2827
try {
2928
path = url.toFileSystemPath(file.url);
3029
} catch (err: any) {
31-
throw new ResolverError(ono.uri(err, `Malformed URI: ${file.url}`), file.url);
30+
const e = err as Error;
31+
e.message = `Malformed URI: ${file.url}: ${e.message}`;
32+
throw new ResolverError(e, file.url);
3233
}
3334
try {
3435
return await fs.promises.readFile(path);
3536
} catch (err: any) {
36-
throw new ResolverError(ono(err, `Error opening file "${path}"`), path);
37+
const e = err as Error;
38+
e.message = `Error opening file ${path}: ${e.message}`;
39+
throw new ResolverError(e, path);
3740
}
3841
},
3942
} as ResolverOptions<JSONSchema>;

lib/resolvers/http.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ono } from "@jsdevtools/ono";
21
import * as url from "../util/url.js";
32
import { ResolverError } from "../util/errors.js";
43
import type { FileInfo, HTTPResolverOptions, JSONSchema } from "../types/index.js";
@@ -78,17 +77,20 @@ async function download<S extends object = JSONSchema>(
7877
try {
7978
const res = await get(u, httpOptions);
8079
if (res.status >= 400) {
81-
throw ono({ status: res.status }, `HTTP ERROR ${res.status}`);
80+
const error = new Error(`HTTP ERROR ${res.status}`) as Error & { status?: number };
81+
error.status = res.status;
82+
throw error;
8283
} else if (res.status >= 300) {
8384
if (!Number.isNaN(httpOptions.redirects) && redirects.length > httpOptions.redirects!) {
84-
throw new ResolverError(
85-
ono(
86-
{ status: res.status },
87-
`Error downloading ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
88-
),
89-
);
85+
const error = new Error(
86+
`Error downloading ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
87+
) as Error & { status?: number };
88+
error.status = res.status;
89+
throw new ResolverError(error);
9090
} else if (!("location" in res.headers) || !res.headers.location) {
91-
throw ono({ status: res.status }, `HTTP ${res.status} redirect with no location header`);
91+
const error = new Error(`HTTP ${res.status} redirect with no location header`) as Error & { status?: number };
92+
error.status = res.status;
93+
throw error;
9294
} else {
9395
const redirectTo = url.resolve(u.href, res.headers.location as string);
9496
return download(redirectTo, httpOptions, redirects);
@@ -101,7 +103,9 @@ async function download<S extends object = JSONSchema>(
101103
return Buffer.alloc(0);
102104
}
103105
} catch (err: any) {
104-
throw new ResolverError(ono(err, `Error downloading ${u.href}`), u.href);
106+
const e = err as Error;
107+
e.message = `Error downloading ${u.href}: ${e.message}`;
108+
throw new ResolverError(e, u.href);
105109
}
106110
}
107111

lib/util/errors.ts

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Ono } from "@jsdevtools/ono";
21
import { getHash, stripHash, toFileSystemPath } from "./url.js";
32
import type $RefParser from "../index.js";
43
import type { ParserOptions } from "../index.js";
@@ -14,7 +13,57 @@ export type JSONParserErrorType =
1413
| "EUNMATCHEDRESOLVER"
1514
| "EMISSINGPOINTER"
1615
| "EINVALIDPOINTER";
16+
const nonJsonTypes = ["function", "symbol", "undefined"];
17+
const protectedProps = ["constructor", "prototype", "__proto__"];
18+
const objectPrototype = Object.getPrototypeOf({});
19+
20+
/**
21+
* Custom JSON serializer for Error objects.
22+
* Returns all built-in error properties, as well as extended properties.
23+
*/
24+
export function toJSON<T extends Error>(this: T): Error & T {
25+
// HACK: We have to cast the objects to `any` so we can use symbol indexers.
26+
// see https://github.com/Microsoft/TypeScript/issues/1863
27+
const pojo: any = {};
28+
const error = this as any;
29+
30+
for (const key of getDeepKeys(error)) {
31+
if (typeof key === "string") {
32+
const value = error[key];
33+
const type = typeof value;
34+
35+
if (!nonJsonTypes.includes(type)) {
36+
pojo[key] = value;
37+
}
38+
}
39+
}
40+
41+
return pojo as Error & T;
42+
}
43+
44+
/**
45+
* Returns own, inherited, enumerable, non-enumerable, string, and symbol keys of `obj`.
46+
* Does NOT return members of the base Object prototype, or the specified omitted keys.
47+
*/
48+
export function getDeepKeys(obj: object, omit: Array<string | symbol> = []): Set<string | symbol> {
49+
let keys: Array<string | symbol> = [];
50+
51+
// Crawl the prototype chain, finding all the string and symbol keys
52+
while (obj && obj !== objectPrototype) {
53+
keys = keys.concat(Object.getOwnPropertyNames(obj), Object.getOwnPropertySymbols(obj));
54+
obj = Object.getPrototypeOf(obj) as object;
55+
}
1756

57+
// De-duplicate the list of keys
58+
const uniqueKeys = new Set(keys);
59+
60+
// Remove any omitted keys
61+
for (const key of omit.concat(protectedProps)) {
62+
uniqueKeys.delete(key);
63+
}
64+
65+
return uniqueKeys;
66+
}
1867
export class JSONParserError extends Error {
1968
public readonly name: string;
2069
public readonly message: string;
@@ -29,10 +78,10 @@ export class JSONParserError extends Error {
2978
this.message = message;
3079
this.source = source;
3180
this.path = null;
32-
33-
Ono.extend(this);
3481
}
3582

83+
toJSON = toJSON.bind(this);
84+
3685
get footprint() {
3786
return `${this.path}+${this.source}+${this.code}+${this.message}`;
3887
}
@@ -52,9 +101,8 @@ export class JSONParserErrorGroup<
52101
this.message = `${this.errors.length} error${
53102
this.errors.length > 1 ? "s" : ""
54103
} occurred while reading '${toFileSystemPath(parser.$refs._root$Ref!.path)}'`;
55-
56-
Ono.extend(this);
57104
}
105+
toJSON = toJSON.bind(this);
58106

59107
static getParserErrors<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
60108
parser: $RefParser<S, O>,

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@
9292
"vitest": "^3.2.1"
9393
},
9494
"dependencies": {
95-
"@jsdevtools/ono": "^7.1.3",
9695
"@types/json-schema": "^7.0.15",
9796
"js-yaml": "^4.1.0"
9897
},

yarn.lock

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ __metadata:
2121
dependencies:
2222
"@eslint/compat": "npm:^1.2.9"
2323
"@eslint/js": "npm:^9.28.0"
24-
"@jsdevtools/ono": "npm:^7.1.3"
2524
"@types/eslint": "npm:^9.6.1"
2625
"@types/js-yaml": "npm:^4.0.9"
2726
"@types/json-schema": "npm:^7.0.15"
@@ -532,13 +531,6 @@ __metadata:
532531
languageName: node
533532
linkType: hard
534533

535-
"@jsdevtools/ono@npm:^7.1.3":
536-
version: 7.1.3
537-
resolution: "@jsdevtools/ono@npm:7.1.3"
538-
checksum: 10c0/a9f7e3e8e3bc315a34959934a5e2f874c423cf4eae64377d3fc9de0400ed9f36cb5fd5ebce3300d2e8f4085f557c4a8b591427a583729a87841fda46e6c216b9
539-
languageName: node
540-
linkType: hard
541-
542534
"@nodelib/fs.scandir@npm:2.1.5":
543535
version: 2.1.5
544536
resolution: "@nodelib/fs.scandir@npm:2.1.5"

0 commit comments

Comments
 (0)