468 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			468 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Acorn
 | ||
| 
 | ||
| [](https://travis-ci.org/acornjs/acorn)
 | ||
| [](https://www.npmjs.com/package/acorn)
 | ||
| [](https://cdnjs.com/libraries/acorn)  
 | ||
| [Author funding status: ](https://marijnhaverbeke.nl/fund/)
 | ||
| 
 | ||
| A tiny, fast JavaScript parser, written completely in JavaScript.
 | ||
| 
 | ||
| ## Community
 | ||
| 
 | ||
| Acorn is open source software released under an
 | ||
| [MIT license](https://github.com/acornjs/acorn/blob/master/LICENSE).
 | ||
| 
 | ||
| You are welcome to
 | ||
| [report bugs](https://github.com/acornjs/acorn/issues) or create pull
 | ||
| requests on [github](https://github.com/acornjs/acorn). For questions
 | ||
| and discussion, please use the
 | ||
| [Tern discussion forum](https://discuss.ternjs.net).
 | ||
| 
 | ||
| ## Installation
 | ||
| 
 | ||
| The easiest way to install acorn is with [`npm`][npm].
 | ||
| 
 | ||
| [npm]: https://www.npmjs.com/
 | ||
| 
 | ||
| ```sh
 | ||
| npm install acorn
 | ||
| ```
 | ||
| 
 | ||
| Alternately, you can download the source and build acorn yourself:
 | ||
| 
 | ||
| ```sh
 | ||
| git clone https://github.com/acornjs/acorn.git
 | ||
| cd acorn
 | ||
| npm install
 | ||
| npm run build
 | ||
| ```
 | ||
| 
 | ||
| ## Components
 | ||
| 
 | ||
| When run in a CommonJS (node.js) or AMD environment, exported values
 | ||
| appear in the interfaces exposed by the individual files, as usual.
 | ||
| When loaded in the browser (Acorn works in any JS-enabled browser more
 | ||
| recent than IE5) without any kind of module management, a single
 | ||
| global object `acorn` will be defined, and all the exported properties
 | ||
| will be added to that.
 | ||
| 
 | ||
| ### Main parser
 | ||
| 
 | ||
| This is implemented in `dist/acorn.js`, and is what you get when you
 | ||
| `require("acorn")` in node.js.
 | ||
| 
 | ||
| **parse**`(input, options)` is used to parse a JavaScript program.
 | ||
| The `input` parameter is a string, `options` can be undefined or an
 | ||
| object setting some of the options listed below. The return value will
 | ||
| be an abstract syntax tree object as specified by the
 | ||
| [ESTree spec][estree].
 | ||
| 
 | ||
| When  encountering   a  syntax   error,  the   parser  will   raise  a
 | ||
| `SyntaxError` object with a meaningful  message. The error object will
 | ||
| have a `pos` property that indicates the character offset at which the
 | ||
| error occurred,  and a `loc`  object that contains a  `{line, column}`
 | ||
| object referring to that same position.
 | ||
| 
 | ||
| [estree]: https://github.com/estree/estree
 | ||
| 
 | ||
| - **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
 | ||
|   either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018) or 10 (2019, partial
 | ||
|   support). This influences support for strict mode, the set of
 | ||
|   reserved words, and support for new syntax features. Default is 7.
 | ||
| 
 | ||
|   **NOTE**: Only 'stage 4' (finalized) ECMAScript features are being
 | ||
|   implemented by Acorn.
 | ||
| 
 | ||
| - **sourceType**: Indicate the mode the code should be parsed in. Can be
 | ||
|   either `"script"` or `"module"`. This influences global strict mode
 | ||
|   and parsing of `import` and `export` declarations.
 | ||
| 
 | ||
| - **onInsertedSemicolon**: If given a callback, that callback will be
 | ||
|   called whenever a missing semicolon is inserted by the parser. The
 | ||
|   callback will be given the character offset of the point where the
 | ||
|   semicolon is inserted as argument, and if `locations` is on, also a
 | ||
|   `{line, column}` object representing this position.
 | ||
| 
 | ||
| - **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing
 | ||
|   commas.
 | ||
| 
 | ||
| - **allowReserved**: If `false`, using a reserved word will generate
 | ||
|   an error. Defaults to `true` for `ecmaVersion` 3, `false` for higher
 | ||
|   versions. When given the value `"never"`, reserved words and
 | ||
|   keywords can also not be used as property names (as in Internet
 | ||
|   Explorer's old parser).
 | ||
| 
 | ||
| - **allowReturnOutsideFunction**: By default, a return statement at
 | ||
|   the top level raises an error. Set this to `true` to accept such
 | ||
|   code.
 | ||
| 
 | ||
| - **allowImportExportEverywhere**: By default, `import` and `export`
 | ||
|   declarations can only appear at a program's top level. Setting this
 | ||
|   option to `true` allows them anywhere where a statement is allowed.
 | ||
|   
 | ||
| - **allowAwaitOutsideFunction**: By default, `await` expressions can only appear inside `async` functions. Setting this option to `true` allows to have top-level `await` expressions. They are still not allowed in non-`async` functions, though.
 | ||
| 
 | ||
| - **allowHashBang**: When this is enabled (off by default), if the
 | ||
|   code starts with the characters `#!` (as in a shellscript), the
 | ||
|   first line will be treated as a comment.
 | ||
| 
 | ||
| - **locations**: When `true`, each node has a `loc` object attached
 | ||
|   with `start` and `end` subobjects, each of which contains the
 | ||
|   one-based line and zero-based column numbers in `{line, column}`
 | ||
|   form. Default is `false`.
 | ||
| 
 | ||
| - **onToken**: If a function is passed for this option, each found
 | ||
|   token will be passed in same format as tokens returned from
 | ||
|   `tokenizer().getToken()`.
 | ||
| 
 | ||
|   If array is passed, each found token is pushed to it.
 | ||
| 
 | ||
|   Note that you are not allowed to call the parser from the
 | ||
|   callback—that will corrupt its internal state.
 | ||
| 
 | ||
| - **onComment**: If a function is passed for this option, whenever a
 | ||
|   comment is encountered the function will be called with the
 | ||
|   following parameters:
 | ||
| 
 | ||
|   - `block`: `true` if the comment is a block comment, false if it
 | ||
|     is a line comment.
 | ||
|   - `text`: The content of the comment.
 | ||
|   - `start`: Character offset of the start of the comment.
 | ||
|   - `end`: Character offset of the end of the comment.
 | ||
| 
 | ||
|   When the `locations` options is on, the `{line, column}` locations
 | ||
|   of the comment’s start and end are passed as two additional
 | ||
|   parameters.
 | ||
| 
 | ||
|   If array is passed for this option, each found comment is pushed
 | ||
|   to it as object in Esprima format:
 | ||
| 
 | ||
|   ```javascript
 | ||
|   {
 | ||
|     "type": "Line" | "Block",
 | ||
|     "value": "comment text",
 | ||
|     "start": Number,
 | ||
|     "end": Number,
 | ||
|     // If `locations` option is on:
 | ||
|     "loc": {
 | ||
|       "start": {line: Number, column: Number}
 | ||
|       "end": {line: Number, column: Number}
 | ||
|     },
 | ||
|     // If `ranges` option is on:
 | ||
|     "range": [Number, Number]
 | ||
|   }
 | ||
|   ```
 | ||
| 
 | ||
|   Note that you are not allowed to call the parser from the
 | ||
|   callback—that will corrupt its internal state.
 | ||
| 
 | ||
| - **ranges**: Nodes have their start and end characters offsets
 | ||
|   recorded in `start` and `end` properties (directly on the node,
 | ||
|   rather than the `loc` object, which holds line/column data. To also
 | ||
|   add a [semi-standardized][range] `range` property holding a
 | ||
|   `[start, end]` array with the same numbers, set the `ranges` option
 | ||
|   to `true`.
 | ||
| 
 | ||
| - **program**: It is possible to parse multiple files into a single
 | ||
|   AST by passing the tree produced by parsing the first file as the
 | ||
|   `program` option in subsequent parses. This will add the toplevel
 | ||
|   forms of the parsed file to the "Program" (top) node of an existing
 | ||
|   parse tree.
 | ||
| 
 | ||
| - **sourceFile**: When the `locations` option is `true`, you can pass
 | ||
|   this option to add a `source` attribute in every node’s `loc`
 | ||
|   object. Note that the contents of this option are not examined or
 | ||
|   processed in any way; you are free to use whatever format you
 | ||
|   choose.
 | ||
| 
 | ||
| - **directSourceFile**: Like `sourceFile`, but a `sourceFile` property
 | ||
|   will be added (regardless of the `location` option) directly to the
 | ||
|   nodes, rather than the `loc` object.
 | ||
| 
 | ||
| - **preserveParens**: If this option is `true`, parenthesized expressions
 | ||
|   are represented by (non-standard) `ParenthesizedExpression` nodes
 | ||
|   that have a single `expression` property containing the expression
 | ||
|   inside parentheses.
 | ||
| 
 | ||
| [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
 | ||
| 
 | ||
| **parseExpressionAt**`(input, offset, options)` will parse a single
 | ||
| expression in a string, and return its AST. It will not complain if
 | ||
| there is more of the string left after the expression.
 | ||
| 
 | ||
| **getLineInfo**`(input, offset)` can be used to get a `{line,
 | ||
| column}` object for a given program string and character offset.
 | ||
| 
 | ||
| **tokenizer**`(input, options)` returns an object with a `getToken`
 | ||
| method that can be called repeatedly to get the next token, a `{start,
 | ||
| end, type, value}` object (with added `loc` property when the
 | ||
| `locations` option is enabled and `range` property when the `ranges`
 | ||
| option is enabled). When the token's type is `tokTypes.eof`, you
 | ||
| should stop calling the method, since it will keep returning that same
 | ||
| token forever.
 | ||
| 
 | ||
| In ES6 environment, returned result can be used as any other
 | ||
| protocol-compliant iterable:
 | ||
| 
 | ||
| ```javascript
 | ||
| for (let token of acorn.tokenizer(str)) {
 | ||
|   // iterate over the tokens
 | ||
| }
 | ||
| 
 | ||
| // transform code to array of tokens:
 | ||
| var tokens = [...acorn.tokenizer(str)];
 | ||
| ```
 | ||
| 
 | ||
| **tokTypes** holds an object mapping names to the token type objects
 | ||
| that end up in the `type` properties of tokens.
 | ||
| 
 | ||
| #### Note on using with [Escodegen][escodegen]
 | ||
| 
 | ||
| Escodegen supports generating comments from AST, attached in
 | ||
| Esprima-specific format. In order to simulate same format in
 | ||
| Acorn, consider following example:
 | ||
| 
 | ||
| ```javascript
 | ||
| var comments = [], tokens = [];
 | ||
| 
 | ||
| var ast = acorn.parse('var x = 42; // answer', {
 | ||
| 	// collect ranges for each node
 | ||
| 	ranges: true,
 | ||
| 	// collect comments in Esprima's format
 | ||
| 	onComment: comments,
 | ||
| 	// collect token ranges
 | ||
| 	onToken: tokens
 | ||
| });
 | ||
| 
 | ||
| // attach comments using collected information
 | ||
| escodegen.attachComments(ast, comments, tokens);
 | ||
| 
 | ||
| // generate code
 | ||
| console.log(escodegen.generate(ast, {comment: true}));
 | ||
| // > 'var x = 42;    // answer'
 | ||
| ```
 | ||
| 
 | ||
| [escodegen]: https://github.com/estools/escodegen
 | ||
| 
 | ||
| ### dist/acorn_loose.js ###
 | ||
| 
 | ||
| This file implements an error-tolerant parser. It exposes a single
 | ||
| function. The loose parser is accessible in node.js via `require("acorn/dist/acorn_loose")`.
 | ||
| 
 | ||
| **parse_dammit**`(input, options)` takes the same arguments and
 | ||
| returns the same syntax tree as the `parse` function in `acorn.js`,
 | ||
| but never raises an error, and will do its best to parse syntactically
 | ||
| invalid code in as meaningful a way as it can. It'll insert identifier
 | ||
| nodes with name `"✖"` as placeholders in places where it can't make
 | ||
| sense of the input. Depends on `acorn.js`, because it uses the same
 | ||
| tokenizer.
 | ||
| 
 | ||
| ### dist/walk.js ###
 | ||
| 
 | ||
| Implements an abstract syntax tree walker. Will store its interface in
 | ||
| `acorn.walk` when loaded without a module system.
 | ||
| 
 | ||
| **simple**`(node, visitors, base, state)` does a 'simple' walk over
 | ||
| a tree. `node` should be the AST node to walk, and `visitors` an
 | ||
| object with properties whose names correspond to node types in the
 | ||
| [ESTree spec][estree]. The properties should contain functions
 | ||
| that will be called with the node object and, if applicable the state
 | ||
| at that point. The last two arguments are optional. `base` is a walker
 | ||
| algorithm, and `state` is a start state. The default walker will
 | ||
| simply visit all statements and expressions and not produce a
 | ||
| meaningful state. (An example of a use of state is to track scope at
 | ||
| each point in the tree.)
 | ||
| 
 | ||
| ```js
 | ||
| const acorn = require("acorn")
 | ||
| const walk = require("acorn/dist/walk")
 | ||
| 
 | ||
| walk.simple(acorn.parse("let x = 10"), {
 | ||
|   Literal(node) {
 | ||
|     console.log(`Found a literal: ${node.value}`)
 | ||
|   }
 | ||
| })
 | ||
| ```
 | ||
| 
 | ||
| **ancestor**`(node, visitors, base, state)` does a 'simple' walk over
 | ||
| a tree, building up an array of ancestor nodes (including the current node)
 | ||
| and passing the array to the callbacks as a third parameter.
 | ||
| 
 | ||
| ```js
 | ||
| const acorn = require("acorn")
 | ||
| const walk = require("acorn/dist/walk")
 | ||
| 
 | ||
| walk.ancestor(acorn.parse("foo('hi')"), {
 | ||
|   Literal(_, ancestors) {
 | ||
|     console.log("This literal's ancestors are:",
 | ||
|                 ancestors.map(n => n.type))
 | ||
|   }
 | ||
| })
 | ||
| ```
 | ||
| 
 | ||
| **recursive**`(node, state, functions, base)` does a 'recursive'
 | ||
| walk, where the walker functions are responsible for continuing the
 | ||
| walk on the child nodes of their target node. `state` is the start
 | ||
| state, and `functions` should contain an object that maps node types
 | ||
| to walker functions. Such functions are called with `(node, state, c)`
 | ||
| arguments, and can cause the walk to continue on a sub-node by calling
 | ||
| the `c` argument on it with `(node, state)` arguments. The optional
 | ||
| `base` argument provides the fallback walker functions for node types
 | ||
| that aren't handled in the `functions` object. If not given, the
 | ||
| default walkers will be used.
 | ||
| 
 | ||
| **make**`(functions, base)` builds a new walker object by using the
 | ||
| walker functions in `functions` and filling in the missing ones by
 | ||
| taking defaults from `base`.
 | ||
| 
 | ||
| **full**`(node, callback, base, state)` does a 'full'
 | ||
| walk over a tree, calling the callback with the arguments (node, state, type)
 | ||
| for each node
 | ||
| 
 | ||
| **fullAncestor**`(node, callback, base, state)` does a 'full' walk over
 | ||
| a tree, building up an array of ancestor nodes (including the current node)
 | ||
| and passing the array to the callbacks as a third parameter.
 | ||
| 
 | ||
| ```js
 | ||
| const acorn = require("acorn")
 | ||
| const walk = require("acorn/dist/walk")
 | ||
| 
 | ||
| walk.full(acorn.parse("1 + 1"), node => {
 | ||
|   console.log(`There's a ${node.type} node at ${node.ch}`)
 | ||
| })
 | ||
| ```
 | ||
| 
 | ||
| **findNodeAt**`(node, start, end, test, base, state)` tries to
 | ||
| locate a node in a tree at the given start and/or end offsets, which
 | ||
| satisfies the predicate `test`. `start` and `end` can be either `null`
 | ||
| (as wildcard) or a number. `test` may be a string (indicating a node
 | ||
| type) or a function that takes `(nodeType, node)` arguments and
 | ||
| returns a boolean indicating whether this node is interesting. `base`
 | ||
| and `state` are optional, and can be used to specify a custom walker.
 | ||
| Nodes are tested from inner to outer, so if two nodes match the
 | ||
| boundaries, the inner one will be preferred.
 | ||
| 
 | ||
| **findNodeAround**`(node, pos, test, base, state)` is a lot like
 | ||
| `findNodeAt`, but will match any node that exists 'around' (spanning)
 | ||
| the given position.
 | ||
| 
 | ||
| **findNodeAfter**`(node, pos, test, base, state)` is similar to
 | ||
| `findNodeAround`, but will match all nodes *after* the given position
 | ||
| (testing outer nodes before inner nodes).
 | ||
| 
 | ||
| ## Command line interface
 | ||
| 
 | ||
| The `bin/acorn` utility can be used to parse a file from the command
 | ||
| line. It accepts as arguments its input file and the following
 | ||
| options:
 | ||
| 
 | ||
| - `--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|--ecma10`: Sets the ECMAScript version
 | ||
|   to parse. Default is version 7.
 | ||
| 
 | ||
| - `--module`: Sets the parsing mode to `"module"`. Is set to `"script"` otherwise.
 | ||
| 
 | ||
| - `--locations`: Attaches a "loc" object to each node with "start" and
 | ||
|   "end" subobjects, each of which contains the one-based line and
 | ||
|   zero-based column numbers in `{line, column}` form.
 | ||
| 
 | ||
| - `--allow-hash-bang`: If the code starts with the characters #! (as in a shellscript), the first line will be treated as a comment.
 | ||
| 
 | ||
| - `--compact`: No whitespace is used in the AST output.
 | ||
| 
 | ||
| - `--silent`: Do not output the AST, just return the exit status.
 | ||
| 
 | ||
| - `--help`: Print the usage information and quit.
 | ||
| 
 | ||
| The utility spits out the syntax tree as JSON data.
 | ||
| 
 | ||
| ## Build system
 | ||
| 
 | ||
| Acorn is written in ECMAScript 6, as a set of small modules, in the
 | ||
| project's `src` directory, and compiled down to bigger ECMAScript 3
 | ||
| files in `dist` using [Browserify](http://browserify.org) and
 | ||
| [Babel](http://babeljs.io/). If you are already using Babel, you can
 | ||
| consider including the modules directly.
 | ||
| 
 | ||
| The command-line test runner (`npm test`) uses the ES6 modules. The
 | ||
| browser-based test page (`test/index.html`) uses the compiled modules.
 | ||
| The `bin/build-acorn.js` script builds the latter from the former.
 | ||
| 
 | ||
| If you are working on Acorn, you'll probably want to try the code out
 | ||
| directly, without an intermediate build step. In your scripts, you can
 | ||
| register the Babel require shim like this:
 | ||
| 
 | ||
|     require("babel-core/register")
 | ||
| 
 | ||
| That will allow you to directly `require` the ES6 modules.
 | ||
| 
 | ||
| ## Plugins
 | ||
| 
 | ||
| Acorn is designed support allow plugins which, within reasonable
 | ||
| bounds, redefine the way the parser works. Plugins can add new token
 | ||
| types and new tokenizer contexts (if necessary), and extend methods in
 | ||
| the parser object. This is not a clean, elegant API—using it requires
 | ||
| an understanding of Acorn's internals, and plugins are likely to break
 | ||
| whenever those internals are significantly changed. But still, it is
 | ||
| _possible_, in this way, to create parsers for JavaScript dialects
 | ||
| without forking all of Acorn. And in principle it is even possible to
 | ||
| combine such plugins, so that if you have, for example, a plugin for
 | ||
| parsing types and a plugin for parsing JSX-style XML literals, you
 | ||
| could load them both and parse code with both JSX tags and types.
 | ||
| 
 | ||
| A plugin should register itself by adding a property to
 | ||
| `acorn.plugins`, which holds a function. Calling `acorn.parse`, a
 | ||
| `plugins` option can be passed, holding an object mapping plugin names
 | ||
| to configuration values (or just `true` for plugins that don't take
 | ||
| options). After the parser object has been created, the initialization
 | ||
| functions for the chosen plugins are called with `(parser,
 | ||
| configValue)` arguments. They are expected to use the `parser.extend`
 | ||
| method to extend parser methods. For example, the `readToken` method
 | ||
| could be extended like this:
 | ||
| 
 | ||
| ```javascript
 | ||
| parser.extend("readToken", function(nextMethod) {
 | ||
|   return function(code) {
 | ||
|     console.log("Reading a token!")
 | ||
|     return nextMethod.call(this, code)
 | ||
|   }
 | ||
| })
 | ||
| ```
 | ||
| 
 | ||
| The `nextMethod` argument passed to `extend`'s second argument is the
 | ||
| previous value of this method, and should usually be called through to
 | ||
| whenever the extended method does not handle the call itself.
 | ||
| 
 | ||
| Similarly, the loose parser allows plugins to register themselves via
 | ||
| `acorn.pluginsLoose`.  The extension mechanism is the same as for the
 | ||
| normal parser:
 | ||
| 
 | ||
| ```javascript
 | ||
| looseParser.extend("readToken", function(nextMethod) {
 | ||
|   return function() {
 | ||
|     console.log("Reading a token in the loose parser!")
 | ||
|     return nextMethod.call(this)
 | ||
|   }
 | ||
| })
 | ||
| ```
 | ||
| 
 | ||
| ### Existing plugins
 | ||
| 
 | ||
|  - [`acorn-jsx`](https://github.com/RReverser/acorn-jsx): Parse [Facebook JSX syntax extensions](https://github.com/facebook/jsx)
 | ||
|  - [`acorn-objj`](https://github.com/cappuccino/acorn-objj): [Objective-J](http://www.cappuccino-project.org/learn/objective-j.html) language parser built as Acorn plugin
 | ||
|  
 | ||
|  Plugins for ECMAScript proposals:
 | ||
|  
 | ||
|  - [`acorn-stage3`](https://github.com/acornjs/acorn-stage3): Parse most stage 3 proposals, bundling:
 | ||
|    - [`acorn-async-iteration`](https://github.com/acornjs/acorn-async-iteration): Parse [async iteration proposal](https://github.com/tc39/proposal-async-iteration)
 | ||
|    - [`acorn-bigint`](https://github.com/acornjs/acorn-bigint): Parse [BigInt proposal](https://github.com/tc39/proposal-bigint)
 | ||
|    - [`acorn-class-fields`](https://github.com/acornjs/acorn-class-fields): Parse [class fields proposal](https://github.com/tc39/proposal-class-fields)
 | ||
|    - [`acorn-dynamic-import`](https://github.com/kesne/acorn-dynamic-import): Parse [import() proposal](https://github.com/tc39/proposal-dynamic-import)
 | ||
|    - [`acorn-import-meta`](https://github.com/acornjs/acorn-import-meta): Parse [import.meta proposal](https://github.com/tc39/proposal-import-meta)
 | ||
|    - [`acorn-numeric-separator`](https://github.com/acornjs/acorn-numeric-separator): Parse [numeric separator proposal](https://github.com/tc39/proposal-numeric-separator)
 | ||
|    - [`acorn-optional-catch-binding`](https://github.com/acornjs/acorn-optional-catch-binding): Parse [optional catch binding proposal](https://github.com/tc39/proposal-optional-catch-binding)
 | ||
|    - [`acorn-private-methods`](https://github.com/acornjs/acorn-private-methods): parse [private methods, getters and setters proposal](https://github.com/tc39/proposal-private-methods)
 | ||
|    - [`acorn5-object-spread`](https://github.com/adrianheine/acorn5-object-spread): Parse [Object Rest/Spread Properties proposal](https://github.com/tc39/proposal-object-rest-spread)
 | ||
|  - [`acorn-object-rest-spread`](https://github.com/victor-homyakov/acorn-object-rest-spread): Parse [Object Rest/Spread Properties proposal](https://github.com/tc39/proposal-object-rest-spread)
 | ||
|  - [`acorn-es7`](https://github.com/angelozerr/acorn-es7): Parse [decorator syntax proposal](https://github.com/wycats/javascript-decorators)
 | ||
|  - [`acorn-static-class-property-initializer`](https://github.com/victor-homyakov/acorn-static-class-property-initializer): Partial support for static class properties from [ES Class Fields & Static Properties Proposal](https://github.com/tc39/proposal-class-public-fields) to support static property initializers in [React components written as ES6+ classes](https://babeljs.io/blog/2015/07/07/react-on-es6-plus)
 |