Skip to content

Add support for serializer presets #157

@zsilbi

Description

@zsilbi

Describe the feature

I think it is almost time we think about supporting extended serializers (like a preset addon for supporting HTML serialization), even though bundle addition is few bytes still is not needed for many (I imagine most of) users.

Originally posted by @pi0 in #155 (comment)

I thought about two ways to approach this:

[A] Handle presets within the Serializer instance

Depending on how many hooks we use this would add some overhead because it checks for possible presets and their handlers before each serialization.

interface SerializerPreset {
  /**
   * Runs before the default serialization.
   *
   * @param input - input value to serialize
   * @param serialize - to use for inner structures
   * @param context - to serialization context
   * @returns The serialized string or undefined if not handled
   */
  serializeBefore?: (
    input: unknown,
    serializer: {
      serialize: (input: unknown) => string;
      context: Map<object, string>;
    },
  ) => string | undefined;

  /**
   * Runs after the default serialization before the `object.entries` check in `serailizeBuiltInType()`
   *
   * @param input- input value to serialize
   * @param type - type of the input object (for example: "HTMLCollection")
   * @param serialize - to use for inner structures
   * @param context - to serialization context
   * @returns The serialized string or undefined if not handled
   */
  serializeAfter?: (
    input: object,
    type: string,
    serializer: {
      serialize: (input: unknown) => string;
      context: Map<object, string>;
    },
  ) => string | undefined;
}

import { serialize } from "ohash";
import customPreset from "ohash/presets/custom";

serialize("hello world", { presets: [customPreset] });

[B] Create customized Serializer prototypes using the presets

This would be a bit faster as prototype creation could be optimized to add hooks only when handlers for that specific hook are provided from the presets.

The extend() helper would allow extending the protoype with custom type handlers directly.
This would eliminate the necessity to use hooks at all in some presets.

interface SerializerPreset {
  /**
   * Allows extending the serializer with custom types.
   *
   * @param serializer - serializer to extend
   * @example
   * ```ts
   * setup(serializer) {
   *   serializer.extend(
   *     "HTMLCollection",
   *     (collection: HTMLCollection, type, { serialize, context }) =>
   *       `${type}${serialize(Array.from(collection))}`
   *   );
   * }
   * ```
   */
  setup?: <const T extends string | string[], V>(serializer: {
    extend: (
      types: T,
      handler: (
        input: V,
        type: T extends string ? T : T[number],
        serializer: {
          serialize: (input: unknown) => string;
          context: Map<object, string>;
        },
      ) => string,
    ) => void;
  }) => void;
  // ...serializeBefore
  // ...serializeAfter
}

import { ohash, createSerializer } from "ohash";
import customPreset from "ohash/presets/custom";

const { serialize, isEqual, hash } = ohash({ presets: [customPreset] });
serialize("hello world");

const customSerialize = createSerializer({ presets: [customPreset] });
customSerialize("hello world");

Creating a custom prototype on each serialize() call would be much slower, so createSerializer() creates the custom Serializer prototype and provides a serialize function to use it with.

Additional information

  • Would you be willing to help implement this feature?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions