2023-02-21 22:33:57 +02:00
|
|
|
import { __addNonEnumProp } from './utils.mjs'
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/**
|
|
|
|
* Get the name of the given plugin config
|
|
|
|
*
|
|
|
|
* @param {(Object|Object[])} plugin the plugin to get the name of
|
|
|
|
* @return {(string|false)} the name, or false if there isn't one
|
|
|
|
*/
|
2023-02-21 22:33:57 +02:00
|
|
|
export function getPluginName(plugin) {
|
2023-02-22 16:09:43 +02:00
|
|
|
const toCheck = Array.isArray(plugin) ? plugin[0] : plugin
|
|
|
|
return toCheck.name || toCheck.plugin?.name || false
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/////////////////
|
|
|
|
// CONSTRUCTOR //
|
|
|
|
/////////////////
|
|
|
|
/**
|
|
|
|
* A class for handling config resolution for a Pattern
|
|
|
|
* @class
|
|
|
|
* @param {Pattern} pattern the pattern whose config is being handled
|
|
|
|
*/
|
2023-02-21 22:33:57 +02:00
|
|
|
export function PatternConfig(pattern) {
|
2023-02-22 16:09:43 +02:00
|
|
|
/** @type {Store} the pattern's store, for logging */
|
2023-02-21 22:33:57 +02:00
|
|
|
this.store = pattern.store
|
2023-02-22 16:09:43 +02:00
|
|
|
|
|
|
|
/** @type {Object} resolved plugins keyed by name */
|
|
|
|
this.plugins = { ...(pattern.designConfig.plugins || {}) }
|
|
|
|
/** @type {Object} resolved options keyed by name */
|
|
|
|
this.options = { ...(pattern.designConfig.options || {}) }
|
|
|
|
/** @type {string[]} required measurements */
|
|
|
|
this.measurements = [...(pattern.designConfig.measurements || [])]
|
|
|
|
/** @type {string[]} optional measurements */
|
|
|
|
this.optionalMeasurements = [...(pattern.designConfig.optionalMeasurements || [])]
|
|
|
|
/** @type {Object} the names of the parts that will be injected */
|
|
|
|
this.inject = {}
|
|
|
|
/** @type {Object} arrays of parts that are direct dependencies of the key */
|
|
|
|
this.directDependencies = {}
|
|
|
|
/** @type {Object} arrays of all dependencies of the key */
|
|
|
|
this.resolvedDependencies = {}
|
|
|
|
/** @type {Object} parts to include in the pattern */
|
|
|
|
this.parts = {}
|
|
|
|
/** @type {Object} which parts are hidden */
|
|
|
|
this.partHide = {}
|
|
|
|
/** @type {Object} which parts hide all their dependencies */
|
|
|
|
this.partHideAll = {}
|
|
|
|
|
|
|
|
/** to track which parts have already been resolved */
|
2023-02-21 22:33:57 +02:00
|
|
|
__addNonEnumProp(this, '__resolvedParts', {})
|
2023-02-22 16:09:43 +02:00
|
|
|
/** @type {Object} to track when to overwrite options */
|
2023-02-21 22:33:57 +02:00
|
|
|
__addNonEnumProp(this, '__mutated', {
|
|
|
|
optionDistance: {},
|
|
|
|
partDistance: {},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/** @type {Boolean} change me to true to get full debugging of the resolution process */
|
2023-02-21 22:33:57 +02:00
|
|
|
const DISTANCE_DEBUG = false
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
////////////////////
|
|
|
|
// PUBLIC METHODs //
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validate that a part meets the requirements to be added to the pattern
|
|
|
|
* @param {Object} part a part configuration
|
|
|
|
* @return {boolean} whether the part is valid
|
|
|
|
*/
|
2023-02-21 22:33:57 +02:00
|
|
|
PatternConfig.prototype.validatePart = function (part) {
|
|
|
|
if (typeof part?.draft !== 'function') {
|
|
|
|
this.store.log.error(`Part must have a draft() method`)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!part.name) {
|
|
|
|
this.store.log.error(`Part must have a name`)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
2023-02-22 16:09:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Chainable method to add a part to the configuration
|
|
|
|
* @param {Object} part
|
|
|
|
*/
|
2023-02-21 22:33:57 +02:00
|
|
|
PatternConfig.prototype.addPart = function (part) {
|
2023-02-22 16:09:43 +02:00
|
|
|
if (this.validatePart(part)) this.__addPart([part])
|
2023-02-21 22:33:57 +02:00
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/** Log the final report on part inheritance order */
|
2023-02-21 22:33:57 +02:00
|
|
|
PatternConfig.prototype.logPartDistances = function () {
|
|
|
|
for (const partName in this.parts) {
|
|
|
|
let qualifier = DISTANCE_DEBUG ? 'final' : ''
|
|
|
|
this.store.log.debug(
|
|
|
|
`⚪️ \`${partName}\` ${qualifier} options priority is __${this.__mutated.partDistance[partName]}__`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/**
|
|
|
|
* Return a configuration in the structure expected by the pattern
|
|
|
|
* @return {Object} contains parts, plugins, measurements, options, optionalMeasurements, resolvedDependencies, directDependencies, inject, draftOrder, partHide, and partHideAll
|
|
|
|
*/
|
2023-02-21 22:33:57 +02:00
|
|
|
PatternConfig.prototype.asConfig = function () {
|
|
|
|
return {
|
|
|
|
parts: this.parts,
|
|
|
|
plugins: this.plugins,
|
|
|
|
measurements: this.measurements,
|
|
|
|
options: this.options,
|
|
|
|
optionalMeasurements: this.optionalMeasurements,
|
|
|
|
resolvedDependencies: this.resolvedDependencies,
|
|
|
|
directDependencies: this.directDependencies,
|
|
|
|
inject: this.inject,
|
|
|
|
draftOrder: this.__resolveDraftOrder(),
|
2023-02-22 16:09:43 +02:00
|
|
|
partHide: this.partHide,
|
|
|
|
partHideAll: this.partHideAll,
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/////////////////////
|
|
|
|
// PRIVATE METHODS //
|
|
|
|
/////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a part's configuration
|
|
|
|
* Uses recursion to also add that part's dependencies
|
|
|
|
* @private
|
|
|
|
* @param {Object[]} depChain an array starting with the current part to add and containing its dependents/descendents in order
|
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__addPart = function (depChain) {
|
|
|
|
// the current part is the head of the chain
|
2023-02-21 22:33:57 +02:00
|
|
|
const part = depChain[0]
|
2023-02-22 16:09:43 +02:00
|
|
|
// the longer the chain, the deeper the part is down it
|
|
|
|
const distance = depChain.length
|
|
|
|
if (!this.parts[part.name]) {
|
2023-02-21 22:33:57 +02:00
|
|
|
this.parts[part.name] = Object.freeze(part)
|
|
|
|
}
|
2023-02-22 16:09:43 +02:00
|
|
|
// if it hasn't been registered with a distance, do that now
|
2023-02-21 22:33:57 +02:00
|
|
|
if (typeof this.__mutated.partDistance[part.name] === 'undefined') {
|
|
|
|
this.__mutated.partDistance[part.name] = distance
|
|
|
|
|
|
|
|
if (DISTANCE_DEBUG)
|
|
|
|
this.store.log.debug(
|
|
|
|
`Base partDistance for \`${part.name}\` is __${this.__mutated.partDistance[part.name]}__`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hide when hideAll is set
|
|
|
|
if (part.hideAll) {
|
2023-02-22 16:09:43 +02:00
|
|
|
this.partHide[part.name] = true
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// resolve its dependencies
|
|
|
|
this.__resolvePartDependencies(depChain)
|
2023-02-21 22:33:57 +02:00
|
|
|
|
|
|
|
// add the part's config
|
|
|
|
this.__addPartConfig(part)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves/Adds a part's design configuration to the pattern config
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Part} part - The part of which to resolve the config
|
2023-02-22 16:09:43 +02:00
|
|
|
* @return this
|
2023-02-21 22:33:57 +02:00
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__addPartConfig = function (part) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// don't resolve a part that's already been resolved
|
2023-02-21 22:33:57 +02:00
|
|
|
if (this.__resolvedParts[part.name]) return this
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
return this.__addPartOptions(part) // add options
|
|
|
|
.__addPartMeasurements(part, false) // add required measurements
|
|
|
|
.__addPartMeasurements(part, true) // add optional measurements
|
|
|
|
.__addPartPlugins(part) // add plugins
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves/Adds a part's configured options to the global config
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Part} part - The part of which to resolve the config
|
2023-02-22 16:09:43 +02:00
|
|
|
* @return {PatternConfig} this - The PatternConfig instance
|
2023-02-21 22:33:57 +02:00
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__addPartOptions = function (part) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// skip empty options
|
2023-02-21 22:33:57 +02:00
|
|
|
if (!part.options) return this
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// get the part's option priority
|
2023-02-21 22:33:57 +02:00
|
|
|
const partDistance = this.__mutated.partDistance?.[part.name] || 0
|
2023-02-22 16:09:43 +02:00
|
|
|
|
|
|
|
// loop through options
|
2023-02-21 22:33:57 +02:00
|
|
|
for (const optionName in part.options) {
|
|
|
|
const option = part.options[optionName]
|
2023-02-22 16:09:43 +02:00
|
|
|
// get the priority of this option's current registration
|
2023-02-21 22:33:57 +02:00
|
|
|
const optionDistance = this.__mutated.optionDistance[optionName]
|
2023-02-22 16:09:43 +02:00
|
|
|
// debug the comparison
|
2023-02-21 22:33:57 +02:00
|
|
|
if (optionDistance && DISTANCE_DEBUG)
|
|
|
|
this.store.log.debug(
|
|
|
|
`optionDistance for __${optionName}__ is __${optionDistance}__ and partDistance for \`${part.name}\` is __${partDistance}__`
|
|
|
|
)
|
2023-02-22 16:09:43 +02:00
|
|
|
|
|
|
|
// if it's never been registered, or it's registered at a further distance
|
2023-02-21 22:33:57 +02:00
|
|
|
if (!optionDistance || optionDistance > partDistance) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// Keep options immutable in the pattern or risk subtle bugs
|
2023-02-21 22:33:57 +02:00
|
|
|
this.options[optionName] = Object.freeze(option)
|
2023-02-22 16:09:43 +02:00
|
|
|
// register the new distance
|
|
|
|
this.__mutated.optionDistance[optionName] = partDistance
|
|
|
|
// debug appropriately
|
2023-02-21 22:33:57 +02:00
|
|
|
this.store.log.debug(
|
|
|
|
optionDistance
|
|
|
|
? `🟣 __${optionName}__ option overwritten by \`${part.name}\``
|
|
|
|
: `🔵 __${optionName}__ option loaded from part \`${part.name}\``
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves/Adds a part's configured measurements to the global config
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Part} part - The part of which to resolve the config
|
2023-02-22 16:09:43 +02:00
|
|
|
* @param {boolean} optional - are these measurements optional?
|
|
|
|
* @return {PatternConfig} this - The PatternConfig instance
|
2023-02-21 22:33:57 +02:00
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__addPartMeasurements = function (part, optional = false) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// which list are we drawing from?
|
2023-02-21 22:33:57 +02:00
|
|
|
const listType = optional ? 'optionalMeasurements' : 'measurements'
|
2023-02-22 16:09:43 +02:00
|
|
|
// if the part has measurements of this type, go through them
|
2023-02-21 22:33:57 +02:00
|
|
|
if (part[listType]) {
|
|
|
|
part[listType].forEach((m) => {
|
2023-02-22 16:09:43 +02:00
|
|
|
// we need to know what lists it's already present on
|
2023-02-21 22:33:57 +02:00
|
|
|
const isInReqList = this.measurements.indexOf(m) !== -1
|
2023-02-22 16:09:43 +02:00
|
|
|
// if it's already registered as required, we're done here
|
|
|
|
if (isInReqList) return
|
|
|
|
|
|
|
|
// check if it's registered as optional
|
2023-02-21 22:33:57 +02:00
|
|
|
const optInd = this.optionalMeasurements.indexOf(m)
|
|
|
|
const isInOptList = optInd !== -1
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// if it is optional and not in the list, push it
|
2023-02-21 22:33:57 +02:00
|
|
|
if (optional && !isInOptList) this.optionalMeasurements.push(m)
|
2023-02-22 16:09:43 +02:00
|
|
|
// if it's not optional
|
2023-02-21 22:33:57 +02:00
|
|
|
if (!optional) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// push it to required list
|
2023-02-21 22:33:57 +02:00
|
|
|
this.measurements.push(m)
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// make sure it's not also registered as optional
|
2023-02-21 22:33:57 +02:00
|
|
|
if (isInOptList) this.optionalMeasurements.splice(optInd, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.store.log.debug(
|
|
|
|
`🟠 __${m}__ measurement is ${optional ? 'optional' : 'required'} in \`${part.name}\``
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves/Adds a part's configured plugins to the global config
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Part} part - The part of which to resolve the config
|
2023-02-22 16:09:43 +02:00
|
|
|
* @return {PatternConfig} this - The PatternConfig instance
|
2023-02-21 22:33:57 +02:00
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__addPartPlugins = function (part) {
|
|
|
|
if (!part.plugins) return this
|
|
|
|
|
|
|
|
const plugins = this.plugins
|
|
|
|
// Side-step immutability of the part object to ensure plugins is an array
|
|
|
|
let partPlugins = part.plugins
|
|
|
|
if (!Array.isArray(partPlugins)) partPlugins = [partPlugins]
|
|
|
|
// Go through list of part plugins
|
|
|
|
for (let plugin of partPlugins) {
|
|
|
|
const name = getPluginName(plugin)
|
|
|
|
this.store.log.debug(
|
|
|
|
plugin.plugin
|
|
|
|
? `🔌 Resolved __${name}__ conditional plugin in \`${part.name}\``
|
|
|
|
: `🔌 Resolved __${name}__ plugin in \`${part.name}\``
|
|
|
|
)
|
|
|
|
// Handle [plugin, data] scenario
|
|
|
|
if (Array.isArray(plugin)) {
|
|
|
|
const pluginObj = { ...plugin[0], data: plugin[1] }
|
|
|
|
plugin = pluginObj
|
|
|
|
}
|
|
|
|
if (!plugins[name]) {
|
|
|
|
// New plugin, so we load it
|
|
|
|
plugins[name] = plugin
|
|
|
|
this.store.log.info(
|
|
|
|
plugin.condition
|
|
|
|
? `New plugin conditionally added: \`${name}\``
|
|
|
|
: `New plugin added: \`${name}\``
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// Existing plugin, takes some more work
|
|
|
|
if (plugin.plugin && plugin.condition) {
|
|
|
|
// Multiple instances of the same plugin with different conditions
|
|
|
|
// will all be added, so we need to change the name.
|
|
|
|
if (plugins[name]?.condition) {
|
|
|
|
plugins[name + '_'] = plugin
|
|
|
|
this.store.log.info(
|
|
|
|
`Plugin \`${name}\` was conditionally added again. Renaming to ${name}_.`
|
|
|
|
)
|
|
|
|
} else
|
|
|
|
this.store.log.info(
|
|
|
|
`Plugin \`${name}\` was requested conditionally, but is already added explicitly. Not loading.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
// swap from a conditional if needed
|
|
|
|
else if (plugins[name].condition) {
|
|
|
|
plugins[name] = plugin
|
|
|
|
this.store.log.info(`Plugin \`${name}\` was explicitly added. Changing from conditional.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// the two types of dependencies
|
|
|
|
const depTypes = ['from', 'after']
|
2023-02-21 22:33:57 +02:00
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/**
|
|
|
|
* Recursively register part dependencies
|
|
|
|
* triggers {@link __addPart} on new parts found during resolution
|
|
|
|
* @param {Object[]} depChain an array starting with the current part to register and containing its dependents/descendents in order
|
|
|
|
* @return {PatternConfig} this
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__resolvePartDependencies = function (depChain) {
|
|
|
|
// the current part is the head of the chain
|
2023-02-21 22:33:57 +02:00
|
|
|
const part = depChain[0]
|
2023-02-22 16:09:43 +02:00
|
|
|
// get or make its array of resolved dependencies
|
|
|
|
this.resolvedDependencies[part.name] = this.resolvedDependencies[part.name] || []
|
2023-02-21 22:33:57 +02:00
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// for each dependency type (from, after)
|
|
|
|
depTypes.forEach((d) => {
|
|
|
|
// if the part has dependencies of that type
|
|
|
|
if (part[d]) {
|
|
|
|
if (DISTANCE_DEBUG) this.store.log.debug(`Processing \`${part.name}\` "${d}:"`)
|
2023-02-21 22:33:57 +02:00
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// enforce an array
|
|
|
|
const depsOfType = Array.isArray(part[d]) ? part[d] : [part[d]]
|
2023-02-21 22:33:57 +02:00
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
// each dependency
|
|
|
|
depsOfType.forEach((dot) => {
|
|
|
|
// add it as a direct dependency of the current part
|
|
|
|
this.__addDependency('directDependencies', part.name, dot.name)
|
|
|
|
// add it as a resolved dependency of all parts in the chain
|
|
|
|
depChain.forEach((c) => this.__addDependency('resolvedDependencies', c.name, dot.name))
|
|
|
|
|
|
|
|
// handle hiding and injecting
|
|
|
|
this.__handlePartDependencyOfType(part, dot.name, d)
|
|
|
|
|
|
|
|
// if the dependency isn't registered, register it
|
|
|
|
if (!this.parts[dot.name]) {
|
2023-02-23 08:13:44 +02:00
|
|
|
// add the part's configuration. this will recursively add the part's dependencies to all parts in the chain
|
2023-02-22 16:09:43 +02:00
|
|
|
this.__addPart([dot, ...depChain])
|
2023-02-23 08:13:44 +02:00
|
|
|
} else {
|
|
|
|
// if it's already registered, recursion won't happen, but we still need to add its resolved dependencies to all parts in the chain
|
|
|
|
this.resolvedDependencies[dot.name].forEach((r) => {
|
|
|
|
depChain.forEach((c) => this.__addDependency('resolvedDependencies', c.name, r))
|
|
|
|
})
|
2023-02-22 16:09:43 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// now that the chain has been registered, recalculate the part distances
|
|
|
|
this.__resolveMutatedPartDistance(part.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a part as either a direct or a resolved dependency
|
|
|
|
* @param {string} dependencyList which list to add the part to, 'resolvedDependencies' or 'directDependencies'
|
|
|
|
* @param {string} partName the name of the part to add the dependency to in the list
|
|
|
|
* @param {string} depName the name of the dependency to add to the list
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__addDependency = function (dependencyList, partName, depName) {
|
|
|
|
this[dependencyList][partName] = this[dependencyList][partName] || []
|
|
|
|
if (dependencyList == 'resolvedDependencies' && DISTANCE_DEBUG)
|
|
|
|
this.store.log.debug(`add ${depName} to ${partName} dependencyResolution`)
|
|
|
|
if (this[dependencyList][partName].indexOf(depName) === -1)
|
|
|
|
this[dependencyList][partName].push(depName)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle dependency-type specific config business
|
|
|
|
* @param {Object} part the part to add the dependency to
|
|
|
|
* @param {string} depName the name of the dependency to add
|
|
|
|
* @param {string} depType the type of dependency, 'from' or 'after'
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__handlePartDependencyOfType = function (part, depName, depType) {
|
2023-02-21 22:33:57 +02:00
|
|
|
switch (depType) {
|
|
|
|
case 'from':
|
2023-02-22 16:09:43 +02:00
|
|
|
this.__setFromHide(part, depName)
|
|
|
|
this.inject[part.name] = depName
|
2023-02-21 22:33:57 +02:00
|
|
|
break
|
|
|
|
case 'after':
|
2023-02-22 16:09:43 +02:00
|
|
|
this.__setAfterHide(part, depName)
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:09:43 +02:00
|
|
|
/**
|
|
|
|
* Resolve part option priority
|
|
|
|
* Recursively bumps priorities down the dependency chain
|
|
|
|
* @param {string} partName the name of the part to resolve
|
|
|
|
* @private
|
|
|
|
*/
|
2023-02-21 22:33:57 +02:00
|
|
|
PatternConfig.prototype.__resolveMutatedPartDistance = function (partName) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// if the part has no dependencies, bail
|
|
|
|
if (!this.directDependencies[partName]) return
|
|
|
|
|
|
|
|
// propose that each of the part's direct dependencies should be at a distance 1 further than the part's distance
|
|
|
|
const proposed_dependency_distance = this.__mutated.partDistance[partName] + 1
|
|
|
|
// check each direct dependency
|
2023-02-21 22:33:57 +02:00
|
|
|
this.directDependencies[partName].forEach((dependency) => {
|
2023-02-22 16:09:43 +02:00
|
|
|
// if the dependency doesn't have a distance, or that distance is less than the proposal
|
2023-02-21 22:33:57 +02:00
|
|
|
if (
|
|
|
|
typeof this.__mutated.partDistance[dependency] === 'undefined' ||
|
2023-02-22 16:09:43 +02:00
|
|
|
this.__mutated.partDistance[dependency] < proposed_dependency_distance
|
2023-02-21 22:33:57 +02:00
|
|
|
) {
|
2023-02-22 16:09:43 +02:00
|
|
|
// set the new distance
|
|
|
|
this.__mutated.partDistance[dependency] = proposed_dependency_distance
|
|
|
|
// bump the dependency's dependencies as well
|
2023-02-21 22:33:57 +02:00
|
|
|
this.__resolveMutatedPartDistance(dependency)
|
|
|
|
}
|
2023-02-22 16:09:43 +02:00
|
|
|
|
2023-02-21 22:33:57 +02:00
|
|
|
if (DISTANCE_DEBUG)
|
|
|
|
this.store.log.debug(
|
|
|
|
`partDistance for \`${dependency}\` is __${this.__mutated.partDistance[dependency]}__`
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves the draft order based on the configuation
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {object} graph - The object of resolved dependencies, used to call itself recursively
|
|
|
|
* @return {Pattern} this - The Pattern instance
|
|
|
|
*/
|
|
|
|
PatternConfig.prototype.__resolveDraftOrder = function () {
|
|
|
|
this.__draftOrder = Object.keys(this.parts).sort(
|
|
|
|
(p1, p2) => this.__mutated.partDistance[p2] - this.__mutated.partDistance[p1]
|
|
|
|
)
|
|
|
|
|
|
|
|
return this.__draftOrder
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-02-22 16:09:43 +02:00
|
|
|
* Sets visibility of a 'from' dependency based on its config
|
2023-02-21 22:33:57 +02:00
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Part} part - The part of which this is a dependency
|
|
|
|
* @param {string} depName - The name of the dependency
|
|
|
|
* @return {Pattern} this - The Pattern instance
|
|
|
|
*/
|
2023-02-22 16:09:43 +02:00
|
|
|
PatternConfig.prototype.__setFromHide = function (part, depName) {
|
2023-02-21 22:33:57 +02:00
|
|
|
if (
|
|
|
|
part.hideDependencies ||
|
|
|
|
part.hideAll ||
|
2023-02-22 16:09:43 +02:00
|
|
|
this.partHide[part.name] ||
|
|
|
|
this.partHideAll[part.name]
|
2023-02-21 22:33:57 +02:00
|
|
|
) {
|
2023-02-22 16:09:43 +02:00
|
|
|
this.partHide[depName] = true
|
|
|
|
this.partHideAll[depName] = true
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets visibility of an 'after' dependency based on its config
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Part} part - The part of which this is a dependency
|
|
|
|
* @param {string} depName - The name of the dependency
|
|
|
|
* @param {int} set - The index of the set in the list of settings
|
|
|
|
* @return {Pattern} this - The Pattern instance
|
|
|
|
*/
|
2023-02-22 16:09:43 +02:00
|
|
|
PatternConfig.prototype.__setAfterHide = function (part, depName) {
|
|
|
|
if (this.partHide[part.name] || this.partHideAll[part.name]) {
|
|
|
|
this.partHide[depName] = true
|
|
|
|
this.partHideAll[depName] = true
|
2023-02-21 22:33:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|