@wmfs/tymly
Version:
A framework for building and sharing workflows in Node.js
148 lines (120 loc) • 3.3 kB
JavaScript
const findComponent = require('../find-component')
const applyToTarget = require('../apply-to-target')
async function secondPass (
loadedComponents,
bootedServices,
messages
) {
messages.heading('Reference resolution - second pass')
const resolvers = findResolvers(
loadedComponents.pluginComponents,
messages
)
if (!resolvers) return
const components = loadedComponents.blueprintComponents
const tymlyRefs = loadedComponents.blueprintRefs
const resolutions = findSecondPassResolutions(
tymlyRefs,
resolvers
)
if (!resolutions) {
messages.subHeading('Nothing to resolve')
return
}
await resolveResolutions(
resolutions,
components,
bootedServices,
messages
)
} // secondPass
function findResolvers (pluginComponents, messages) {
const refResolvers = pluginComponents && pluginComponents.tymlyRefResolvers
if (!refResolvers) {
return
}
messages.subHeading('Loading tymlyRefResolvers')
const found = []
for (const [refId, resolver] of Object.entries(refResolvers)) {
const module = resolver.componentModule
if (isResolver(module)) {
messages.info(`Found ${refId}`)
found.push(module)
}
}
return found.length ? found : null
} // findResolvers
function isResolver (module) {
return isFunction(module.canResolve) &&
isFunction(module.resolver)
} // isResolver
function isFunction (candidate) {
return typeof candidate === 'function'
} // isFunction
function findSecondPassResolutions (
tymlyRefs,
resolvers
) {
const secondPass = []
for (const [type, referees] of Object.entries(tymlyRefs)) {
for (const [targetName, references] of Object.entries(referees)) {
const target = `${type}:${targetName}`
const refs = references
.filter(ref => !ref.when)
.map(ref => {
ref.resolver = findResolver(ref.ref, resolvers)
return ref
})
.filter(ref => ref.resolver)
refs.forEach(r =>
secondPass.push({
target,
path: r.path,
ref: r.ref,
resolver: r.resolver
})
)
refs.forEach(r => { r.when = 'secondPass' })
} // refs ...
} // for ...
return secondPass.length ? secondPass : null
} // findSecondPassResolutions
function findResolver (reference, resolvers) {
const refParts = reference.split(':')
for (const resolver of resolvers) {
if (resolver.canResolve(refParts)) {
return resolver.resolver
}
}
return null
} // canResolve
async function resolveResolutions (
toResolve,
components,
bootedServices,
messages
) {
messages.subHeading('Resolving tymlyRefs')
for (const { target, path, ref, resolver } of toResolve) {
const [targetName, targetObj] = findComponent(target, components)
const refObj = await resolveResolution(
ref,
resolver,
bootedServices
)
if (!refObj) {
throw new Error(`Could not resolve ${ref} in ${target}`)
}
applyToTarget(targetObj, refObj, path)
messages.info(`Resolved ${ref} in ${targetName}`)
}
} // resolveResolutions
function resolveResolution (
ref,
resolver,
bootedServices
) {
const refParts = ref.split(':')
return resolver(refParts, bootedServices)
}
module.exports = secondPass