-
Notifications
You must be signed in to change notification settings - Fork 13
/
index.js
165 lines (148 loc) · 6.36 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
"use strict"
const getPrototype = Object.getPrototypeOf || (o => o.__proto__)
const getAcorn = Parser => {
if (Parser.acorn) return Parser.acorn
const acorn = require("acorn")
if (acorn.version.indexOf("6.") != 0 && acorn.version.indexOf("6.0.") == 0 && acorn.version.indexOf("7.") != 0) {
throw new Error(`acorn-private-class-elements requires acorn@^6.1.0 or acorn@7.0.0, not ${acorn.version}`)
}
// Make sure `Parser` comes from the same acorn as we `require`d,
// otherwise the comparisons fail.
for (let cur = Parser; cur && cur !== acorn.Parser; cur = getPrototype(cur)) {
if (cur !== acorn.Parser) {
throw new Error("acorn-private-class-elements does not support mixing different acorn copies")
}
}
return acorn
}
module.exports = function(Parser) {
// Only load this plugin once.
if (Parser.prototype.parsePrivateName) {
return Parser
}
const acorn = getAcorn(Parser)
Parser = class extends Parser {
_branch() {
this.__branch = this.__branch || new Parser({ecmaVersion: this.options.ecmaVersion}, this.input)
this.__branch.end = this.end
this.__branch.pos = this.pos
this.__branch.type = this.type
this.__branch.value = this.value
this.__branch.containsEsc = this.containsEsc
return this.__branch
}
parsePrivateClassElementName(element) {
element.computed = false
element.key = this.parsePrivateName()
if (element.key.name == "constructor") this.raise(element.key.start, "Classes may not have a private element named constructor")
const accept = {get: "set", set: "get"}[element.kind]
const privateBoundNames = this._privateBoundNames
if (Object.prototype.hasOwnProperty.call(privateBoundNames, element.key.name) && privateBoundNames[element.key.name] !== accept) {
this.raise(element.start, "Duplicate private element")
}
privateBoundNames[element.key.name] = element.kind || true
delete this._unresolvedPrivateNames[element.key.name]
return element.key
}
parsePrivateName() {
const node = this.startNode()
node.name = this.value
this.next()
this.finishNode(node, "PrivateIdentifier")
if (this.options.allowReserved == "never") this.checkUnreserved(node)
return node
}
// Parse # token
getTokenFromCode(code) {
if (code === 35) {
++this.pos
const word = this.readWord1()
return this.finishToken(this.privateIdentifierToken, word)
}
return super.getTokenFromCode(code)
}
// Manage stacks and check for undeclared private names
parseClass(node, isStatement) {
const oldOuterPrivateBoundNames = this._outerPrivateBoundNames
this._outerPrivateBoundNames = this._privateBoundNames
this._privateBoundNames = Object.create(this._privateBoundNames || null)
const oldOuterUnresolvedPrivateNames = this._outerUnresolvedPrivateNames
this._outerUnresolvedPrivateNames = this._unresolvedPrivateNames
this._unresolvedPrivateNames = Object.create(null)
const _return = super.parseClass(node, isStatement)
const unresolvedPrivateNames = this._unresolvedPrivateNames
this._privateBoundNames = this._outerPrivateBoundNames
this._outerPrivateBoundNames = oldOuterPrivateBoundNames
this._unresolvedPrivateNames = this._outerUnresolvedPrivateNames
this._outerUnresolvedPrivateNames = oldOuterUnresolvedPrivateNames
if (!this._unresolvedPrivateNames) {
const names = Object.keys(unresolvedPrivateNames)
if (names.length) {
names.sort((n1, n2) => unresolvedPrivateNames[n1] - unresolvedPrivateNames[n2])
this.raise(unresolvedPrivateNames[names[0]], "Usage of undeclared private name")
}
} else Object.assign(this._unresolvedPrivateNames, unresolvedPrivateNames)
return _return
}
// Class heritage is evaluated with outer private environment
parseClassSuper(node) {
const privateBoundNames = this._privateBoundNames
this._privateBoundNames = this._outerPrivateBoundNames
const unresolvedPrivateNames = this._unresolvedPrivateNames
this._unresolvedPrivateNames = this._outerUnresolvedPrivateNames
const _return = super.parseClassSuper(node)
this._privateBoundNames = privateBoundNames
this._unresolvedPrivateNames = unresolvedPrivateNames
return _return
}
// Parse private element access
parseSubscript(base, startPos, startLoc, _noCalls, _maybeAsyncArrow, _optionalChained) {
const optionalSupported = this.options.ecmaVersion >= 11 && acorn.tokTypes.questionDot
const branch = this._branch()
if (!(
(branch.eat(acorn.tokTypes.dot) || (optionalSupported && branch.eat(acorn.tokTypes.questionDot))) &&
branch.type == this.privateIdentifierToken
)) {
return super.parseSubscript.apply(this, arguments)
}
let optional = false
if (!this.eat(acorn.tokTypes.dot)) {
this.expect(acorn.tokTypes.questionDot)
optional = true
}
let node = this.startNodeAt(startPos, startLoc)
node.object = base
node.computed = false
if (optionalSupported) {
node.optional = optional
}
if (this.type == this.privateIdentifierToken) {
if (base.type == "Super") {
this.raise(this.start, "Cannot access private element on super")
}
node.property = this.parsePrivateName()
if (!this._privateBoundNames || !this._privateBoundNames[node.property.name]) {
if (!this._unresolvedPrivateNames) {
this.raise(node.property.start, "Usage of undeclared private name")
}
this._unresolvedPrivateNames[node.property.name] = node.property.start
}
} else {
node.property = this.parseIdent(true)
}
return this.finishNode(node, "MemberExpression")
}
// Prohibit delete of private class elements
parseMaybeUnary(refDestructuringErrors, sawUnary) {
const _return = super.parseMaybeUnary(refDestructuringErrors, sawUnary)
if (_return.operator == "delete") {
if (_return.argument.type == "MemberExpression" && _return.argument.property.type == "PrivateIdentifier") {
this.raise(_return.start, "Private elements may not be deleted")
}
}
return _return
}
}
Parser.prototype.privateIdentifierToken = new acorn.TokenType("privateIdentifier")
return Parser
}