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.
|
// need to be enriched at this level.
|
||||||
// Nested settings are the un-enriched block settings that are to be passed on
|
// Nested settings are the un-enriched block settings that are to be passed on
|
||||||
// and enriched at a deeper level.
|
// and enriched at a deeper level.
|
||||||
let componentSettings
|
let dynamicSettings
|
||||||
|
let staticSettings
|
||||||
let nestedSettings
|
let nestedSettings
|
||||||
|
|
||||||
|
// The context keys that
|
||||||
|
|
||||||
// The enriched component settings
|
// The enriched component settings
|
||||||
let enrichedSettings
|
let enrichedSettings
|
||||||
|
|
||||||
|
@ -108,15 +111,23 @@
|
||||||
$: rawSettings = getRawSettings(instance)
|
$: rawSettings = getRawSettings(instance)
|
||||||
$: instanceKey = Helpers.hashString(JSON.stringify(rawSettings))
|
$: instanceKey = Helpers.hashString(JSON.stringify(rawSettings))
|
||||||
|
|
||||||
// Update and enrich component settings
|
// Parse and split component settings into categories
|
||||||
$: updateSettings(rawSettings, instanceKey, settingsDefinition, $context)
|
$: updateSettings(rawSettings, instanceKey, settingsDefinition)
|
||||||
|
|
||||||
|
// Enrich component settings
|
||||||
|
$: enrichComponentSettings(dynamicSettings, $context)
|
||||||
|
|
||||||
// Evaluate conditional UI settings and store any component setting changes
|
// Evaluate conditional UI settings and store any component setting changes
|
||||||
// which need to be made
|
// which need to be made
|
||||||
$: evaluateConditions(enrichedSettings?._conditions)
|
$: evaluateConditions(enrichedSettings?._conditions)
|
||||||
|
|
||||||
// Build up the final settings object to be passed to the component
|
// Build up the final settings object to be passed to the component
|
||||||
$: cacheSettings(enrichedSettings, nestedSettings, conditionalSettings)
|
$: cacheSettings(
|
||||||
|
staticSettings,
|
||||||
|
enrichedSettings,
|
||||||
|
nestedSettings,
|
||||||
|
conditionalSettings
|
||||||
|
)
|
||||||
|
|
||||||
// Update component context
|
// Update component context
|
||||||
$: componentStore.set({
|
$: componentStore.set({
|
||||||
|
@ -185,48 +196,55 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates and enriches component settings when raw settings change
|
// Updates and enriches component settings when raw settings change
|
||||||
const updateSettings = (settings, key, settingsDefinition, context) => {
|
const updateSettings = (settings, key, settingsDefinition) => {
|
||||||
const instanceChanged = key !== lastInstanceKey
|
const instanceChanged = key !== lastInstanceKey
|
||||||
|
if (!instanceChanged) {
|
||||||
// Derive component and nested settings if the instance changed
|
return
|
||||||
if (instanceChanged) {
|
} else {
|
||||||
splitRawSettings(settings, settingsDefinition)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enrich component settings
|
|
||||||
enrichComponentSettings(componentSettings, context, instanceChanged)
|
|
||||||
|
|
||||||
// Update instance key
|
|
||||||
if (instanceChanged) {
|
|
||||||
lastInstanceKey = key
|
lastInstanceKey = key
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Splits the raw settings into those destined for the component itself
|
// Derive static, dynamic and nested settings if the instance changed
|
||||||
// and nexted settings for child components inside blocks
|
let newStaticSettings = { ...settings }
|
||||||
const splitRawSettings = (rawSettings, settingsDefinition) => {
|
let newDynamicSettings = { ...settings }
|
||||||
let newComponentSettings = { ...rawSettings }
|
let newNestedSettings = { ...settings }
|
||||||
let newNestedSettings = { ...rawSettings }
|
|
||||||
settingsDefinition?.forEach(setting => {
|
settingsDefinition?.forEach(setting => {
|
||||||
if (setting.nested) {
|
if (setting.nested) {
|
||||||
delete newComponentSettings[setting.key]
|
delete newStaticSettings[setting.key]
|
||||||
|
delete newDynamicSettings[setting.key]
|
||||||
} else {
|
} else {
|
||||||
delete newNestedSettings[setting.key]
|
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
|
nestedSettings = newNestedSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enriches any string component props using handlebars
|
// Enriches any string component props using handlebars
|
||||||
const enrichComponentSettings = (rawSettings, context, instanceChanged) => {
|
const enrichComponentSettings = (settings, context) => {
|
||||||
const contextChanged = context.key !== lastContextKey
|
const contextChanged = context.key !== lastContextKey
|
||||||
|
|
||||||
// Skip enrichment if the context and instance are unchanged
|
|
||||||
if (!contextChanged) {
|
if (!contextChanged) {
|
||||||
if (!instanceChanged) {
|
|
||||||
return
|
return
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
lastContextKey = context.key
|
lastContextKey = context.key
|
||||||
}
|
}
|
||||||
|
@ -236,7 +254,7 @@
|
||||||
const enrichmentTime = latestUpdateTime
|
const enrichmentTime = latestUpdateTime
|
||||||
|
|
||||||
// Enrich settings with context
|
// Enrich settings with context
|
||||||
const newEnrichedSettings = enrichProps(rawSettings, context)
|
const newEnrichedSettings = enrichProps(settings, context)
|
||||||
|
|
||||||
// Abandon this update if a newer update has started
|
// Abandon this update if a newer update has started
|
||||||
if (enrichmentTime !== latestUpdateTime) {
|
if (enrichmentTime !== latestUpdateTime) {
|
||||||
|
@ -271,8 +289,13 @@
|
||||||
// Combines and caches all settings which will be passed to the component
|
// Combines and caches all settings which will be passed to the component
|
||||||
// instance. Settings are aggressively memoized to avoid triggering svelte
|
// instance. Settings are aggressively memoized to avoid triggering svelte
|
||||||
// reactive statements as much as possible.
|
// reactive statements as much as possible.
|
||||||
const cacheSettings = (enriched, nested, conditional) => {
|
const cacheSettings = (staticSettings, enriched, nested, conditional) => {
|
||||||
const allSettings = { ...enriched, ...nested, ...conditional }
|
const allSettings = {
|
||||||
|
...staticSettings,
|
||||||
|
...enriched,
|
||||||
|
...nested,
|
||||||
|
...conditional,
|
||||||
|
}
|
||||||
if (!cachedSettings) {
|
if (!cachedSettings) {
|
||||||
cachedSettings = { ...allSettings }
|
cachedSettings = { ...allSettings }
|
||||||
initialSettings = cachedSettings
|
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.
|
* Props are deeply cloned so that no mutation is done to the source object.
|
||||||
*/
|
*/
|
||||||
export const enrichDataBindings = (props, context) => {
|
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()
|
const hbsInstance = handlebars.create()
|
||||||
registerAll(hbsInstance)
|
registerAll(hbsInstance)
|
||||||
const hbsInstanceNoHelpers = handlebars.create()
|
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) {
|
function testObject(object) {
|
||||||
// JSON stringify will fail if there are any cycles, stops infinite recursion
|
// 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.
|
* 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
|
* @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."
|
throw "Cannot process non-string types."
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// finalising adds a helper, can't do this with no helpers
|
const template = createTemplate(string, opts.noHelpers, opts.cacheTemplates)
|
||||||
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 now = Math.floor(Date.now() / 1000) * 1000
|
const now = Math.floor(Date.now() / 1000) * 1000
|
||||||
return processors.postprocess(
|
return processors.postprocess(
|
||||||
template({
|
template({
|
||||||
|
@ -154,8 +173,8 @@ module.exports.isValid = (string, opts) => {
|
||||||
// don't really need a real context to check if its valid
|
// don't really need a real context to check if its valid
|
||||||
const context = {}
|
const context = {}
|
||||||
try {
|
try {
|
||||||
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
const template = createTemplate(string, opts.noHelpers, opts.cache)
|
||||||
instance.compile(processors.preprocess(string, false))(context)
|
template(context)
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err && err.message ? err.message : err
|
const msg = err && err.message ? err.message : err
|
||||||
|
|
Loading…
Reference in New Issue