From 98f3048078e2574477d9c7b392bf1984101e87c2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 6 May 2024 20:14:20 +0300 Subject: [PATCH] feature: goldstein: add ability to parse no async code with await --- README.md | 16 +++++++ package.json | 5 +- packages/goldstein/index.js | 2 + packages/goldstein/index.spec.js | 39 ++++++++++++++-- packages/goldstein/parser.js | 2 + packages/keyword-no-async/fixture/no-async.gs | 5 +- packages/keyword-no-async/fixture/no-async.js | 6 +-- packages/keyword-no-async/index.js | 46 +++++++++---------- packages/keyword-no-async/index.spec.js | 4 +- 9 files changed, 89 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index b6ddbff..9a8d4f9 100644 --- a/README.md +++ b/README.md @@ -407,6 +407,22 @@ That absolutely fine, it will be converted to: function hello() {} ``` +### `asyn`-less `Function` with `await` + +```gs +function hello() { + await world(); +} +``` + +In js: + +```js +async function hello() { + await world(); +} +``` + ## How to contribute? Clone the registry, create a new keyword with a prefix `keyword-`, then create directory `fixture` and put there two files with extensions `.js` and `.gs`. Half way done 🥳! diff --git a/package.json b/package.json index 7b073ba..427a606 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@putout/plugin-declare": "^4.0.0", "@putout/plugin-logical-expressions": "^5.0.0", "@putout/plugin-try-catch": "^3.0.0", + "@putout/plugin-promises": "^14.1.0", "@putout/printer": "^8.0.1", "acorn": "^8.7.1", "esbuild": "^0.20.1", @@ -45,6 +46,7 @@ "devDependencies": { "@babel/core": "^8.0.0-alpha.5", "@cloudcmd/stub": "^4.0.1", + "@putout/plugin-goldstein": "./rules/goldstein", "@putout/test": "^9.0.1", "c8": "^9.1.0", "check-dts": "^0.7.1", @@ -58,8 +60,7 @@ "redlint": "^3.14.1", "runsome": "^1.0.0", "supertape": "^10.1.0", - "typescript": "^5.0.3", - "@putout/plugin-goldstein": "./rules/goldstein" + "typescript": "^5.0.3" }, "engines": { "node": ">=18" diff --git a/packages/goldstein/index.js b/packages/goldstein/index.js index 3d59edb..b93d1b1 100644 --- a/packages/goldstein/index.js +++ b/packages/goldstein/index.js @@ -3,6 +3,7 @@ import {print} from '@putout/printer'; import tryCatchPlugin from '@putout/plugin-try-catch'; import declarePlugin from '@putout/plugin-declare'; import logicalExpressionsPlugin from '@putout/plugin-logical-expressions'; +import promisesPlugin from '@putout/plugin-promises'; import {parse} from './parser.js'; export * from './parser.js'; @@ -19,6 +20,7 @@ export const compile = (source, options = {}) => { ['try-catch', tryCatchPlugin], ['declare', declarePlugin], ['logical-expressions', logicalExpressionsPlugin], + ['promises', promisesPlugin], ], }); diff --git a/packages/goldstein/index.spec.js b/packages/goldstein/index.spec.js index b10e504..eff63b1 100644 --- a/packages/goldstein/index.spec.js +++ b/packages/goldstein/index.spec.js @@ -11,10 +11,15 @@ import { test('goldstein: compile', (t) => { const result = compile(` fn hello() { + return 'world' } `); - const expected = 'function hello() {}\n'; + const expected = montag` + function hello() { + return 'world'; + }\n + `; t.equal(result, expected); t.end(); @@ -115,11 +120,15 @@ test('goldstein: compile: freeze', (t) => { test('goldstein: compile: sourceType', (t) => { const result = compile(montag` - export fn hello() {}; + export fn hello() { + return 'world'; + }; `); const expected = montag` - export function hello() {}; + export function hello() { + return 'world'; + }; `; @@ -162,11 +171,14 @@ test('goldstein: compile: curry', (t) => { test('goldstein: compile: arrow', (t) => { const result = compile(montag` function hello() => { + return 'world'; } `); const expected = montag` - function hello() {}\n + function hello() { + return 'world'; + }\n `; t.equal(result, expected); @@ -320,3 +332,22 @@ test('goldstein: convert', (t) => { t.equal(result, expected); t.end(); }); + +test('goldstein: convert: no-async', (t) => { + const source = montag` + function hello() { + await world(); + } + `; + + const result = convert(source); + + const expected = montag` + async function hello() { + await world(); + }\n + `; + + t.equal(result, expected); + t.end(); +}); diff --git a/packages/goldstein/parser.js b/packages/goldstein/parser.js index 38b53ba..094a6c8 100644 --- a/packages/goldstein/parser.js +++ b/packages/goldstein/parser.js @@ -12,6 +12,7 @@ import keywordIf from '../keyword-if/index.js'; import keywordImport from '../keyword-import/index.js'; import keywordArrow from '../keyword-arrow/index.js'; import keywordAddArray from '../keyword-add-array/index.js'; +import keywordNoAsync from '../keyword-no-async/index.js'; const defaultKeywords = { keywordFn, @@ -26,6 +27,7 @@ const defaultKeywords = { keywordArrow, keywordAddArray, stringInterpolation, + keywordNoAsync, }; export const keywords = defaultKeywords; diff --git a/packages/keyword-no-async/fixture/no-async.gs b/packages/keyword-no-async/fixture/no-async.gs index afb252d..4be35b3 100644 --- a/packages/keyword-no-async/fixture/no-async.gs +++ b/packages/keyword-no-async/fixture/no-async.gs @@ -1,2 +1,3 @@ -function hello() => {} -function world() {} +function hello() { + await world(); +} \ No newline at end of file diff --git a/packages/keyword-no-async/fixture/no-async.js b/packages/keyword-no-async/fixture/no-async.js index fb4cb62..27e5836 100644 --- a/packages/keyword-no-async/fixture/no-async.js +++ b/packages/keyword-no-async/fixture/no-async.js @@ -1,3 +1,3 @@ -function hello() {} - -function world() {} +async function hello() { + await world(); +} diff --git a/packages/keyword-no-async/index.js b/packages/keyword-no-async/index.js index d2620e3..b8d7493 100644 --- a/packages/keyword-no-async/index.js +++ b/packages/keyword-no-async/index.js @@ -1,36 +1,36 @@ -import {tokTypes as tt} from 'acorn'; +const {defineProperty} = Object; export default function fn(Parser) { return class extends Parser { - parseBlock(createNewLexicalScope, node, exitStrict) { - if (createNewLexicalScope === void 0) - createNewLexicalScope = true; - - if (node === void 0) - node = this.startNode(); + parseFunction(node, statement, allowExpressionBody, isAsync, forInit) { + return super.parseFunction(node, statement, allowExpressionBody, true, forInit); + } + + checkUnreserved({start, end, name}) { + if (this.inGenerator && name === 'yield') + this.raiseRecoverable(start, `Cannot use 'yield' as identifier inside a generator`); - node.body = []; - // optionally parse arrow - this.eat(tt.arrow); - this.expect(tt.braceL); + if (this.inAsync && name === 'await') + this.raiseRecoverable(start, `Cannot use 'await' as identifier inside an async function`); - if (createNewLexicalScope) - this.enterScope(0); + if (this.currentThisScope().inClassFieldInit && name === 'arguments') + this.raiseRecoverable(start, `Cannot use 'arguments' in class field initializer`); - while (this.type !== tt.braceR) { - const stmt = this.parseStatement(null); - node.body.push(stmt); - } + if (this.inClassStaticBlock && (name === 'arguments' || name === 'await')) + this.raise(start, `Cannot use ${name} in class static initialization block`); - if (exitStrict) - this.strict = false; + if (this.keywords.test(name)) + this.raise(start, `Unexpected keyword '` + name + `'`); - this.next(); + if (this.options.ecmaVersion < 6 && this.input.slice(start, end).includes('\\')) + return; - if (createNewLexicalScope) - this.exitScope(); + const re = this.strict ? this.reservedWordsStrict : this.reservedWords; - return this.finishNode(node, 'BlockStatement'); + if (re.test(name) && !this.inAsync && name === 'await') + defineProperty(this, 'inAsync', { + value: true, + }); } }; } diff --git a/packages/keyword-no-async/index.spec.js b/packages/keyword-no-async/index.spec.js index e3ef232..7a1c0aa 100644 --- a/packages/keyword-no-async/index.spec.js +++ b/packages/keyword-no-async/index.spec.js @@ -3,7 +3,7 @@ import keywordFn from './index.js'; const test = createTest(import.meta.url, keywordFn); -test('goldstein: keyword: arrow', (t) => { - t.compile('arrow'); +test('goldstein: keyword: no-async', (t) => { + t.compile('no-async'); t.end(); });