From 01d6d6f37d41742a0029fcfa2f44d532bef07ddd Mon Sep 17 00:00:00 2001 From: coderiaser Date: Thu, 12 Dec 2024 12:42:44 +0200 Subject: [PATCH] feature: goldstein: keyword-export-no-const: add --- README.md | 13 +++ packages/goldstein/index.spec.js | 14 +++ packages/goldstein/parser.js | 2 + .../fixture/export-no-const.gs | 1 + .../fixture/export-no-const.js | 1 + packages/keyword-export-no-const/index.js | 88 +++++++++++++++++++ .../keyword-export-no-const/index.spec.js | 9 ++ 7 files changed, 128 insertions(+) create mode 100644 packages/keyword-export-no-const/fixture/export-no-const.gs create mode 100644 packages/keyword-export-no-const/fixture/export-no-const.js create mode 100644 packages/keyword-export-no-const/index.js create mode 100644 packages/keyword-export-no-const/index.spec.js diff --git a/README.md b/README.md index 74a61da..7bd600b 100644 --- a/README.md +++ b/README.md @@ -513,6 +513,19 @@ The same as: const a = require('a'); ``` +### Export without `const` + +```gs +export x = () => {}; +``` + +The same as: + +```js +export const x = () => {}; +``` + + ## 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/packages/goldstein/index.spec.js b/packages/goldstein/index.spec.js index 08e94f8..ddbbc95 100644 --- a/packages/goldstein/index.spec.js +++ b/packages/goldstein/index.spec.js @@ -589,3 +589,17 @@ test('goldstein: compile: missing comma', (t) => { t.equal(result, expected); t.end(); }); + +test('goldstein: compile: export-no-const', (t) => { + const source = montag` + export x = () => {}; + `; + + const result = compile(source); + const expected = montag` + export const x = () => {};\n + `; + + t.equal(result, expected); + t.end(); +}); diff --git a/packages/goldstein/parser.js b/packages/goldstein/parser.js index 32fd466..afae72c 100644 --- a/packages/goldstein/parser.js +++ b/packages/goldstein/parser.js @@ -20,6 +20,7 @@ import keywordUselessSemicolon from '../keyword-useless-semicolon/index.js'; import keywordAssignFrom from '../keyword-assign-from/index.js'; import internalParseMaybeAssign from '../internal-parse-maybe-assign/index.js'; import operatorSafeAssignment from '../operator-safe-assignment/index.js'; +import keywordExportNoConst from '../keyword-export-no-const/index.js'; const {values} = Object; @@ -41,6 +42,7 @@ const defaultKeywords = { keywordUselessComma, keywordUselessSemicolon, keywordAssignFrom, + keywordExportNoConst, operatorSafeAssignment, }; diff --git a/packages/keyword-export-no-const/fixture/export-no-const.gs b/packages/keyword-export-no-const/fixture/export-no-const.gs new file mode 100644 index 0000000..14b20ca --- /dev/null +++ b/packages/keyword-export-no-const/fixture/export-no-const.gs @@ -0,0 +1 @@ +export x = () => {}; \ No newline at end of file diff --git a/packages/keyword-export-no-const/fixture/export-no-const.js b/packages/keyword-export-no-const/fixture/export-no-const.js new file mode 100644 index 0000000..7f5fe6a --- /dev/null +++ b/packages/keyword-export-no-const/fixture/export-no-const.js @@ -0,0 +1 @@ +export const x = () => {}; diff --git a/packages/keyword-export-no-const/index.js b/packages/keyword-export-no-const/index.js new file mode 100644 index 0000000..f30c29a --- /dev/null +++ b/packages/keyword-export-no-const/index.js @@ -0,0 +1,88 @@ +import {types} from 'putout'; +import {tokTypes as tt} from '../operator/index.js'; + +const { + VariableDeclaration, + VariableDeclarator, +} = types; + +export default function keywordExportNoConst(Parser) { + return class extends Parser { + shouldParseExportStatement() { + if (!this.type.keyword) + return true; + + return super.shouldParseExportStatement(); + } + + parseExport(node, exports) { + this.next(); + + /* c8 ignore start */ + // export * from '...' + if (this.eat(tt.star)) + return this.parseExportAllDeclaration(node, exports); + /* c8 ignore end */ + + /* c8 ignore start */ + if (this.eat(tt._default)) { + // export default ... + this.checkExport(exports, 'default', this.lastTokStart); + node.declaration = this.parseExportDefaultDeclaration(); + + return this.finishNode(node, 'ExportDefaultDeclaration'); + } + + /* c8 ignore end */ + // export var|const|let|function|class ... + if (this.shouldParseExportStatement()) { + node.declaration = this.parseExportDeclaration(node); + + if (node.declaration.type === 'VariableDeclaration') + this.checkVariableExport(exports, node.declaration.declarations); + + if (node.declaration.type === 'ExpressionStatement') + node.declaration = VariableDeclaration('const', [ + VariableDeclarator(node.declaration.expression.left, node.declaration.expression.right), + ]); + else + this.checkExport(exports, node.declaration.id, node.declaration.id.start); + + node.specifiers = []; + node.source = null; + } else { + // export { x, y as z } [from '...'] + node.declaration = null; + node.specifiers = this.parseExportSpecifiers(exports); + + if (this.eatContextual('from')) { + if (this.type !== tt.string) + this.unexpected(); + + node.source = this.parseExprAtom(); + + if (this.options.ecmaVersion >= 16) + node.attributes = this.parseWithClause(); + } else { + for (let i = 0, list = node.specifiers; i < list.length; ++i) { + // check for keywords used as local names + const spec = list[i]; + + this.checkUnreserved(spec.local); + // check if export is defined + this.checkLocalExport(spec.local); + + if (spec.local.type === 'Literal') + this.raise(spec.local.start, 'A string literal cannot be used as an exported binding without `from`.'); + } + + node.source = null; + } + + this.semicolon(); + } + + return this.finishNode(node, 'ExportNamedDeclaration'); + } + }; +} diff --git a/packages/keyword-export-no-const/index.spec.js b/packages/keyword-export-no-const/index.spec.js new file mode 100644 index 0000000..b820dc5 --- /dev/null +++ b/packages/keyword-export-no-const/index.spec.js @@ -0,0 +1,9 @@ +import {createTest} from '../test/index.js'; +import keywordExportNoConst from './index.js'; + +const test = createTest(import.meta.url, keywordExportNoConst); + +test('goldstein: keyword: export-no-const', (t) => { + t.compile('export-no-const'); + t.end(); +});