1 | /*
|
---|
2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
3 | Author Florent Cailhol @ooflorent
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | const { ConcatSource } = require("webpack-sources");
|
---|
9 |
|
---|
10 | /** @typedef {import("webpack-sources").Source} Source */
|
---|
11 | /** @typedef {import("./Generator").GenerateContext} GenerateContext */
|
---|
12 |
|
---|
13 | /**
|
---|
14 | * @param {InitFragment} fragment the init fragment
|
---|
15 | * @param {number} index index
|
---|
16 | * @returns {[InitFragment, number]} tuple with both
|
---|
17 | */
|
---|
18 | const extractFragmentIndex = (fragment, index) => [fragment, index];
|
---|
19 |
|
---|
20 | /**
|
---|
21 | * @param {[InitFragment, number]} a first pair
|
---|
22 | * @param {[InitFragment, number]} b second pair
|
---|
23 | * @returns {number} sort value
|
---|
24 | */
|
---|
25 | const sortFragmentWithIndex = ([a, i], [b, j]) => {
|
---|
26 | const stageCmp = a.stage - b.stage;
|
---|
27 | if (stageCmp !== 0) return stageCmp;
|
---|
28 | const positionCmp = a.position - b.position;
|
---|
29 | if (positionCmp !== 0) return positionCmp;
|
---|
30 | return i - j;
|
---|
31 | };
|
---|
32 |
|
---|
33 | /**
|
---|
34 | * @template Context
|
---|
35 | */
|
---|
36 | class InitFragment {
|
---|
37 | /**
|
---|
38 | * @param {string|Source} content the source code that will be included as initialization code
|
---|
39 | * @param {number} stage category of initialization code (contribute to order)
|
---|
40 | * @param {number} position position in the category (contribute to order)
|
---|
41 | * @param {string=} key unique key to avoid emitting the same initialization code twice
|
---|
42 | * @param {string|Source=} endContent the source code that will be included at the end of the module
|
---|
43 | */
|
---|
44 | constructor(content, stage, position, key, endContent) {
|
---|
45 | this.content = content;
|
---|
46 | this.stage = stage;
|
---|
47 | this.position = position;
|
---|
48 | this.key = key;
|
---|
49 | this.endContent = endContent;
|
---|
50 | }
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * @param {Context} context context
|
---|
54 | * @returns {string|Source} the source code that will be included as initialization code
|
---|
55 | */
|
---|
56 | getContent(context) {
|
---|
57 | return this.content;
|
---|
58 | }
|
---|
59 |
|
---|
60 | /**
|
---|
61 | * @param {Context} context context
|
---|
62 | * @returns {string|Source=} the source code that will be included at the end of the module
|
---|
63 | */
|
---|
64 | getEndContent(context) {
|
---|
65 | return this.endContent;
|
---|
66 | }
|
---|
67 |
|
---|
68 | static addToSource(source, initFragments, context) {
|
---|
69 | if (initFragments.length > 0) {
|
---|
70 | // Sort fragments by position. If 2 fragments have the same position,
|
---|
71 | // use their index.
|
---|
72 | const sortedFragments = initFragments
|
---|
73 | .map(extractFragmentIndex)
|
---|
74 | .sort(sortFragmentWithIndex);
|
---|
75 |
|
---|
76 | // Deduplicate fragments. If a fragment has no key, it is always included.
|
---|
77 | const keyedFragments = new Map();
|
---|
78 | for (const [fragment] of sortedFragments) {
|
---|
79 | if (typeof fragment.mergeAll === "function") {
|
---|
80 | if (!fragment.key) {
|
---|
81 | throw new Error(
|
---|
82 | `InitFragment with mergeAll function must have a valid key: ${fragment.constructor.name}`
|
---|
83 | );
|
---|
84 | }
|
---|
85 | const oldValue = keyedFragments.get(fragment.key);
|
---|
86 | if (oldValue === undefined) {
|
---|
87 | keyedFragments.set(fragment.key, fragment);
|
---|
88 | } else if (Array.isArray(oldValue)) {
|
---|
89 | oldValue.push(fragment);
|
---|
90 | } else {
|
---|
91 | keyedFragments.set(fragment.key, [oldValue, fragment]);
|
---|
92 | }
|
---|
93 | continue;
|
---|
94 | } else if (typeof fragment.merge === "function") {
|
---|
95 | const oldValue = keyedFragments.get(fragment.key);
|
---|
96 | if (oldValue !== undefined) {
|
---|
97 | keyedFragments.set(fragment.key, fragment.merge(oldValue));
|
---|
98 | continue;
|
---|
99 | }
|
---|
100 | }
|
---|
101 | keyedFragments.set(fragment.key || Symbol(), fragment);
|
---|
102 | }
|
---|
103 |
|
---|
104 | const concatSource = new ConcatSource();
|
---|
105 | const endContents = [];
|
---|
106 | for (let fragment of keyedFragments.values()) {
|
---|
107 | if (Array.isArray(fragment)) {
|
---|
108 | fragment = fragment[0].mergeAll(fragment);
|
---|
109 | }
|
---|
110 | concatSource.add(fragment.getContent(context));
|
---|
111 | const endContent = fragment.getEndContent(context);
|
---|
112 | if (endContent) {
|
---|
113 | endContents.push(endContent);
|
---|
114 | }
|
---|
115 | }
|
---|
116 |
|
---|
117 | concatSource.add(source);
|
---|
118 | for (const content of endContents.reverse()) {
|
---|
119 | concatSource.add(content);
|
---|
120 | }
|
---|
121 | return concatSource;
|
---|
122 | } else {
|
---|
123 | return source;
|
---|
124 | }
|
---|
125 | }
|
---|
126 | }
|
---|
127 |
|
---|
128 | InitFragment.prototype.merge = undefined;
|
---|
129 |
|
---|
130 | InitFragment.STAGE_CONSTANTS = 10;
|
---|
131 | InitFragment.STAGE_ASYNC_BOUNDARY = 20;
|
---|
132 | InitFragment.STAGE_HARMONY_EXPORTS = 30;
|
---|
133 | InitFragment.STAGE_HARMONY_IMPORTS = 40;
|
---|
134 | InitFragment.STAGE_PROVIDES = 50;
|
---|
135 | InitFragment.STAGE_ASYNC_DEPENDENCIES = 60;
|
---|
136 | InitFragment.STAGE_ASYNC_HARMONY_IMPORTS = 70;
|
---|
137 |
|
---|
138 | module.exports = InitFragment;
|
---|