rollup-plugin-concurrent-top-level-await 0.0.6 → 0.0.8
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.
- package/README.md +35 -13
- package/dist/index.mjs +31 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,12 +24,46 @@ import concurrentTopLevelAwait from "rollup-plugin-concurrent-top-level-await";
|
|
|
24
24
|
export default {
|
|
25
25
|
plugins: [
|
|
26
26
|
concurrentTopLevelAwait({
|
|
27
|
-
include: "**/*.
|
|
27
|
+
include: "**/*.js",
|
|
28
28
|
}),
|
|
29
29
|
],
|
|
30
30
|
};
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
### Which modules to include
|
|
34
|
+
|
|
35
|
+
The plugin needs to handle not only modules that directly contain a top-level `await`, but also their ancestor modules up to the lowest common ancestor. Ancestor modules must be transformed to handle the asynchronous completion of their children concurrently. As an example, consider the following module structure:
|
|
36
|
+
|
|
37
|
+
```mermaid
|
|
38
|
+
flowchart LR
|
|
39
|
+
app[app.js]
|
|
40
|
+
moduleA[moduleA.js]
|
|
41
|
+
moduleB[moduleB.js]
|
|
42
|
+
moduleC[moduleC.js]
|
|
43
|
+
tla1[tla1.js]
|
|
44
|
+
tla2[tla2.js]
|
|
45
|
+
tla3[tla3.js]
|
|
46
|
+
other[other.js]
|
|
47
|
+
|
|
48
|
+
app --> moduleA
|
|
49
|
+
moduleA --> moduleB
|
|
50
|
+
moduleA --> moduleC
|
|
51
|
+
moduleB --> tla1
|
|
52
|
+
moduleC --> tla2
|
|
53
|
+
moduleC --> tla3
|
|
54
|
+
app --> other
|
|
55
|
+
|
|
56
|
+
classDef tla fill:#ffe6e6,stroke:#ff0000,color:#660000
|
|
57
|
+
classDef ancestor fill:#fff4cc,stroke:#ffcc00,color:#663300
|
|
58
|
+
classDef unaffected fill:#e6ffe6,stroke:#00cc00,color:#006600
|
|
59
|
+
|
|
60
|
+
class tla1,tla2,tla3 tla
|
|
61
|
+
class moduleA,moduleB,moduleC ancestor
|
|
62
|
+
class app,other unaffected
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If the red modules contain top level awaits, these and their yellow ancestors should be included in the plugin's `include` option.
|
|
66
|
+
|
|
33
67
|
## Known Limitations
|
|
34
68
|
|
|
35
69
|
### Execution Order
|
|
@@ -63,18 +97,6 @@ can lead to `const` variables being assignable at runtime instead of throwing an
|
|
|
63
97
|
|
|
64
98
|
Additionally, variable declarations may be hoisted, which removes temporal dead zone (TDZ) checks.
|
|
65
99
|
|
|
66
|
-
### Exporting var
|
|
67
|
-
|
|
68
|
-
The plugin only checks the very top level of a module for potentially exported variables. This means that if a `var`
|
|
69
|
-
variable is declared inside a block but exported, the variable might not get handled correctly.
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
if (something) {
|
|
73
|
-
var myValue = 42;
|
|
74
|
-
}
|
|
75
|
-
export default myValue;
|
|
76
|
-
```
|
|
77
|
-
|
|
78
100
|
### Class Decorators
|
|
79
101
|
|
|
80
102
|
Class declarations still get evaluated before any top level await expressions. This means that if a class decorator
|
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { createFilter } from "@rollup/pluginutils";
|
|
2
2
|
import MagicString from "magic-string";
|
|
3
3
|
|
|
4
|
-
//#region src/
|
|
4
|
+
//#region src/ast.ts
|
|
5
5
|
function isFunctionNode(node) {
|
|
6
6
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "MethodDefinition" || node.type === "Property" && node.value.type === "FunctionExpression";
|
|
7
7
|
}
|
|
8
|
+
function visitScope(node, cb) {
|
|
9
|
+
if (node?.type == null) return false;
|
|
10
|
+
if (cb(node)) return true;
|
|
11
|
+
if (isFunctionNode(node)) return false;
|
|
12
|
+
return Object.values(node).flat().some((n) => visitScope(n, cb));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/hasTopLevelAwait.ts
|
|
8
17
|
function isAwaitNode(node) {
|
|
9
18
|
return node.type === "AwaitExpression" || node.type === "ForOfStatement" && node.await || node.type === "VariableDeclaration" && node.kind === "await using";
|
|
10
19
|
}
|
|
11
|
-
function hasTopLevelAwait(
|
|
12
|
-
|
|
13
|
-
if (isAwaitNode(ast)) return true;
|
|
14
|
-
if (isFunctionNode(ast)) return false;
|
|
15
|
-
return Object.values(ast).flat().some(hasTopLevelAwait);
|
|
20
|
+
function hasTopLevelAwait(node) {
|
|
21
|
+
return visitScope(node, isAwaitNode);
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
//#endregion
|
|
@@ -159,10 +165,14 @@ function tansformAndMoveDeclarationsToModuleScope(s, ast, asyncImports) {
|
|
|
159
165
|
if (node.declaration?.type === "VariableDeclaration") {
|
|
160
166
|
s = s.appendLeft(moduleScopeEnd, ";export ");
|
|
161
167
|
s = s.remove(node.start, node.declaration.start);
|
|
162
|
-
s =
|
|
168
|
+
s = moveVariableDeclarationToModuleScope(s, node.declaration, moduleScopeEnd);
|
|
163
169
|
}
|
|
164
|
-
}
|
|
165
|
-
|
|
170
|
+
} else if (node.type === "VariableDeclaration") if (node.kind.endsWith("using")) s = moveVariableDeclarationWithUsingToModuleScope(s, node, moduleScopeEnd);
|
|
171
|
+
else s = moveVariableDeclarationToModuleScope(s, node, moduleScopeEnd);
|
|
172
|
+
else visitScope(node, (n) => {
|
|
173
|
+
if (n.type === "VariableDeclaration" && n.kind === "var") s = moveVariableDeclarationToModuleScope(s, n, moduleScopeEnd);
|
|
174
|
+
return false;
|
|
175
|
+
});
|
|
166
176
|
if (isDeclaration(node.type) || node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" && isDeclaration(node.declaration?.type) || node.type === "ExportNamedDeclaration" && node.declaration == null || node.type === "ExportDefaultDeclaration" || node.type === "ExportAllDeclaration") if (node.start > moduleScopeEnd) {
|
|
167
177
|
s = s.appendRight(node.start, ";\n");
|
|
168
178
|
s = s.move(node.start, node.end, moduleScopeEnd);
|
|
@@ -173,7 +183,7 @@ function tansformAndMoveDeclarationsToModuleScope(s, ast, asyncImports) {
|
|
|
173
183
|
function isDeclaration(type) {
|
|
174
184
|
return type === "ClassDeclaration" || type === "FunctionDeclaration";
|
|
175
185
|
}
|
|
176
|
-
function
|
|
186
|
+
function moveVariableDeclarationToModuleScope(s, node, declarationsEnd) {
|
|
177
187
|
const kind = replaceConstWithLet(node.kind);
|
|
178
188
|
const names = node.declarations.flatMap((decl) => getNames(decl.id)).join(", ");
|
|
179
189
|
s = s.appendRight(node.declarations[0].start, ";(");
|
|
@@ -182,6 +192,17 @@ function moveVarDeclarationToModuleScope(s, node, declarationsEnd) {
|
|
|
182
192
|
s = s.remove(node.start, node.declarations[0].start);
|
|
183
193
|
return s;
|
|
184
194
|
}
|
|
195
|
+
function moveVariableDeclarationWithUsingToModuleScope(s, node, declarationsEnd) {
|
|
196
|
+
node.declarations.forEach((declaration) => {
|
|
197
|
+
const id = declaration.id;
|
|
198
|
+
if (id.type !== "Identifier") throw new Error("'using' declarations may not have binding patterns.");
|
|
199
|
+
const name = id.name;
|
|
200
|
+
s = s.appendRight(id.start, `__tla_using_`);
|
|
201
|
+
s = s.appendLeft(node.end, `;\n${name} = __tla_using_${name};`);
|
|
202
|
+
s = s.appendLeft(declarationsEnd, `\nlet ${name};\n`);
|
|
203
|
+
});
|
|
204
|
+
return s;
|
|
205
|
+
}
|
|
185
206
|
function replaceConstWithLet(value) {
|
|
186
207
|
if (value === "const") return "let";
|
|
187
208
|
return value;
|
package/package.json
CHANGED