webpack 4.36.1 → 4.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,15 +27,16 @@ const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
27
27
  const ModuleTemplate = require("./ModuleTemplate");
28
28
  const RuntimeTemplate = require("./RuntimeTemplate");
29
29
  const ChunkRenderError = require("./ChunkRenderError");
30
- const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
31
30
  const Stats = require("./Stats");
32
31
  const Semaphore = require("./util/Semaphore");
33
32
  const createHash = require("./util/createHash");
34
- const Queue = require("./util/Queue");
35
33
  const SortableSet = require("./util/SortableSet");
36
34
  const GraphHelpers = require("./GraphHelpers");
37
35
  const ModuleDependency = require("./dependencies/ModuleDependency");
38
36
  const compareLocations = require("./compareLocations");
37
+ const { Logger, LogType } = require("./logging/Logger");
38
+ const ErrorHelpers = require("./ErrorHelpers");
39
+ const buildChunkGraph = require("./buildChunkGraph");
39
40
 
40
41
  /** @typedef {import("./Module")} Module */
41
42
  /** @typedef {import("./Compiler")} Compiler */
@@ -95,6 +96,14 @@ const compareLocations = require("./compareLocations");
95
96
  * @property {DependenciesBlockVariable[]} variables
96
97
  */
97
98
 
99
+ /**
100
+ * @typedef {Object} LogEntry
101
+ * @property {string} type
102
+ * @property {any[]} args
103
+ * @property {number} time
104
+ * @property {string[]=} trace
105
+ */
106
+
98
107
  /**
99
108
  * @param {Chunk} a first chunk to sort by id
100
109
  * @param {Chunk} b second chunk to sort by id
@@ -155,16 +164,6 @@ const byNameOrHash = (a, b) => {
155
164
  return 0;
156
165
  };
157
166
 
158
- /**
159
- * @template T
160
- * @param {Set<T>} a first set
161
- * @param {Set<T>} b second set
162
- * @returns {number} cmp
163
- */
164
- const bySetSize = (a, b) => {
165
- return a.size - b.size;
166
- };
167
-
168
167
  /**
169
168
  * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
170
169
  * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
@@ -384,6 +383,9 @@ class Compilation extends Tapable {
384
383
  "compilerIndex"
385
384
  ]),
386
385
 
386
+ /** @type {SyncBailHook<string, LogEntry>} */
387
+ log: new SyncBailHook(["origin", "logEntry"]),
388
+
387
389
  // TODO the following hooks are weirdly located here
388
390
  // TODO move them for webpack 5
389
391
  /** @type {SyncHook<object, Module>} */
@@ -469,6 +471,8 @@ class Compilation extends Tapable {
469
471
  this.warnings = [];
470
472
  /** @type {Compilation[]} */
471
473
  this.children = [];
474
+ /** @type {Map<string, LogEntry[]>} */
475
+ this.logging = new Map();
472
476
  /** @type {Map<DepConstructor, ModuleFactory>} */
473
477
  this.dependencyFactories = new Map();
474
478
  /** @type {Map<DepConstructor, DependencyTemplate>} */
@@ -499,6 +503,69 @@ class Compilation extends Tapable {
499
503
  return new Stats(this);
500
504
  }
501
505
 
506
+ /**
507
+ * @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
508
+ * @returns {Logger} a logger with that name
509
+ */
510
+ getLogger(name) {
511
+ if (!name) {
512
+ throw new TypeError("Compilation.getLogger(name) called without a name");
513
+ }
514
+ /** @type {LogEntry[] | undefined} */
515
+ let logEntries;
516
+ return new Logger((type, args) => {
517
+ if (typeof name === "function") {
518
+ name = name();
519
+ if (!name) {
520
+ throw new TypeError(
521
+ "Compilation.getLogger(name) called with a function not returning a name"
522
+ );
523
+ }
524
+ }
525
+ let trace;
526
+ switch (type) {
527
+ case LogType.warn:
528
+ case LogType.error:
529
+ case LogType.trace:
530
+ trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
531
+ .split("\n")
532
+ .slice(3);
533
+ break;
534
+ }
535
+ /** @type {LogEntry} */
536
+ const logEntry = {
537
+ time: Date.now(),
538
+ type,
539
+ args,
540
+ trace
541
+ };
542
+ if (this.hooks.log.call(name, logEntry) === undefined) {
543
+ if (logEntry.type === LogType.profileEnd) {
544
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
545
+ if (typeof console.profileEnd === "function") {
546
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
547
+ console.profileEnd(`[${name}] ${logEntry.args[0]}`);
548
+ }
549
+ }
550
+ if (logEntries === undefined) {
551
+ logEntries = this.logging.get(name);
552
+ if (logEntries === undefined) {
553
+ logEntries = [];
554
+ this.logging.set(name, logEntries);
555
+ }
556
+ }
557
+ logEntries.push(logEntry);
558
+ if (logEntry.type === LogType.profile) {
559
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
560
+ if (typeof console.profile === "function") {
561
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
562
+ console.profile(`[${name}] ${logEntry.args[0]}`);
563
+ }
564
+ }
565
+ }
566
+ });
567
+ }
568
+
502
569
  /**
503
570
  * @typedef {Object} AddModuleResult
504
571
  * @property {Module} module the added or existing module
@@ -846,11 +913,6 @@ class Compilation extends Tapable {
846
913
  iterationDependencies(dependencies);
847
914
 
848
915
  const afterBuild = () => {
849
- if (currentProfile) {
850
- const afterBuilding = Date.now();
851
- currentProfile.building = afterBuilding - afterFactory;
852
- }
853
-
854
916
  if (recursive && addModuleResult.dependencies) {
855
917
  this.processModuleDependencies(dependentModule, callback);
856
918
  } else {
@@ -992,11 +1054,6 @@ class Compilation extends Tapable {
992
1054
  module.addReason(null, dependency);
993
1055
 
994
1056
  const afterBuild = () => {
995
- if (currentProfile) {
996
- const afterBuilding = Date.now();
997
- currentProfile.building = afterBuilding - afterFactory;
998
- }
999
-
1000
1057
  if (addModuleResult.dependencies) {
1001
1058
  this.processModuleDependencies(module, err => {
1002
1059
  if (err) return callback(err);
@@ -1217,7 +1274,10 @@ class Compilation extends Tapable {
1217
1274
 
1218
1275
  this.assignDepth(module);
1219
1276
  }
1220
- this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
1277
+ buildChunkGraph(
1278
+ this,
1279
+ /** @type {Entrypoint[]} */ (this.chunkGroups.slice())
1280
+ );
1221
1281
  this.sortModules(this.modules);
1222
1282
  this.hooks.afterChunks.call(this.chunks);
1223
1283
 
@@ -1505,460 +1565,6 @@ class Compilation extends Tapable {
1505
1565
  return this.hooks.dependencyReference.call(ref, dependency, module);
1506
1566
  }
1507
1567
 
1508
- /**
1509
- * This method creates the Chunk graph from the Module graph
1510
- * @private
1511
- * @param {TODO[]} inputChunkGroups chunk groups which are processed
1512
- * @returns {void}
1513
- */
1514
- processDependenciesBlocksForChunkGroups(inputChunkGroups) {
1515
- // Process is splitting into two parts:
1516
- // Part one traverse the module graph and builds a very basic chunks graph
1517
- // in chunkDependencies.
1518
- // Part two traverse every possible way through the basic chunk graph and
1519
- // tracks the available modules. While traversing it connects chunks with
1520
- // eachother and Blocks with Chunks. It stops traversing when all modules
1521
- // for a chunk are already available. So it doesn't connect unneeded chunks.
1522
-
1523
- /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup, couldBeFiltered: boolean}[]>} */
1524
- const chunkDependencies = new Map();
1525
- const allCreatedChunkGroups = new Set();
1526
-
1527
- // PREPARE
1528
- /** @type {Map<DependenciesBlock, { modules: Module[], blocks: AsyncDependenciesBlock[]}>} */
1529
- const blockInfoMap = new Map();
1530
-
1531
- /**
1532
- * @param {Dependency} d dependency to iterate over
1533
- * @returns {void}
1534
- */
1535
- const iteratorDependency = d => {
1536
- // We skip Dependencies without Reference
1537
- const ref = this.getDependencyReference(currentModule, d);
1538
- if (!ref) {
1539
- return;
1540
- }
1541
- // We skip Dependencies without Module pointer
1542
- const refModule = ref.module;
1543
- if (!refModule) {
1544
- return;
1545
- }
1546
- // We skip weak Dependencies
1547
- if (ref.weak) {
1548
- return;
1549
- }
1550
-
1551
- blockInfoModules.add(refModule);
1552
- };
1553
-
1554
- /**
1555
- * @param {AsyncDependenciesBlock} b blocks to prepare
1556
- * @returns {void}
1557
- */
1558
- const iteratorBlockPrepare = b => {
1559
- blockInfoBlocks.push(b);
1560
- blockQueue.push(b);
1561
- };
1562
-
1563
- /** @type {Module} */
1564
- let currentModule;
1565
- /** @type {DependenciesBlock} */
1566
- let block;
1567
- /** @type {DependenciesBlock[]} */
1568
- let blockQueue;
1569
- /** @type {Set<Module>} */
1570
- let blockInfoModules;
1571
- /** @type {AsyncDependenciesBlock[]} */
1572
- let blockInfoBlocks;
1573
-
1574
- for (const module of this.modules) {
1575
- blockQueue = [module];
1576
- currentModule = module;
1577
- while (blockQueue.length > 0) {
1578
- block = blockQueue.pop();
1579
- blockInfoModules = new Set();
1580
- blockInfoBlocks = [];
1581
-
1582
- if (block.variables) {
1583
- iterationBlockVariable(block.variables, iteratorDependency);
1584
- }
1585
-
1586
- if (block.dependencies) {
1587
- iterationOfArrayCallback(block.dependencies, iteratorDependency);
1588
- }
1589
-
1590
- if (block.blocks) {
1591
- iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
1592
- }
1593
-
1594
- const blockInfo = {
1595
- modules: Array.from(blockInfoModules),
1596
- blocks: blockInfoBlocks
1597
- };
1598
- blockInfoMap.set(block, blockInfo);
1599
- }
1600
- }
1601
-
1602
- // PART ONE
1603
-
1604
- /** @type {Map<ChunkGroup, { index: number, index2: number }>} */
1605
- const chunkGroupCounters = new Map();
1606
- for (const chunkGroup of inputChunkGroups) {
1607
- chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 });
1608
- }
1609
-
1610
- let nextFreeModuleIndex = 0;
1611
- let nextFreeModuleIndex2 = 0;
1612
-
1613
- /** @type {Map<DependenciesBlock, ChunkGroup>} */
1614
- const blockChunkGroups = new Map();
1615
-
1616
- /** @type {Set<DependenciesBlock>} */
1617
- const blocksWithNestedBlocks = new Set();
1618
-
1619
- const ADD_AND_ENTER_MODULE = 0;
1620
- const ENTER_MODULE = 1;
1621
- const PROCESS_BLOCK = 2;
1622
- const LEAVE_MODULE = 3;
1623
-
1624
- /**
1625
- * @typedef {Object} QueueItem
1626
- * @property {number} action
1627
- * @property {DependenciesBlock} block
1628
- * @property {Module} module
1629
- * @property {Chunk} chunk
1630
- * @property {ChunkGroup} chunkGroup
1631
- */
1632
-
1633
- /**
1634
- * @param {ChunkGroup} chunkGroup chunk group
1635
- * @returns {QueueItem} queue item
1636
- */
1637
- const chunkGroupToQueueItem = chunkGroup => ({
1638
- action: ENTER_MODULE,
1639
- block: chunkGroup.chunks[0].entryModule,
1640
- module: chunkGroup.chunks[0].entryModule,
1641
- chunk: chunkGroup.chunks[0],
1642
- chunkGroup
1643
- });
1644
-
1645
- // Start with the provided modules/chunks
1646
- /** @type {QueueItem[]} */
1647
- let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse();
1648
- /** @type {QueueItem[]} */
1649
- let queueDelayed = [];
1650
-
1651
- /** @type {Module} */
1652
- let module;
1653
- /** @type {Chunk} */
1654
- let chunk;
1655
- /** @type {ChunkGroup} */
1656
- let chunkGroup;
1657
-
1658
- // For each async Block in graph
1659
- /**
1660
- * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
1661
- * @returns {void}
1662
- */
1663
- const iteratorBlock = b => {
1664
- // 1. We create a chunk for this Block
1665
- // but only once (blockChunkGroups map)
1666
- let c = blockChunkGroups.get(b);
1667
- if (c === undefined) {
1668
- c = this.namedChunkGroups.get(b.chunkName);
1669
- if (c && c.isInitial()) {
1670
- this.errors.push(
1671
- new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
1672
- );
1673
- c = chunkGroup;
1674
- } else {
1675
- c = this.addChunkInGroup(
1676
- b.groupOptions || b.chunkName,
1677
- module,
1678
- b.loc,
1679
- b.request
1680
- );
1681
- chunkGroupCounters.set(c, { index: 0, index2: 0 });
1682
- blockChunkGroups.set(b, c);
1683
- allCreatedChunkGroups.add(c);
1684
- }
1685
- } else {
1686
- // TODO webpack 5 remove addOptions check
1687
- if (c.addOptions) c.addOptions(b.groupOptions);
1688
- c.addOrigin(module, b.loc, b.request);
1689
- }
1690
-
1691
- // 2. We store the Block+Chunk mapping as dependency for the chunk
1692
- let deps = chunkDependencies.get(chunkGroup);
1693
- if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
1694
- deps.push({
1695
- block: b,
1696
- chunkGroup: c,
1697
- couldBeFiltered: true
1698
- });
1699
-
1700
- // 3. We enqueue the DependenciesBlock for traversal
1701
- queueDelayed.push({
1702
- action: PROCESS_BLOCK,
1703
- block: b,
1704
- module: module,
1705
- chunk: c.chunks[0],
1706
- chunkGroup: c
1707
- });
1708
- };
1709
-
1710
- // Iterative traversal of the Module graph
1711
- // Recursive would be simpler to write but could result in Stack Overflows
1712
- while (queue.length) {
1713
- while (queue.length) {
1714
- const queueItem = queue.pop();
1715
- module = queueItem.module;
1716
- block = queueItem.block;
1717
- chunk = queueItem.chunk;
1718
- chunkGroup = queueItem.chunkGroup;
1719
-
1720
- switch (queueItem.action) {
1721
- case ADD_AND_ENTER_MODULE: {
1722
- // We connect Module and Chunk when not already done
1723
- if (chunk.addModule(module)) {
1724
- module.addChunk(chunk);
1725
- } else {
1726
- // already connected, skip it
1727
- break;
1728
- }
1729
- }
1730
- // fallthrough
1731
- case ENTER_MODULE: {
1732
- if (chunkGroup !== undefined) {
1733
- const index = chunkGroup.getModuleIndex(module);
1734
- if (index === undefined) {
1735
- chunkGroup.setModuleIndex(
1736
- module,
1737
- chunkGroupCounters.get(chunkGroup).index++
1738
- );
1739
- }
1740
- }
1741
-
1742
- if (module.index === null) {
1743
- module.index = nextFreeModuleIndex++;
1744
- }
1745
-
1746
- queue.push({
1747
- action: LEAVE_MODULE,
1748
- block,
1749
- module,
1750
- chunk,
1751
- chunkGroup
1752
- });
1753
- }
1754
- // fallthrough
1755
- case PROCESS_BLOCK: {
1756
- // get prepared block info
1757
- const blockInfo = blockInfoMap.get(block);
1758
-
1759
- // Traverse all referenced modules
1760
- for (let i = blockInfo.modules.length - 1; i >= 0; i--) {
1761
- const refModule = blockInfo.modules[i];
1762
- if (chunk.containsModule(refModule)) {
1763
- // skip early if already connected
1764
- continue;
1765
- }
1766
- // enqueue the add and enter to enter in the correct order
1767
- // this is relevant with circular dependencies
1768
- queue.push({
1769
- action: ADD_AND_ENTER_MODULE,
1770
- block: refModule,
1771
- module: refModule,
1772
- chunk,
1773
- chunkGroup
1774
- });
1775
- }
1776
-
1777
- // Traverse all Blocks
1778
- iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
1779
-
1780
- if (blockInfo.blocks.length > 0 && module !== block) {
1781
- blocksWithNestedBlocks.add(block);
1782
- }
1783
- break;
1784
- }
1785
- case LEAVE_MODULE: {
1786
- if (chunkGroup !== undefined) {
1787
- const index = chunkGroup.getModuleIndex2(module);
1788
- if (index === undefined) {
1789
- chunkGroup.setModuleIndex2(
1790
- module,
1791
- chunkGroupCounters.get(chunkGroup).index2++
1792
- );
1793
- }
1794
- }
1795
-
1796
- if (module.index2 === null) {
1797
- module.index2 = nextFreeModuleIndex2++;
1798
- }
1799
- break;
1800
- }
1801
- }
1802
- }
1803
- const tempQueue = queue;
1804
- queue = queueDelayed.reverse();
1805
- queueDelayed = tempQueue;
1806
- }
1807
-
1808
- // PART TWO
1809
- /** @type {Set<Module>} */
1810
- let newAvailableModules;
1811
-
1812
- /**
1813
- * @typedef {Object} ChunkGroupInfo
1814
- * @property {Set<Module>} minAvailableModules current minimal set of modules available at this point
1815
- * @property {Set<Module>[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
1816
- */
1817
-
1818
- /** @type {Map<ChunkGroup, ChunkGroupInfo>} */
1819
- const chunkGroupInfoMap = new Map();
1820
-
1821
- /** @type {Queue<ChunkGroup>} */
1822
- const queue2 = new Queue(inputChunkGroups);
1823
-
1824
- for (const chunkGroup of inputChunkGroups) {
1825
- chunkGroupInfoMap.set(chunkGroup, {
1826
- minAvailableModules: undefined,
1827
- availableModulesToBeMerged: [new Set()]
1828
- });
1829
- }
1830
-
1831
- /**
1832
- * Helper function to check if all modules of a chunk are available
1833
- *
1834
- * @param {ChunkGroup} chunkGroup the chunkGroup to scan
1835
- * @param {Set<Module>} availableModules the comparitor set
1836
- * @returns {boolean} return true if all modules of a chunk are available
1837
- */
1838
- const areModulesAvailable = (chunkGroup, availableModules) => {
1839
- for (const chunk of chunkGroup.chunks) {
1840
- for (const module of chunk.modulesIterable) {
1841
- if (!availableModules.has(module)) return false;
1842
- }
1843
- }
1844
- return true;
1845
- };
1846
-
1847
- // For each edge in the basic chunk graph
1848
- /**
1849
- * @param {TODO} dep the dependency used for filtering
1850
- * @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
1851
- * to modules that are already available. Also filters circular dependencies in the chunks graph
1852
- */
1853
- const filterFn = dep => {
1854
- const depChunkGroup = dep.chunkGroup;
1855
- if (!dep.couldBeFiltered) return true;
1856
- if (blocksWithNestedBlocks.has(dep.block)) return true;
1857
- if (areModulesAvailable(depChunkGroup, newAvailableModules)) {
1858
- return false; // break all modules are already available
1859
- }
1860
- dep.couldBeFiltered = false;
1861
- return true;
1862
- };
1863
-
1864
- // Iterative traversing of the basic chunk graph
1865
- while (queue2.length) {
1866
- chunkGroup = queue2.dequeue();
1867
- const info = chunkGroupInfoMap.get(chunkGroup);
1868
- const availableModulesToBeMerged = info.availableModulesToBeMerged;
1869
- let minAvailableModules = info.minAvailableModules;
1870
-
1871
- // 1. Get minimal available modules
1872
- // It doesn't make sense to traverse a chunk again with more available modules.
1873
- // This step calculates the minimal available modules and skips traversal when
1874
- // the list didn't shrink.
1875
- availableModulesToBeMerged.sort(bySetSize);
1876
- let changed = false;
1877
- for (const availableModules of availableModulesToBeMerged) {
1878
- if (minAvailableModules === undefined) {
1879
- minAvailableModules = new Set(availableModules);
1880
- info.minAvailableModules = minAvailableModules;
1881
- changed = true;
1882
- } else {
1883
- for (const m of minAvailableModules) {
1884
- if (!availableModules.has(m)) {
1885
- minAvailableModules.delete(m);
1886
- changed = true;
1887
- }
1888
- }
1889
- }
1890
- }
1891
- availableModulesToBeMerged.length = 0;
1892
- if (!changed) continue;
1893
-
1894
- // 2. Get the edges at this point of the graph
1895
- const deps = chunkDependencies.get(chunkGroup);
1896
- if (!deps) continue;
1897
- if (deps.length === 0) continue;
1898
-
1899
- // 3. Create a new Set of available modules at this points
1900
- newAvailableModules = new Set(minAvailableModules);
1901
- for (const chunk of chunkGroup.chunks) {
1902
- for (const m of chunk.modulesIterable) {
1903
- newAvailableModules.add(m);
1904
- }
1905
- }
1906
-
1907
- // 4. Foreach remaining edge
1908
- const nextChunkGroups = new Set();
1909
- for (let i = 0; i < deps.length; i++) {
1910
- const dep = deps[i];
1911
-
1912
- // Filter inline, rather than creating a new array from `.filter()`
1913
- if (!filterFn(dep)) {
1914
- continue;
1915
- }
1916
- const depChunkGroup = dep.chunkGroup;
1917
- const depBlock = dep.block;
1918
-
1919
- // 5. Connect block with chunk
1920
- GraphHelpers.connectDependenciesBlockAndChunkGroup(
1921
- depBlock,
1922
- depChunkGroup
1923
- );
1924
-
1925
- // 6. Connect chunk with parent
1926
- GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
1927
-
1928
- nextChunkGroups.add(depChunkGroup);
1929
- }
1930
-
1931
- // 7. Enqueue further traversal
1932
- for (const nextChunkGroup of nextChunkGroups) {
1933
- let nextInfo = chunkGroupInfoMap.get(nextChunkGroup);
1934
- if (nextInfo === undefined) {
1935
- nextInfo = {
1936
- minAvailableModules: undefined,
1937
- availableModulesToBeMerged: []
1938
- };
1939
- chunkGroupInfoMap.set(nextChunkGroup, nextInfo);
1940
- }
1941
- nextInfo.availableModulesToBeMerged.push(newAvailableModules);
1942
-
1943
- // As queue deduplicates enqueued items this makes sure that a ChunkGroup
1944
- // is not enqueued twice
1945
- queue2.enqueue(nextChunkGroup);
1946
- }
1947
- }
1948
-
1949
- // Remove all unconnected chunk groups
1950
- for (const chunkGroup of allCreatedChunkGroups) {
1951
- if (chunkGroup.getNumberOfParents() === 0) {
1952
- for (const chunk of chunkGroup.chunks) {
1953
- const idx = this.chunks.indexOf(chunk);
1954
- if (idx >= 0) this.chunks.splice(idx, 1);
1955
- chunk.remove("unconnected");
1956
- }
1957
- chunkGroup.remove("unconnected");
1958
- }
1959
- }
1960
- }
1961
-
1962
1568
  /**
1963
1569
  *
1964
1570
  * @param {Module} module module relationship for removal