From f6d5a7708b8b7b64e221106929692c13add7366f Mon Sep 17 00:00:00 2001 From: caub Date: Fri, 23 Aug 2019 23:01:21 +0200 Subject: [PATCH] attempt at parsing once --- package.json | 2 + src/plugins/js-eval/babelPlugins.js | 54 +++++++++++++++ src/plugins/js-eval/jsEvalPlugin.js | 46 ++++++------- src/plugins/js-eval/processTopLevelAwait.js | 73 +++++++++------------ 4 files changed, 105 insertions(+), 70 deletions(-) create mode 100644 src/plugins/js-eval/babelPlugins.js diff --git a/package.json b/package.json index 6ac62f1..48e2490 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.5.5", "@babel/generator": "^7.5.5", "@babel/parser": "^7.5.5", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", "@babel/plugin-proposal-class-properties": "^7.1.0", "@babel/plugin-proposal-decorators": "^7.4.4", "@babel/plugin-proposal-do-expressions": "^7.0.0", @@ -31,6 +32,7 @@ "@babel/plugin-proposal-partial-application": "^7.4.4", "@babel/plugin-proposal-pipeline-operator": "^7.0.0", "@babel/plugin-proposal-throw-expressions": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", "@babel/plugin-syntax-bigint": "^7.0.0", "@babel/plugin-syntax-import-meta": "^7.0.0", "@babel/plugin-transform-modules-commonjs": "^7.5.0", diff --git a/src/plugins/js-eval/babelPlugins.js b/src/plugins/js-eval/babelPlugins.js new file mode 100644 index 0000000..7765d78 --- /dev/null +++ b/src/plugins/js-eval/babelPlugins.js @@ -0,0 +1,54 @@ +// all babel proposal plugins https://github.com/babel/babel/tree/master/packages (preset-stage-n packages are depreacted https://babeljs.io/docs/en/next/babel-preset-stage-1) +// if there are new ones, feel free to add them +exports.transformPlugins = [ + '@babel/plugin-proposal-async-generator-functions', + '@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', + '@babel/plugin-proposal-unicode-property-regex' +]; + + +// @babel/parser plugins https://babeljs.io/docs/en/next/babel-parser.html#ecmascript-proposals-https-githubcom-babel-proposals +exports.parserPlugins = [ + 'asyncGenerators', + 'bigInt', + ['decorators', { decoratorsBeforeExport: true }], + 'classProperties', + 'classPrivateProperties', + 'classPrivateMethods', + 'doExpressions', + 'dynamicImport', + 'exportDefaultFrom', + 'exportNamespaceFrom', + 'functionBind', + 'functionSent', + 'importMeta', + 'logicalAssignment', + 'nullishCoalescingOperator', + 'numericSeparator', + // 'objectRestSpread', // no need, node has it since a long time + 'optionalCatchBinding', + 'optionalChaining', + 'partialApplication', + ['pipelineOperator', { proposal: 'minimal' }], + 'throwExpressions' +] diff --git a/src/plugins/js-eval/jsEvalPlugin.js b/src/plugins/js-eval/jsEvalPlugin.js index 6c07ead..ff89a88 100644 --- a/src/plugins/js-eval/jsEvalPlugin.js +++ b/src/plugins/js-eval/jsEvalPlugin.js @@ -1,9 +1,12 @@ const cp = require('child_process'); const babel = require('@babel/core'); +const babelGenerator = require('@babel/generator').default; const jsEval = require('./jsEval'); const processTopLevelAwait = require('./processTopLevelAwait'); +const { transformPlugins } = require('./babelPlugins'); -const helpMsg = `n> node stable, h> node harmony, b> babel, s> node vm.Script, m> node vm.SourceTextModule, e> engine262`; + +const helpMsg = `n> node stable, h> node --harmony, b> babel, s> node vm.Script, m> node vm.SourceTextModule, e> engine262`; // default jseval run command const CMD = ['node', '--no-warnings', '/run/run.js']; @@ -15,29 +18,7 @@ const babelTransformOpts = { 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 = {} }) => { @@ -46,11 +27,22 @@ const jsEvalPlugin = async ({ mentionUser, respond, message, selfConfig = {} }) if (mode === '?') return respond((mentionUser ? `${mentionUser}, ` : '') + helpMsg); let code = message.slice(2); - if (mode === 'b') { - code = (await babel.transformAsync(code, babelTransformOpts)).code; + const noAwait = /\bawait\b/.test(code); + + if (mode === 'b' && noAwait) { + code = (await babel.transformAsync(code, { plugins: transformPlugins })).code; } - code = processTopLevelAwait(code) || code; // it returns null when no TLA is found + if (!noAwait) { // there's maybe a TLA await + const iiafe = processTopLevelAwait(code); + if (iiafe) { // there's a TLA + if (mode === 'b') { + code = (await babel.transformFromAstAsync(iiafe, code, { plugins: transformPlugins })).code; + } else { + code = babelGenerator(iiafe).code; + } + } + } try { const result = await jsEval( diff --git a/src/plugins/js-eval/processTopLevelAwait.js b/src/plugins/js-eval/processTopLevelAwait.js index f2bc109..7b897d5 100644 --- a/src/plugins/js-eval/processTopLevelAwait.js +++ b/src/plugins/js-eval/processTopLevelAwait.js @@ -1,41 +1,20 @@ const babelParser = require('@babel/parser'); 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 { parserPlugins } = require('./babelPlugins'); +/** + * + * @param {string} src + * @return {object} ast + */ function processTopLevelAwait(src) { let root; try { - root = babelParser.parse(src, babelParseOpts); + root = babelParser.parse(src, { + allowAwaitOutsideFunction: true, + plugins: parserPlugins + }); } catch (error) { return null; // if code is not valid, don't bother } @@ -90,20 +69,28 @@ function processTopLevelAwait(src) { }; const iiafe = { - type: 'CallExpression', - callee: { - type: 'ArrowFunctionExpression', - async: true, - params: [], - body: { - type: 'BlockStatement', - body: root.program.body - }, - }, - arguments: [] + type: 'Program', + sourceType: 'script', + body: [{ + type: 'ExpressionStatement', + expression: { + type: 'CallExpression', + callee: { + type: 'ArrowFunctionExpression', + async: true, + params: [], + body: { + type: 'BlockStatement', + body: root.program.body + }, + }, + arguments: [] + } + }], }; + // const iiafe = t.program([t.expressionStatement(t.callExpression(t.arrowFunctionExpression([], t.blockStatement(root.program.body)), []))]) - return babelGenerator(iiafe).code; + return iiafe; } module.exports = processTopLevelAwait;