Skip to content

Commit

Permalink
implement experimentalTopLevelAwait option (#2235)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford authored and lukastaegert committed Jul 2, 2018
1 parent cd437b7 commit cf96883
Show file tree
Hide file tree
Showing 15 changed files with 85 additions and 13 deletions.
13 changes: 12 additions & 1 deletion src/Chunk.ts
Expand Up @@ -1057,6 +1057,16 @@ export default class Chunk {
dep => dep.reexports && dep.reexports.length !== 0
);

const usesTopLevelAwait = this.orderedModules.some(module => module.usesTopLevelAwait);
if (usesTopLevelAwait && options.format !== 'es' && options.format !== 'system') {
error({
code: 'INVALID_TLA_FORMAT',
message: `Module format ${
options.format
} does not support top-level await. Use the "es" or "system" output formats rather.`
});
}

const magicString = finalise(
this.renderedSource,
{
Expand All @@ -1070,7 +1080,8 @@ export default class Chunk {
dependencies: this.renderedDeclarations.dependencies,
exports: this.renderedDeclarations.exports,
graph: this.graph,
isEntryModuleFacade: this.isEntryModuleFacade
isEntryModuleFacade: this.isEntryModuleFacade,
usesTopLevelAwait
},
options
);
Expand Down
4 changes: 4 additions & 0 deletions src/Graph.ts
Expand Up @@ -218,6 +218,10 @@ export default class Graph {
this.acornOptions.plugins.dynamicImport = true;
this.acornOptions.plugins.importMeta = true;

if (options.experimentalTopLevelAwait) {
(<any>this.acornOptions).allowAwaitOutsideFunction = true;
}

acornPluginsToInject.push(...ensureArray(options.acornInjectPlugins));
this.acornParse = acornPluginsToInject.reduce((acc, plugin) => plugin(acc), acorn).parse;
}
Expand Down
6 changes: 5 additions & 1 deletion src/Module.ts
Expand Up @@ -95,6 +95,7 @@ export interface AstContext {
traceExport: (name: string) => Variable;
traceVariable: (name: string) => Variable;
treeshake: boolean;
usesTopLevelAwait: boolean;
varOrConst: string;
warn: (warning: RollupWarning, pos: number) => void;
}
Expand Down Expand Up @@ -177,6 +178,7 @@ export default class Module {
entryPointsHash: Uint8Array;
chunk: Chunk;
exportAllModules: (Module | ExternalModule)[];
usesTopLevelAwait: boolean = false;

private ast: Program;
private astContext: AstContext;
Expand Down Expand Up @@ -265,8 +267,8 @@ export default class Module {
getReexports: this.getReexports.bind(this),
getModuleExecIndex: () => this.execIndex,
getModuleName: this.basename.bind(this),
includeNamespace: this.includeNamespace.bind(this),
imports: this.imports,
includeNamespace: this.includeNamespace.bind(this),
isCrossChunkImport: importDescription => importDescription.module.chunk !== this.chunk,
magicString: this.magicString,
moduleContext: this.context,
Expand All @@ -278,6 +280,7 @@ export default class Module {
traceExport: this.traceExport.bind(this),
traceVariable: this.traceVariable.bind(this),
treeshake: this.graph.treeshake,
usesTopLevelAwait: false,
varOrConst: this.graph.varOrConst,
warn: this.warn.bind(this)
};
Expand Down Expand Up @@ -639,6 +642,7 @@ export default class Module {
render(options: RenderOptions): MagicString {
const magicString = this.magicString.clone();
this.ast.render(magicString, options);
this.usesTopLevelAwait = this.astContext.usesTopLevelAwait;
return magicString;
}

Expand Down
20 changes: 20 additions & 0 deletions src/ast/nodes/AwaitExpression.ts
@@ -1,12 +1,32 @@
import MagicString from 'magic-string';
import { RenderOptions } from '../../utils/renderHelpers';
import { ExecutionPathOptions } from '../ExecutionPathOptions';
import ArrowFunctionExpression from './ArrowFunctionExpression';
import * as NodeType from './NodeType';
import FunctionNode from './shared/FunctionNode';
import { Node } from './shared/Node';
import { ExpressionNode, NodeBase } from './shared/Node';

export default class AwaitExpression extends NodeBase {
type: NodeType.tAwaitExpression;
argument: ExpressionNode;

include() {
super.include();
if (!this.context.usesTopLevelAwait) {
let parent = this.parent;
do {
if (parent instanceof FunctionNode || parent instanceof ArrowFunctionExpression) return;
} while ((parent = <Node>(<Node>parent).parent));
this.context.usesTopLevelAwait = true;
}
}

hasEffects(options: ExecutionPathOptions) {
return super.hasEffects(options) || !options.ignoreReturnAwaitYield();
}

render(code: MagicString, options: RenderOptions) {
super.render(code, options);
}
}
8 changes: 3 additions & 5 deletions src/ast/nodes/shared/FunctionNode.ts
@@ -1,7 +1,7 @@
import CallOptions from '../../CallOptions';
import { ExecutionPathOptions } from '../../ExecutionPathOptions';
import BlockScope from '../../scopes/FunctionScope';
import FunctionScope from '../../scopes/FunctionScope';
import BlockScope from '../../scopes/FunctionScope';
import Scope from '../../scopes/Scope';
import { ObjectPath, UNKNOWN_EXPRESSION } from '../../values';
import BlockStatement from '../BlockStatement';
Expand Down Expand Up @@ -78,10 +78,8 @@ export default class FunctionNode extends NodeBase {
}

parseNode(esTreeNode: GenericEsTreeNode) {
this.body = <BlockStatement>new this.context.nodeConstructors.BlockStatement(
esTreeNode.body,
this,
new Scope(this.scope)
this.body = <BlockStatement>(
new this.context.nodeConstructors.BlockStatement(esTreeNode.body, this, new Scope(this.scope))
);
super.parseNode(esTreeNode);
}
Expand Down
1 change: 1 addition & 0 deletions src/finalisers/index.ts
Expand Up @@ -21,6 +21,7 @@ export interface FinaliserOptions {
exports: ChunkExports;
graph: Graph;
isEntryModuleFacade: boolean;
usesTopLevelAwait: boolean;
}

export type Finaliser = (
Expand Down
14 changes: 12 additions & 2 deletions src/finalisers/system.ts
Expand Up @@ -19,7 +19,15 @@ function getStarExcludes({ dependencies, exports }: ModuleDeclarations) {

export default function system(
magicString: MagicStringBundle,
{ graph, indentString: t, intro, outro, dependencies, exports }: FinaliserOptions,
{
graph,
indentString: t,
intro,
outro,
dependencies,
exports,
usesTopLevelAwait
}: FinaliserOptions,
options: OutputOptions
) {
const n = options.compact ? '' : '\n';
Expand Down Expand Up @@ -143,7 +151,9 @@ export default function system(
.join(`,${_}`)}],`
: ''
}${n}`;
wrapperStart += `${t}${t}execute:${_}function${_}()${_}{${n}${n}`;
wrapperStart += `${t}${t}execute:${_}${
usesTopLevelAwait ? `async${_}` : ''
}function${_}()${_}{${n}${n}`;
if (hoistedExports.length)
wrapperStart += `${t}${t}${t}` + hoistedExports.join(`${n}${t}${t}${t}`) + n + n;

Expand Down
3 changes: 2 additions & 1 deletion src/rollup/types.d.ts
Expand Up @@ -210,8 +210,9 @@ export interface InputOptions {
context?: string;
moduleContext?: string | ((id: string) => string) | { [id: string]: string };
watch?: WatcherOptions;
inlineDynamicImports?: boolean;
experimentalCodeSplitting?: boolean;
experimentalDynamicImport?: boolean;
experimentalTopLevelAwait?: boolean;
preserveSymlinks?: boolean;
experimentalPreserveModules?: boolean;
optimizeChunks?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/utils/mergeOptions.ts
Expand Up @@ -202,6 +202,7 @@ function getInputOptions(
context: config.context,
experimentalCodeSplitting: getOption('experimentalCodeSplitting'),
experimentalPreserveModules: getOption('experimentalPreserveModules'),
experimentalTopLevelAwait: getOption('experimentalTopLevelAwait'),
external: getExternal(config, command),
inlineDynamicImports: getOption('inlineDynamicImports', false),
input: getOption('input'),
Expand Down
2 changes: 1 addition & 1 deletion test/form/index.js
Expand Up @@ -79,7 +79,7 @@ function runTestCaseInDir(dir) {
return runRollupTest(dir + '/_actual.js', dir + '/_expected.js', 'esm');
}

FORMATS.forEach(format =>
(config.formats || FORMATS).forEach(format =>
it('generates ' + format, () =>
runRollupTest(
dir + '/_actual/' + format + '.js',
Expand Down
7 changes: 7 additions & 0 deletions test/form/samples/tla/_config.js
@@ -0,0 +1,7 @@
module.exports = {
description: 'top-level await support',
formats: ['system', 'es'],
options: {
experimentalTopLevelAwait: true
}
};
1 change: 1 addition & 0 deletions test/form/samples/tla/_expected/es.js
@@ -0,0 +1 @@
await operation();
10 changes: 10 additions & 0 deletions test/form/samples/tla/_expected/system.js
@@ -0,0 +1,10 @@
System.register([], function (exports, module) {
'use strict';
return {
execute: async function () {

await operation();

}
};
});
4 changes: 4 additions & 0 deletions test/form/samples/tla/main.js
@@ -0,0 +1,4 @@
await operation();

if (false)
await treeshakenOperation();
4 changes: 2 additions & 2 deletions test/misc/optionList.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cf96883

Please sign in to comment.