rollup-plugin-concurrent-top-level-await 0.0.9 → 0.2.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 +20 -4
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +65 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,16 @@ export default {
|
|
|
30
30
|
};
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
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
|
|
|
@@ -90,6 +99,14 @@ postponed until the subgraph is analyzed. This may lead to slower builds.
|
|
|
90
99
|
|
|
91
100
|
If you notice significant performance degradation, please open an issue.
|
|
92
101
|
|
|
102
|
+
### Exposed Module Structure
|
|
103
|
+
|
|
104
|
+
Because the execution of modules gets wrapped in functions, the bundled output will contain more information about the source module structure. This may be a consideration for projects where code obfuscation is important.
|
|
105
|
+
|
|
106
|
+
### Tree Shaking
|
|
107
|
+
|
|
108
|
+
Wrapping code in functions may reduce tree shaking effectiveness. We mitigate this where possible, such as by not wrapping declarations.
|
|
109
|
+
|
|
93
110
|
### Changing Variable Types
|
|
94
111
|
|
|
95
112
|
In the process of transforming the code, top level `const` declarations may get replaced with `let` declarations. This
|
|
@@ -97,7 +114,6 @@ can lead to `const` variables being assignable at runtime instead of throwing an
|
|
|
97
114
|
|
|
98
115
|
Additionally, variable declarations may be hoisted, which removes temporal dead zone (TDZ) checks.
|
|
99
116
|
|
|
100
|
-
###
|
|
117
|
+
### Default export class name
|
|
101
118
|
|
|
102
|
-
|
|
103
|
-
relies on a top level await expression, it may not work as expected.
|
|
119
|
+
When using `export default class {}`, the runtime `.name` of the exported value will be `<generatedVariablePrefix>_default` (e.g. `__tla_default`) instead of `default`.
|
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,84 +128,96 @@ var AsyncModuleTracker = class {
|
|
|
128
128
|
|
|
129
129
|
//#endregion
|
|
130
130
|
//#region src/transform.ts
|
|
131
|
-
function transform(s, ast, asyncImports, hasAwait) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
s
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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 = transformAndMoveDeclarationsToModuleScope(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
|
|
152
|
+
function transformAndMoveDeclarationsToModuleScope(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 =
|
|
161
|
-
s
|
|
157
|
+
const tlaImport = `\nimport { ${variablePrefix}_access as ${variablePrefix}${i}} from '${node.source.value}';`;
|
|
158
|
+
s.appendLeft(node.end, tlaImport);
|
|
162
159
|
i++;
|
|
163
160
|
}
|
|
161
|
+
if (node.type === "ClassDeclaration") {
|
|
162
|
+
s.appendLeft(moduleScopeEnd, `let ${node.id.name};\n`);
|
|
163
|
+
s.appendRight(node.start, `${node.id.name} = `);
|
|
164
|
+
}
|
|
164
165
|
if (node.type === "ExportNamedDeclaration") {
|
|
165
166
|
if (node.declaration?.type === "VariableDeclaration") {
|
|
166
|
-
s
|
|
167
|
-
s
|
|
168
|
-
|
|
167
|
+
s.appendLeft(moduleScopeEnd, "export ");
|
|
168
|
+
s.remove(node.start, node.declaration.start);
|
|
169
|
+
moveVariableDeclarationToModuleScope(s, node.declaration, moduleScopeEnd);
|
|
170
|
+
} else if (node.declaration?.type === "ClassDeclaration") {
|
|
171
|
+
s.appendLeft(moduleScopeEnd, `export let ${node.declaration.id.name};\n`);
|
|
172
|
+
const declarationStart = getClassDeclarationStart(node.declaration);
|
|
173
|
+
s.remove(node.start, declarationStart);
|
|
174
|
+
s.appendRight(declarationStart, `${node.declaration.id.name} = `);
|
|
169
175
|
}
|
|
170
|
-
} else if (node.type === "VariableDeclaration") if (node.kind.endsWith("using"))
|
|
171
|
-
else
|
|
176
|
+
} else if (node.type === "VariableDeclaration") if (node.kind.endsWith("using")) moveVariableDeclarationWithUsingToModuleScope(s, node, moduleScopeEnd, variablePrefix);
|
|
177
|
+
else moveVariableDeclarationToModuleScope(s, node, moduleScopeEnd);
|
|
172
178
|
else visitScope(node, (n) => {
|
|
173
|
-
if (n.type === "VariableDeclaration" && n.kind === "var")
|
|
179
|
+
if (n.type === "VariableDeclaration" && n.kind === "var") moveVariableDeclarationToModuleScope(s, n, moduleScopeEnd);
|
|
174
180
|
return false;
|
|
175
181
|
});
|
|
176
|
-
if (node.type === "ExportDefaultDeclaration" && !
|
|
177
|
-
|
|
178
|
-
s
|
|
179
|
-
|
|
180
|
-
s
|
|
182
|
+
if (node.type === "ExportDefaultDeclaration" && !isFunctionDeclaration(node.declaration.type)) {
|
|
183
|
+
const variableName = node.declaration.type === "ClassDeclaration" ? node.declaration.id?.name ?? `${variablePrefix}_default` : `${variablePrefix}_default`;
|
|
184
|
+
s.appendLeft(moduleScopeEnd, `let ${variableName};\nexport { ${variableName} as default };\n`);
|
|
185
|
+
const declarationStart = node.declaration.type === "ClassDeclaration" ? getClassDeclarationStart(node.declaration) : node.declaration.start;
|
|
186
|
+
s.remove(node.start, declarationStart);
|
|
187
|
+
s.appendRight(declarationStart, `${variableName} = (`);
|
|
188
|
+
s.appendLeft(node.declaration.end, ");");
|
|
181
189
|
}
|
|
182
|
-
if (
|
|
183
|
-
s
|
|
184
|
-
s
|
|
190
|
+
if (isFunctionDeclaration(node.type) || node.type === "ImportDeclaration" || node.type === "ExportDefaultDeclaration" && isFunctionDeclaration(node.declaration.type) || node.type === "ExportNamedDeclaration" && isFunctionDeclaration(node.declaration?.type) || node.type === "ExportNamedDeclaration" && node.declaration == null || node.type === "ExportAllDeclaration") if (node.start > moduleScopeEnd) {
|
|
191
|
+
s.appendLeft(node.end, "\n");
|
|
192
|
+
s.move(node.start, node.end, moduleScopeEnd);
|
|
193
|
+
s.appendLeft(node.start, ";");
|
|
185
194
|
} else moduleScopeEnd = node.end;
|
|
186
195
|
}
|
|
187
|
-
return
|
|
196
|
+
return moduleScopeEnd;
|
|
197
|
+
}
|
|
198
|
+
function isFunctionDeclaration(type) {
|
|
199
|
+
return type === "FunctionDeclaration";
|
|
188
200
|
}
|
|
189
|
-
function
|
|
190
|
-
return
|
|
201
|
+
function getClassDeclarationStart(node) {
|
|
202
|
+
return node.decorators[0]?.start ?? node.start;
|
|
191
203
|
}
|
|
192
204
|
function moveVariableDeclarationToModuleScope(s, node, declarationsEnd) {
|
|
193
205
|
const kind = replaceConstWithLet(node.kind);
|
|
194
206
|
const names = node.declarations.flatMap((decl) => getNames(decl.id)).join(", ");
|
|
195
|
-
s
|
|
196
|
-
s
|
|
197
|
-
s
|
|
198
|
-
s
|
|
207
|
+
s.appendRight(node.declarations[0].start, ";(");
|
|
208
|
+
s.appendLeft(node.declarations[node.declarations.length - 1].end, ")");
|
|
209
|
+
s.appendLeft(declarationsEnd, `${kind} ${names};\n`);
|
|
210
|
+
s.remove(node.start, node.declarations[0].start);
|
|
199
211
|
return s;
|
|
200
212
|
}
|
|
201
|
-
function moveVariableDeclarationWithUsingToModuleScope(s, node, declarationsEnd) {
|
|
213
|
+
function moveVariableDeclarationWithUsingToModuleScope(s, node, declarationsEnd, variablePrefix) {
|
|
202
214
|
node.declarations.forEach((declaration) => {
|
|
203
215
|
const id = declaration.id;
|
|
204
216
|
if (id.type !== "Identifier") throw new Error("'using' declarations may not have binding patterns.");
|
|
205
217
|
const name = id.name;
|
|
206
|
-
s
|
|
207
|
-
s
|
|
208
|
-
s
|
|
218
|
+
s.appendRight(id.start, `${variablePrefix}_using_`);
|
|
219
|
+
s.appendLeft(node.end, `\n${name} = ${variablePrefix}_using_${name};`);
|
|
220
|
+
s.appendLeft(declarationsEnd, `let ${name};\n`);
|
|
209
221
|
});
|
|
210
222
|
return s;
|
|
211
223
|
}
|
|
@@ -260,8 +272,8 @@ function concurrentTopLevelAwait(options = {}) {
|
|
|
260
272
|
return declaration;
|
|
261
273
|
}))).filter(Boolean);
|
|
262
274
|
if (!(asyncImports.length > 0 || hasAwait)) return;
|
|
263
|
-
|
|
264
|
-
|
|
275
|
+
const s = new MagicString(code);
|
|
276
|
+
transform(s, ast, asyncImports, hasAwait, options.generatedVariablePrefix ?? "__tla");
|
|
265
277
|
return {
|
|
266
278
|
code: s.toString(),
|
|
267
279
|
map: options.sourceMap !== false ? s.generateMap({ hires: true }) : null
|
package/package.json
CHANGED