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.
Files changed (3) hide show
  1. package/README.md +35 -13
  2. package/dist/index.mjs +31 -10
  3. 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: "**/*.ts",
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/hasTopLevelAwait.ts
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(ast) {
12
- if (ast?.type == null) return false;
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 = moveVarDeclarationToModuleScope(s, node.declaration, moduleScopeEnd);
168
+ s = moveVariableDeclarationToModuleScope(s, node.declaration, moduleScopeEnd);
163
169
  }
164
- }
165
- if (node.type === "VariableDeclaration") s = moveVarDeclarationToModuleScope(s, node, moduleScopeEnd);
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 moveVarDeclarationToModuleScope(s, node, declarationsEnd) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rollup-plugin-concurrent-top-level-await",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Rollup (and Vite) plugin enabling concurrent execution of modules that contain top level await.",
5
5
  "keywords": [
6
6
  "rollup-plugin",