acorn-class-fields
Advanced tools
Comparing version 0.1.2 to 0.2.0
@@ -78,3 +78,4 @@ { | ||
"error", | ||
"declaration" | ||
"declaration", | ||
{ "allowArrowFunctions": true } | ||
], | ||
@@ -81,0 +82,0 @@ "function-paren-newline": "off", |
@@ -0,1 +1,6 @@ | ||
## 0.2.0 (2018-09-14) | ||
* Update to new acorn 6 interface | ||
* Change license to MIT | ||
## 0.1.2 (2018-01-26) | ||
@@ -2,0 +7,0 @@ |
158
index.js
"use strict" | ||
module.exports = require("./inject")(require("acorn")) | ||
const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g | ||
const acorn = require("acorn") | ||
const tt = acorn.tokTypes | ||
const TokenType = acorn.TokenType | ||
function maybeParseFieldValue(field) { | ||
if (this.eat(tt.eq)) { | ||
const oldInFieldValue = this._inFieldValue | ||
this._inFieldValue = true | ||
field.value = this.parseExpression() | ||
this._inFieldValue = oldInFieldValue | ||
} else field.value = null | ||
} | ||
function parsePrivateName() { | ||
const node = this.startNode() | ||
node.name = this.value | ||
this.next() | ||
this.finishNode(node, "PrivateName") | ||
if (this.options.allowReserved == "never") this.checkUnreserved(node) | ||
return node | ||
} | ||
const privateNameToken = new TokenType("privateName") | ||
module.exports = function(Parser) { | ||
return class extends Parser { | ||
// Parse # token | ||
getTokenFromCode(code) { | ||
if (code === 35) { | ||
++this.pos | ||
const word = this.readWord1() | ||
return this.finishToken(privateNameToken, word) | ||
} | ||
return super.getTokenFromCode(code) | ||
} | ||
// Manage stacks and check for undeclared private names | ||
parseClass(node, isStatement) { | ||
this._privateBoundNamesStack = this._privateBoundNamesStack || [] | ||
const privateBoundNames = Object.create(this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1] || null) | ||
this._privateBoundNamesStack.push(privateBoundNames) | ||
this._unresolvedPrivateNamesStack = this._unresolvedPrivateNamesStack || [] | ||
const unresolvedPrivateNames = Object.create(null) | ||
this._unresolvedPrivateNamesStack.push(unresolvedPrivateNames) | ||
const _return = super.parseClass(node, isStatement) | ||
this._privateBoundNamesStack.pop() | ||
this._unresolvedPrivateNamesStack.pop() | ||
if (!this._unresolvedPrivateNamesStack.length) { | ||
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._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1], unresolvedPrivateNames) | ||
return _return | ||
} | ||
// Parse private fields | ||
parseClassElement() { | ||
if (this.eat(tt.semi)) return null | ||
const node = this.startNode() | ||
if (!(this.options.ecmaVersion >= 8) || this.type != privateNameToken) { | ||
// Special-case for `async`, since `parseClassMember` currently looks | ||
// for `(` to determine whether `async` is a method name | ||
if (this.isContextual("async")) { | ||
skipWhiteSpace.lastIndex = this.pos | ||
let skip = skipWhiteSpace.exec(this.input) | ||
let next = this.input.charAt(this.pos + skip[0].length) | ||
if (next === ";" || next === "=") { | ||
node.key = this.parseIdent(true) | ||
node.computed = false | ||
maybeParseFieldValue.call(this, node) | ||
this.finishNode(node, "FieldDefinition") | ||
this.semicolon() | ||
return node | ||
} | ||
} | ||
return super.parseClassElement() | ||
} | ||
node.key = parsePrivateName.call(this) | ||
node.computed = false | ||
if (node.key.name == "constructor") this.raise(node.start, "Classes may not have a field named constructor") | ||
if (Object.prototype.hasOwnProperty.call(this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1], node.key.name)) this.raise(node.start, "Duplicate private element") | ||
this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1][node.key.name] = true | ||
delete this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][node.key.name] | ||
maybeParseFieldValue.call(this, node) | ||
this.finishNode(node, "FieldDefinition") | ||
this.semicolon() | ||
return node | ||
} | ||
// Parse public fields | ||
parseClassMethod(method, isGenerator, isAsync) { | ||
if (isGenerator || isAsync || method.kind != "method" || method.static || this.options.ecmaVersion < 8 || this.type == tt.parenL) { | ||
return super.parseClassMethod(method, isGenerator, isAsync) | ||
} | ||
maybeParseFieldValue.call(this, method) | ||
delete method.kind | ||
delete method.static | ||
method = this.finishNode(method, "FieldDefinition") | ||
this.semicolon() | ||
return method | ||
} | ||
// Parse private element access | ||
parseSubscripts(base, startPos, startLoc, noCalls) { | ||
for (let computed; ;) { | ||
if ((computed = this.eat(tt.bracketL)) || this.eat(tt.dot)) { | ||
let node = this.startNodeAt(startPos, startLoc) | ||
node.object = base | ||
if (computed) { | ||
node.property = this.parseExpression() | ||
} else if (this.type == privateNameToken) { | ||
node.property = parsePrivateName.call(this) | ||
if (!this._privateBoundNamesStack.length || !this._privateBoundNamesStack[this._privateBoundNamesStack.length - 1][node.property.name]) { | ||
this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length - 1][node.property.name] = node.property.start | ||
} | ||
} else { | ||
node.property = this.parseIdent(true) | ||
} | ||
node.computed = Boolean(computed) | ||
if (computed) this.expect(tt.bracketR) | ||
base = this.finishNode(node, "MemberExpression") | ||
} else { | ||
return super.parseSubscripts(base, startPos, startLoc, noCalls) | ||
} | ||
} | ||
} | ||
// 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 == "PrivateName") { | ||
this.raise(_return.start, "Private elements may not be deleted") | ||
} | ||
} | ||
return _return | ||
} | ||
// Prohibit arguments in class field initializers | ||
parseIdent(liberal, isBinding) { | ||
const ident = super.parseIdent(liberal, isBinding) | ||
if (this._inFieldValue && ident.name == "arguments") this.raise(ident.start, "A class field initializer may not contain arguments") | ||
return ident | ||
} | ||
// Prohibit super in class field initializers | ||
parseExprAtom(refDestructuringErrors) { | ||
const atom = super.parseExprAtom(refDestructuringErrors) | ||
if (this._inFieldValue && atom.type == "Super") this.raise(atom.start, "A class field initializer may not contain super") | ||
return atom | ||
} | ||
} | ||
} |
@@ -15,3 +15,3 @@ { | ||
}, | ||
"license": "AGPL-3.0", | ||
"license": "MIT", | ||
"scripts": { | ||
@@ -22,13 +22,14 @@ "test": "mocha", | ||
}, | ||
"dependencies": { | ||
"acorn": "^5.3.0" | ||
"peerDependencies": { | ||
"acorn": "^6.0.0" | ||
}, | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"devDependencies": { | ||
"eslint": "^4.13.1", | ||
"eslint-plugin-node": "^5.2.1", | ||
"mocha": "^4.0.1", | ||
"test262": "git+https://github.com/tc39/test262.git#66df349af32c3f293936f7a947ac18bfb1ecda5c", | ||
"test262-parser-runner": "^0.2.0" | ||
"acorn": "^6.0.0", | ||
"eslint": "^5.5.0", | ||
"eslint-plugin-node": "^7.0.1", | ||
"mocha": "^5.2.0", | ||
"test262": "git+https://github.com/tc39/test262.git#e286bfa00086226f781a3ed4e0a6295634b8ed11", | ||
"test262-parser-runner": "^0.4.0" | ||
} | ||
} |
@@ -11,24 +11,12 @@ # Class fields support for Acorn | ||
You can use this module directly in order to get an Acorn instance with the plugin installed: | ||
This module provides a plugin that can be used to extend the Acorn `Parser` class: | ||
```javascript | ||
var acorn = require('acorn-class-fields'); | ||
const {Parser} = require('acorn'); | ||
const classFields = require('acorn-class-fields'); | ||
Parser.extend(classFields).parse('class X { x = 0 }'); | ||
``` | ||
Or you can use `inject.js` for injecting the plugin into your own version of Acorn like this: | ||
```javascript | ||
var acorn = require('acorn-class-fields/inject')(require('./custom-acorn')); | ||
``` | ||
Then, use the `plugins` option to enable the plugiin: | ||
```javascript | ||
var ast = acorn.parse(code, { | ||
plugins: { classFields: true } | ||
}); | ||
``` | ||
## License | ||
This plugin is released under the [GNU Affero General Public License](./LICENSE). | ||
This plugin is released under an [MIT License](./LICENSE). |
@@ -6,13 +6,10 @@ "use strict" | ||
const run = require("test262-parser-runner") | ||
const parse = require(".").parse | ||
const acorn = require("acorn") | ||
const Parser = acorn.Parser.extend(require(".")) | ||
const unsupportedFeatures = [ | ||
"async-iteration", | ||
"BigInt", | ||
"object-rest", | ||
"object-spread", | ||
"optional-catch-binding", | ||
"regexp-lookbehind", | ||
"regexp-named-groups", | ||
"regexp-unicode-property-escapes" | ||
"class-static-fields-public", | ||
"class-static-fields-private", | ||
"class-static-methods-private" | ||
] | ||
@@ -23,3 +20,3 @@ | ||
run( | ||
(content, options) => parse(content, {sourceType: options.sourceType, ecmaVersion: 9, plugins: { classFields: true }}), | ||
(content, options) => Parser.parse(content, {sourceType: options.sourceType, ecmaVersion: 9}), | ||
{ | ||
@@ -26,0 +23,0 @@ testsDirectory: path.dirname(require.resolve("test262/package.json")), |
223
test/test.js
"use strict" | ||
const assert = require("assert") | ||
const acorn = require("..") | ||
const acorn = require("acorn"), classFields = require("..") | ||
const Parser = acorn.Parser.extend(classFields) | ||
function test(text, expectedResult, additionalOptions) { | ||
it(text, function () { | ||
const result = acorn.parse(text, Object.assign({ ecmaVersion: 9, plugins: { classFields: true } }, additionalOptions)) | ||
const result = Parser.parse(text, Object.assign({ ecmaVersion: 9 }, additionalOptions)) | ||
if (expectedResult) { | ||
assert.deepEqual(result.body[0], expectedResult) | ||
assert.deepStrictEqual(result.body[0], expectedResult) | ||
} | ||
@@ -18,5 +19,5 @@ }) | ||
try { | ||
acorn.parse(text, Object.assign({ ecmaVersion: 9, plugins: { classFields: true } }, additionalOptions)) | ||
Parser.parse(text, Object.assign({ ecmaVersion: 9 }, additionalOptions)) | ||
} catch (e) { | ||
assert.equal(e.message, expectedResult) | ||
assert.strictEqual(e.message, expectedResult) | ||
failed = true | ||
@@ -27,2 +28,3 @@ } | ||
} | ||
const newNode = (start, props) => Object.assign(new acorn.Node({options: {}}, start), props) | ||
@@ -69,95 +71,80 @@ describe("acorn-class-fields", function () { | ||
const body = getBody(10) | ||
return { | ||
return newNode(0, { | ||
type: "ClassDeclaration", | ||
start: 0, | ||
end: body.end + 2, | ||
id: { | ||
id: newNode(6, { | ||
type: "Identifier", | ||
start: 6, | ||
end: 7, | ||
name: "A" | ||
}, | ||
}), | ||
superClass: null, | ||
body: { | ||
body: newNode(8, { | ||
type: "ClassBody", | ||
start: 8, | ||
end: body.end + 2, | ||
body: [body] | ||
} | ||
} | ||
}) | ||
}) | ||
} }, | ||
{ text: "class A { %s; }", ast: getBody => { | ||
const body = getBody(10) | ||
return { | ||
return newNode(0, { | ||
type: "ClassDeclaration", | ||
start: 0, | ||
end: body.end + 3, | ||
id: { | ||
id: newNode(6, { | ||
type: "Identifier", | ||
start: 6, | ||
end: 7, | ||
name: "A" | ||
}, | ||
}), | ||
superClass: null, | ||
body: { | ||
body: newNode(8, { | ||
type: "ClassBody", | ||
start: 8, | ||
end: body.end + 3, | ||
body: [body] | ||
} | ||
} | ||
}) | ||
}) | ||
} }, | ||
{ text: "class A { %s; #y }", ast: getBody => { | ||
const body = getBody(10) | ||
return { | ||
return newNode(0, { | ||
type: "ClassDeclaration", | ||
start: 0, | ||
end: body.end + 6, | ||
id: { | ||
id: newNode(6, { | ||
type: "Identifier", | ||
start: 6, | ||
end: 7, | ||
name: "A" | ||
}, | ||
}), | ||
superClass: null, | ||
body: { | ||
body: newNode(8, { | ||
type: "ClassBody", | ||
start: 8, | ||
end: body.end + 6, | ||
body: [body, { | ||
body: [body, newNode(body.end + 2, { | ||
type: "FieldDefinition", | ||
start: body.end + 2, | ||
end: body.end + 4, | ||
key: { | ||
key: newNode(body.end + 2, { | ||
type: "PrivateName", | ||
start: body.end + 2, | ||
end: body.end + 4, | ||
name: "y" | ||
}, | ||
}), | ||
value: null, | ||
computed: false | ||
} ] | ||
} | ||
} | ||
}) ] | ||
}) | ||
}) | ||
} }, | ||
{ text: "class A { %s;a() {} }", ast: getBody => { | ||
const body = getBody(10) | ||
return { | ||
return newNode(0, { | ||
type: "ClassDeclaration", | ||
start: 0, | ||
end: body.end + 9, | ||
id: { | ||
id: newNode(6, { | ||
type: "Identifier", | ||
start: 6, | ||
end: 7, | ||
name: "A" | ||
}, | ||
}), | ||
superClass: null, | ||
body: { | ||
body: newNode(8, { | ||
type: "ClassBody", | ||
start: 8, | ||
end: body.end + 9, | ||
body: [ body, { | ||
body: [ body, newNode(body.end + 1, { | ||
type: "MethodDefinition", | ||
start: body.end + 1, | ||
end: body.end + 7, | ||
@@ -167,11 +154,9 @@ kind: "method", | ||
computed: false, | ||
key: { | ||
key: newNode(body.end + 1, { | ||
type: "Identifier", | ||
start: body.end + 1, | ||
end: body.end + 2, | ||
name: "a" | ||
}, | ||
value: { | ||
}), | ||
value: newNode(body.end + 2, { | ||
type: "FunctionExpression", | ||
start: body.end + 2, | ||
end: body.end + 7, | ||
@@ -183,35 +168,30 @@ id: null, | ||
params: [], | ||
body: { | ||
body: newNode(body.end + 5, { | ||
type: "BlockStatement", | ||
start: body.end + 5, | ||
end: body.end + 7, | ||
body: [] | ||
} | ||
} | ||
} ] | ||
} | ||
} | ||
}) | ||
}) | ||
}) ] | ||
}) | ||
}) | ||
} }, | ||
{ text: "class A { %s\na() {} }", ast: getBody => { | ||
const body = getBody(10) | ||
return { | ||
return newNode(0, { | ||
type: "ClassDeclaration", | ||
start: 0, | ||
end: body.end + 9, | ||
id: { | ||
id: newNode(6, { | ||
type: "Identifier", | ||
start: 6, | ||
end: 7, | ||
name: "A" | ||
}, | ||
}), | ||
superClass: null, | ||
body: { | ||
body: newNode(8, { | ||
type: "ClassBody", | ||
start: 8, | ||
end: body.end + 9, | ||
body: [ | ||
body, | ||
{ | ||
newNode(body.end + 1, { | ||
type: "MethodDefinition", | ||
start: body.end + 1, | ||
end: body.end + 7, | ||
@@ -221,11 +201,9 @@ kind: "method", | ||
computed: false, | ||
key: { | ||
key: newNode(body.end + 1, { | ||
type: "Identifier", | ||
start: body.end + 1, | ||
end: body.end + 2, | ||
name: "a" | ||
}, | ||
value: { | ||
}), | ||
value: newNode(body.end + 2, { | ||
type: "FunctionExpression", | ||
start: body.end + 2, | ||
end: body.end + 7, | ||
@@ -237,13 +215,12 @@ id: null, | ||
params: [], | ||
body: { | ||
body: newNode(body.end + 5, { | ||
type: "BlockStatement", | ||
start: body.end + 5, | ||
end: body.end + 7, | ||
body: [] | ||
} | ||
} | ||
} | ||
}) | ||
}) | ||
}) | ||
] | ||
} | ||
} | ||
}) | ||
}) | ||
} }, | ||
@@ -253,109 +230,92 @@ ]; | ||
[ | ||
{ body: "x", passes: true, ast: start => ({ | ||
{ body: "x", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 1, | ||
key: { | ||
key: newNode(start, { | ||
type: "Identifier", | ||
start: start, | ||
end: start + 1, | ||
name: "x" | ||
}, | ||
}), | ||
value: null, | ||
computed: false | ||
}) }, | ||
{ body: "x = 0", passes: true, ast: start => ({ | ||
{ body: "x = 0", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 5, | ||
key: { | ||
key: newNode(start, { | ||
type: "Identifier", | ||
start: start, | ||
end: start + 1, | ||
name: "x" | ||
}, | ||
value: { | ||
}), | ||
value: newNode(start + 4, { | ||
type: "Literal", | ||
start: start + 4, | ||
end: start + 5, | ||
value: 0, | ||
raw: "0" | ||
}, | ||
}), | ||
computed: false | ||
}) }, | ||
{ body: "[x]", passes: true, ast: start => ({ | ||
{ body: "[x]", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 3, | ||
computed: true, | ||
key: { | ||
key: newNode(start + 1, { | ||
type: "Identifier", | ||
start: start + 1, | ||
end: start + 2, | ||
name: "x" | ||
}, | ||
}), | ||
value: null | ||
}) }, | ||
{ body: "[x] = 0", passes: true, ast: start => ({ | ||
{ body: "[x] = 0", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 7, | ||
computed: true, | ||
key: { | ||
key: newNode(start + 1, { | ||
type: "Identifier", | ||
start: start + 1, | ||
end: start + 2, | ||
name: "x" | ||
}, | ||
value: { | ||
}), | ||
value: newNode(start + 6, { | ||
type: "Literal", | ||
start: start + 6, | ||
end: start + 7, | ||
value: 0, | ||
raw: "0" | ||
} | ||
}) | ||
}) }, | ||
{ body: "#x", passes: true, ast: start => ({ | ||
{ body: "#x", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 2, | ||
computed: false, | ||
key: { | ||
key: newNode(start, { | ||
type: "PrivateName", | ||
start: start, | ||
end: start + 2, | ||
name: "x" | ||
}, | ||
}), | ||
value: null, | ||
}) }, | ||
{ body: "#x = 0", passes: true, ast: start => ({ | ||
{ body: "#x = 0", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 6, | ||
computed: false, | ||
key: { | ||
key: newNode(start, { | ||
type: "PrivateName", | ||
start: start, | ||
end: start + 2, | ||
name: "x" | ||
}, | ||
value: { | ||
}), | ||
value: newNode(start + 5, { | ||
type: "Literal", | ||
start: start + 5, | ||
end: start + 6, | ||
value: 0, | ||
raw: "0" | ||
} | ||
}) | ||
}) }, | ||
{ body: "async", passes: true, ast: start => ({ | ||
{ body: "async", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 5, | ||
key: { | ||
key: newNode(start, { | ||
type: "Identifier", | ||
start: start, | ||
end: start + 5, | ||
name: "async" | ||
}, | ||
}), | ||
value: null, | ||
@@ -365,19 +325,16 @@ computed: false | ||
{ body: "async = 5", passes: true, ast: start => ({ | ||
{ body: "async = 5", passes: true, ast: start => newNode(start, { | ||
type: "FieldDefinition", | ||
start: start, | ||
end: start + 9, | ||
key: { | ||
key: newNode(start, { | ||
type: "Identifier", | ||
start: start, | ||
end: start + 5, | ||
name: "async" | ||
}, | ||
value: { | ||
}), | ||
value: newNode(start + 8, { | ||
type: "Literal", | ||
start: 18, | ||
end: 19, | ||
end: start + 9, | ||
raw: "5", | ||
value: 5 | ||
}, | ||
}), | ||
computed: false | ||
@@ -384,0 +341,0 @@ }) }, |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
0
100
28995
6
10
794
22
+ Addedacorn@6.4.2(transitive)
- Removedacorn@^5.3.0
- Removedacorn@5.7.4(transitive)