UNPKG

5.21 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8/** @typedef {import("./Dependency")} Dependency */
9/** @typedef {import("./Module")} Module */
10/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
11
12/**
13 * Module itself is not connected, but transitive modules are connected transitively.
14 */
15const TRANSITIVE_ONLY = Symbol("transitive only");
16
17/**
18 * While determining the active state, this flag is used to signal a circular connection.
19 */
20const CIRCULAR_CONNECTION = Symbol("circular connection");
21
22/** @typedef {boolean | typeof TRANSITIVE_ONLY | typeof CIRCULAR_CONNECTION} ConnectionState */
23
24/**
25 * @param {ConnectionState} a first
26 * @param {ConnectionState} b second
27 * @returns {ConnectionState} merged
28 */
29const addConnectionStates = (a, b) => {
30 if (a === true || b === true) return true;
31 if (a === false) return b;
32 if (b === false) return a;
33 if (a === TRANSITIVE_ONLY) return b;
34 if (b === TRANSITIVE_ONLY) return a;
35 return a;
36};
37
38/**
39 * @param {ConnectionState} a first
40 * @param {ConnectionState} b second
41 * @returns {ConnectionState} intersected
42 */
43const intersectConnectionStates = (a, b) => {
44 if (a === false || b === false) return false;
45 if (a === true) return b;
46 if (b === true) return a;
47 if (a === CIRCULAR_CONNECTION) return b;
48 if (b === CIRCULAR_CONNECTION) return a;
49 return a;
50};
51
52class ModuleGraphConnection {
53 /**
54 * @param {Module|null} originModule the referencing module
55 * @param {Dependency|null} dependency the referencing dependency
56 * @param {Module} module the referenced module
57 * @param {string=} explanation some extra detail
58 * @param {boolean=} weak the reference is weak
59 * @param {false | function(ModuleGraphConnection, RuntimeSpec): ConnectionState=} condition condition for the connection
60 */
61 constructor(
62 originModule,
63 dependency,
64 module,
65 explanation,
66 weak = false,
67 condition = undefined
68 ) {
69 this.originModule = originModule;
70 this.resolvedOriginModule = originModule;
71 this.dependency = dependency;
72 this.resolvedModule = module;
73 this.module = module;
74 this.weak = weak;
75 this.conditional = !!condition;
76 this._active = condition !== false;
77 /** @type {function(ModuleGraphConnection, RuntimeSpec): ConnectionState} */
78 this.condition = condition || undefined;
79 /** @type {Set<string>} */
80 this.explanations = undefined;
81 if (explanation) {
82 this.explanations = new Set();
83 this.explanations.add(explanation);
84 }
85 }
86
87 clone() {
88 const clone = new ModuleGraphConnection(
89 this.resolvedOriginModule,
90 this.dependency,
91 this.resolvedModule,
92 undefined,
93 this.weak,
94 this.condition
95 );
96 clone.originModule = this.originModule;
97 clone.module = this.module;
98 clone.conditional = this.conditional;
99 clone._active = this._active;
100 if (this.explanations) clone.explanations = new Set(this.explanations);
101 return clone;
102 }
103
104 /**
105 * @param {function(ModuleGraphConnection, RuntimeSpec): ConnectionState} condition condition for the connection
106 * @returns {void}
107 */
108 addCondition(condition) {
109 if (this.conditional) {
110 const old = this.condition;
111 this.condition = (c, r) =>
112 intersectConnectionStates(old(c, r), condition(c, r));
113 } else if (this._active) {
114 this.conditional = true;
115 this.condition = condition;
116 }
117 }
118
119 /**
120 * @param {string} explanation the explanation to add
121 * @returns {void}
122 */
123 addExplanation(explanation) {
124 if (this.explanations === undefined) {
125 this.explanations = new Set();
126 }
127 this.explanations.add(explanation);
128 }
129
130 get explanation() {
131 if (this.explanations === undefined) return "";
132 return Array.from(this.explanations).join(" ");
133 }
134
135 // TODO webpack 5 remove
136 get active() {
137 throw new Error("Use getActiveState instead");
138 }
139
140 /**
141 * @param {RuntimeSpec} runtime the runtime
142 * @returns {boolean} true, if the connection is active
143 */
144 isActive(runtime) {
145 if (!this.conditional) return this._active;
146 return this.condition(this, runtime) !== false;
147 }
148
149 /**
150 * @param {RuntimeSpec} runtime the runtime
151 * @returns {boolean} true, if the connection is active
152 */
153 isTargetActive(runtime) {
154 if (!this.conditional) return this._active;
155 return this.condition(this, runtime) === true;
156 }
157
158 /**
159 * @param {RuntimeSpec} runtime the runtime
160 * @returns {ConnectionState} true: fully active, false: inactive, TRANSITIVE: direct module inactive, but transitive connection maybe active
161 */
162 getActiveState(runtime) {
163 if (!this.conditional) return this._active;
164 return this.condition(this, runtime);
165 }
166
167 /**
168 * @param {boolean} value active or not
169 * @returns {void}
170 */
171 setActive(value) {
172 this.conditional = false;
173 this._active = value;
174 }
175
176 set active(value) {
177 throw new Error("Use setActive instead");
178 }
179}
180
181/** @typedef {typeof TRANSITIVE_ONLY} TRANSITIVE_ONLY */
182/** @typedef {typeof CIRCULAR_CONNECTION} CIRCULAR_CONNECTION */
183
184module.exports = ModuleGraphConnection;
185module.exports.addConnectionStates = addConnectionStates;
186module.exports.TRANSITIVE_ONLY = /** @type {typeof TRANSITIVE_ONLY} */ (
187 TRANSITIVE_ONLY
188);
189module.exports.CIRCULAR_CONNECTION = /** @type {typeof CIRCULAR_CONNECTION} */ (
190 CIRCULAR_CONNECTION
191);