diff --git a/.eslintrc.js b/.eslintrc.js index da62299..9afd1b3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,13 @@ -/* @flow */ - module.exports = { - extends: require.resolve( - "@krakenjs/grumbler-scripts/config/.eslintrc-browser" - ), + extends: + "./node_modules/@krakenjs/grumbler-scripts/config/.eslintrc-typescript.js", + + globals: { + __TEST__: true, + }, + + rules: { + // off for initial ts conversion + "@typescript-eslint/no-implicit-any-catch": "off", + }, }; diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index e0bdc91..0000000 --- a/.flowconfig +++ /dev/null @@ -1,15 +0,0 @@ -[ignore] -.*/node_modules/babel-plugin-flow-runtime -.*/node_modules/flow-runtime -.*/node_modules/npm -.*/node_modules/eslint-plugin-compat -.*/node_modules/jsonlint -.*/node_modules/resolve -.*/dist/module -[include] -[libs] -flow-typed -node_modules/@krakenjs/grumbler-scripts/declarations.js -[options] -module.name_mapper='^src\(.*\)$' -> '/src/\1' -experimental.const_params=false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 80619fc..527d944 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,9 +27,6 @@ jobs: - name: 👕 Lint commit messages uses: wagoid/commitlint-github-action@v4 - - name: ▶️ Run flow-typed script - run: npm run flow-typed - - name: ▶️ Run build script run: npm run build diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..9bf8100 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + extends: "@krakenjs/grumbler-scripts/config/.babelrc-browser", + presets: ["@krakenjs/grumbler-scripts/config/flow-ts-babel-preset"], +}; diff --git a/babel.config.json b/babel.config.json deleted file mode 100644 index c55c063..0000000 --- a/babel.config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@krakenjs/grumbler-scripts/config/.babelrc-browser" -} diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index dcb52b5..0000000 --- a/karma.conf.js +++ /dev/null @@ -1,15 +0,0 @@ -/* @flow */ -/* eslint import/no-default-export: off */ - -import { getKarmaConfig } from "@krakenjs/grumbler-scripts/config/karma.conf"; - -import { WEBPACK_CONFIG_TEST } from "./webpack.config"; - -export default function configKarma(karma: Object) { - const karmaConfig = getKarmaConfig(karma, { - basePath: __dirname, - webpack: WEBPACK_CONFIG_TEST, - }); - - karma.set(karmaConfig); -} diff --git a/package.json b/package.json index 905fbe8..ad6081f 100644 --- a/package.json +++ b/package.json @@ -3,25 +3,27 @@ "version": "2.0.0", "description": "Javascript module template.", "main": "index.js", + "types": "dist/types/index.d.ts", "scripts": { - "setup": "npm install && npm run flow-typed", - "lint": "eslint src/ test/ *.js --ext .js,.jsx", - "flow-typed": "rm -rf ./flow-typed && flow-typed install && flow-typed install mocha@4", - "flow": "flow", + "build": "npm run test && npm run babel && npm run webpack && npm run build:types", + "build:flow": "find ./dist -type f -not -path './node_modules/*' -name '*.d.ts' -exec sh -c 'flowgen --add-flow-header $1 -o ${1%.*.*}.js.flow' _ '{}' \\;", + "build:tsc": "tsc src/**/** --outDir ./dist/esm --declaration --emitDeclarationOnly", + "build:types": "npm run build:tsc && npm run build:flow", + "webpack": "cross-env NODE_ENV=production babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --progress", + "babel": "cross-env NODE_ENV=production babel src/ --out-dir ./dist/esm/ --extensions .ts,.tsx", + "tsc": "tsc", "format": "prettier --write --ignore-unknown .", "format:check": "prettier --check .", - "karma": "cross-env NODE_ENV=test babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/karma start", - "babel": "babel src/ --out-dir dist/module", - "webpack": "babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --progress", - "test": "npm run format:check && npm run lint && npm run flow-typed && npm run flow && npm run karma", - "build": "npm run test && npm run babel && npm run webpack", + "test": "npm run format:check && npm run lint && npm run tsc --no-emit && npm run vitest", + "lint": "eslint --ext ts,tsx,js,jsx src/ test/", "clean": "rimraf dist coverage", - "reinstall": "rimraf flow-typed && rimraf node_modules && npm install && flow-typed install", - "debug": "cross-env NODE_ENV=debug", - "prepare": "husky install", "prerelease": "npm run clean && npm run build && git add dist && git commit -m 'ci: check in dist folder' || echo 'Nothing to distribute'", "release": "standard-version", - "postrelease": "git push && git push --follow-tags && npm publish" + "postrelease": "git push && git push --follow-tags && npm publish", + "debug": "cross-env NODE_ENV=debug", + "prepare": "husky install", + "vitest": "vitest run --dom --coverage", + "vitest:watch": "vitest watch --dom --coverage --ui" }, "standard-version": { "types": [ @@ -92,13 +94,24 @@ "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", "@krakenjs/grumbler-scripts": "^7.0.0", - "flow-bin": "0.155.0", - "husky": "^7.0.4", + "@types/webpack": "^5.28.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "@vitest/ui": "^0.16.0", + "c8": "^7.11.0", + "flowgen": "^1.16.0", + "happy-dom": "^2.55.0", + "husky": "^7.0.0", "lint-staged": "^12.4.0", "prettier": "^2.6.2", - "standard-version": "^9.3.2" + "standard-version": "^9.3.2", + "ts-node": "^10.8.1", + "typescript": "4.6.3", + "utility-types": "^3.10.0", + "vitest": "^0.16.0" }, "lint-staged": { "*": "prettier --write --ignore-unknown" - } + }, + "dependencies": {} } diff --git a/src/common.js b/src/common.ts similarity index 65% rename from src/common.js rename to src/common.ts index 73d3bfb..e4a637a 100644 --- a/src/common.js +++ b/src/common.ts @@ -1,17 +1,20 @@ -/* @flow */ +import type { $Values } from "utility-types"; import { TYPE } from "./constants"; import type { CustomSerializedType } from "./types"; -export function isSerializedType(item: mixed): boolean { +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any +export function isSerializedType(item: any): boolean { return ( typeof item === "object" && item !== null && + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access typeof item.__type__ === "string" ); } -export function determineType(val: mixed): $Values | void { +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any +export function determineType(val: any): $Values | undefined { if (typeof val === "undefined") { return TYPE.UNDEFINED; } @@ -33,16 +36,15 @@ export function determineType(val: mixed): $Values | void { return TYPE.ERROR; } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (typeof val.then === "function") { return TYPE.PROMISE; } - // $FlowFixMe method-unbinding if (Object.prototype.toString.call(val) === "[object RegExp]") { return TYPE.REGEX; } - // $FlowFixMe method-unbinding if (Object.prototype.toString.call(val) === "[object Date]") { return TYPE.DATE; } @@ -63,7 +65,7 @@ export function determineType(val: mixed): $Values | void { } } -export function serializeType( +export function serializeType( type: T, val: V ): CustomSerializedType { diff --git a/src/constants.js b/src/constants.js deleted file mode 100644 index 88ac8be..0000000 --- a/src/constants.js +++ /dev/null @@ -1,16 +0,0 @@ -/* @flow */ - -export const TYPE = { - FUNCTION: ("function": "function"), - ERROR: ("error": "error"), - PROMISE: ("promise": "promise"), - REGEX: ("regex": "regex"), - DATE: ("date": "date"), - ARRAY: ("array": "array"), - OBJECT: ("object": "object"), - STRING: ("string": "string"), - NUMBER: ("number": "number"), - BOOLEAN: ("boolean": "boolean"), - NULL: ("null": "null"), - UNDEFINED: ("undefined": "undefined"), -}; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..ea551df --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,14 @@ +export const TYPE = { + FUNCTION: "function", + ERROR: "error", + PROMISE: "promise", + REGEX: "regex", + DATE: "date", + ARRAY: "array", + OBJECT: "object", + STRING: "string", + NUMBER: "number", + BOOLEAN: "boolean", + NULL: "null", + UNDEFINED: "undefined", +} as const; diff --git a/src/deserialize.js b/src/deserialize.ts similarity index 50% rename from src/deserialize.js rename to src/deserialize.ts index 4b2e075..c38a96e 100644 --- a/src/deserialize.js +++ b/src/deserialize.ts @@ -1,17 +1,16 @@ -/* @flow */ - -import type { Thenable } from "./types"; import { TYPE } from "./constants"; import { determineType, isSerializedType } from "./common"; +import type { + SerializedError, + SerializedRegex, + SerializedDate, +} from "./serializers"; import { deserializeFunction, deserializeError, - type SerializedError, deserializePromise, deserializeRegex, - type SerializedRegex, deserializeDate, - type SerializedDate, deserializeArray, deserializeObject, deserializeString, @@ -20,27 +19,28 @@ import { deserializeNull, deserializeUndefined, } from "./serializers"; +import type { CustomSerializedType } from "./types"; -type Deserializer = (serializedValue: S, key: string) => V; +type Deserializer = (serializedValue: S, key: string) => V; type PrimitiveDeserializer = (serializedValue: S, key: string) => V; type Deserializers = { - function?: Deserializer, - error?: Deserializer, - promise?: Deserializer, - regex?: Deserializer, - date?: Deserializer, - array?: PrimitiveDeserializer<$ReadOnlyArray>, - object?: PrimitiveDeserializer, - string?: PrimitiveDeserializer, - number?: PrimitiveDeserializer, - boolean?: PrimitiveDeserializer, - null?: PrimitiveDeserializer, - [string]: Deserializer, - undefined?: PrimitiveDeserializer, + function: Deserializer; + error: Deserializer; + promise: Deserializer; + regex: Deserializer; + date: Deserializer; + array: PrimitiveDeserializer>; + object: PrimitiveDeserializer>; + string: PrimitiveDeserializer; + number: PrimitiveDeserializer; + boolean: PrimitiveDeserializer; + null: PrimitiveDeserializer; + undefined: PrimitiveDeserializer; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: Deserializer; }; -// $FlowFixMe const DESERIALIZER: Deserializers = { [TYPE.FUNCTION]: deserializeFunction, [TYPE.ERROR]: deserializeError, @@ -56,19 +56,23 @@ const DESERIALIZER: Deserializers = { [TYPE.UNDEFINED]: deserializeUndefined, }; -// $FlowFixMe -const defaultDeserializers: Deserializers = {}; +const defaultDeserializers: Partial = {}; -export function deserialize( +export function deserialize( str: string, - deserializers: Deserializers = defaultDeserializers + deserializers: Partial = defaultDeserializers ): T { if (str === TYPE.UNDEFINED) { - // $FlowFixMe + // the lib allows undefined returns but doesnt expect type assertions for it. need fixing + // @ts-ignore return; } - function replacer(key, val): ?mixed { + function replacer( + key: string, + val: CustomSerializedType + ): unknown | null | undefined { + // @ts-ignore - function this has unknown caller if (isSerializedType(this)) { return val; } @@ -88,8 +92,7 @@ export function deserialize( return value; } - // $FlowFixMe - const deserializer = deserializers[type] || DESERIALIZER[type]; + const deserializer = deserializers[type] ?? DESERIALIZER[type]; if (!deserializer) { return value; @@ -98,5 +101,6 @@ export function deserialize( return deserializer(value, key); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(str, replacer); } diff --git a/src/index.js b/src/index.ts similarity index 92% rename from src/index.js rename to src/index.ts index b8594de..bcbad86 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,5 +1,3 @@ -/* @flow */ - export * from "./serialize"; export * from "./deserialize"; export * from "./serializers"; diff --git a/src/serialize.js b/src/serialize.ts similarity index 57% rename from src/serialize.js rename to src/serialize.ts index a054f56..cddedb0 100644 --- a/src/serialize.js +++ b/src/serialize.ts @@ -1,4 +1,4 @@ -/* @flow */ +import type { $Values } from "utility-types"; import { TYPE } from "./constants"; import type { @@ -7,15 +7,17 @@ import type { NativeSerializedType, } from "./types"; import { determineType, isSerializedType } from "./common"; +import type { + SerializedError, + SerializedRegex, + SerializedDate, +} from "./serializers"; import { serializeFunction, serializeError, - type SerializedError, serializePromise, serializeRegex, - type SerializedRegex, serializeDate, - type SerializedDate, serializeArray, serializeObject, serializeString, @@ -25,48 +27,58 @@ import { serializeUndefined, } from "./serializers"; -type NativeSerializer> = ( +type NativeSerializer> = ( value: V, key: string ) => NativeSerializedType; -type CustomSerializer = ( +type CustomSerializer = ( value: V, key: string ) => CustomSerializedType; -type PrimitiveSerializer = (value: V, key: string) => S; -type CustomOrPrimitiveSerializer = - | CustomSerializer - | PrimitiveSerializer; -type NativeOrCustomOrPrimitiveSerializer = - | NativeSerializer - | CustomOrPrimitiveSerializer; +type PrimitiveSerializer = (value: V, key: string) => S; +type CustomOrPrimitiveSerializer = + | CustomSerializer + | PrimitiveSerializer; +type NativeOrCustomOrPrimitiveSerializer< + V, + S, + T extends $Values +> = NativeSerializer | CustomOrPrimitiveSerializer; -type Serializers = {| - function?: CustomOrPrimitiveSerializer, +type Serializers = { + // allow Function keyword as we do expect a generic Function type + // eslint-disable-next-line @typescript-eslint/ban-types + function?: CustomOrPrimitiveSerializer; error?: NativeOrCustomOrPrimitiveSerializer< Error, SerializedError, typeof TYPE.ERROR - >, - promise?: CustomOrPrimitiveSerializer, + >; + promise?: CustomOrPrimitiveSerializer; regex?: NativeOrCustomOrPrimitiveSerializer< RegExp, SerializedRegex, typeof TYPE.REGEX - >, + >; date?: NativeOrCustomOrPrimitiveSerializer< Date, SerializedDate, typeof TYPE.DATE - >, - array?: CustomOrPrimitiveSerializer<$ReadOnlyArray, typeof TYPE.ARRAY>, - object?: CustomOrPrimitiveSerializer, - string?: CustomOrPrimitiveSerializer, - number?: CustomOrPrimitiveSerializer, - boolean?: CustomOrPrimitiveSerializer, - null?: CustomOrPrimitiveSerializer, - undefined?: CustomOrPrimitiveSerializer, -|}; + >; + array?: CustomOrPrimitiveSerializer< + ReadonlyArray, + typeof TYPE.ARRAY + >; + object?: CustomOrPrimitiveSerializer< + Record, + typeof TYPE.OBJECT + >; + string?: CustomOrPrimitiveSerializer; + number?: CustomOrPrimitiveSerializer; + boolean?: CustomOrPrimitiveSerializer; + null?: CustomOrPrimitiveSerializer; + undefined?: CustomOrPrimitiveSerializer; +}; const SERIALIZER: Serializers = { [TYPE.FUNCTION]: serializeFunction, @@ -83,16 +95,18 @@ const SERIALIZER: Serializers = { [TYPE.UNDEFINED]: serializeUndefined, }; -// $FlowFixMe const defaultSerializers: Serializers = {}; -export function serialize( +export function serialize( obj: T, serializers: Serializers = defaultSerializers ): string { - function replacer(key): ?mixed { + function replacer(key: string): unknown | null | undefined { + // @ts-ignore - this has unknown caller + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access const val = this[key]; + // @ts-ignore = this has unknown caller if (isSerializedType(this)) { return val; } @@ -103,13 +117,14 @@ export function serialize( return val; } - // $FlowFixMe - const serializer = serializers[type] || SERIALIZER[type]; + const serializer = serializers[type] ?? SERIALIZER[type]; if (!serializer) { return val; } + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return serializer(val, key); } diff --git a/src/serializers/array.js b/src/serializers/array.js deleted file mode 100644 index 3451726..0000000 --- a/src/serializers/array.js +++ /dev/null @@ -1,15 +0,0 @@ -/* @flow */ - -export type SerializedArray = $ReadOnlyArray; - -export function serializeArray( - val: $ReadOnlyArray -): SerializedArray { - return val; -} - -export function deserializeArray( - val: SerializedArray -): $ReadOnlyArray { - return val; -} diff --git a/src/serializers/array.ts b/src/serializers/array.ts new file mode 100644 index 0000000..08cac69 --- /dev/null +++ b/src/serializers/array.ts @@ -0,0 +1,9 @@ +export type SerializedArray = ReadonlyArray; + +export function serializeArray(val: ReadonlyArray): SerializedArray { + return val; +} + +export function deserializeArray(val: SerializedArray): ReadonlyArray { + return val; +} diff --git a/src/serializers/boolean.js b/src/serializers/boolean.ts similarity index 94% rename from src/serializers/boolean.js rename to src/serializers/boolean.ts index 98f5256..86ddc52 100644 --- a/src/serializers/boolean.js +++ b/src/serializers/boolean.ts @@ -1,5 +1,3 @@ -/* @flow */ - export type SerializedBoolean = boolean; export function serializeBoolean(val: boolean): SerializedBoolean { diff --git a/src/serializers/date.js b/src/serializers/date.ts similarity index 96% rename from src/serializers/date.js rename to src/serializers/date.ts index 73e8583..0f216f9 100644 --- a/src/serializers/date.js +++ b/src/serializers/date.ts @@ -1,5 +1,3 @@ -/* @flow */ - import { serializeType } from "../common"; import { TYPE } from "../constants"; import type { NativeSerializedType } from "../types"; diff --git a/src/serializers/error.js b/src/serializers/error.js deleted file mode 100644 index c300a33..0000000 --- a/src/serializers/error.js +++ /dev/null @@ -1,44 +0,0 @@ -/* @flow */ - -import { serializeType } from "../common"; -import { TYPE } from "../constants"; -import type { NativeSerializedType } from "../types"; - -import { serializeObject } from "./object"; - -export type SerializedError = {| - message: string, - stack: string, - code: string | number | void, - data: mixed, -|}; - -export function serializeError({ - message, - stack, - // $FlowFixMe - code, - // $FlowFixMe - data, -}: Error): NativeSerializedType { - return serializeType(TYPE.ERROR, { message, stack, code, data }); -} - -export function deserializeError({ - message, - stack, - code, - data, -}: SerializedError): Error { - const error = new Error(message); - // $FlowFixMe - error.code = code; - - if (data) { - // $FlowFixMe - error.data = serializeObject(data); - } - - error.stack = `${stack}\n\n${error.stack}`; - return error; -} diff --git a/src/serializers/error.ts b/src/serializers/error.ts new file mode 100644 index 0000000..e1a61b0 --- /dev/null +++ b/src/serializers/error.ts @@ -0,0 +1,48 @@ +import { serializeType } from "../common"; +import { TYPE } from "../constants"; +import type { NativeSerializedType } from "../types"; + +import { serializeObject } from "./object"; + +export type SerializedError = { + message: string; + stack: string | undefined; + code: string | undefined; + data?: Record; +}; + +export type ExtendedError = { + data?: Record; + // eslint-disable-next-line no-undef +} & NodeJS.ErrnoException; + +export function serializeError({ + message, + stack, + code, + data, +}: ExtendedError): NativeSerializedType { + return serializeType(TYPE.ERROR, { + message, + stack, + code, + data, + }); +} + +export function deserializeError({ + message, + stack, + code, + data, +}: SerializedError): ExtendedError { + const error: ExtendedError = new Error(message); + error.code = code; + + if (data) { + error.data = serializeObject(data); + } + + error.stack = `${stack}\n\n${error.stack}`; + return error; +} diff --git a/src/serializers/function.js b/src/serializers/function.js deleted file mode 100644 index 68156b2..0000000 --- a/src/serializers/function.js +++ /dev/null @@ -1,13 +0,0 @@ -/* @flow */ - -export type SerializedFunction = void; - -export function serializeFunction(): SerializedFunction { - // pass -} - -export function deserializeFunction() { - throw new Error( - `Function serialization is not implemented; nothing to deserialize` - ); -} diff --git a/src/serializers/function.ts b/src/serializers/function.ts new file mode 100644 index 0000000..a2eb60d --- /dev/null +++ b/src/serializers/function.ts @@ -0,0 +1,9 @@ +export function serializeFunction(): void { + // pass +} + +export function deserializeFunction(): Error { + throw new Error( + `Function serialization is not implemented; nothing to deserialize` + ); +} diff --git a/src/serializers/index.js b/src/serializers/index.ts similarity index 96% rename from src/serializers/index.js rename to src/serializers/index.ts index 8b8f4d4..8752be1 100644 --- a/src/serializers/index.js +++ b/src/serializers/index.ts @@ -1,5 +1,3 @@ -/* @flow */ - export * from "./array"; export * from "./boolean"; export * from "./date"; diff --git a/src/serializers/null.js b/src/serializers/null.ts similarity index 93% rename from src/serializers/null.js rename to src/serializers/null.ts index 7e421be..a02d2f5 100644 --- a/src/serializers/null.js +++ b/src/serializers/null.ts @@ -1,5 +1,3 @@ -/* @flow */ - export type SerializedNull = null; export function serializeNull(val: null): SerializedNull { diff --git a/src/serializers/number.js b/src/serializers/number.ts similarity index 94% rename from src/serializers/number.js rename to src/serializers/number.ts index b909d88..1990574 100644 --- a/src/serializers/number.js +++ b/src/serializers/number.ts @@ -1,5 +1,3 @@ -/* @flow */ - export type SerializedNumber = number; export function serializeNumber(val: number): SerializedNumber { diff --git a/src/serializers/object.js b/src/serializers/object.js deleted file mode 100644 index d83a33d..0000000 --- a/src/serializers/object.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -export type SerializedObject = Object; - -export function serializeObject(val: Object): SerializedObject { - return val; -} - -export function deserializeObject(val: SerializedObject): Object { - return val; -} diff --git a/src/serializers/object.ts b/src/serializers/object.ts new file mode 100644 index 0000000..c956547 --- /dev/null +++ b/src/serializers/object.ts @@ -0,0 +1,13 @@ +export type SerializedObject = Record; + +export function serializeObject( + val: Record +): SerializedObject { + return val; +} + +export function deserializeObject( + val: SerializedObject +): Record { + return val; +} diff --git a/src/serializers/promise.js b/src/serializers/promise.js deleted file mode 100644 index 5228bc5..0000000 --- a/src/serializers/promise.js +++ /dev/null @@ -1,15 +0,0 @@ -/* @flow */ - -import type { Thenable } from "../types"; - -export type SerializedPromise = void; - -export function serializePromise(): SerializedPromise { - // pass -} - -export function deserializePromise(): Thenable { - throw new Error( - `Promise serialization is not implemented; nothing to deserialize` - ); -} diff --git a/src/serializers/promise.ts b/src/serializers/promise.ts new file mode 100644 index 0000000..6bfc7e4 --- /dev/null +++ b/src/serializers/promise.ts @@ -0,0 +1,9 @@ +export function serializePromise(): void { + // pass +} + +export function deserializePromise(): Error { + throw new Error( + `Promise serialization is not implemented; nothing to deserialize` + ); +} diff --git a/src/serializers/regex.js b/src/serializers/regex.ts similarity index 97% rename from src/serializers/regex.js rename to src/serializers/regex.ts index 880002b..47d470e 100644 --- a/src/serializers/regex.js +++ b/src/serializers/regex.ts @@ -1,5 +1,3 @@ -/* @flow */ - import { serializeType } from "../common"; import { TYPE } from "../constants"; import type { NativeSerializedType } from "../types"; diff --git a/src/serializers/string.js b/src/serializers/string.ts similarity index 94% rename from src/serializers/string.js rename to src/serializers/string.ts index eeeb2d8..4306040 100644 --- a/src/serializers/string.js +++ b/src/serializers/string.ts @@ -1,5 +1,3 @@ -/* @flow */ - export type SerializedString = string; export function serializeString(val: string): SerializedString { diff --git a/src/serializers/undefined.js b/src/serializers/undefined.ts similarity index 84% rename from src/serializers/undefined.js rename to src/serializers/undefined.ts index c6ef86e..4bb2e58 100644 --- a/src/serializers/undefined.js +++ b/src/serializers/undefined.ts @@ -1,13 +1,11 @@ -/* @flow */ - import type { NativeSerializedType } from "../types"; import { serializeType } from "../common"; import { TYPE } from "../constants"; -export type SerializedUndefined = void; +export type SerializedUndefined = undefined; export function serializeUndefined( - val: void + val: undefined ): NativeSerializedType { return serializeType(TYPE.UNDEFINED, val); } diff --git a/src/types.js b/src/types.js deleted file mode 100644 index 96d8580..0000000 --- a/src/types.js +++ /dev/null @@ -1,26 +0,0 @@ -/* @flow */ - -import { TYPE } from "./constants"; - -// export something to force webpack to see this as an ES module -export const TYPES = true; - -// eslint-disable-next-line flowtype/require-exact-type -export type Thenable = { - then: ( - onSuccess?: (val?: mixed) => mixed, - onError?: (err?: mixed) => mixed - ) => Thenable, - catch: (onError?: (err?: mixed) => mixed) => Thenable, -}; - -// eslint-disable-next-line flowtype/require-exact-type -export type NativeSerializedType, V: mixed> = { - __type__: T, - __val__: V, -}; - -export type CustomSerializedType = {| - __type__: T, - __val__: V, -|}; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..07579f7 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,26 @@ +import type { $Values } from "utility-types"; + +import type { TYPE } from "./constants"; + +// export something to force webpack to see this as an ES module +export const TYPES = true; + +export type TypeSerializableTypes = $Values; + +export type Thenable = { + then: ( + onSuccess?: (val?: unknown) => unknown, + onError?: (err?: unknown) => unknown + ) => Thenable; + catch: (onError?: (err?: unknown) => unknown) => Thenable; +}; + +export type NativeSerializedType, V> = { + __type__: T; + __val__: V; +}; + +export type CustomSerializedType = { + __type__: T; + __val__: V; +}; diff --git a/test/tests/basic.js b/test/basic.test.ts similarity index 89% rename from test/tests/basic.js rename to test/basic.test.ts index 23b40b0..5177514 100644 --- a/test/tests/basic.js +++ b/test/basic.test.ts @@ -1,11 +1,12 @@ -/* @flow */ +import { describe, it } from "vitest"; -import { serialize, deserialize } from "../../src"; +import { serialize, deserialize, type ExtendedError } from "../src"; describe("basic type cases", () => { it("should serialize a date", () => { const val = new Date(); - const result = deserialize(serialize(val)); + const result = deserialize(serialize(val)); + if (result.toJSON() !== val.toJSON()) { throw new Error(`Expected ${result.toJSON()} to equal ${val.toJSON()}`); } @@ -14,6 +15,7 @@ describe("basic type cases", () => { it("should serialize a boolean", () => { const val = true; const result = deserialize(serialize(val)); + if (result !== val) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -24,6 +26,7 @@ describe("basic type cases", () => { it("should serialize a string", () => { const val = "hello world\nsup"; const result = deserialize(serialize(val)); + if (result !== val) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -34,6 +37,7 @@ describe("basic type cases", () => { it("should serialize a number", () => { const val = 12345; const result = deserialize(serialize(val)); + if (result !== val) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -44,6 +48,7 @@ describe("basic type cases", () => { it("should serialize a float", () => { const val = 123.45; const result = deserialize(serialize(val)); + if (result !== val) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -54,6 +59,7 @@ describe("basic type cases", () => { it("should serialize an array", () => { const val = [1, 2, 3, "hello", { "5": 6 }]; const result = deserialize(serialize(val)); + if (JSON.stringify(result) !== JSON.stringify(val)) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -64,6 +70,7 @@ describe("basic type cases", () => { it("should serialize an object", () => { const val = { woop: [1, 2, 3, "hello", { "5": 6 }], floop: 5 }; const result = deserialize(serialize(val)); + if (JSON.stringify(result) !== JSON.stringify(val)) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -73,7 +80,8 @@ describe("basic type cases", () => { it("should serialize a regex", () => { const val = /hello world[123]/; - const result = deserialize(serialize(val)); + const result = deserialize(serialize(val)); + if (result.source !== val.source) { throw new Error(`Expected ${result.source} to equal ${val.source}`); } @@ -82,6 +90,7 @@ describe("basic type cases", () => { it("should serialize null", () => { const val = null; const result = deserialize(serialize(val)); + if (result !== val) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${JSON.stringify(val)}` @@ -92,6 +101,7 @@ describe("basic type cases", () => { it("should serialize undefined", () => { const val = undefined; const result = deserialize(serialize(val)); + if (result !== val) { throw new Error( `Expected ${JSON.stringify(result)} to equal ${ @@ -103,7 +113,8 @@ describe("basic type cases", () => { it("should serialize undefined in an object", () => { const obj = { foo: undefined }; - const result = deserialize(serialize(obj)); + const result = deserialize>(serialize(obj)); + if (result.foo !== obj.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${ @@ -116,8 +127,11 @@ describe("basic type cases", () => { it("should serialize undefined in an array", () => { const arr = [undefined]; const result = deserialize(serialize(arr)); + + // @ts-ignore if (arr[0] !== result[0]) { throw new Error( + // @ts-ignore `Expected ${JSON.stringify(result[0]) || "undefined"} to equal ${ JSON.stringify(arr[0]) || "undefined" }` @@ -126,37 +140,39 @@ describe("basic type cases", () => { }); it("should serialize an error", () => { - const val = new Error("meep"); - // $FlowFixMe + const val: ExtendedError = new Error("meep"); val.code = "ERROR_55"; - // $FlowFixMe val.data = { zerp: "blerp" }; - const result = deserialize(serialize(val)); + const result = deserialize(serialize(val)); + if (!(result instanceof Error)) { throw new TypeError(`Expected result to be an instance of error`); } + if (result.message !== val.message) { throw new Error( `Expected message ${result.message} to equal ${val.message}` ); } - // $FlowFixMe + if (result.code !== val.code) { - // $FlowFixMe throw new Error(`Expected message ${result.code} to equal ${val.code}`); } + if (!result.data || result.data.zerp !== "blerp") { throw new Error(`Expected err.data to be serialized`); } - if (result.stack.indexOf(val.stack)) { + + // @ts-ignore + if (result?.stack.indexOf(val?.stack)) { throw new Error(`Expected stack ${result.stack} to contain ${val.stack}`); } }); it("should silently remove promises", () => { - // eslint-disable-next-line no-restricted-globals, compat/compat, promise/no-native const val = Promise.resolve(1); const result = deserialize(serialize(val)); + if (result !== undefined) { throw new Error(`Expected ${result} to equal undefined`); } @@ -166,7 +182,9 @@ describe("basic type cases", () => { const val = function foo(bar: string): string { return bar; }; + const result = deserialize(serialize(val)); + if (result !== undefined) { throw new Error(`Expected ${result} to equal undefined`); } diff --git a/test/tests/custom.js b/test/custom.test.ts similarity index 84% rename from test/tests/custom.js rename to test/custom.test.ts index 828839a..2bb5290 100644 --- a/test/tests/custom.js +++ b/test/custom.test.ts @@ -1,5 +1,5 @@ -/* @flow */ /* eslint max-lines: off */ +import { describe, it } from "vitest"; import { TYPE, @@ -8,11 +8,10 @@ import { serializeType, serializeObject, type CustomSerializedType, -} from "../../src"; +} from "../src"; describe("custom type cases", () => { const CUSTOM_SERIALIZATION = "CUSTOM_SERIALIZATION"; - it("should serialize a date with a custom serializer and deserializer", () => { const val = { foo: new Date() }; @@ -21,8 +20,8 @@ describe("custom type cases", () => { const serializers = { [TYPE.DATE]: ( - value, - key + value: Date, + key: string ): CustomSerializedType => { if (value.toJSON() !== val.foo.toJSON()) { throw new Error( @@ -41,7 +40,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -58,7 +57,11 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); + if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); } @@ -72,8 +75,8 @@ describe("custom type cases", () => { const serializers = { [TYPE.BOOLEAN]: ( - value, - key + value: boolean, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -92,7 +95,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -109,7 +112,11 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); + if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); } @@ -123,8 +130,8 @@ describe("custom type cases", () => { const serializers = { [TYPE.STRING]: ( - value, - key + value: string, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -143,7 +150,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -159,8 +166,10 @@ describe("custom type cases", () => { return deserializedValue; }, }; - - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -169,13 +178,14 @@ describe("custom type cases", () => { it("should serialize a number with a custom serializer and deserializer", () => { const val = { foo: 12345 }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.NUMBER]: ( - value, - key + value: number, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -194,7 +204,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -210,8 +220,10 @@ describe("custom type cases", () => { return deserializedValue; }, }; - - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${val.foo}`); @@ -220,13 +232,14 @@ describe("custom type cases", () => { it("should serialize a float with a custom serializer and deserializer", () => { const val = { foo: 123.45 }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.NUMBER]: ( - value, - key + value: number, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -245,7 +258,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -262,7 +275,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${val.foo}`); @@ -271,13 +287,14 @@ describe("custom type cases", () => { it("should serialize an array with a custom serializer and deserializer", () => { const val = { foo: [1, 2, 3, "hello", { "5": 6 }] }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.ARRAY]: ( - value, - key + value: unknown, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -296,7 +313,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -313,7 +330,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -322,15 +342,17 @@ describe("custom type cases", () => { it("should serialize an object with a custom serializer and deserializer", () => { const val = { foo: { woop: [1, 2, 3, "hello", { "5": 6 }], floop: 5 } }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.OBJECT]: ( - value, - key - ): CustomSerializedType => { + value: Record, + key: string + ): CustomSerializedType => { if (value !== val.foo) { + // @ts-ignore return serializeObject(value); } @@ -343,7 +365,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -360,7 +382,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -369,13 +394,14 @@ describe("custom type cases", () => { it("should serialize a regex with a custom serializer and deserializer", () => { const val = { foo: /hello world[123]/ }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.REGEX]: ( - value, - key + value: RegExp, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -392,9 +418,8 @@ describe("custom type cases", () => { return serializeType(CUSTOM_SERIALIZATION, serializedValue); }, }; - const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -411,7 +436,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -419,14 +447,15 @@ describe("custom type cases", () => { }); it("should serialize null with a custom serializer and deserializer", () => { - const val = { foo: null }; + const val: Record = { foo: null }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.NULL]: ( - value, - key + value: null, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -443,9 +472,8 @@ describe("custom type cases", () => { return serializeType(CUSTOM_SERIALIZATION, serializedValue); }, }; - const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -462,7 +490,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -471,13 +502,14 @@ describe("custom type cases", () => { it("should serialize an error with a custom serializer and deserializer", () => { const val = { foo: new Error("meep") }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.ERROR]: ( - value, - key + value: Error, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -494,9 +526,8 @@ describe("custom type cases", () => { return serializeType(CUSTOM_SERIALIZATION, serializedValue); }, }; - const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -513,7 +544,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -521,15 +555,15 @@ describe("custom type cases", () => { }); it("should serialize a promise with a custom serializer and deserializer", () => { - // eslint-disable-next-line no-restricted-globals, compat/compat, promise/no-native const val = { foo: Promise.resolve(1) }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.PROMISE]: ( - value, - key + value: unknown, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -548,7 +582,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -565,7 +599,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -578,13 +615,14 @@ describe("custom type cases", () => { return bar; }, }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.FUNCTION]: ( - value, - key + value: unknown, + key: string ): CustomSerializedType => { if (value !== val.foo) { throw new Error( @@ -603,7 +641,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -620,7 +658,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>( + serialize(val, serializers), + deserializers + ); if (result.foo !== deserializedValue) { throw new Error(`Expected ${result.foo} to equal ${deserializedValue}`); @@ -635,13 +676,14 @@ describe("custom type cases", () => { }, ], }; + const serializedValue = `serialized::${Math.random().toString()}`; const deserializedValue = `deserialized::${Math.random().toString()}`; const serializers = { [TYPE.FUNCTION]: ( - value, - key + value: unknown, + key: string ): CustomSerializedType => { if (value !== val.blerp[0]) { throw new Error( @@ -660,7 +702,7 @@ describe("custom type cases", () => { }; const deserializers = { - [CUSTOM_SERIALIZATION]: (value, key) => { + [CUSTOM_SERIALIZATION]: (value: unknown, key: string) => { if (value !== serializedValue) { throw new Error( `Expected ${JSON.stringify(value)} to equal ${JSON.stringify( @@ -677,7 +719,10 @@ describe("custom type cases", () => { }, }; - const result = deserialize(serialize(val, serializers), deserializers); + const result = deserialize>>( + serialize(val, serializers), + deserializers + ); if (result.blerp[0] !== deserializedValue) { throw new Error( diff --git a/test/tests/embedded.js b/test/embedded.test.ts similarity index 70% rename from test/tests/embedded.js rename to test/embedded.test.ts index a054958..8475f27 100644 --- a/test/tests/embedded.js +++ b/test/embedded.test.ts @@ -1,11 +1,14 @@ -/* @flow */ +import { describe, it } from "vitest"; -import { serialize, deserialize } from "../../src"; +import { serialize, deserialize, type ExtendedError } from "../src"; describe("basic type cases", () => { it("should serialize a date embedded in an object", () => { - const val = { foo: new Date() }; - const result = deserialize(serialize(val)); + const val = { + foo: new Date(), + }; + const result = deserialize>(serialize(val)); + if (result.foo.toJSON() !== val.foo.toJSON()) { throw new Error( `Expected ${result.foo.toJSON()} to equal ${val.foo.toJSON()}` @@ -14,8 +17,11 @@ describe("basic type cases", () => { }); it("should serialize a boolean embedded in an object", () => { - const val = { foo: true }; - const result = deserialize(serialize(val)); + const val = { + foo: true, + }; + const result = deserialize>(serialize(val)); + if (result.foo !== val.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -26,8 +32,11 @@ describe("basic type cases", () => { }); it("should serialize a string embedded in an object", () => { - const val = { foo: "hello world\nsup" }; - const result = deserialize(serialize(val)); + const val = { + foo: "hello world\nsup", + }; + const result = deserialize>(serialize(val)); + if (result.foo !== val.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -38,8 +47,11 @@ describe("basic type cases", () => { }); it("should serialize a number embedded in an object", () => { - const val = { foo: 12345 }; - const result = deserialize(serialize(val)); + const val = { + foo: 12345, + }; + const result = deserialize>(serialize(val)); + if (result.foo !== val.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -51,7 +63,8 @@ describe("basic type cases", () => { it("should serialize a float embedded in an object", () => { const val = { foo: 123.45 }; - const result = deserialize(serialize(val)); + const result = deserialize>(serialize(val)); + if (result.foo !== val.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -63,7 +76,8 @@ describe("basic type cases", () => { it("should serialize an array embedded in an object", () => { const val = { foo: [1, 2, 3, "hello", { "5": 6 }] }; - const result = deserialize(serialize(val)); + const result = deserialize>(serialize(val)); + if (JSON.stringify(result.foo) !== JSON.stringify(val.foo)) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -75,7 +89,8 @@ describe("basic type cases", () => { it("should serialize an object embedded in an object", () => { const val = { foo: { woop: [1, 2, 3, "hello", { "5": 6 }], floop: 5 } }; - const result = deserialize(serialize(val)); + const result = deserialize>(serialize(val)); + if (JSON.stringify(result.foo) !== JSON.stringify(val.foo)) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -86,8 +101,11 @@ describe("basic type cases", () => { }); it("should serialize a regex embedded in an object", () => { - const val = { foo: /hello world[123]/ }; - const result = deserialize(serialize(val)); + const val = { + foo: /hello world[123]/, + }; + const result = deserialize>(serialize(val)); + if (result.foo.source !== val.foo.source) { throw new Error( `Expected ${JSON.stringify( @@ -99,7 +117,8 @@ describe("basic type cases", () => { it("should serialize null embedded in an object", () => { const val = { foo: null }; - const result = deserialize(serialize(val)); + const result = deserialize>(serialize(val)); + if (result.foo !== val.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${JSON.stringify( @@ -111,7 +130,8 @@ describe("basic type cases", () => { it("should serialize undefined embedded in an object", () => { const val = { foo: undefined }; - const result = deserialize(serialize(val)); + const result = deserialize>(serialize(val)); + if (result.foo !== val.foo) { throw new Error( `Expected ${JSON.stringify(result.foo)} to equal ${ @@ -122,25 +142,27 @@ describe("basic type cases", () => { }); it("should serialize an error embedded in an object", () => { - const val = { foo: new Error("meep") }; - // $FlowFixMe + const val: Record = { foo: new Error("meep") }; val.foo.code = "ERROR_55"; - const result = deserialize(serialize(val)); + const result = deserialize>(serialize(val)); + if (!(result.foo instanceof Error)) { throw new TypeError(`Expected result.foo to be an instance of error`); } + if (result.foo.message !== val.foo.message) { throw new Error( `Expected message ${result.foo.message} to equal ${val.foo.message}` ); } - // $FlowFixMe + if (result.foo.code !== val.foo.code) { throw new Error( - // $FlowFixMe `Expected message ${result.foo.code} to equal ${val.foo.code}` ); } + + // @ts-ignore if (result.foo.stack.indexOf(val.foo.stack) === -1) { throw new Error( `Expected stack ${result.foo.stack} to contain ${val.foo.stack}` @@ -149,21 +171,25 @@ describe("basic type cases", () => { }); it("should silently remove promises embedded in an object", () => { - // eslint-disable-next-line no-restricted-globals, compat/compat, promise/no-native const val = { foo: Promise.resolve(1) }; - const result = deserialize(serialize(val)); + const result = deserialize>>(serialize(val)); if (result.foo !== undefined) { + // beacuse this silently removes embedded promises we need to ignore the check + // eslint-disable-next-line @typescript-eslint/no-base-to-string throw new Error(`Expected ${result.foo} to equal undefined`); } }); - it("should silently remove functions embedded in an object", () => { + it("should silently remove functions embedded in an object", () => { const val = { foo: function foo(bar: string): string { return bar; }, }; - const result = deserialize(serialize(val)); + + // eslint-disable-next-line @typescript-eslint/ban-types + const result = deserialize>(serialize(val)); + if (result.foo !== undefined) { throw new Error(`Expected ${result.foo} to equal undefined`); } diff --git a/test/tests/error.js b/test/error.test.ts similarity index 88% rename from test/tests/error.js rename to test/error.test.ts index 06fb31f..419354d 100644 --- a/test/tests/error.js +++ b/test/error.test.ts @@ -1,6 +1,6 @@ -/* @flow */ +import { describe, it } from "vitest"; -import { deserialize } from "../../src"; +import { deserialize } from "../src"; describe("error cases", () => { it("should error while trying to deserialize a function", () => { diff --git a/test/index.js b/test/index.js deleted file mode 100644 index d30bfb7..0000000 --- a/test/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/* @flow */ - -import "./util"; -import "./tests"; diff --git a/test/tests/index.js b/test/tests/index.js deleted file mode 100644 index 906641b..0000000 --- a/test/tests/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/* @flow */ - -import "./basic"; -import "./error"; -import "./embedded"; -import "./custom"; diff --git a/test/util.js b/test/util.js deleted file mode 100644 index f710e08..0000000 --- a/test/util.js +++ /dev/null @@ -1,10 +0,0 @@ -/* @flow */ - -window.console.karma = function consoleKarma() { - const karma = - window.karma || - (window.top && window.top.karma) || - (window.opener && window.opener.karma); - karma.log("debug", arguments); - console.log.apply(console, arguments); // eslint-disable-line no-console -}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a071093 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@krakenjs/grumbler-scripts/config/tsconfig.json" +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..5883cfd --- /dev/null +++ b/vite.config.js @@ -0,0 +1,31 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable spaced-comment */ +/// + +// Configure Vitest (https://vitest.dev/config/) + +import path from "path"; + +import { defineConfig } from "vite"; + +// eslint-disable-next-line import/no-default-export +export default defineConfig({ + build: { + lib: { + entry: path.resolve(__dirname, "src/index.ts"), + name: "crossDomainUtil", + fileName: (format) => `cross-domain-utils.${format}.js`, + formats: ["es", "umd"], + }, + sourcemap: true, + rollupOptions: { + ouput: { + preserveModules: true, + }, + }, + }, + test: { + /* for example, use global to avoid globals imports (describe, test, expect): */ + // globals: true, + }, +}); diff --git a/webpack.config.js b/webpack.config.ts similarity index 90% rename from webpack.config.js rename to webpack.config.ts index f8af7f9..544f150 100644 --- a/webpack.config.js +++ b/webpack.config.ts @@ -1,6 +1,7 @@ /* @flow */ /* eslint import/no-nodejs-modules: off, import/no-default-export: off */ +// @ts-ignore import { getWebpackConfig } from "@krakenjs/grumbler-scripts/config/webpack.config"; import type { WebpackConfig } from "@krakenjs/grumbler-scripts/config/types"; @@ -8,11 +9,13 @@ const FILE_NAME = "universalSerialize"; const MODULE_NAME = "universal-serialize"; export const WEBPACK_CONFIG: WebpackConfig = getWebpackConfig({ + entry: "./src/index.ts", filename: `${FILE_NAME}.js`, modulename: MODULE_NAME, }); export const WEBPACK_CONFIG_MIN: WebpackConfig = getWebpackConfig({ + entry: "./src/index.ts", filename: `${FILE_NAME}.min.js`, modulename: MODULE_NAME, minify: true, @@ -22,6 +25,7 @@ export const WEBPACK_CONFIG_MIN: WebpackConfig = getWebpackConfig({ }); export const WEBPACK_CONFIG_TEST: WebpackConfig = getWebpackConfig({ + entry: "./src/index.ts", modulename: MODULE_NAME, options: { devtool: "inline-source-map",