Skip to content

Commit

Permalink
Wait for SystemJS to reparent module before processing top level awaits
Browse files Browse the repository at this point in the history
  • Loading branch information
rpetrich committed Dec 20, 2021
1 parent 0c7aa4c commit eea0acd
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 22 deletions.
1 change: 1 addition & 0 deletions async-to-promises.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const environments = {
},
pluginMapping: {
"transform-modules-commonjs": "@babel/plugin-transform-modules-commonjs",
"transform-modules-systemjs": "@babel/plugin-transform-modules-systemjs",
"transform-parameters": "@babel/plugin-transform-parameters",
"transform-classes": "@babel/plugin-transform-classes",
"external-helpers": "@babel/plugin-external-helpers",
Expand Down
69 changes: 47 additions & 22 deletions async-to-promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ type UsedHelpers = { [key in HelperName]: true };

interface PluginState {
readonly opts: Partial<Readonly<AsyncToPromisesConfiguration>>;
processedTopLevelAwait?: true;
hasTopLevelAwait?: boolean;
usedHelpers?: UsedHelpers;
}

Expand Down Expand Up @@ -349,6 +349,10 @@ const numberNames = ["zero", "one", "two", "three", "four", "five", "six", "seve

type CompatibleSubset<New, Old> = Partial<New> & Pick<New, keyof New & keyof Old>;

interface FindAwaitExpressionState {
awaitPath?: NodePath;
}

// function generate(node?: Node): string {
// if (node === undefined) {
// return "";
Expand Down Expand Up @@ -4791,38 +4795,59 @@ export default function ({
},
};

const findAwaitExpressionVisitor: Visitor<FindAwaitExpressionState> = {
AwaitExpression(path) {
this.awaitPath = path;
path.stop();
},
};

// Main babel plugin implementation and top level visitor
return {
manipulateOptions(_options: any, parserOptions: { plugins: string[] }) {
parserOptions.plugins.push("asyncGenerators");
},
visitor: {
AwaitExpression(path) {
if (!path.getFunctionParent() && !this.processedTopLevelAwait) {
this.processedTopLevelAwait = true;
switch (readConfigKey(this.opts, "topLevelAwait")) {
case "simple": {
const programPath = path.scope.getProgramParent().path;
rewriteAsyncBlock({ state: this }, programPath, [], undefined, false, true);
break;
}
case "return": {
const programPath = path.scope.getProgramParent().path;
rewriteAsyncBlock({ state: this }, programPath, [], undefined, false, false);
break;
}
case "ignore":
break;
default:
throw path.buildCodeFrameError(
`Top level await is not supported unless experimental topLevelAwait: "simple" or topLevelAwait: "return" options are specified!`,
TypeError
);
}
if (!path.getFunctionParent() && !this.hasTopLevelAwait) {
this.hasTopLevelAwait = true;
}
},
Program: {
exit(path) {
if (this.hasTopLevelAwait) {
// rediscover the top level await path since it may have been reorganized by a module plugin
let rediscoverState = {} as FindAwaitExpressionState;
path.traverse(findAwaitExpressionVisitor, rediscoverState);
if (rediscoverState.awaitPath !== undefined) {
const functionParent = rediscoverState.awaitPath.getFunctionParent();
const topLevelAwaitParent = functionParent ? functionParent.get("body") : path;
switch (readConfigKey(this.opts, "topLevelAwait")) {
case "simple": {
rewriteAsyncBlock({ state: this }, topLevelAwaitParent, [], undefined, false, true);
break;
}
case "return": {
rewriteAsyncBlock(
{ state: this },
topLevelAwaitParent,
[],
undefined,
false,
false
);
break;
}
case "ignore":
break;
default:
throw rediscoverState.awaitPath.buildCodeFrameError(
`Top level await is not supported unless experimental topLevelAwait: "simple" or topLevelAwait: "return" options are specified!`,
TypeError
);
}
}
}
const usedHelpers = this.usedHelpers;
if (usedHelpers !== undefined) {
const file = getFile(path);
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@babel/plugin-proposal-class-properties": "^7.16.5",
"@babel/plugin-transform-classes": "^7.16.5",
"@babel/plugin-transform-modules-commonjs": "^7.16.5",
"@babel/plugin-transform-modules-systemjs": "^7.16.5",
"@babel/plugin-transform-parameters": "^7.16.5",
"@babel/preset-env": "^7.16.5",
"@babel/traverse": "^7.16.5",
Expand Down
1 change: 1 addition & 0 deletions tests/top level await export/hoisted.js

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

1 change: 1 addition & 0 deletions tests/top level await export/inlined.js

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

5 changes: 5 additions & 0 deletions tests/top level await export/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const head = 1;
const response = await fetch("https://www.example.com/");
export const body = 2;
const json = await response.json();
export const tail = json;
9 changes: 9 additions & 0 deletions tests/top level await export/options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"supportedBabels": ["babel 7"],
"checkSyntax": false,
"module": true,
"plugins": ["transform-modules-systemjs"],
"options": {
"topLevelAwait": "return"
}
}
1 change: 1 addition & 0 deletions tests/top level await export/output.js

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

0 comments on commit eea0acd

Please sign in to comment.