Skip to content

Commit

Permalink
wip: memoize initial value resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
juice49 committed May 10, 2024
1 parent 80b4715 commit 7ae1d01
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ export function getTemplatePermissions({
template,
item.parameters,
context,
{
useCache: true,
},
)

return {template, item, resolvedInitialValue}
Expand Down
70 changes: 57 additions & 13 deletions packages/sanity/src/core/templates/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,56 @@ export type Serializeable<T> = {
serialize(): T
}

interface Options {
useCache?: boolean
}

/** @internal */
export function isBuilder(template: unknown): template is Serializeable<Template> {
return isRecord(template) && typeof template.serialize === 'function'
}

const cache = new WeakMap<
InitialValueResolver<unknown, unknown>,
Record<string, unknown | Promise<unknown>>
>()

/** @internal */
// returns the "resolved" value from an initial value property (e.g. type.initialValue)
// eslint-disable-next-line require-await
export async function resolveValue<Params, InitialValue>(
initialValueOpt: InitialValueProperty<Params, InitialValue>,
params: Params | undefined,
context: InitialValueResolverContext,
options?: Options,
): Promise<InitialValue | undefined> {
return typeof initialValueOpt === 'function'
? (initialValueOpt as InitialValueResolver<Params, InitialValue>)(params, context)
: initialValueOpt
const useCache = options?.useCache

if (typeof initialValueOpt === 'function') {
const cached = cache.get(initialValueOpt as InitialValueResolver<unknown, unknown>)

const key = JSON.stringify([
params,
[context.projectId, context.dataset, context.currentUser?.id],
])

if (useCache && cached?.[key]) {
return cached[key] as InitialValue | Promise<InitialValue>
}

const value = (initialValueOpt as InitialValueResolver<Params, InitialValue>)(params, context)

if (useCache) {
cache.set(initialValueOpt as InitialValueResolver<unknown, unknown>, {
...cached,
[key]: value,
})
}

return value
}

return initialValueOpt
}

/** @internal */
Expand All @@ -45,18 +79,19 @@ export async function resolveInitialValue(
template: Template,
params: {[key: string]: any} = {},
context: InitialValueResolverContext,
options?: Options,
): Promise<{[key: string]: any}> {
// Template builder?
if (isBuilder(template)) {
return resolveInitialValue(schema, template.serialize(), params, context)
return resolveInitialValue(schema, template.serialize(), params, context, options)
}

const {id, schemaType: schemaTypeName, value} = template
if (!value) {
throw new Error(`Template "${id}" has invalid "value" property`)
}

let resolvedValue = await resolveValue(value, params, context)
let resolvedValue = await resolveValue(value, params, context, options)

if (!isRecord(resolvedValue)) {
throw new Error(
Expand All @@ -79,8 +114,13 @@ export async function resolveInitialValue(
}

const newValue = deepAssign(
(await resolveInitialValueForType(schemaType, params, DEFAULT_MAX_RECURSION_DEPTH, context)) ||
{},
(await resolveInitialValueForType(
schemaType,
params,
DEFAULT_MAX_RECURSION_DEPTH,
context,
options,
)) || {},
resolvedValue as Record<string, unknown>,
)

Expand Down Expand Up @@ -120,29 +160,31 @@ export function resolveInitialValueForType<Params extends Record<string, unknown
*/
maxDepth = DEFAULT_MAX_RECURSION_DEPTH,
context: InitialValueResolverContext,
options?: Options,
): Promise<any> {
if (maxDepth <= 0) {
return Promise.resolve(undefined)
}

if (isObjectSchemaType(type)) {
return resolveInitialObjectValue(type, params, maxDepth, context)
return resolveInitialObjectValue(type, params, maxDepth, context, options)
}

if (isArraySchemaType(type)) {
return resolveInitialArrayValue(type, params, maxDepth, context)
return resolveInitialArrayValue(type, params, maxDepth, context, options)
}

return resolveValue(type.initialValue, params, context)
return resolveValue(type.initialValue, params, context, options)
}

async function resolveInitialArrayValue<Params extends Record<string, unknown>>(
type: SchemaType,
params: Params,
maxDepth: number,
context: InitialValueResolverContext,
options?: Options,
): Promise<any> {
const initialArray = await resolveValue(type.initialValue, undefined, context)
const initialArray = await resolveValue(type.initialValue, undefined, context, options)

if (!Array.isArray(initialArray)) {
return undefined
Expand All @@ -154,7 +196,7 @@ async function resolveInitialArrayValue<Params extends Record<string, unknown>>(
return isObjectSchemaType(itemType)
? {
...initialItem,
...(await resolveInitialValueForType(itemType, params, maxDepth - 1, context)),
...(await resolveInitialValueForType(itemType, params, maxDepth - 1, context, options)),
_key: randomKey(),
}
: initialItem
Expand All @@ -168,9 +210,10 @@ export async function resolveInitialObjectValue<Params extends Record<string, un
params: Params,
maxDepth: number,
context: InitialValueResolverContext,
options?: Options,
): Promise<any> {
const initialObject: Record<string, unknown> = {
...((await resolveValue(type.initialValue, params, context)) || {}),
...((await resolveValue(type.initialValue, params, context, options)) || {}),
}

const fieldValues: Record<string, any> = {}
Expand All @@ -181,6 +224,7 @@ export async function resolveInitialObjectValue<Params extends Record<string, un
params,
maxDepth - 1,
context,
options,
)
if (initialFieldValue !== undefined && initialFieldValue !== null) {
fieldValues[field.name] = initialFieldValue
Expand Down

0 comments on commit 7ae1d01

Please sign in to comment.