Skip to content

Commit

Permalink
fix TLA with esnext syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
caub committed Aug 28, 2019
1 parent 8c512f4 commit d98956c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 66 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@babel/core": "^7.5.5",
"@babel/generator": "^7.5.5",
"@babel/parser": "^7.5.5",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.4.4",
Expand All @@ -34,6 +35,7 @@
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/plugin-transform-modules-commonjs": "^7.5.0",
"@babel/plugin-transform-typescript": "^7.5.5",
"@babel/traverse": "^7.5.5",
"bluebird": "^3.5.2",
"chalk": "^2.4.1",
"cheerio": "^0.22.0",
Expand All @@ -44,7 +46,6 @@
"json5": "^2.1.0",
"node-fetch": "^2.3.0",
"prettier": "^1.14.3",
"recast": "^0.18.1",
"shell-escape": "^0.2.0",
"superagent": "^3.8.3"
},
Expand Down
60 changes: 31 additions & 29 deletions src/plugins/js-eval/jsEvalPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,44 @@ const CMD = ['node', '--no-warnings', '/run/run.js'];
const CMD_SHIMS = ['node', '-r', '/run/node_modules/airbnb-js-shims/target/es2019', '--no-warnings', '/run/run.js'];
const CMD_HARMONY = ['node', '--harmony', '--experimental-vm-modules', '--experimental-modules', '--no-warnings', '/run/run.js'];

const babelTransformOpts = {
parserOpts: {
allowAwaitOutsideFunction: true,
allowReturnOutsideFunction: true,
},
plugins: [
'@babel/plugin-transform-typescript',
'@babel/plugin-transform-modules-commonjs', // required by dynamicImport
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: false }], // must be before class-properties https://babeljs.io/docs/en/babel-plugin-proposal-decorators#note-compatibility-with-babel-plugin-proposal-class-properties
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-do-expressions',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-function-bind',
'@babel/plugin-proposal-json-strings',
'@babel/plugin-proposal-logical-assignment-operators',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-optional-catch-binding',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-partial-application',
['@babel/plugin-proposal-pipeline-operator', { proposal: 'minimal' }],
'@babel/plugin-proposal-throw-expressions',
'@babel/plugin-proposal-dynamic-import',
'@babel/plugin-syntax-bigint',
'@babel/plugin-syntax-import-meta',
]
};

const jsEvalPlugin = async ({ mentionUser, respond, message, selfConfig = {} }) => {
if (!/^[nhbsme?]>/.test(message)) return;
const mode = message[0];
if (mode === '?') return respond((mentionUser ? `${mentionUser}, ` : '') + helpMsg);
let code = message.slice(2);

if (mode === 'b') {
code = (await babel.transformAsync(code, {
parserOpts: {
allowAwaitOutsideFunction: true,
allowReturnOutsideFunction: true,
},
plugins: [
'@babel/plugin-transform-typescript',
'@babel/plugin-transform-modules-commonjs', // required by dynamicImport
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: false }], // must be before class-properties https://babeljs.io/docs/en/babel-plugin-proposal-decorators#note-compatibility-with-babel-plugin-proposal-class-properties
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-do-expressions',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-function-bind',
'@babel/plugin-proposal-json-strings',
'@babel/plugin-proposal-logical-assignment-operators',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-optional-catch-binding',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-partial-application',
['@babel/plugin-proposal-pipeline-operator', { proposal: 'minimal' }],
'@babel/plugin-proposal-throw-expressions',
'@babel/plugin-proposal-dynamic-import',
'@babel/plugin-syntax-bigint',
'@babel/plugin-syntax-import-meta',
]
})).code;
code = (await babel.transformAsync(code, babelTransformOpts)).code;
}

code = processTopLevelAwait(code) || code; // it returns null when no TLA is found
Expand Down
97 changes: 61 additions & 36 deletions src/plugins/js-eval/processTopLevelAwait.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,72 @@
const babelParser = require('@babel/parser');
const recast = require('recast');
const babelTraverse = require('@babel/traverse').default;
const babelGenerator = require('@babel/generator').default;

const babelParseOpts = {
allowAwaitOutsideFunction: true,
plugins: [
'throwExpressions',
'bigInt',
['decorators', { decoratorsBeforeExport: true }],
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'doExpressions',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'functionBind',
'functionSent',
'importMeta',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
// 'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'partialApplication',
['pipelineOperator', { proposal: 'minimal' }],
'throwExpressions'
]
};

const b = recast.types.builders;

function processTopLevelAwait(src) {
let root;

try {
root = recast.parse(src, {
parser: {
parse(src) {
return babelParser.parse(src, { allowAwaitOutsideFunction: true });
}
}
});
root = babelParser.parse(src, babelParseOpts);
} catch (error) {
return null; // if code is not valid, don't bother
}

let containsAwait = false;
let containsReturn = false;

recast.visit(root, {
visitNode: function (path) {
const node = path.value;

switch (node.type) {
babelTraverse(root, {
enter(path) {
switch (path.type) {
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'ArrowFunctionExpression':
case 'MethodDefinition':
case 'ClassMethod':
// stop when entering a new function scope:
return false;
return path.stop();

case 'ForOfStatement':
if (node.await === true) {
if (path.node.await === true) {
containsAwait = true;
}
return this.traverse(path);
return;

case 'AwaitExpression':
containsAwait = true;
return this.traverse(path);
return;

case 'ReturnStatement':
containsReturn = true;
return this.traverse(path);

default:
return this.traverse(path);
return;
}
}
});
Expand All @@ -65,20 +84,26 @@ function processTopLevelAwait(src) {
}

// replace last node with a returnStatement of this node
root.program.body[root.program.body.length - 1] = b.returnStatement(last);

const iiafe = b.callExpression(
b.arrowFunctionExpression(
[],
b.blockStatement(root.program.body),
true
),
[]
);

iiafe.callee.async = true;

return recast.print(iiafe).code;
root.program.body[root.program.body.length - 1] = {
type: 'ReturnStatement',
argument: last
};

const iiafe = {
type: 'CallExpression',
callee: {
type: 'ArrowFunctionExpression',
async: true,
params: [],
body: {
type: 'BlockStatement',
body: root.program.body
},
},
arguments: []
};

return babelGenerator(iiafe).code;
}

module.exports = processTopLevelAwait;

0 comments on commit d98956c

Please sign in to comment.