rollup-plugin-concurrent-top-level-await 0.0.9 → 0.1.0

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 CHANGED
@@ -30,7 +30,16 @@ export default {
30
30
  };
31
31
  ```
32
32
 
33
- ### Which modules to include
33
+ ## Options
34
+
35
+ | Option | Type | Default | Description |
36
+ | ------------------------- | --------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
37
+ | `include` | `FilterPattern` | `undefined` | A pattern specifying which files to include. See [below](#which-modules-to-include) to determine which modules to include. |
38
+ | `exclude` | `FilterPattern` | `undefined` | A pattern specifying which files to exclude. Must still follow the [same considerations](#which-modules-to-include) as `include`. |
39
+ | `sourceMap` | `boolean` | `true` | Whether to generate source maps for transformed files. |
40
+ | `generatedVariablePrefix` | `string` | `"__tla"` | Prefix used for internal variables generated by the plugin. Change this if it conflicts with variable names in your code. |
41
+
42
+ ### Which modules to include?
34
43
 
35
44
  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
45
 
package/dist/index.d.mts CHANGED
@@ -7,6 +7,12 @@ declare function concurrentTopLevelAwait(options?: {
7
7
  include?: FilterPattern;
8
8
  exclude?: FilterPattern;
9
9
  sourceMap?: boolean;
10
+ /**
11
+ * Prefix used for internal variables generated by the plugin.
12
+ * Change this if it conflicts with variable names in your code.
13
+ * @default "__tla"
14
+ */
15
+ generatedVariablePrefix?: string;
10
16
  }): {
11
17
  name: string;
12
18
  apply: "build";
package/dist/index.mjs CHANGED
@@ -128,63 +128,61 @@ var AsyncModuleTracker = class {
128
128
 
129
129
  //#endregion
130
130
  //#region src/transform.ts
131
- function transform(s, ast, asyncImports, hasAwait) {
132
- let declarationsEnd;
133
- [s, declarationsEnd] = tansformAndMoveDeclarationsToModuleScope(s, ast, asyncImports);
134
- s = s.appendRight(declarationsEnd, ";\nasync function __exec() {\n");
135
- s = s.append("}\n");
136
- const tlas = `[${asyncImports.map((_, i) => `__tla${i}`).join()}].flatMap(a => {
137
- try {
138
- const result = a();
139
- if (Array.isArray(result)) {
140
- return result
141
- }
142
- return [a];
143
- } catch {
144
- return []; // happens for cyclic dependencies
145
- }
146
- })`;
147
- const execWrapper = asyncImports.length === 0 ? "__exec();" : `Promise.all(${tlas}.map(e => e())).then(() => __exec());`;
148
- if (hasAwait) s = s.append(`const __tla = ${execWrapper}; const __todo = __tla;`);
149
- else s = s.append(`const __tla = ${tlas};
150
- const __todo = ${execWrapper};`);
151
- s = s.append("if (import.meta.useTla) await __todo;");
152
- s = s.append("export function __tla_access() { return __tla; };");
153
- return s;
131
+ function transform(s, ast, asyncImports, hasAwait, variablePrefix) {
132
+ const declarationsEnd = tansformAndMoveDeclarationsToModuleScope(s, ast, asyncImports, variablePrefix);
133
+ s.appendRight(declarationsEnd, `async function ${variablePrefix}_initModuleExports() {\n`);
134
+ s.append("\n}\n");
135
+ const asyncDeps = `[${asyncImports.map((_, i) => `${variablePrefix}${i}`).join()}].flatMap(a => {
136
+ try {
137
+ const result = a();
138
+ if (Array.isArray(result)) {
139
+ return result
140
+ }
141
+ return [a];
142
+ } catch {
143
+ return []; // happens for cyclic dependencies
144
+ }
145
+ })`;
146
+ const initModuleExportsAfterDeps = asyncImports.length === 0 ? `${variablePrefix}_initModuleExports()` : `Promise.all(${asyncDeps}.map(e => e())).then(() => ${variablePrefix}_initModuleExports())`;
147
+ if (hasAwait) s.append(`const ${variablePrefix} = ${initModuleExportsAfterDeps};\nconst ${variablePrefix}_initPromise = ${variablePrefix};\n`);
148
+ else s.append(`const ${variablePrefix} = ${asyncDeps};\nconst ${variablePrefix}_initPromise = ${initModuleExportsAfterDeps};\n`);
149
+ s.append(`if (import.meta.useTla) await ${variablePrefix}_initPromise;\n`);
150
+ s.append(`export function ${variablePrefix}_access() { return ${variablePrefix}; };\n`);
154
151
  }
155
- function tansformAndMoveDeclarationsToModuleScope(s, ast, asyncImports) {
152
+ function tansformAndMoveDeclarationsToModuleScope(s, ast, asyncImports, variablePrefix) {
156
153
  let moduleScopeEnd = 0;
157
154
  let i = 0;
158
155
  for (const node of ast.body) {
159
156
  if (asyncImports.includes(node)) {
160
- const tlaImport = `;import { __tla_access as __tla${i}} from '${node.source.value}';`;
161
- s = s.appendLeft(node.end, tlaImport);
157
+ const tlaImport = `\nimport { ${variablePrefix}_access as ${variablePrefix}${i}} from '${node.source.value}';`;
158
+ s.appendLeft(node.end, tlaImport);
162
159
  i++;
163
160
  }
164
161
  if (node.type === "ExportNamedDeclaration") {
165
162
  if (node.declaration?.type === "VariableDeclaration") {
166
- s = s.appendLeft(moduleScopeEnd, ";export ");
167
- s = s.remove(node.start, node.declaration.start);
168
- s = moveVariableDeclarationToModuleScope(s, node.declaration, moduleScopeEnd);
163
+ s.appendLeft(moduleScopeEnd, "export ");
164
+ s.remove(node.start, node.declaration.start);
165
+ moveVariableDeclarationToModuleScope(s, node.declaration, moduleScopeEnd);
169
166
  }
170
- } else if (node.type === "VariableDeclaration") if (node.kind.endsWith("using")) s = moveVariableDeclarationWithUsingToModuleScope(s, node, moduleScopeEnd);
171
- else s = moveVariableDeclarationToModuleScope(s, node, moduleScopeEnd);
167
+ } else if (node.type === "VariableDeclaration") if (node.kind.endsWith("using")) moveVariableDeclarationWithUsingToModuleScope(s, node, moduleScopeEnd, variablePrefix);
168
+ else moveVariableDeclarationToModuleScope(s, node, moduleScopeEnd);
172
169
  else visitScope(node, (n) => {
173
- if (n.type === "VariableDeclaration" && n.kind === "var") s = moveVariableDeclarationToModuleScope(s, n, moduleScopeEnd);
170
+ if (n.type === "VariableDeclaration" && n.kind === "var") moveVariableDeclarationToModuleScope(s, n, moduleScopeEnd);
174
171
  return false;
175
172
  });
176
173
  if (node.type === "ExportDefaultDeclaration" && !isDeclaration(node.declaration.type)) {
177
- s = s.appendLeft(moduleScopeEnd, "let __tla_default;\nexport { __tla_default as default };\n");
178
- s = s.remove(node.start, node.declaration.start);
179
- s = s.appendRight(node.declaration.start, ";__tla_default = (");
180
- s = s.appendLeft(node.declaration.end, ");");
174
+ s.appendLeft(moduleScopeEnd, `let ${variablePrefix}_default;\nexport { ${variablePrefix}_default as default };\n`);
175
+ s.remove(node.start, node.declaration.start);
176
+ s.appendRight(node.declaration.start, `${variablePrefix}_default = (`);
177
+ s.appendLeft(node.declaration.end, ");");
181
178
  }
182
179
  if (isDeclaration(node.type) || node.type === "ImportDeclaration" || node.type === "ExportDefaultDeclaration" && isDeclaration(node.declaration.type) || node.type === "ExportNamedDeclaration" && isDeclaration(node.declaration?.type) || node.type === "ExportNamedDeclaration" && node.declaration == null || node.type === "ExportAllDeclaration") if (node.start > moduleScopeEnd) {
183
- s = s.appendRight(node.start, ";\n");
184
- s = s.move(node.start, node.end, moduleScopeEnd);
180
+ s.appendLeft(node.end, "\n");
181
+ s.move(node.start, node.end, moduleScopeEnd);
182
+ s.appendLeft(node.start, ";");
185
183
  } else moduleScopeEnd = node.end;
186
184
  }
187
- return [s, moduleScopeEnd];
185
+ return moduleScopeEnd;
188
186
  }
189
187
  function isDeclaration(type) {
190
188
  return type === "ClassDeclaration" || type === "FunctionDeclaration";
@@ -192,20 +190,20 @@ function isDeclaration(type) {
192
190
  function moveVariableDeclarationToModuleScope(s, node, declarationsEnd) {
193
191
  const kind = replaceConstWithLet(node.kind);
194
192
  const names = node.declarations.flatMap((decl) => getNames(decl.id)).join(", ");
195
- s = s.appendRight(node.declarations[0].start, ";(");
196
- s = s.appendLeft(node.declarations[node.declarations.length - 1].end, ")");
197
- s = s.appendLeft(declarationsEnd, `\n${kind} ${names};\n`);
198
- s = s.remove(node.start, node.declarations[0].start);
193
+ s.appendRight(node.declarations[0].start, ";(");
194
+ s.appendLeft(node.declarations[node.declarations.length - 1].end, ")");
195
+ s.appendLeft(declarationsEnd, `${kind} ${names};\n`);
196
+ s.remove(node.start, node.declarations[0].start);
199
197
  return s;
200
198
  }
201
- function moveVariableDeclarationWithUsingToModuleScope(s, node, declarationsEnd) {
199
+ function moveVariableDeclarationWithUsingToModuleScope(s, node, declarationsEnd, variablePrefix) {
202
200
  node.declarations.forEach((declaration) => {
203
201
  const id = declaration.id;
204
202
  if (id.type !== "Identifier") throw new Error("'using' declarations may not have binding patterns.");
205
203
  const name = id.name;
206
- s = s.appendRight(id.start, `__tla_using_`);
207
- s = s.appendLeft(node.end, `;\n${name} = __tla_using_${name};`);
208
- s = s.appendLeft(declarationsEnd, `\nlet ${name};\n`);
204
+ s.appendRight(id.start, `${variablePrefix}_using_`);
205
+ s.appendLeft(node.end, `\n${name} = ${variablePrefix}_using_${name};`);
206
+ s.appendLeft(declarationsEnd, `let ${name};\n`);
209
207
  });
210
208
  return s;
211
209
  }
@@ -260,8 +258,8 @@ function concurrentTopLevelAwait(options = {}) {
260
258
  return declaration;
261
259
  }))).filter(Boolean);
262
260
  if (!(asyncImports.length > 0 || hasAwait)) return;
263
- let s = new MagicString(code);
264
- s = transform(s, ast, asyncImports, hasAwait);
261
+ const s = new MagicString(code);
262
+ transform(s, ast, asyncImports, hasAwait, options.generatedVariablePrefix ?? "__tla");
265
263
  return {
266
264
  code: s.toString(),
267
265
  map: options.sourceMap !== false ? s.generateMap({ hires: true }) : null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rollup-plugin-concurrent-top-level-await",
3
- "version": "0.0.9",
3
+ "version": "0.1.0",
4
4
  "description": "Rollup (and Vite) plugin enabling concurrent execution of modules that contain top level await.",
5
5
  "keywords": [
6
6
  "rollup-plugin",