You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
5.1 KiB
191 lines
5.1 KiB
/* |
|
MIT License http://www.opensource.org/licenses/mit-license.php |
|
Author Tobias Koppers @sokra |
|
*/ |
|
|
|
"use strict"; |
|
|
|
/** @typedef {import("./Dependency")} Dependency */ |
|
/** @typedef {import("./Module")} Module */ |
|
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */ |
|
|
|
/** |
|
* Module itself is not connected, but transitive modules are connected transitively. |
|
*/ |
|
const TRANSITIVE_ONLY = Symbol("transitive only"); |
|
|
|
/** |
|
* While determining the active state, this flag is used to signal a circular connection. |
|
*/ |
|
const CIRCULAR_CONNECTION = Symbol("circular connection"); |
|
|
|
/** @typedef {boolean | typeof TRANSITIVE_ONLY | typeof CIRCULAR_CONNECTION} ConnectionState */ |
|
|
|
/** |
|
* @param {ConnectionState} a first |
|
* @param {ConnectionState} b second |
|
* @returns {ConnectionState} merged |
|
*/ |
|
const addConnectionStates = (a, b) => { |
|
if (a === true || b === true) return true; |
|
if (a === false) return b; |
|
if (b === false) return a; |
|
if (a === TRANSITIVE_ONLY) return b; |
|
if (b === TRANSITIVE_ONLY) return a; |
|
return a; |
|
}; |
|
|
|
/** |
|
* @param {ConnectionState} a first |
|
* @param {ConnectionState} b second |
|
* @returns {ConnectionState} intersected |
|
*/ |
|
const intersectConnectionStates = (a, b) => { |
|
if (a === false || b === false) return false; |
|
if (a === true) return b; |
|
if (b === true) return a; |
|
if (a === CIRCULAR_CONNECTION) return b; |
|
if (b === CIRCULAR_CONNECTION) return a; |
|
return a; |
|
}; |
|
|
|
class ModuleGraphConnection { |
|
/** |
|
* @param {Module|null} originModule the referencing module |
|
* @param {Dependency|null} dependency the referencing dependency |
|
* @param {Module} module the referenced module |
|
* @param {string=} explanation some extra detail |
|
* @param {boolean=} weak the reference is weak |
|
* @param {false | function(ModuleGraphConnection, RuntimeSpec): ConnectionState=} condition condition for the connection |
|
*/ |
|
constructor( |
|
originModule, |
|
dependency, |
|
module, |
|
explanation, |
|
weak = false, |
|
condition = undefined |
|
) { |
|
this.originModule = originModule; |
|
this.resolvedOriginModule = originModule; |
|
this.dependency = dependency; |
|
this.resolvedModule = module; |
|
this.module = module; |
|
this.weak = weak; |
|
this.conditional = !!condition; |
|
this._active = condition !== false; |
|
/** @type {function(ModuleGraphConnection, RuntimeSpec): ConnectionState} */ |
|
this.condition = condition || undefined; |
|
/** @type {Set<string>} */ |
|
this.explanations = undefined; |
|
if (explanation) { |
|
this.explanations = new Set(); |
|
this.explanations.add(explanation); |
|
} |
|
} |
|
|
|
clone() { |
|
const clone = new ModuleGraphConnection( |
|
this.resolvedOriginModule, |
|
this.dependency, |
|
this.resolvedModule, |
|
undefined, |
|
this.weak, |
|
this.condition |
|
); |
|
clone.originModule = this.originModule; |
|
clone.module = this.module; |
|
clone.conditional = this.conditional; |
|
clone._active = this._active; |
|
if (this.explanations) clone.explanations = new Set(this.explanations); |
|
return clone; |
|
} |
|
|
|
/** |
|
* @param {function(ModuleGraphConnection, RuntimeSpec): ConnectionState} condition condition for the connection |
|
* @returns {void} |
|
*/ |
|
addCondition(condition) { |
|
if (this.conditional) { |
|
const old = this.condition; |
|
this.condition = (c, r) => |
|
intersectConnectionStates(old(c, r), condition(c, r)); |
|
} else if (this._active) { |
|
this.conditional = true; |
|
this.condition = condition; |
|
} |
|
} |
|
|
|
/** |
|
* @param {string} explanation the explanation to add |
|
* @returns {void} |
|
*/ |
|
addExplanation(explanation) { |
|
if (this.explanations === undefined) { |
|
this.explanations = new Set(); |
|
} |
|
this.explanations.add(explanation); |
|
} |
|
|
|
get explanation() { |
|
if (this.explanations === undefined) return ""; |
|
return Array.from(this.explanations).join(" "); |
|
} |
|
|
|
// TODO webpack 5 remove |
|
get active() { |
|
throw new Error("Use getActiveState instead"); |
|
} |
|
|
|
/** |
|
* @param {RuntimeSpec} runtime the runtime |
|
* @returns {boolean} true, if the connection is active |
|
*/ |
|
isActive(runtime) { |
|
if (!this.conditional) return this._active; |
|
return this.condition(this, runtime) !== false; |
|
} |
|
|
|
/** |
|
* @param {RuntimeSpec} runtime the runtime |
|
* @returns {boolean} true, if the connection is active |
|
*/ |
|
isTargetActive(runtime) { |
|
if (!this.conditional) return this._active; |
|
return this.condition(this, runtime) === true; |
|
} |
|
|
|
/** |
|
* @param {RuntimeSpec} runtime the runtime |
|
* @returns {ConnectionState} true: fully active, false: inactive, TRANSITIVE: direct module inactive, but transitive connection maybe active |
|
*/ |
|
getActiveState(runtime) { |
|
if (!this.conditional) return this._active; |
|
return this.condition(this, runtime); |
|
} |
|
|
|
/** |
|
* @param {boolean} value active or not |
|
* @returns {void} |
|
*/ |
|
setActive(value) { |
|
this.conditional = false; |
|
this._active = value; |
|
} |
|
|
|
set active(value) { |
|
throw new Error("Use setActive instead"); |
|
} |
|
} |
|
|
|
/** @typedef {typeof TRANSITIVE_ONLY} TRANSITIVE_ONLY */ |
|
/** @typedef {typeof CIRCULAR_CONNECTION} CIRCULAR_CONNECTION */ |
|
|
|
module.exports = ModuleGraphConnection; |
|
module.exports.addConnectionStates = addConnectionStates; |
|
module.exports.TRANSITIVE_ONLY = /** @type {typeof TRANSITIVE_ONLY} */ ( |
|
TRANSITIVE_ONLY |
|
); |
|
module.exports.CIRCULAR_CONNECTION = /** @type {typeof CIRCULAR_CONNECTION} */ ( |
|
CIRCULAR_CONNECTION |
|
);
|
|
|