diff --git a/src/helpers/unstable_mapSliceZone.ts b/src/helpers/mapSliceZone.ts similarity index 68% rename from src/helpers/unstable_mapSliceZone.ts rename to src/helpers/mapSliceZone.ts index fc03d9cd..909c56c0 100644 --- a/src/helpers/unstable_mapSliceZone.ts +++ b/src/helpers/mapSliceZone.ts @@ -11,6 +11,9 @@ type LazyModule = () => Promise; */ type MaybeLazyModule = T | LazyModule; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyFunction = (...args: any[]) => any; + /** * Returns the type of a `SliceLike` type. * @@ -24,7 +27,7 @@ type ExtractSliceType = 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. */ @@ -35,7 +38,7 @@ type SliceLikeRestV2 = 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. */ @@ -45,7 +48,7 @@ type SliceLikeGraphQL = { /** * 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. @@ -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, @@ -116,8 +119,8 @@ 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; }; @@ -125,12 +128,9 @@ type SliceMapperArgs< /** * A record of mappers. */ -export type Mappers< - TSlice extends SliceLike = SliceLike, - TContext = unknown, -> = { - [P in ExtractSliceType]: MaybeLazyModule< - Mapper< +type SliceMappers = { + [P in ExtractSliceType]?: MaybeLazyModule< + SliceMapper< Extract>, // eslint-disable-next-line @typescript-eslint/no-explicit-any any, @@ -143,7 +143,7 @@ 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 | undefined | void = | Record @@ -151,52 +151,59 @@ export type Mapper< | void, TContext = unknown, > = ( - args: MapperArgs, + args: SliceMapperArgs, ) => TMappedSlice | Promise; -/** - * Arguments provided to a mapper function. - */ -export type MapperArgs< - TSlice extends SliceLike = SliceLike, - TContext = unknown, -> = SliceMapperArgs; - /** * Unwraps a lazily loaded mapper module. */ -type ResolveLazyMapperModule> = - TMapper extends LazyModule - ? Awaited> extends { - default: unknown; - } - ? Awaited>["default"] - : Awaited> - : TMapper; +type ResolveLazySliceMapperModule< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + TSliceMapper extends SliceMapper | LazyModule, +> = TSliceMapper extends LazyModule + ? Awaited> extends { + default: unknown; + } + ? Awaited>["default"] + : Awaited> + : 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, + TSliceMappers extends SliceMappers< + SliceLike>, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any + >, > = TSliceLike extends Slice - ? TSliceLike["slice_type"] extends keyof TMappers - ? SliceLikeRestV2 & - MappedSliceLike & - Awaited< - ReturnType< - ResolveLazyMapperModule + ? TSliceLike["slice_type"] extends keyof TSliceMappers + ? TSliceMappers[TSliceLike["slice_type"]] extends AnyFunction + ? SliceLikeRestV2 & + MappedSliceLike & + Awaited< + ReturnType< + ResolveLazySliceMapperModule< + TSliceMappers[TSliceLike["slice_type"]] + > + > > - > + : TSliceLike : TSliceLike : TSliceLike extends SliceLikeGraphQL - ? TSliceLike["type"] extends keyof TMappers - ? SliceLikeGraphQL & - MappedSliceLike & - Awaited< - ReturnType> - > + ? TSliceLike["type"] extends keyof TSliceMappers + ? TSliceMappers[TSliceLike["type"]] extends AnyFunction + ? SliceLikeGraphQL & + MappedSliceLike & + Awaited< + ReturnType< + ResolveLazySliceMapperModule + > + > + : TSliceLike : TSliceLike : never; @@ -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, TContext = unknown, >( sliceZone: SliceZoneLike, - mappers: TMappers, + mappers: TSliceMappers, context?: TContext, -): Promise[]> { +): 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; @@ -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` diff --git a/src/index.ts b/src/index.ts index d6e25b1f..adefa60b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import type { HTMLRichTextFunctionSerializer, HTMLRichTextMapSerializer, } from "./helpers/asHTML"; +import { mapSliceZone } from "./helpers/mapSliceZone"; import { filter } from "./filter"; @@ -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` diff --git a/test/helpers-unstable_mapSliceZone.test.ts b/test/helpers-mapSliceZone.test.ts similarity index 92% rename from test/helpers-unstable_mapSliceZone.test.ts rename to test/helpers-mapSliceZone.test.ts index 60136bb8..8a71c7a2 100644 --- a/test/helpers-unstable_mapSliceZone.test.ts +++ b/test/helpers-mapSliceZone.test.ts @@ -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({ @@ -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, { @@ -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, { @@ -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, { @@ -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, { @@ -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, { @@ -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, { @@ -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, { @@ -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, { @@ -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" }), }); diff --git a/test/index.test.ts b/test/index.test.ts index 165b7494..1963441f 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -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); +});