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 | */
|
---|
15 | const TRANSITIVE_ONLY = Symbol("transitive only");
|
---|
16 |
|
---|
17 | /**
|
---|
18 | * While determining the active state, this flag is used to signal a circular connection.
|
---|
19 | */
|
---|
20 | const 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 | */
|
---|
29 | const 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 | */
|
---|
43 | const 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 |
|
---|
52 | class 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 |
|
---|
184 | module.exports = ModuleGraphConnection;
|
---|
185 | module.exports.addConnectionStates = addConnectionStates;
|
---|
186 | module.exports.TRANSITIVE_ONLY = /** @type {typeof TRANSITIVE_ONLY} */ (
|
---|
187 | TRANSITIVE_ONLY
|
---|
188 | );
|
---|
189 | module.exports.CIRCULAR_CONNECTION = /** @type {typeof CIRCULAR_CONNECTION} */ (
|
---|
190 | CIRCULAR_CONNECTION
|
---|
191 | );
|
---|