Skip to content

Commit 0c6ee6b

Browse files
Add plugins for syntax parity with Node.js 12 (#10)
Add plugins for syntax parity with Node.js 12
2 parents b3b830b + ae4126d commit 0c6ee6b

File tree

12 files changed

+587
-9
lines changed

12 files changed

+587
-9
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ var acorn = require('acorn-node')
2929

3030
The API is the same as [acorn](https://github.com/acornjs/acorn), but the following syntax features are enabled by default:
3131

32-
- Bigint syntax (via [acorn-bigint](https://github.com/acornjs/acorn-bigint]))
32+
- Bigint syntax (via [acorn-bigint](https://github.com/acornjs/acorn-bigint]), MIT)
33+
- Public and private class instance fields (via [acorn-class-fields](https://github.com/acornjs/acorn-class-fields), MIT)
34+
- Public and private class static fields (via [acorn-static-class-features](https://github.com/acornjs/acorn-static-class-features), MIT)
3335
- Dynamic `import()` (via [acorn-dynamic-import](https://github.com/kesne/acorn-dynamic-import))
34-
- The `import.meta` property (via [acorn-import-meta](https://github.com/acornjs/acorn-import-meta))
36+
- The `import.meta` property (via [acorn-import-meta](https://github.com/acornjs/acorn-import-meta), MIT)
37+
- `export * as ns from` syntax (via [acorn-export-ns-from](https://github.com/acornjs/acorn-export-ns-from), MIT)
3538

3639
And the following options have different defaults from acorn, to match Node modules:
3740

build.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ var buble = require('buble')
55

66
var HEADER = '/* Generated by `npm run build`, do not edit! */\n\n'
77

8-
function compile (name, output) { // eslint-disable-line no-unused-vars
8+
function compile (name, output, fix) {
99
console.log(name, '→', output)
1010
mkdirp.sync(path.dirname(path.join(__dirname, output)))
1111
var source = fs.readFileSync(require.resolve(name), 'utf8')
12+
if (fix) source = fix(source)
1213
var result = buble.transform(source, {
1314
transforms: {
1415
dangerousForOf: true
@@ -17,5 +18,15 @@ function compile (name, output) { // eslint-disable-line no-unused-vars
1718
fs.writeFileSync(path.join(__dirname, output), HEADER + result.code, 'utf8')
1819
}
1920

21+
function privateClassElements (str) {
22+
return str.replace('acorn-private-class-elements', '../private-class-elements')
23+
}
24+
2025
compile('acorn-bigint', './lib/bigint/index.js')
2126
compile('acorn-import-meta', './lib/import-meta/index.js')
27+
compile('acorn-export-ns-from', './lib/export-ns-from/index.js')
28+
compile('acorn-class-fields', './lib/class-fields/index.js', privateClassElements)
29+
compile('acorn-static-class-features', './lib/static-class-features/index.js', privateClassElements)
30+
compile('acorn-private-class-elements', './lib/private-class-elements/index.js', function (str) {
31+
return str.replace('class extends Parser', 'class Parser_ extends Parser')
32+
})

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ var xtend = require('xtend')
33

44
var CJSParser = acorn.Parser
55
.extend(require('./lib/bigint'))
6+
.extend(require('./lib/class-fields'))
7+
.extend(require('./lib/static-class-features'))
68
.extend(require('acorn-dynamic-import').default)
79
var ESModulesParser = CJSParser
10+
.extend(require('./lib/export-ns-from'))
811
.extend(require('./lib/import-meta'))
912

1013
function mapOptions (opts) {
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/* Generated by `npm run build`, do not edit! */
2+
3+
"use strict"
4+
5+
var acorn = require("acorn")
6+
if (acorn.version.indexOf("6.") != 0 || acorn.version.indexOf("6.0.") == 0) {
7+
throw new Error(("acorn-private-class-elements requires acorn@^6.1.0, not " + (acorn.version)))
8+
}
9+
var tt = acorn.tokTypes
10+
var TokenType = acorn.TokenType
11+
12+
module.exports = function(Parser) {
13+
// Only load this plugin once.
14+
if (Parser.prototype.parsePrivateName) {
15+
return Parser
16+
}
17+
18+
// Make sure `Parser` comes from the same acorn as our `tt`,
19+
// otherwise the comparisons fail.
20+
var cur = Parser
21+
while (cur && cur !== acorn.Parser) {
22+
cur = cur.__proto__
23+
}
24+
if (cur !== acorn.Parser) {
25+
throw new Error("acorn-private-class-elements does not support mixing different acorn copies")
26+
}
27+
28+
Parser = /*@__PURE__*/(function (Parser) {
29+
function Parser_ () {
30+
Parser.apply(this, arguments);
31+
}
32+
33+
if ( Parser ) Parser_.__proto__ = Parser;
34+
Parser_.prototype = Object.create( Parser && Parser.prototype );
35+
Parser_.prototype.constructor = Parser_;
36+
37+
Parser_.prototype._branch = function _branch () {
38+
this.__branch = this.__branch || new Parser({ecmaVersion: this.options.ecmaVersion}, this.input)
39+
this.__branch.end = this.end
40+
this.__branch.pos = this.pos
41+
this.__branch.type = this.type
42+
this.__branch.value = this.value
43+
this.__branch.containsEsc = this.containsEsc
44+
return this.__branch
45+
};
46+
47+
Parser_.prototype.parsePrivateClassElementName = function parsePrivateClassElementName (element) {
48+
element.computed = false
49+
element.key = this.parsePrivateName()
50+
if (element.key.name == "constructor") { this.raise(element.key.start, "Classes may not have a private element named constructor") }
51+
var accept = {get: "set", set: "get"}[element.kind]
52+
var privateBoundNames = this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1]
53+
if (Object.prototype.hasOwnProperty.call(privateBoundNames, element.key.name) && privateBoundNames[element.key.name] !== accept) {
54+
this.raise(element.start, "Duplicate private element")
55+
}
56+
privateBoundNames[element.key.name] = element.kind || true
57+
delete this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][element.key.name]
58+
return element.key
59+
};
60+
61+
Parser_.prototype.parsePrivateName = function parsePrivateName () {
62+
var node = this.startNode()
63+
node.name = this.value
64+
this.next()
65+
this.finishNode(node, "PrivateName")
66+
if (this.options.allowReserved == "never") { this.checkUnreserved(node) }
67+
return node
68+
};
69+
70+
// Parse # token
71+
Parser_.prototype.getTokenFromCode = function getTokenFromCode (code) {
72+
if (code === 35) {
73+
++this.pos
74+
var word = this.readWord1()
75+
return this.finishToken(this.privateNameToken, word)
76+
}
77+
return Parser.prototype.getTokenFromCode.call(this, code)
78+
};
79+
80+
// Manage stacks and check for undeclared private names
81+
Parser_.prototype.parseClass = function parseClass (node, isStatement) {
82+
this._privateBoundNamesStack = this._privateBoundNamesStack || []
83+
var privateBoundNames = Object.create(this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1] || null)
84+
this._privateBoundNamesStack.push(privateBoundNames)
85+
this._unresolvedPrivateNamesStack = this._unresolvedPrivateNamesStack || []
86+
var unresolvedPrivateNames = Object.create(null)
87+
this._unresolvedPrivateNamesStack.push(unresolvedPrivateNames)
88+
var _return = Parser.prototype.parseClass.call(this, node, isStatement)
89+
this._privateBoundNamesStack.pop()
90+
this._unresolvedPrivateNamesStack.pop()
91+
if (!this._unresolvedPrivateNamesStack.length) {
92+
var names = Object.keys(unresolvedPrivateNames)
93+
if (names.length) {
94+
names.sort(function (n1, n2) { return unresolvedPrivateNames[n1] - unresolvedPrivateNames[n2]; })
95+
this.raise(unresolvedPrivateNames[names[0]], "Usage of undeclared private name")
96+
}
97+
} else { Object.assign(this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1], unresolvedPrivateNames) }
98+
return _return
99+
};
100+
101+
// Parse private element access
102+
Parser_.prototype.parseSubscript = function parseSubscript (base, startPos, startLoc, noCalls, maybeAsyncArrow) {
103+
if (!this.eat(tt.dot)) {
104+
return Parser.prototype.parseSubscript.call(this, base, startPos, startLoc, noCalls, maybeAsyncArrow)
105+
}
106+
var node = this.startNodeAt(startPos, startLoc)
107+
node.object = base
108+
node.computed = false
109+
if (this.type == this.privateNameToken) {
110+
node.property = this.parsePrivateName()
111+
if (!this._privateBoundNamesStack.length || !this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1][node.property.name]) {
112+
this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][node.property.name] = node.property.start
113+
}
114+
} else {
115+
node.property = this.parseIdent(true)
116+
}
117+
return this.finishNode(node, "MemberExpression")
118+
};
119+
120+
// Prohibit delete of private class elements
121+
Parser_.prototype.parseMaybeUnary = function parseMaybeUnary (refDestructuringErrors, sawUnary) {
122+
var _return = Parser.prototype.parseMaybeUnary.call(this, refDestructuringErrors, sawUnary)
123+
if (_return.operator == "delete") {
124+
if (_return.argument.type == "MemberExpression" && _return.argument.property.type == "PrivateName") {
125+
this.raise(_return.start, "Private elements may not be deleted")
126+
}
127+
}
128+
return _return
129+
};
130+
131+
return Parser_;
132+
}(Parser))
133+
Parser.prototype.privateNameToken = new TokenType("privateName")
134+
return Parser
135+
}

lib/bigint/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var tt = acorn.tokTypes
77
var isIdentifierStart = acorn.isIdentifierStart
88

99
module.exports = function(Parser) {
10-
return (function (Parser) {
10+
return /*@__PURE__*/(function (Parser) {
1111
function anonymous () {
1212
Parser.apply(this, arguments);
1313
}
@@ -55,7 +55,7 @@ module.exports = function(Parser) {
5555
}
5656

5757
var str = this.input.slice(start, this.pos)
58-
var val = typeof BigInt !== "undefined" && BigInt.parseInt ? BigInt.parseInt(str) : null
58+
var val = typeof BigInt !== "undefined" ? BigInt(str) : null
5959
++this.pos
6060
return this.finishToken(tt.num, val)
6161
};

lib/class-fields/index.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* Generated by `npm run build`, do not edit! */
2+
3+
"use strict"
4+
5+
var acorn = require("acorn")
6+
var tt = acorn.tokTypes
7+
var privateClassElements = require("../private-class-elements")
8+
9+
function maybeParseFieldValue(field) {
10+
if (this.eat(tt.eq)) {
11+
var oldInFieldValue = this._inFieldValue
12+
this._inFieldValue = true
13+
field.value = this.parseExpression()
14+
this._inFieldValue = oldInFieldValue
15+
} else { field.value = null }
16+
}
17+
18+
module.exports = function(Parser) {
19+
Parser = privateClassElements(Parser)
20+
return /*@__PURE__*/(function (Parser) {
21+
function anonymous () {
22+
Parser.apply(this, arguments);
23+
}
24+
25+
if ( Parser ) anonymous.__proto__ = Parser;
26+
anonymous.prototype = Object.create( Parser && Parser.prototype );
27+
anonymous.prototype.constructor = anonymous;
28+
29+
anonymous.prototype.parseClassElement = function parseClassElement (_constructorAllowsSuper) {
30+
if (this.options.ecmaVersion >= 8 && (this.type == tt.name || this.type == this.privateNameToken || this.type == tt.bracketL || this.type == tt.string)) {
31+
var branch = this._branch()
32+
if (branch.type == tt.bracketL) {
33+
var count = 0
34+
do {
35+
if (branch.eat(tt.bracketL)) { ++count }
36+
else if (branch.eat(tt.bracketR)) { --count }
37+
else { branch.next() }
38+
} while (count > 0)
39+
} else { branch.next() }
40+
if (branch.type == tt.eq || branch.canInsertSemicolon() || branch.type == tt.semi) {
41+
var node = this.startNode()
42+
if (this.type == this.privateNameToken) {
43+
this.parsePrivateClassElementName(node)
44+
} else {
45+
this.parsePropertyName(node)
46+
}
47+
if ((node.key.type === "Identifier" && node.key.name === "constructor") ||
48+
(node.key.type === "Literal" && node.key.value === "constructor")) {
49+
this.raise(node.key.start, "Classes may not have a field called constructor")
50+
}
51+
maybeParseFieldValue.call(this, node)
52+
this.finishNode(node, "FieldDefinition")
53+
this.semicolon()
54+
return node
55+
}
56+
}
57+
58+
return Parser.prototype.parseClassElement.apply(this, arguments)
59+
};
60+
61+
// Prohibit arguments in class field initializers
62+
anonymous.prototype.parseIdent = function parseIdent (liberal, isBinding) {
63+
var ident = Parser.prototype.parseIdent.call(this, liberal, isBinding)
64+
if (this._inFieldValue && ident.name == "arguments") { this.raise(ident.start, "A class field initializer may not contain arguments") }
65+
return ident
66+
};
67+
68+
return anonymous;
69+
}(Parser))
70+
}

lib/export-ns-from/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* Generated by `npm run build`, do not edit! */
2+
3+
"use strict"
4+
5+
var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g
6+
7+
var tt = require("acorn").tokTypes
8+
9+
module.exports = function(Parser) {
10+
return /*@__PURE__*/(function (Parser) {
11+
function anonymous () {
12+
Parser.apply(this, arguments);
13+
}
14+
15+
if ( Parser ) anonymous.__proto__ = Parser;
16+
anonymous.prototype = Object.create( Parser && Parser.prototype );
17+
anonymous.prototype.constructor = anonymous;
18+
19+
anonymous.prototype.parseExport = function parseExport (node, exports) {
20+
skipWhiteSpace.lastIndex = this.pos
21+
var skip = skipWhiteSpace.exec(this.input)
22+
var next = this.input.charAt(this.pos + skip[0].length)
23+
if (next !== "*") { return Parser.prototype.parseExport.call(this, node, exports) }
24+
25+
this.next()
26+
var specifier = this.startNode()
27+
this.expect(tt.star)
28+
if (this.eatContextual("as")) {
29+
node.declaration = null
30+
specifier.exported = this.parseIdent(true)
31+
this.checkExport(exports, specifier.exported.name, this.lastTokStart)
32+
node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")]
33+
}
34+
this.expectContextual("from")
35+
if (this.type !== tt.string) { this.unexpected() }
36+
node.source = this.parseExprAtom()
37+
this.semicolon()
38+
return this.finishNode(node, node.specifiers ? "ExportNamedDeclaration" : "ExportAllDeclaration")
39+
};
40+
41+
return anonymous;
42+
}(Parser))
43+
}

lib/import-meta/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var nextTokenIsDot = function (parser) {
1414
}
1515

1616
module.exports = function(Parser) {
17-
return (function (Parser) {
17+
return /*@__PURE__*/(function (Parser) {
1818
function anonymous () {
1919
Parser.apply(this, arguments);
2020
}

0 commit comments

Comments
 (0)