Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
chore: split into more files (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
KATT authored Oct 2, 2023
1 parent 51f4b23 commit 6e294b4
Show file tree
Hide file tree
Showing 23 changed files with 128 additions and 112 deletions.
12 changes: 12 additions & 0 deletions src/createTson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable eslint-comments/disable-enable-pair */

import { createTsonDeserialize, createTsonParser } from "./deserialize.js";
import { createTsonSerialize, createTsonStringify } from "./serialize.js";
import { TsonOptions } from "./types.js";

export const createTson = (opts: TsonOptions) => ({
deserialize: createTsonDeserialize(opts),
parse: createTsonParser(opts),
serialize: createTsonSerialize(opts),
stringify: createTsonStringify(opts),
});
58 changes: 58 additions & 0 deletions src/deserialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* eslint-disable @typescript-eslint/no-explicit-any, eslint-comments/disable-enable-pair */

import { isTsonTuple } from "./internals/isTsonTuple.js";
import { mapOrReturn } from "./internals/mapOrReturn.js";
import {
TsonDeserializeFn,
TsonNonce,
TsonOptions,
TsonParseFn,
TsonSerialized,
TsonTransformerSerializeDeserialize,
} from "./types.js";

type WalkFn = (value: unknown) => unknown;
type WalkerFactory = (nonce: TsonNonce) => WalkFn;

type AnyTsonTransformerSerializeDeserialize =
TsonTransformerSerializeDeserialize<any, any>;

export function createTsonDeserialize(opts: TsonOptions): TsonDeserializeFn {
const typeByKey: Record<string, AnyTsonTransformerSerializeDeserialize> = {};

for (const handler of opts.types) {
if (handler.key) {
if (typeByKey[handler.key]) {
throw new Error(`Multiple handlers for key ${handler.key} found`);
}

typeByKey[handler.key] =
handler as AnyTsonTransformerSerializeDeserialize;
}
}

const walker: WalkerFactory = (nonce) => {
const walk: WalkFn = (value) => {
if (isTsonTuple(value, nonce)) {
const [type, serializedValue] = value;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const transformer = typeByKey[type]!;
return transformer.deserialize(walk(serializedValue));
}

return mapOrReturn(value, walk);
};

return walk;
};

return ((obj: TsonSerialized) =>
walker(obj.nonce)(obj.json)) as TsonDeserializeFn;
}

export function createTsonParser(opts: TsonOptions): TsonParseFn {
const deserializer = createTsonDeserialize(opts);

return ((str: string) =>
deserializer(JSON.parse(str) as TsonSerialized)) as TsonParseFn;
}
2 changes: 1 addition & 1 deletion src/handlers/tsonBigint.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonMap, tsonSet } from "./index.js";
import { tsonBigint } from "./tsonBigint.js";

Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonDate.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonDate } from "./index.js";

test("Date", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonMap.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonMap } from "./index.js";

test("Map", () => {
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/tsonNumberGuard.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, test } from "vitest";

import { expectError } from "../testUtils.js";
import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { expectError } from "../internals/testUtils.js";
import { tsonNumberGuard } from "./index.js";

test("number", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonRegExp.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonRegExp } from "./index.js";

test("regex", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonSet.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonSet } from "./index.js";

test("Set", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonSymbol.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonSymbol } from "./index.js";

test("symbol", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonURL.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonURL } from "./index.js";

test("URL", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonUndefined.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { tsonUndefined } from "./index.js";

test("undefined", () => {
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/tsonUnknownObjectGuard.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assert, expect, test } from "vitest";

import { expectError } from "../testUtils.js";
import { createTson } from "../tson.js";
import { createTson } from "../createTson.js";
import { expectError } from "../internals/testUtils.js";
import { tsonSet } from "./index.js";
import {
UnknownObjectGuardError,
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/tsonUnknownObjectGuard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TsonError } from "../errors.js";
import { isPlainObject } from "../isPlainObject.js";
import { isPlainObject } from "../internals/isPlainObject.js";
import { TsonType } from "../types.js";

export class UnknownObjectGuardError extends TsonError {
Expand Down
12 changes: 6 additions & 6 deletions src/tson.test.ts → src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { expect, test } from "vitest";

import { expectError } from "./testUtils.js";
import { createTson } from "./tson.js";
import { TsonType } from "./types.js";
import { TsonOptions, TsonType, createTson } from "./index.js";
import { expectError } from "./internals/testUtils.js";

test("multiple handlers for primitive string found", () => {
const stringHandler: TsonType<string, never> = {
primitive: "string",
};
const opts: TsonOptions = {
types: [stringHandler, stringHandler],
};
expect(() => {
createTson({
types: [stringHandler, stringHandler],
});
createTson(opts);
}).toThrowErrorMatchingInlineSnapshot(
'"Multiple handlers for primitive string found"',
);
Expand Down
11 changes: 4 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
export {
createTson,
createTsonDeserialize,
createTsonParser,
createTsonSerialize,
createTsonStringify,
} from "./tson.js";
export { createTson } from "./createTson.js";
export { createTsonDeserialize, createTsonParser } from "./deserialize.js";
export { createTsonSerialize, createTsonStringify } from "./serialize.js";

export * from "./handlers/index.js";

export type { TsonType } from "./types.js";
export type { TsonOptions } from "./types.js";
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions src/internals/isTsonTuple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TsonTuple } from "../types.js";

export function isTsonTuple(v: unknown, nonce: string): v is TsonTuple {
return Array.isArray(v) && v.length === 3 && v[2] === nonce;
}
26 changes: 26 additions & 0 deletions src/internals/mapOrReturn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { isPlainObject } from "./isPlainObject.js";

/**
* Maps over an object or array, returning a new object or array with the same keys.
* If the input is not an object or array, the input is returned.
*/

export function mapOrReturn(
input: unknown,
fn: (val: unknown, key: number | string) => unknown,
): unknown {
if (Array.isArray(input)) {
return input.map(fn);
}

if (isPlainObject(input)) {
const output: typeof input = {};
for (const [key, value] of Object.entries(input)) {
output[key] = fn(value, key);
}

return output;
}

return input;
}
File renamed without changes.
86 changes: 2 additions & 84 deletions src/tson.ts → src/serialize.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,23 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-explicit-any, eslint-comments/disable-enable-pair */
import { CircularReferenceError } from "./errors.js";
import { isPlainObject } from "./isPlainObject.js";
import { mapOrReturn } from "./internals/mapOrReturn.js";
import {
TsonAllTypes,
TsonDeserializeFn,
TsonNonce,
TsonOptions,
TsonParseFn,
TsonSerializeFn,
TsonSerialized,
TsonSerializedValue,
TsonStringifyFn,
TsonTransformerSerializeDeserialize,
TsonTuple,
TsonTypeHandlerKey,
TsonTypeTesterCustom,
TsonTypeTesterPrimitive,
} from "./types.js";

function isTsonTuple(v: unknown, nonce: string): v is TsonTuple {
return Array.isArray(v) && v.length === 3 && v[2] === nonce;
}

type WalkFn = (value: unknown) => unknown;
type WalkerFactory = (nonce: TsonNonce) => WalkFn;

type AnyTsonTransformerSerializeDeserialize =
TsonTransformerSerializeDeserialize<any, any>;

export function createTsonDeserialize(opts: TsonOptions): TsonDeserializeFn {
const typeByKey: Record<string, AnyTsonTransformerSerializeDeserialize> = {};

for (const handler of opts.types) {
if (handler.key) {
if (typeByKey[handler.key]) {
throw new Error(`Multiple handlers for key ${handler.key} found`);
}

typeByKey[handler.key] =
handler as AnyTsonTransformerSerializeDeserialize;
}
}

const walker: WalkerFactory = (nonce) => {
const walk: WalkFn = (value) => {
if (isTsonTuple(value, nonce)) {
const [type, serializedValue] = value;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const transformer = typeByKey[type]!;
return transformer.deserialize(walk(serializedValue));
}

return mapOrReturn(value, walk);
};

return walk;
};

return ((obj: TsonSerialized) =>
walker(obj.nonce)(obj.json)) as TsonDeserializeFn;
}

export function createTsonParser(opts: TsonOptions): TsonParseFn {
const deserializer = createTsonDeserialize(opts);

return ((str: string) =>
deserializer(JSON.parse(str) as TsonSerialized)) as TsonParseFn;
}

export function createTsonStringify(opts: TsonOptions): TsonStringifyFn {
const serializer = createTsonSerialize(opts);

Expand Down Expand Up @@ -187,34 +136,3 @@ export function createTsonSerialize(opts: TsonOptions): TsonSerializeFn {
} as TsonSerialized<any>;
}) as TsonSerializeFn;
}

/**
* Maps over an object or array, returning a new object or array with the same keys.
* If the input is not an object or array, the input is returned.
*/
function mapOrReturn(
input: unknown,
fn: (val: unknown, key: number | string) => unknown,
): unknown {
if (Array.isArray(input)) {
return input.map(fn);
}

if (isPlainObject(input)) {
const output: typeof input = {};
for (const [key, value] of Object.entries(input)) {
output[key] = fn(value, key);
}

return output;
}

return input;
}

export const createTson = (opts: TsonOptions) => ({
deserialize: createTsonDeserialize(opts),
parse: createTsonParser(opts),
serialize: createTsonSerialize(opts),
stringify: createTsonStringify(opts),
});
2 changes: 1 addition & 1 deletion src/stringify.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { expect, test } from "vitest";

import { createTson } from "./createTson.js";
import { tsonBigint } from "./handlers/tsonBigint.js";
import { tsonMap } from "./handlers/tsonMap.js";
import { tsonSet } from "./handlers/tsonSet.js";
import { tsonUndefined } from "./handlers/tsonUndefined.js";
import { createTson } from "./tson.js";

test("lets have a look at the stringified output", () => {
const t = createTson({
Expand Down
2 changes: 1 addition & 1 deletion src/types.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expectTypeOf, test } from "vitest";

import { createTson } from "./createTson.js";
import { tsonBigint } from "./handlers/tsonBigint.js";
import { createTson } from "./tson.js";
import "./types.js";

test("types", () => {
Expand Down

0 comments on commit 6e294b4

Please sign in to comment.