Add experimental support for caching the creation of HBS template functions
This commit is contained in:
parent
bfb2d637fc
commit
e7b02aec04
|
@ -33,9 +33,12 @@
|
|||
// need to be enriched at this level.
|
||||
// Nested settings are the un-enriched block settings that are to be passed on
|
||||
// and enriched at a deeper level.
|
||||
let componentSettings
|
||||
let dynamicSettings
|
||||
let staticSettings
|
||||
let nestedSettings
|
||||
|
||||
// The context keys that
|
||||
|
||||
// The enriched component settings
|
||||
let enrichedSettings
|
||||
|
||||
|
@ -108,15 +111,23 @@
|
|||
$: rawSettings = getRawSettings(instance)
|
||||
$: instanceKey = Helpers.hashString(JSON.stringify(rawSettings))
|
||||
|
||||
// Update and enrich component settings
|
||||
$: updateSettings(rawSettings, instanceKey, settingsDefinition, $context)
|
||||
// Parse and split component settings into categories
|
||||
$: updateSettings(rawSettings, instanceKey, settingsDefinition)
|
||||
|
||||
// Enrich component settings
|
||||
$: enrichComponentSettings(dynamicSettings, $context)
|
||||
|
||||
// Evaluate conditional UI settings and store any component setting changes
|
||||
// which need to be made
|
||||
$: evaluateConditions(enrichedSettings?._conditions)
|
||||
|
||||
// Build up the final settings object to be passed to the component
|
||||
$: cacheSettings(enrichedSettings, nestedSettings, conditionalSettings)
|
||||
$: cacheSettings(
|
||||
staticSettings,
|
||||
enrichedSettings,
|
||||
nestedSettings,
|
||||
conditionalSettings
|
||||
)
|
||||
|
||||
// Update component context
|
||||
$: componentStore.set({
|
||||
|
@ -185,48 +196,55 @@
|
|||
}
|
||||
|
||||
// Updates and enriches component settings when raw settings change
|
||||
const updateSettings = (settings, key, settingsDefinition, context) => {
|
||||
const updateSettings = (settings, key, settingsDefinition) => {
|
||||
const instanceChanged = key !== lastInstanceKey
|
||||
|
||||
// Derive component and nested settings if the instance changed
|
||||
if (instanceChanged) {
|
||||
splitRawSettings(settings, settingsDefinition)
|
||||
}
|
||||
|
||||
// Enrich component settings
|
||||
enrichComponentSettings(componentSettings, context, instanceChanged)
|
||||
|
||||
// Update instance key
|
||||
if (instanceChanged) {
|
||||
if (!instanceChanged) {
|
||||
return
|
||||
} else {
|
||||
lastInstanceKey = key
|
||||
}
|
||||
}
|
||||
|
||||
// Splits the raw settings into those destined for the component itself
|
||||
// and nexted settings for child components inside blocks
|
||||
const splitRawSettings = (rawSettings, settingsDefinition) => {
|
||||
let newComponentSettings = { ...rawSettings }
|
||||
let newNestedSettings = { ...rawSettings }
|
||||
// Derive static, dynamic and nested settings if the instance changed
|
||||
let newStaticSettings = { ...settings }
|
||||
let newDynamicSettings = { ...settings }
|
||||
let newNestedSettings = { ...settings }
|
||||
settingsDefinition?.forEach(setting => {
|
||||
if (setting.nested) {
|
||||
delete newComponentSettings[setting.key]
|
||||
delete newStaticSettings[setting.key]
|
||||
delete newDynamicSettings[setting.key]
|
||||
} else {
|
||||
delete newNestedSettings[setting.key]
|
||||
|
||||
// This is a non-nested setting, but we need to find out if it is
|
||||
// static or dynamic
|
||||
const value = rawSettings[setting.key]
|
||||
if (value == null) {
|
||||
delete newDynamicSettings[setting.key]
|
||||
} else if (typeof value === "string" && value.includes("{{")) {
|
||||
delete newStaticSettings[setting.key]
|
||||
} else if (typeof value === "object") {
|
||||
const stringified = JSON.stringify(value)
|
||||
if (stringified.includes("{{")) {
|
||||
delete newStaticSettings[setting.key]
|
||||
} else {
|
||||
delete newDynamicSettings[setting.key]
|
||||
}
|
||||
} else {
|
||||
delete newDynamicSettings[setting.key]
|
||||
}
|
||||
}
|
||||
})
|
||||
componentSettings = newComponentSettings
|
||||
|
||||
staticSettings = newStaticSettings
|
||||
dynamicSettings = newDynamicSettings
|
||||
nestedSettings = newNestedSettings
|
||||
}
|
||||
|
||||
// Enriches any string component props using handlebars
|
||||
const enrichComponentSettings = (rawSettings, context, instanceChanged) => {
|
||||
const enrichComponentSettings = (settings, context) => {
|
||||
const contextChanged = context.key !== lastContextKey
|
||||
|
||||
// Skip enrichment if the context and instance are unchanged
|
||||
if (!contextChanged) {
|
||||
if (!instanceChanged) {
|
||||
return
|
||||
}
|
||||
return
|
||||
} else {
|
||||
lastContextKey = context.key
|
||||
}
|
||||
|
@ -236,7 +254,7 @@
|
|||
const enrichmentTime = latestUpdateTime
|
||||
|
||||
// Enrich settings with context
|
||||
const newEnrichedSettings = enrichProps(rawSettings, context)
|
||||
const newEnrichedSettings = enrichProps(settings, context)
|
||||
|
||||
// Abandon this update if a newer update has started
|
||||
if (enrichmentTime !== latestUpdateTime) {
|
||||
|
@ -271,8 +289,13 @@
|
|||
// Combines and caches all settings which will be passed to the component
|
||||
// instance. Settings are aggressively memoized to avoid triggering svelte
|
||||
// reactive statements as much as possible.
|
||||
const cacheSettings = (enriched, nested, conditional) => {
|
||||
const allSettings = { ...enriched, ...nested, ...conditional }
|
||||
const cacheSettings = (staticSettings, enriched, nested, conditional) => {
|
||||
const allSettings = {
|
||||
...staticSettings,
|
||||
...enriched,
|
||||
...nested,
|
||||
...conditional,
|
||||
}
|
||||
if (!cachedSettings) {
|
||||
cachedSettings = { ...allSettings }
|
||||
initialSettings = cachedSettings
|
||||
|
|
|
@ -24,5 +24,5 @@ export const enrichDataBinding = async (input, context) => {
|
|||
* Props are deeply cloned so that no mutation is done to the source object.
|
||||
*/
|
||||
export const enrichDataBindings = (props, context) => {
|
||||
return processObjectSync(cloneDeep(props), context)
|
||||
return processObjectSync(cloneDeep(props), context, { cache: true })
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ const manifest = require("../manifest.json")
|
|||
const hbsInstance = handlebars.create()
|
||||
registerAll(hbsInstance)
|
||||
const hbsInstanceNoHelpers = handlebars.create()
|
||||
const defaultOpts = { noHelpers: false }
|
||||
const defaultOpts = { noHelpers: false, cacheTemplates: false }
|
||||
|
||||
/**
|
||||
* utility function to check if the object is valid
|
||||
* Utility function to check if the object is valid.
|
||||
*/
|
||||
function testObject(object) {
|
||||
// JSON stringify will fail if there are any cycles, stops infinite recursion
|
||||
|
@ -21,6 +21,32 @@ function testObject(object) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HBS template function for a given string, and optionally caches it.
|
||||
*/
|
||||
let templateCache = {}
|
||||
function createTemplate(string, noHelpers, cache) {
|
||||
// Finalising adds a helper, can't do this with no helpers
|
||||
const shouldFinalise = !noHelpers
|
||||
const key = `${string}${shouldFinalise}`
|
||||
|
||||
// Reuse the cached template is possible
|
||||
if (cache && templateCache[key]) {
|
||||
return templateCache[key]
|
||||
}
|
||||
|
||||
string = processors.preprocess(string, shouldFinalise)
|
||||
|
||||
// This does not throw an error when template can't be fulfilled,
|
||||
// have to try correct beforehand
|
||||
const instance = noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
||||
const template = instance.compile(string, {
|
||||
strict: false,
|
||||
})
|
||||
templateCache[key] = template
|
||||
return template
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an input object this will recurse through all props to try and update any handlebars statements within.
|
||||
* @param {object|array} object The input structure which is to be recursed, it is important to note that
|
||||
|
@ -104,14 +130,7 @@ module.exports.processStringSync = (string, context, opts) => {
|
|||
throw "Cannot process non-string types."
|
||||
}
|
||||
try {
|
||||
// finalising adds a helper, can't do this with no helpers
|
||||
const shouldFinalise = !opts.noHelpers
|
||||
string = processors.preprocess(string, shouldFinalise)
|
||||
// this does not throw an error when template can't be fulfilled, have to try correct beforehand
|
||||
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
||||
const template = instance.compile(string, {
|
||||
strict: false,
|
||||
})
|
||||
const template = createTemplate(string, opts.noHelpers, opts.cacheTemplates)
|
||||
const now = Math.floor(Date.now() / 1000) * 1000
|
||||
return processors.postprocess(
|
||||
template({
|
||||
|
@ -154,8 +173,8 @@ module.exports.isValid = (string, opts) => {
|
|||
// don't really need a real context to check if its valid
|
||||
const context = {}
|
||||
try {
|
||||
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
||||
instance.compile(processors.preprocess(string, false))(context)
|
||||
const template = createTemplate(string, opts.noHelpers, opts.cache)
|
||||
template(context)
|
||||
return true
|
||||
} catch (err) {
|
||||
const msg = err && err.message ? err.message : err
|
||||
|
|
Loading…
Reference in New Issue