Skip to content

Instantly share code, notes, and snippets.

@dbritto-dev
Forked from Pagebakers/create-page.tsx
Last active August 3, 2024 04:27
Show Gist options
  • Save dbritto-dev/2e62f19cab4b9a086676e2cb17cd913f to your computer and use it in GitHub Desktop.
Save dbritto-dev/2e62f19cab4b9a086676e2cb17cd913f to your computer and use it in GitHub Desktop.

Revisions

  1. dbritto-dev revised this gist Aug 3, 2024. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -53,8 +53,8 @@ function parseParams<Schema extends readonly string[] | AnyZodObject>(params: Re
    }

    export const createPage = <
    const Params extends readonly string[] | AnyZodObject,
    const SearchParams extends readonly string[] | AnyZodObject,
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>,
    >(
    props: CreatePageProps<Params, SearchParams, Loader>,
  2. dbritto-dev revised this gist Aug 3, 2024. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -61,7 +61,6 @@ export const createPage = <
    ) => {
    const { params: paramsSchema, searchParams: searchParamsSchema, component: PageComponent, loader, metadata } = props;

    // We don't really care about the types here since it's internal
    async function Page(props: {
    params: Record<string, string>;
    searchParams: Record<string, string>;
  3. dbritto-dev revised this gist Aug 3, 2024. 1 changed file with 2 additions and 5 deletions.
    7 changes: 2 additions & 5 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -69,11 +69,8 @@ export const createPage = <
    const params = parseParams(props.params, paramsSchema);
    const searchParams = parseParams(props.searchParams, searchParamsSchema);

    const pageProps: {
    params: InferParams<Params>;
    searchParams: InferParams<SearchParams>;
    data: InferLoaderData<Loader>;
    } = {
    const pageProps = {
    data: undefined as InferLoaderData<Loader>,
    params,
    searchParams,
    };
  4. dbritto-dev revised this gist Aug 3, 2024. 1 changed file with 52 additions and 61 deletions.
    113 changes: 52 additions & 61 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -1,135 +1,126 @@
    import type { AnyZodObject, z } from 'zod'
    import type { Metadata, ResolvingMetadata } from 'next'
    import type { AnyZodObject, z } from "zod";
    import type { Metadata, ResolvingMetadata } from "next";

    type InferParams<Params> = Params extends readonly string[]
    ? {
    [K in Params[number]]?: string
    [K in Params[number]]?: string;
    }
    : Params extends AnyZodObject
    ? z.infer<Params>
    : unknown
    ? z.infer<Params>
    : unknown;

    type LoaderFn<
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject
    SearchParams extends readonly string[] | AnyZodObject,
    > = (args: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    }) => Promise<unknown>
    params: InferParams<Params>;
    searchParams: InferParams<SearchParams>;
    }) => Promise<unknown>;

    type InferLoaderData<Loader> = Loader extends (args: unknown) => Promise<infer T> ? T : unknown
    type InferLoaderData<Loader> = Loader extends (args: unknown) => Promise<infer T> ? T : unknown;

    export interface CreatePageProps<
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>,
    > {
    params?: Params
    searchParams?: SearchParams
    loader?: Loader
    params?: Params;
    searchParams?: SearchParams;
    loader?: Loader;
    metadata?:
    | Metadata
    | ((
    args: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    params: InferParams<Params>;
    searchParams: InferParams<SearchParams>;
    data: InferLoaderData<Loader>;
    },
    parent: ResolvingMetadata
    ) => Promise<Metadata>)
    parent: ResolvingMetadata,
    ) => Promise<Metadata>);
    component: React.ComponentType<{
    params: InferParams<Params>
    searchParams?: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    }>
    params: InferParams<Params>;
    searchParams?: InferParams<SearchParams>;
    data: InferLoaderData<Loader>;
    }>;
    }

    function parseParams<Schema extends readonly string[] | AnyZodObject>(
    params: Record<string, string>,
    schema?: Schema
    ) {
    if (schema && 'parse' in schema) {
    return schema.parse(params) as InferParams<Schema>
    function parseParams<Schema extends readonly string[] | AnyZodObject>(params: Record<string, string>, schema?: Schema) {
    if (schema && "parse" in schema) {
    return schema.parse(params) as InferParams<Schema>;
    }

    return params as InferParams<Schema>
    return params as InferParams<Schema>;
    }

    export const createPage = <
    const Params extends readonly string[] | AnyZodObject,
    const SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>,
    >(
    props: CreatePageProps<Params, SearchParams, Loader>
    props: CreatePageProps<Params, SearchParams, Loader>,
    ) => {
    const {
    params: paramsSchema,
    searchParams: searchParamsSchema,
    component: PageComponent,
    loader,
    metadata,
    } = props
    const { params: paramsSchema, searchParams: searchParamsSchema, component: PageComponent, loader, metadata } = props;

    // We don't really care about the types here since it's internal
    async function Page(props: {
    params: Record<string, string>
    searchParams: Record<string, string>
    params: Record<string, string>;
    searchParams: Record<string, string>;
    }) {
    const params = parseParams(props.params, paramsSchema)
    const searchParams = parseParams(props.searchParams, searchParamsSchema)
    const params = parseParams(props.params, paramsSchema);
    const searchParams = parseParams(props.searchParams, searchParamsSchema);

    const pageProps: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    params: InferParams<Params>;
    searchParams: InferParams<SearchParams>;
    data: InferLoaderData<Loader>;
    } = {
    params,
    searchParams,
    } as never
    };

    if (typeof loader === 'function') {
    pageProps.data = (await loader(pageProps)) as InferLoaderData<Loader>
    if (typeof loader === "function") {
    pageProps.data = (await loader(pageProps)) as InferLoaderData<Loader>;
    }

    return <PageComponent {...pageProps} />
    return <PageComponent {...pageProps} />;
    }

    if (typeof metadata === 'function') {
    if (typeof metadata === "function") {
    return {
    generateMetaData: async (
    {
    params,
    searchParams,
    }: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    params: InferParams<Params>;
    searchParams: InferParams<SearchParams>;
    },
    parent: ResolvingMetadata
    parent: ResolvingMetadata,
    ) => {
    const data = (
    typeof loader === 'function'
    typeof loader === "function"
    ? await loader({
    params,
    searchParams,
    })
    : undefined
    ) as InferLoaderData<Loader>
    ) as InferLoaderData<Loader>;

    return metadata(
    {
    params,
    searchParams,
    data,
    },
    parent
    )
    parent,
    );
    },
    Page,
    }
    };
    }

    return {
    metadata,
    Page,
    }
    }
    };
    };
  5. dbritto-dev revised this gist Aug 3, 2024. 1 changed file with 27 additions and 27 deletions.
    54 changes: 27 additions & 27 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -1,33 +1,28 @@
    import { AnyZodObject, z } from 'zod'
    import { Metadata, ResolvingMetadata } from 'next'
    import type { AnyZodObject, z } from 'zod'
    import type { Metadata, ResolvingMetadata } from 'next'

    type InferParams<Params> = Params extends readonly string[]
    ? {
    [K in Params[number]]?: string
    }
    : Params extends AnyZodObject
    ? z.infer<Params>
    : unknown
    ? z.infer<Params>
    : unknown

    type LoaderFn<
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject
    > = (args: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    }) => Promise<any>
    }) => Promise<unknown>

    type InferLoaderData<Loader> = Loader extends (args: any) => Promise<infer T>
    ? T
    : unknown
    type InferLoaderData<Loader> = Loader extends (args: unknown) => Promise<infer T> ? T : unknown

    export interface CreatePageProps<
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<
    Params,
    SearchParams
    >,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>
    > {
    params?: Params
    searchParams?: SearchParams
    @@ -40,7 +35,7 @@ export interface CreatePageProps<
    searchParams: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    },
    parent: ResolvingMetadata,
    parent: ResolvingMetadata
    ) => Promise<Metadata>)
    component: React.ComponentType<{
    params: InferParams<Params>
    @@ -51,7 +46,7 @@ export interface CreatePageProps<

    function parseParams<Schema extends readonly string[] | AnyZodObject>(
    params: Record<string, string>,
    schema?: Schema,
    schema?: Schema
    ) {
    if (schema && 'parse' in schema) {
    return schema.parse(params) as InferParams<Schema>
    @@ -63,12 +58,9 @@ function parseParams<Schema extends readonly string[] | AnyZodObject>(
    export const createPage = <
    const Params extends readonly string[] | AnyZodObject,
    const SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<
    Params,
    SearchParams
    >,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<Params, SearchParams>
    >(
    props: CreatePageProps<Params, SearchParams, Loader>,
    props: CreatePageProps<Params, SearchParams, Loader>
    ) => {
    const {
    params: paramsSchema,
    @@ -79,17 +71,24 @@ export const createPage = <
    } = props

    // We don't really care about the types here since it's internal
    async function Page(props: { params: Record<string, string>, searchParams: Record<string, string> }) {
    async function Page(props: {
    params: Record<string, string>
    searchParams: Record<string, string>
    }) {
    const params = parseParams(props.params, paramsSchema)
    const searchParams = parseParams(props.searchParams, searchParamsSchema)

    let pageProps: any = {
    const pageProps: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    } = {
    params,
    searchParams,
    }
    } as never

    if (typeof loader === 'function') {
    pageProps.data = await loader(pageProps)
    pageProps.data = (await loader(pageProps)) as InferLoaderData<Loader>
    }

    return <PageComponent {...pageProps} />
    @@ -105,23 +104,24 @@ export const createPage = <
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    },
    parent: ResolvingMetadata,
    parent: ResolvingMetadata
    ) => {
    const data =
    const data = (
    typeof loader === 'function'
    ? await loader({
    params,
    searchParams,
    })
    : undefined
    ) as InferLoaderData<Loader>

    return metadata(
    {
    params,
    searchParams,
    data,
    },
    parent,
    parent
    )
    },
    Page,
  6. dbritto-dev revised this gist Aug 3, 2024. 1 changed file with 3 additions and 8 deletions.
    11 changes: 3 additions & 8 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@ import { Metadata, ResolvingMetadata } from 'next'

    type InferParams<Params> = Params extends readonly string[]
    ? {
    [K in Params[number]]: string
    [K in Params[number]]?: string
    }
    : Params extends AnyZodObject
    ? z.infer<Params>
    @@ -79,7 +79,7 @@ export const createPage = <
    } = props

    // We don't really care about the types here since it's internal
    async function Page(props: any) {
    async function Page(props: { params: Record<string, string>, searchParams: Record<string, string> }) {
    const params = parseParams(props.params, paramsSchema)
    const searchParams = parseParams(props.searchParams, searchParamsSchema)

    @@ -89,12 +89,7 @@ export const createPage = <
    }

    if (typeof loader === 'function') {
    const data = await loader(pageProps)

    pageProps = {
    ...pageProps,
    data,
    }
    pageProps.data = await loader(pageProps)
    }

    return <PageComponent {...pageProps} />
  7. @Pagebakers Pagebakers revised this gist Jul 1, 2024. 1 changed file with 27 additions and 9 deletions.
    36 changes: 27 additions & 9 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -49,6 +49,17 @@ export interface CreatePageProps<
    }>
    }

    function parseParams<Schema extends readonly string[] | AnyZodObject>(
    params: Record<string, string>,
    schema?: Schema,
    ) {
    if (schema && 'parse' in schema) {
    return schema.parse(params) as InferParams<Schema>
    }

    return params as InferParams<Schema>
    }

    export const createPage = <
    const Params extends readonly string[] | AnyZodObject,
    const SearchParams extends readonly string[] | AnyZodObject,
    @@ -59,19 +70,26 @@ export const createPage = <
    >(
    props: CreatePageProps<Params, SearchParams, Loader>,
    ) => {
    const { component: PageComponent, title, loader, metadata } = props
    const {
    params: paramsSchema,
    searchParams: searchParamsSchema,
    component: PageComponent,
    loader,
    metadata,
    } = props

    // We don't really care about the types here since it's internally
    // We don't really care about the types here since it's internal
    async function Page(props: any) {
    let pageProps = {
    ...props,
    const params = parseParams(props.params, paramsSchema)
    const searchParams = parseParams(props.searchParams, searchParamsSchema)

    let pageProps: any = {
    params,
    searchParams,
    }

    if (typeof loader === 'function') {
    const data = await loader({
    params: props.params,
    searchParams: props.searchParams,
    })
    const data = await loader(pageProps)

    pageProps = {
    ...pageProps,
    @@ -119,4 +137,4 @@ export const createPage = <
    metadata,
    Page,
    }
    }
    }
  8. @Pagebakers Pagebakers created this gist Jul 1, 2024.
    122 changes: 122 additions & 0 deletions create-page.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,122 @@
    import { AnyZodObject, z } from 'zod'
    import { Metadata, ResolvingMetadata } from 'next'

    type InferParams<Params> = Params extends readonly string[]
    ? {
    [K in Params[number]]: string
    }
    : Params extends AnyZodObject
    ? z.infer<Params>
    : unknown

    type LoaderFn<
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject,
    > = (args: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    }) => Promise<any>

    type InferLoaderData<Loader> = Loader extends (args: any) => Promise<infer T>
    ? T
    : unknown

    export interface CreatePageProps<
    Params extends readonly string[] | AnyZodObject,
    SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<
    Params,
    SearchParams
    >,
    > {
    params?: Params
    searchParams?: SearchParams
    loader?: Loader
    metadata?:
    | Metadata
    | ((
    args: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    },
    parent: ResolvingMetadata,
    ) => Promise<Metadata>)
    component: React.ComponentType<{
    params: InferParams<Params>
    searchParams?: InferParams<SearchParams>
    data: InferLoaderData<Loader>
    }>
    }

    export const createPage = <
    const Params extends readonly string[] | AnyZodObject,
    const SearchParams extends readonly string[] | AnyZodObject,
    Loader extends LoaderFn<Params, SearchParams> = LoaderFn<
    Params,
    SearchParams
    >,
    >(
    props: CreatePageProps<Params, SearchParams, Loader>,
    ) => {
    const { component: PageComponent, title, loader, metadata } = props

    // We don't really care about the types here since it's internally
    async function Page(props: any) {
    let pageProps = {
    ...props,
    }

    if (typeof loader === 'function') {
    const data = await loader({
    params: props.params,
    searchParams: props.searchParams,
    })

    pageProps = {
    ...pageProps,
    data,
    }
    }

    return <PageComponent {...pageProps} />
    }

    if (typeof metadata === 'function') {
    return {
    generateMetaData: async (
    {
    params,
    searchParams,
    }: {
    params: InferParams<Params>
    searchParams: InferParams<SearchParams>
    },
    parent: ResolvingMetadata,
    ) => {
    const data =
    typeof loader === 'function'
    ? await loader({
    params,
    searchParams,
    })
    : undefined

    return metadata(
    {
    params,
    searchParams,
    data,
    },
    parent,
    )
    },
    Page,
    }
    }

    return {
    metadata,
    Page,
    }
    }