Skip to content

Commit

Permalink
feat: stable mapSliceZone (previously unstable_mapSliceZone) (#336)
Browse files Browse the repository at this point in the history
* feat: stable `mapSliceZone`

* fix: export deprecated `unstable_mapSliceZone`

* test: verify unstable alias
  • Loading branch information
angeloashmore authored Mar 27, 2024
1 parent bd55071 commit b6852a4
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 68 deletions.
127 changes: 70 additions & 57 deletions src/helpers/unstable_mapSliceZone.ts → src/helpers/mapSliceZone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type LazyModule<T> = () => Promise<T | { default: T }>;
*/
type MaybeLazyModule<T> = T | LazyModule<T>;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyFunction = (...args: any[]) => any;

/**
* Returns the type of a `SliceLike` type.
*
Expand All @@ -24,7 +27,7 @@ type ExtractSliceType<TSlice extends SliceLike> = TSlice extends Slice

/**
* The minimum required properties to represent a Prismic Slice from the Prismic
* Rest API V2 for the `unstable_mapSliceZone()` helper.
* Rest API V2 for the `mapSliceZone()` helper.
*
* @typeParam SliceType - Type name of the Slice.
*/
Expand All @@ -35,7 +38,7 @@ type SliceLikeRestV2<TSliceType extends string = string> = Pick<

/**
* The minimum required properties to represent a Prismic Slice from the Prismic
* GraphQL API for the `unstable_mapSliceZone()` helper.
* GraphQL API for the `mapSliceZone()` helper.
*
* @typeParam SliceType - Type name of the Slice.
*/
Expand All @@ -45,7 +48,7 @@ type SliceLikeGraphQL<TSliceType extends string = string> = {

/**
* The minimum required properties to represent a Prismic Slice for the
* `unstable_mapSliceZone()` helper.
* `mapSliceZone()` helper.
*
* If using Prismic's Rest API V2, use the `Slice` export from
* `@prismicio/client` for a full interface.
Expand Down Expand Up @@ -84,11 +87,11 @@ type MappedSliceLike = {

/**
* Arguments for a function mapping content from a Prismic Slice using the
* `unstable_mapSliceZone()` helper.
* `mapSliceZone()` helper.
*
* @typeParam TSlice - The Slice passed as a prop.
* @typeParam TContext - Arbitrary data passed to `unstable_mapSliceZone()` and
* made available to all Slice mappers.
* @typeParam TContext - Arbitrary data passed to `mapSliceZone()` and made
* available to all Slice mappers.
*/
type SliceMapperArgs<
TSlice extends SliceLike = SliceLike,
Expand Down Expand Up @@ -116,21 +119,18 @@ type SliceMapperArgs<
>;

/**
* Arbitrary data passed to `unstable_mapSliceZone()` and made available to
* all Slice mappers.
* Arbitrary data passed to `mapSliceZone()` and made available to all Slice
* mappers.
*/
context: TContext;
};

/**
* A record of mappers.
*/
export type Mappers<
TSlice extends SliceLike = SliceLike,
TContext = unknown,
> = {
[P in ExtractSliceType<TSlice>]: MaybeLazyModule<
Mapper<
type SliceMappers<TSlice extends SliceLike = SliceLike, TContext = unknown> = {
[P in ExtractSliceType<TSlice>]?: MaybeLazyModule<
SliceMapper<
Extract<TSlice, SliceLike<P>>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any,
Expand All @@ -143,60 +143,67 @@ export type Mappers<
* A function that maps a Slice and its metadata to a modified version. The
* return value will replace the Slice in the Slice Zone.
*/
export type Mapper<
export type SliceMapper<
TSlice extends SliceLike = SliceLike,
TMappedSlice extends Record<string, unknown> | undefined | void =
| Record<string, unknown>
| undefined
| void,
TContext = unknown,
> = (
args: MapperArgs<TSlice, TContext>,
args: SliceMapperArgs<TSlice, TContext>,
) => TMappedSlice | Promise<TMappedSlice>;

/**
* Arguments provided to a mapper function.
*/
export type MapperArgs<
TSlice extends SliceLike = SliceLike,
TContext = unknown,
> = SliceMapperArgs<TSlice, TContext>;

/**
* Unwraps a lazily loaded mapper module.
*/
type ResolveLazyMapperModule<TMapper extends Mapper | LazyModule<Mapper>> =
TMapper extends LazyModule<Mapper>
? Awaited<ReturnType<TMapper>> extends {
default: unknown;
}
? Awaited<ReturnType<TMapper>>["default"]
: Awaited<ReturnType<TMapper>>
: TMapper;
type ResolveLazySliceMapperModule<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TSliceMapper extends SliceMapper<any, any> | LazyModule<SliceMapper>,
> = TSliceMapper extends LazyModule<SliceMapper>
? Awaited<ReturnType<TSliceMapper>> extends {
default: unknown;
}
? Awaited<ReturnType<TSliceMapper>>["default"]
: Awaited<ReturnType<TSliceMapper>>
: TSliceMapper;

/**
* Transforms a Slice into its mapped version.
*/
type MapSliceLike<
TSliceLike extends SliceLike,
TMappers extends Mappers,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TSliceLike extends SliceLike<any>,
TSliceMappers extends SliceMappers<
SliceLike<ExtractSliceType<TSliceLike>>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any
>,
> = TSliceLike extends Slice
? TSliceLike["slice_type"] extends keyof TMappers
? SliceLikeRestV2<TSliceLike["slice_type"]> &
MappedSliceLike &
Awaited<
ReturnType<
ResolveLazyMapperModule<TMappers[TSliceLike["slice_type"]]>
? TSliceLike["slice_type"] extends keyof TSliceMappers
? TSliceMappers[TSliceLike["slice_type"]] extends AnyFunction
? SliceLikeRestV2<TSliceLike["slice_type"]> &
MappedSliceLike &
Awaited<
ReturnType<
ResolveLazySliceMapperModule<
TSliceMappers[TSliceLike["slice_type"]]
>
>
>
>
: TSliceLike
: TSliceLike
: TSliceLike extends SliceLikeGraphQL
? TSliceLike["type"] extends keyof TMappers
? SliceLikeGraphQL<TSliceLike["type"]> &
MappedSliceLike &
Awaited<
ReturnType<ResolveLazyMapperModule<TMappers[TSliceLike["type"]]>>
>
? TSliceLike["type"] extends keyof TSliceMappers
? TSliceMappers[TSliceLike["type"]] extends AnyFunction
? SliceLikeGraphQL<TSliceLike["type"]> &
MappedSliceLike &
Awaited<
ReturnType<
ResolveLazySliceMapperModule<TSliceMappers[TSliceLike["type"]]>
>
>
: TSliceLike
: TSliceLike
: never;

Expand All @@ -210,31 +217,34 @@ type MapSliceLike<
* @example
*
* ```typescript
* const mappedSliceZone = await unstable_mapSliceZone(page.data.slices, {
* const mappedSliceZone = await mapSliceZone(page.data.slices, {
* code_block: ({ slice }) => ({
* codeHTML: await highlight(slice.primary.code),
* }),
* });
* ```
*
* @experimental Names and implementations may change in the future.
* `unstable_mapSliceZone()` does not follow SemVer.
*/
export function unstable_mapSliceZone<
export function mapSliceZone<
TSliceLike extends SliceLike,
TMappers extends Mappers,
TSliceMappers extends SliceMappers<TSliceLike, TContext>,
TContext = unknown,
>(
sliceZone: SliceZoneLike<TSliceLike>,
mappers: TMappers,
mappers: TSliceMappers,
context?: TContext,
): Promise<MapSliceLike<TSliceLike, TMappers>[]> {
): Promise<
MapSliceLike<
TSliceLike,
// @ts-expect-error - I don't know how to fix this type
TSliceMappers
>[]
> {
return Promise.all(
sliceZone.map(async (slice, index, slices) => {
const isRestSliceType = "slice_type" in slice;
const sliceType = isRestSliceType ? slice.slice_type : slice.type;

const mapper = mappers[sliceType];
const mapper = mappers[sliceType as keyof typeof mappers];

if (!mapper) {
return slice;
Expand All @@ -244,7 +254,10 @@ export function unstable_mapSliceZone<

// `result` may be a mapper function OR a module
// containing a mapper function.
let result = await mapper(mapperArgs);
let result = await mapper(
// @ts-expect-error - I don't know how to fix this type
mapperArgs,
);

// `result` is a module containing a mapper function,
// we need to dig out the mapper function. `result`
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
HTMLRichTextFunctionSerializer,
HTMLRichTextMapSerializer,
} from "./helpers/asHTML";
import { mapSliceZone } from "./helpers/mapSliceZone";

import { filter } from "./filter";

Expand Down Expand Up @@ -69,14 +70,20 @@ export { asHTML } from "./helpers/asHTML";
export { asImageSrc } from "./helpers/asImageSrc";
export { asImageWidthSrcSet } from "./helpers/asImageWidthSrcSet";
export { asImagePixelDensitySrcSet } from "./helpers/asImagePixelDensitySrcSet";
export { unstable_mapSliceZone } from "./helpers/unstable_mapSliceZone";
export * as isFilled from "./helpers/isFilled";

/**
* @deprecated Renamed to `mapSliceZone`
*/
const unstable_mapSliceZone = mapSliceZone;
export { mapSliceZone, unstable_mapSliceZone };

// Conversion helper.
export { documentToLinkField } from "./helpers/documentToLinkField";

export type { LinkResolverFunction } from "./helpers/asLink";
export type { AsLinkAttrsConfig } from "./helpers/asLinkAttrs";
export type { SliceMapper } from "./helpers/mapSliceZone";

/**
* @deprecated Renamed to `HTMLRichTextMapSerializer`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TestContext, expect, it, vi } from "vitest";

import { unstable_mapSliceZone } from "../src";
import { mapSliceZone } from "../src";

const generateTestData = (ctx: TestContext) => {
const model1 = ctx.mock.model.sharedSlice({
Expand Down Expand Up @@ -35,7 +35,7 @@ const generateTestData = (ctx: TestContext) => {
it("maps a Slice Zone", async (ctx) => {
const { sliceZone, model1, model2 } = generateTestData(ctx);

const actual = await unstable_mapSliceZone(
const actual = await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -53,7 +53,7 @@ it("maps a Slice Zone", async (ctx) => {
it("supports mapping functions that return undefined", async (ctx) => {
const { sliceZone, model1, model2 } = generateTestData(ctx);

const actual = await unstable_mapSliceZone(
const actual = await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -71,7 +71,7 @@ it("supports mapping functions that return undefined", async (ctx) => {
it("supports async mapping functions", async (ctx) => {
const { sliceZone, model1, model2 } = generateTestData(ctx);

const actual = await unstable_mapSliceZone(
const actual = await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -89,7 +89,7 @@ it("supports async mapping functions", async (ctx) => {
it("supports overriding id and slice_type properties", async (ctx) => {
const { sliceZone, model1, model2 } = generateTestData(ctx);

const actual = await unstable_mapSliceZone(
const actual = await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -110,7 +110,7 @@ it("provides Slice data to mapping functions", async (ctx) => {
const mapper1 = vi.fn();
const mapper2 = vi.fn();

await unstable_mapSliceZone(
await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand Down Expand Up @@ -141,7 +141,7 @@ it("supports context", async (ctx) => {

const context = { foo: "bar" };

await unstable_mapSliceZone(
await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -168,7 +168,7 @@ it("supports context", async (ctx) => {
it("supports lazy-loaded mapping functions", async (ctx) => {
const { sliceZone, model1, model2 } = generateTestData(ctx);

const actual = await unstable_mapSliceZone(
const actual = await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -188,7 +188,7 @@ it("supports lazy-loaded mapping functions", async (ctx) => {
it("skips Slices without a mapping function", async (ctx) => {
const { sliceZone, model1 } = generateTestData(ctx);

const actual = await unstable_mapSliceZone(
const actual = await mapSliceZone(
// @ts-expect-error Remove this comment after v7.3.0 is published.
sliceZone,
{
Expand All @@ -205,7 +205,7 @@ it("skips Slices without a mapping function", async (ctx) => {
it("supports GraphQL Slice Zones", async () => {
const sliceZone = [{ type: "foo" }, { type: "bar" }];

const actual = await unstable_mapSliceZone(sliceZone, {
const actual = await mapSliceZone(sliceZone, {
foo: () => ({ foo: "bar" }),
bar: () => ({ baz: "qux" }),
});
Expand Down
5 changes: 5 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,8 @@ it("webhook type mapping", () => {
it("predicate is a temporary alias for filter", () => {
expect(prismic.predicate).toStrictEqual(prismic.filter);
});

// TODO: Remove when we remove support for deprecated `unstable_mapSliceZone` export.
it("unstable_mapSliceZone is a temporary alias for mapSliceZone", () => {
expect(prismic.unstable_mapSliceZone).toStrictEqual(prismic.mapSliceZone);
});

0 comments on commit b6852a4

Please sign in to comment.