mod_workspace_mergeTemplates.js
/**
## /workspace/mergeTemplates
The workspace is cached in the module scope to allow for the mergeObjectTemplates(layer) method to assign template objects defined in a JSON layer to the workspace.templates{}.
@requires /utils/merge
@requires /utils/envReplace
@requires /workspace/getTemplate
@requires /workspace/cache
@module /workspace/mergeTemplates
*/
const merge = require('../utils/merge')
const envReplace = require('../utils/envReplace')
const getTemplate = require('./getTemplate')
const workspaceCache = require('./cache')
let workspace
/**
@function mergeTemplates
@async
@description
The mergeTemplates method will be called for a layer or locale obj.
The locale or layer object will be merged with a template defined as obj.template string property.
The method will check for a template matching the obj.key string property if obj.template is undefined.
An array of templates can be defined as obj.templates[]. The templates will be merged into the obj in the order the template keys are in the templates[] array.
@param {Object} obj
@property {string} [obj.template] Key of template for the object.
@property {string} obj.key Fallback for lookup of template if not an implicit property.
@property {array} [obj.templates] An array of template keys to be merged into the object.
@returns {Promise} The layer or locale provided as obj param.
*/
module.exports = async function mergeTemplates(obj) {
// Cache workspace in module scope for template assignment.
workspace = await workspaceCache()
// The object has an implicit template to merge into.
if (obj.template) {
const template = await getTemplate(obj.template)
// Failed to get template matching obj.template from template.src!
if (template.err instanceof Error) {
obj.err ??= []
obj.err.push(template.err.message)
// The template is not in the workspace.templates{}
} else if (template instanceof Error) {
obj.err ??= []
obj.err.push(template.message)
} else {
// Merge obj --> template
// Template must be cloned to prevent cross polination and array aggregation.
obj = merge(structuredClone(template), obj)
}
// Check whether the object key exist as template if no implicit template has been defined.
} else if (Object.hasOwn(workspace.templates, obj.key)) {
obj.err ??= []
obj.err.push(`Template matching ${obj.key} exists in workspace.`)
}
for (const template_key of obj.templates || []) {
const template = await getTemplate(template_key)
// Failed to retrieve template matching template_key
if (template.err instanceof Error) {
obj.err ??= []
obj.err.push(template.err.message)
// A template matching the template_key does not exist.
} else if (template instanceof Error) {
obj.err ??= []
obj.err.push(`${template_key}: ${template.message}`)
} else {
//The object key must not be overwritten by a template key.
delete template.key;
//The object template must not be overwritten by a templates template.
delete template.template;
// Merge template --> obj
obj = merge(obj, template)
}
}
// Substitute ${SRC_*} in object string.
obj = envReplace(obj)
// Assign templates to workspace.
assignWorkspaceTemplates(obj)
// Assign default workspace dbs if not defined in template.
obj.dbs ??= workspace.dbs
return obj
}
/**
@function assignWorkspaceTemplates
@description
The method parses an object for a template object property. The template property value will be assigned to the workspace.templates{} object matching the template key value.
The template._type property will be set to 'template' indicating that the templates origin is in the workspace. It is possible to overassign _type:'core' templates which are loaded from the /mod/workspace/templates directory.
The method will call itself for nested objects.
@param {Object} obj
*/
function assignWorkspaceTemplates(obj) {
// Return early if object is null or empty
if (obj === null) return;
if (obj instanceof Object && !Object.keys(obj)) return;
Object.entries(obj).forEach(entry => {
// Process template objects - if found, add type and merge into workspace templates
if (entry[0] === 'template' && entry[1].key) {
entry[1]._type = 'template';
workspace.templates[entry[1].key] = Object.assign(workspace.templates[entry[1].key] || {}, entry[1])
return;
}
// Recursively process each item if we find an array
if (Array.isArray(entry[1])) {
entry[1].forEach(assignWorkspaceTemplates)
return;
}
// Recursively process nested objects
if (entry[1] instanceof Object) {
assignWorkspaceTemplates(entry[1]);
}
})
}