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