From 8467b91fedba4e75c840ae5e515c5f1f5b689d01 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 29 Apr 2020 20:14:33 +0800 Subject: [PATCH 1/8] - Travis: Remove old versions; add 12, 14, 16, 17 - npm: Add recommended `bugs`, `keywords`, `dependencies` and use `author`/`contributors` - npm: Update devDeps, and switch to Babel 7/preset-env - npm: Use simple run script syntax - npm: Drop `package-lock.json` in favor of npmrc for consistency with other estools projects - Maintenance: Add `.editorconfig` - Maintenance: Drop use of gulpfile - Update: Mocha API - Testing: Add nyc - Testing: Use `chai/register-expect` - Testing: Drop bundled `esprima` in favor of using versioned - Testing: Update espree API to supply `sourceType`/`ecmaVersion` - Testing: Enable `espree` for dynamic import and adjust test expectation to take into account full Program context - Docs: Use more modern syntax in examples --- .babelrc => .babelrc.json | 2 +- .editorconfig | 18 + .npmignore | 16 - .npmrc | 1 + .travis.yml | 7 +- README.md | 26 +- gulpfile.js | 70 - package.json | 59 +- test/checkDump.js | 2 - test/es6.js | 50 +- test/replace.js | 1 - test/third_party/esprima.js | 5353 ----------------------------------- test/traverse.js | 1 - test/type.js | 1 - 14 files changed, 97 insertions(+), 5510 deletions(-) rename .babelrc => .babelrc.json (60%) create mode 100644 .editorconfig delete mode 100644 .npmignore create mode 100644 .npmrc delete mode 100644 gulpfile.js delete mode 100644 test/third_party/esprima.js diff --git a/.babelrc b/.babelrc.json similarity index 60% rename from .babelrc rename to .babelrc.json index 2c37d88..6f36093 100644 --- a/.babelrc +++ b/.babelrc.json @@ -1,5 +1,5 @@ { - "presets": [["env", { + "presets": [["@babel/preset-env", { "targets": { "node": "4.0" } diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d83d722 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.json] +indent_size = 2 + +[*.yml] +indent_size = 2 diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 8dc4f90..0000000 --- a/.npmignore +++ /dev/null @@ -1,16 +0,0 @@ -npm-debug.log -.babelrc -.DS_Store -.vimrc.local -t.js -.travis.yml -.npmignore -/tmp/ -/.git/ -/node_modules/ -/tools/ -/test/ -/build/ -/cover_html/ -/coverage/ -/.coverage_data/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..c1ca392 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock = false diff --git a/.travis.yml b/.travis.yml index 1d9b145..7f092b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ arch: - ppc64le language: node_js node_js: - - 4 - - 6 - - 8 + - 12 + - 14 + - 16 + - 17 diff --git a/README.md b/README.md index ccd3377..a0daf7c 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ The following code will output all variables declared at the root of a file. ```javascript estraverse.traverse(ast, { - enter: function (node, parent) { + enter (node, parent) { if (node.type == 'FunctionExpression' || node.type == 'FunctionDeclaration') return estraverse.VisitorOption.Skip; }, - leave: function (node, parent) { + leave (node, parent) { if (node.type == 'VariableDeclarator') console.log(node.id.name); } @@ -29,7 +29,7 @@ We can use `this.skip`, `this.remove` and `this.break` functions instead of usin ```javascript estraverse.traverse(ast, { - enter: function (node) { + enter (node) { this.break(); } }); @@ -38,8 +38,8 @@ estraverse.traverse(ast, { And estraverse provides `estraverse.replace` function. When returning node from `enter`/`leave`, current node is replaced with it. ```javascript -result = estraverse.replace(tree, { - enter: function (node) { +const result = estraverse.replace(tree, { + enter (node) { // Replace it with replaced. if (node.type === 'Literal') return replaced; @@ -51,7 +51,7 @@ By passing `visitor.keys` mapping, we can extend estraverse traversing functiona ```javascript // This tree contains a user-defined `TestExpression` node. -var tree = { +const tree = { type: 'TestExpression', // This 'argument' is the property containing the other **node**. @@ -64,7 +64,7 @@ var tree = { extended: true }; estraverse.traverse(tree, { - enter: function (node) { }, + enter (node) { }, // Extending the existing traversing rules. keys: { @@ -78,7 +78,7 @@ By passing `visitor.fallback` option, we can control the behavior when encounter ```javascript // This tree contains a user-defined `TestExpression` node. -var tree = { +const tree = { type: 'TestExpression', // This 'argument' is the property containing the other **node**. @@ -91,7 +91,7 @@ var tree = { extended: true }; estraverse.traverse(tree, { - enter: function (node) { }, + enter (node) { }, // Iterating the child **nodes** of unknown nodes. fallback: 'iteration' @@ -102,7 +102,7 @@ When `visitor.fallback` is a function, we can determine which keys to visit on e ```javascript // This tree contains a user-defined `TestExpression` node. -var tree = { +const tree = { type: 'TestExpression', // This 'argument' is the property containing the other **node**. @@ -115,11 +115,11 @@ var tree = { extended: true }; estraverse.traverse(tree, { - enter: function (node) { }, + enter (node) { }, // Skip the `argument` property of each node - fallback: function(node) { - return Object.keys(node).filter(function(key) { + fallback(node) { + return Object.keys(node).filter((key) => { return key !== 'argument'; }); } diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 8772bbc..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (C) 2014 Yusuke Suzuki - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -'use strict'; - -var gulp = require('gulp'), - git = require('gulp-git'), - bump = require('gulp-bump'), - filter = require('gulp-filter'), - tagVersion = require('gulp-tag-version'); - -var TEST = [ 'test/*.js' ]; -var POWERED = [ 'powered-test/*.js' ]; -var SOURCE = [ 'src/**/*.js' ]; - -/** - * Bumping version number and tagging the repository with it. - * Please read http://semver.org/ - * - * You can use the commands - * - * gulp patch # makes v0.1.0 -> v0.1.1 - * gulp feature # makes v0.1.1 -> v0.2.0 - * gulp release # makes v0.2.1 -> v1.0.0 - * - * To bump the version numbers accordingly after you did a patch, - * introduced a feature or made a backwards-incompatible release. - */ - -function inc(importance) { - // get all the files to bump version in - return gulp.src(['./package.json']) - // bump the version number in those files - .pipe(bump({type: importance})) - // save it back to filesystem - .pipe(gulp.dest('./')) - // commit the changed version number - .pipe(git.commit('Bumps package version')) - // read only one file to get the version number - .pipe(filter('package.json')) - // **tag it in the repository** - .pipe(tagVersion({ - prefix: '' - })); -} - -gulp.task('patch', [ ], function () { return inc('patch'); }) -gulp.task('minor', [ ], function () { return inc('minor'); }) -gulp.task('major', [ ], function () { return inc('major'); }) diff --git a/package.json b/package.json index a863218..dccd687 100644 --- a/package.json +++ b/package.json @@ -2,39 +2,60 @@ "name": "estraverse", "description": "ECMAScript JS AST traversal functions", "homepage": "https://github.com/estools/estraverse", + "bugs": "https://github.com/estools/estraverse/issues", "main": "estraverse.js", "version": "5.3.0", "engines": { "node": ">=4.0" }, - "maintainers": [ - { - "name": "Yusuke Suzuki", - "email": "utatane.tea@gmail.com", - "web": "http://github.com/Constellation" - } + "keywords": [ + "traversal" + ], + "author": { + "name": "Yusuke Suzuki", + "email": "utatane.tea@gmail.com", + "web": "http://github.com/Constellation" + }, + "contributors": [ + "Brett Zamir" + ], + "files": [ + "estraverse.js", + "dist" ], "repository": { "type": "git", "url": "http://github.com/estools/estraverse.git" }, + "dependencies": {}, "devDependencies": { - "babel-preset-env": "^1.6.1", - "babel-register": "^6.3.13", - "chai": "^2.1.1", - "espree": "^1.11.0", - "gulp": "^3.8.10", - "gulp-bump": "^0.2.2", - "gulp-filter": "^2.0.0", - "gulp-git": "^1.0.1", - "gulp-tag-version": "^1.3.0", - "jshint": "^2.5.6", - "mocha": "^2.1.0" + "@babel/core": "^7.17.5", + "@babel/preset-env": "^7.16.11", + "@babel/register": "^7.17.0", + "chai": "^4.3.6", + "espree": "^9.3.1", + "esprima": "^4.0.1", + "jshint": "^2.13.4", + "mocha": "^9.2.1", + "nyc": "^15.1.0" }, "license": "BSD-2-Clause", + "nyc": { + "branches": 100, + "lines": 100, + "functions": 100, + "statements": 100, + "reporter": [ + "html", + "text" + ], + "exclude": [ + "test" + ] + }, "scripts": { - "test": "npm run-script lint && npm run-script unit-test", + "test": "npm run lint && npm run unit-test", "lint": "jshint estraverse.js", - "unit-test": "mocha --compilers js:babel-register" + "unit-test": "nyc mocha --require chai/register-expect.js --require @babel/register" } } diff --git a/test/checkDump.js b/test/checkDump.js index f1732db..964ddfc 100644 --- a/test/checkDump.js +++ b/test/checkDump.js @@ -20,8 +20,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import { expect } from 'chai'; - export default function checkDump(dump, expected) { expect(normalize(expected)).to.be.equal(normalize(dump)); } diff --git a/test/es6.js b/test/es6.js index 09509ca..bd503f9 100644 --- a/test/es6.js +++ b/test/es6.js @@ -24,7 +24,7 @@ import Dumper from './dumper'; import checkDump from './checkDump'; -import { parse as esprima } from './third_party/esprima'; +import { parse as esprima } from 'esprima'; import { parse as espree } from 'espree'; function classDeclaration() { @@ -250,9 +250,8 @@ describe('export', function() { describe('import', function() { it('default specifier #1', function() { const tree = espree(`import Cocoa from 'rabbit-house'`, { - ecmaFeatures: { - modules: true - } + ecmaVersion: 2015, + sourceType: 'module' }); checkDump(Dumper.dump(tree), ` @@ -271,9 +270,8 @@ describe('import', function() { it('named specifier #1', function() { const tree = espree(`import {Cocoa, Cappuccino as Chino} from 'rabbit-house'`, { - ecmaFeatures: { - modules: true - } + ecmaVersion: 2015, + sourceType: 'module' }); checkDump(Dumper.dump(tree), ` @@ -300,9 +298,8 @@ describe('import', function() { it('namespace specifier #1', function() { const tree = espree(`import * as RabbitHouse from 'rabbit-house'`, { - ecmaFeatures: { - modules: true - } + ecmaVersion: 2015, + sourceType: 'module' }); checkDump(Dumper.dump(tree), ` @@ -322,27 +319,20 @@ describe('import', function() { describe('dynamic import', function() { it('expression pattern #1', function() { - // TODO: espree currently doesn't support dynamic imports. Update when it does - // const tree = espree(`import('rabbit-house')`, { - // ecmaFeatures: { - // modules: true - // } - // }); - - const tree = { - type: 'ImportExpression', - source: { - type: 'Literal', - value: 'rabbit-house' - } - }; + const tree = espree(`import('rabbit-house')`, { + ecmaVersion: 2020 + }); - checkDump(Dumper.dump(tree), ` - enter - ImportExpression - enter - Literal - leave - Literal - leave - ImportExpression - `); + checkDump(Dumper.dump(tree), ` + enter - Program + enter - ExpressionStatement + enter - ImportExpression + enter - Literal + leave - Literal + leave - ImportExpression + leave - ExpressionStatement + leave - Program + `); }); }); diff --git a/test/replace.js b/test/replace.js index 90a954f..fae2462 100644 --- a/test/replace.js +++ b/test/replace.js @@ -22,7 +22,6 @@ import { replace, VisitorOption } from '..'; -import { expect } from 'chai'; describe('replace', function() { it('can simplify expressions', function() { diff --git a/test/third_party/esprima.js b/test/third_party/esprima.js deleted file mode 100644 index a0ac203..0000000 --- a/test/third_party/esprima.js +++ /dev/null @@ -1,5353 +0,0 @@ -/* - Copyright (C) 2013 Ariya Hidayat - Copyright (C) 2013 Thaddee Tyl - Copyright (C) 2012 Ariya Hidayat - Copyright (C) 2012 Mathias Bynens - Copyright (C) 2012 Joost-Wim Boekesteijn - Copyright (C) 2012 Kris Kowal - Copyright (C) 2012 Yusuke Suzuki - Copyright (C) 2012 Arpad Borsos - Copyright (C) 2011 Ariya Hidayat - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -(function (root, factory) { - 'use strict'; - - // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, - // Rhino, and plain browser loading. - - /* istanbul ignore next */ - if (typeof define === 'function' && define.amd) { - define(['exports'], factory); - } else if (typeof exports !== 'undefined') { - factory(exports); - } else { - factory((root.esprima = {})); - } -}(this, function (exports) { - 'use strict'; - - var Token, - TokenName, - FnExprTokens, - Syntax, - PropertyKind, - Messages, - Regex, - SyntaxTreeDelegate, - ClassPropertyType, - source, - strict, - index, - lineNumber, - lineStart, - length, - delegate, - lookahead, - state, - extra; - - Token = { - BooleanLiteral: 1, - EOF: 2, - Identifier: 3, - Keyword: 4, - NullLiteral: 5, - NumericLiteral: 6, - Punctuator: 7, - StringLiteral: 8, - RegularExpression: 9, - Template: 10 - }; - - TokenName = {}; - TokenName[Token.BooleanLiteral] = 'Boolean'; - TokenName[Token.EOF] = ''; - TokenName[Token.Identifier] = 'Identifier'; - TokenName[Token.Keyword] = 'Keyword'; - TokenName[Token.NullLiteral] = 'Null'; - TokenName[Token.NumericLiteral] = 'Numeric'; - TokenName[Token.Punctuator] = 'Punctuator'; - TokenName[Token.StringLiteral] = 'String'; - TokenName[Token.RegularExpression] = 'RegularExpression'; - - // A function following one of those tokens is an expression. - FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', - 'return', 'case', 'delete', 'throw', 'void', - // assignment operators - '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', - '&=', '|=', '^=', ',', - // binary/unary operators - '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', - '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', - '<=', '<', '>', '!=', '!==']; - - Syntax = { - ArrayExpression: 'ArrayExpression', - ArrayPattern: 'ArrayPattern', - ArrowFunctionExpression: 'ArrowFunctionExpression', - AssignmentExpression: 'AssignmentExpression', - BinaryExpression: 'BinaryExpression', - BlockStatement: 'BlockStatement', - BreakStatement: 'BreakStatement', - CallExpression: 'CallExpression', - CatchClause: 'CatchClause', - ClassBody: 'ClassBody', - ClassDeclaration: 'ClassDeclaration', - ClassExpression: 'ClassExpression', - ComprehensionBlock: 'ComprehensionBlock', - ComprehensionExpression: 'ComprehensionExpression', - ConditionalExpression: 'ConditionalExpression', - ContinueStatement: 'ContinueStatement', - DebuggerStatement: 'DebuggerStatement', - DoWhileStatement: 'DoWhileStatement', - EmptyStatement: 'EmptyStatement', - ExportDeclaration: 'ExportDeclaration', - ExportBatchSpecifier: 'ExportBatchSpecifier', - ExportSpecifier: 'ExportSpecifier', - ExpressionStatement: 'ExpressionStatement', - ForInStatement: 'ForInStatement', - ForOfStatement: 'ForOfStatement', - ForStatement: 'ForStatement', - FunctionDeclaration: 'FunctionDeclaration', - FunctionExpression: 'FunctionExpression', - Identifier: 'Identifier', - IfStatement: 'IfStatement', - ImportDeclaration: 'ImportDeclaration', - ImportDefaultSpecifier: 'ImportDefaultSpecifier', - ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', - ImportSpecifier: 'ImportSpecifier', - LabeledStatement: 'LabeledStatement', - Literal: 'Literal', - LogicalExpression: 'LogicalExpression', - MemberExpression: 'MemberExpression', - MethodDefinition: 'MethodDefinition', - NewExpression: 'NewExpression', - ObjectExpression: 'ObjectExpression', - ObjectPattern: 'ObjectPattern', - Program: 'Program', - Property: 'Property', - ReturnStatement: 'ReturnStatement', - SequenceExpression: 'SequenceExpression', - SpreadElement: 'SpreadElement', - SwitchCase: 'SwitchCase', - SwitchStatement: 'SwitchStatement', - TaggedTemplateExpression: 'TaggedTemplateExpression', - TemplateElement: 'TemplateElement', - TemplateLiteral: 'TemplateLiteral', - ThisExpression: 'ThisExpression', - ThrowStatement: 'ThrowStatement', - TryStatement: 'TryStatement', - UnaryExpression: 'UnaryExpression', - UpdateExpression: 'UpdateExpression', - VariableDeclaration: 'VariableDeclaration', - VariableDeclarator: 'VariableDeclarator', - WhileStatement: 'WhileStatement', - WithStatement: 'WithStatement', - YieldExpression: 'YieldExpression' - }; - - PropertyKind = { - Data: 1, - Get: 2, - Set: 4 - }; - - ClassPropertyType = { - 'static': 'static', - prototype: 'prototype' - }; - - // Error messages should be identical to V8. - Messages = { - UnexpectedToken: 'Unexpected token %0', - UnexpectedNumber: 'Unexpected number', - UnexpectedString: 'Unexpected string', - UnexpectedIdentifier: 'Unexpected identifier', - UnexpectedReserved: 'Unexpected reserved word', - UnexpectedTemplate: 'Unexpected quasi %0', - UnexpectedEOS: 'Unexpected end of input', - NewlineAfterThrow: 'Illegal newline after throw', - InvalidRegExp: 'Invalid regular expression', - UnterminatedRegExp: 'Invalid regular expression: missing /', - InvalidLHSInAssignment: 'Invalid left-hand side in assignment', - InvalidLHSInFormalsList: 'Invalid left-hand side in formals list', - InvalidLHSInForIn: 'Invalid left-hand side in for-in', - MultipleDefaultsInSwitch: 'More than one default clause in switch statement', - NoCatchOrFinally: 'Missing catch or finally after try', - UnknownLabel: 'Undefined label \'%0\'', - Redeclaration: '%0 \'%1\' has already been declared', - IllegalContinue: 'Illegal continue statement', - IllegalBreak: 'Illegal break statement', - IllegalDuplicateClassProperty: 'Illegal duplicate property in class definition', - IllegalClassConstructorProperty: 'Illegal constructor property in class definition', - IllegalReturn: 'Illegal return statement', - IllegalYield: 'Illegal yield expression', - IllegalSpread: 'Illegal spread element', - StrictModeWith: 'Strict mode code may not include a with statement', - StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', - StrictVarName: 'Variable name may not be eval or arguments in strict mode', - StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', - StrictParamDupe: 'Strict mode function may not have duplicate parameter names', - ParameterAfterRestParameter: 'Rest parameter must be final parameter of an argument list', - DefaultRestParameter: 'Rest parameter can not have a default value', - ElementAfterSpreadElement: 'Spread must be the final element of an element list', - ObjectPatternAsRestParameter: 'Invalid rest parameter', - ObjectPatternAsSpread: 'Invalid spread argument', - StrictFunctionName: 'Function name may not be eval or arguments in strict mode', - StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', - StrictDelete: 'Delete of an unqualified identifier in strict mode.', - StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode', - AccessorDataProperty: 'Object literal may not have data and accessor property with the same name', - AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name', - StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', - StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', - StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', - StrictReservedWord: 'Use of future reserved word in strict mode', - MissingFromClause: 'Missing from clause', - NoAsAfterImportNamespace: 'Missing as after import *', - InvalidModuleSpecifier: 'Invalid module specifier', - IllegalImportDeclaration: 'Illegal import declaration', - IllegalExportDeclaration: 'Illegal export declaration', - NoUninitializedConst: 'Const must be initialized', - ComprehensionRequiresBlock: 'Comprehension must have at least one block', - ComprehensionError: 'Comprehension Error', - EachNotAllowed: 'Each is not supported' - }; - - // See also tools/generate-unicode-regex.py. - Regex = { - NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'), - NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]') - }; - - // Ensure the condition is true, otherwise throw an error. - // This is only to have a better contract semantic, i.e. another safety net - // to catch a logic error. The condition shall be fulfilled in normal case. - // Do NOT use this to enforce a certain condition on any user input. - - function assert(condition, message) { - /* istanbul ignore if */ - if (!condition) { - throw new Error('ASSERT: ' + message); - } - } - - function StringMap() { - this.$data = {}; - } - - StringMap.prototype.get = function (key) { - key = '$' + key; - return this.$data[key]; - }; - - StringMap.prototype.set = function (key, value) { - key = '$' + key; - this.$data[key] = value; - return this; - }; - - StringMap.prototype.has = function (key) { - key = '$' + key; - return Object.prototype.hasOwnProperty.call(this.$data, key); - }; - - StringMap.prototype.delete = function (key) { - key = '$' + key; - return delete this.$data[key]; - }; - - function isDecimalDigit(ch) { - return (ch >= 48 && ch <= 57); // 0..9 - } - - function isHexDigit(ch) { - return '0123456789abcdefABCDEF'.indexOf(ch) >= 0; - } - - function isOctalDigit(ch) { - return '01234567'.indexOf(ch) >= 0; - } - - - // 7.2 White Space - - function isWhiteSpace(ch) { - return (ch === 32) || // space - (ch === 9) || // tab - (ch === 0xB) || - (ch === 0xC) || - (ch === 0xA0) || - (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0); - } - - // 7.3 Line Terminators - - function isLineTerminator(ch) { - return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); - } - - // 7.6 Identifier Names and Identifiers - - function isIdentifierStart(ch) { - return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) - (ch >= 65 && ch <= 90) || // A..Z - (ch >= 97 && ch <= 122) || // a..z - (ch === 92) || // \ (backslash) - ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch))); - } - - function isIdentifierPart(ch) { - return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) - (ch >= 65 && ch <= 90) || // A..Z - (ch >= 97 && ch <= 122) || // a..z - (ch >= 48 && ch <= 57) || // 0..9 - (ch === 92) || // \ (backslash) - ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch))); - } - - // 7.6.1.2 Future Reserved Words - - function isFutureReservedWord(id) { - switch (id) { - case 'class': - case 'enum': - case 'export': - case 'extends': - case 'import': - case 'super': - return true; - default: - return false; - } - } - - function isStrictModeReservedWord(id) { - switch (id) { - case 'implements': - case 'interface': - case 'package': - case 'private': - case 'protected': - case 'public': - case 'static': - case 'yield': - case 'let': - return true; - default: - return false; - } - } - - function isRestrictedWord(id) { - return id === 'eval' || id === 'arguments'; - } - - // 7.6.1.1 Keywords - - function isKeyword(id) { - if (strict && isStrictModeReservedWord(id)) { - return true; - } - - // 'const' is specialized as Keyword in V8. - // 'yield' is only treated as a keyword in strict mode. - // 'let' is for compatiblity with SpiderMonkey and ES.next. - // Some others are from future reserved words. - - switch (id.length) { - case 2: - return (id === 'if') || (id === 'in') || (id === 'do'); - case 3: - return (id === 'var') || (id === 'for') || (id === 'new') || - (id === 'try') || (id === 'let'); - case 4: - return (id === 'this') || (id === 'else') || (id === 'case') || - (id === 'void') || (id === 'with') || (id === 'enum'); - case 5: - return (id === 'while') || (id === 'break') || (id === 'catch') || - (id === 'throw') || (id === 'const') || - (id === 'class') || (id === 'super'); - case 6: - return (id === 'return') || (id === 'typeof') || (id === 'delete') || - (id === 'switch') || (id === 'export') || (id === 'import'); - case 7: - return (id === 'default') || (id === 'finally') || (id === 'extends'); - case 8: - return (id === 'function') || (id === 'continue') || (id === 'debugger'); - case 10: - return (id === 'instanceof'); - default: - return false; - } - } - - // 7.4 Comments - - function addComment(type, value, start, end, loc) { - var comment; - assert(typeof start === 'number', 'Comment must have valid position'); - - // Because the way the actual token is scanned, often the comments - // (if any) are skipped twice during the lexical analysis. - // Thus, we need to skip adding a comment if the comment array already - // handled it. - if (state.lastCommentStart >= start) { - return; - } - state.lastCommentStart = start; - - comment = { - type: type, - value: value - }; - if (extra.range) { - comment.range = [start, end]; - } - if (extra.loc) { - comment.loc = loc; - } - extra.comments.push(comment); - if (extra.attachComment) { - extra.leadingComments.push(comment); - extra.trailingComments.push(comment); - } - } - - function skipSingleLineComment() { - var start, loc, ch, comment; - - start = index - 2; - loc = { - start: { - line: lineNumber, - column: index - lineStart - 2 - } - }; - - while (index < length) { - ch = source.charCodeAt(index); - ++index; - if (isLineTerminator(ch)) { - if (extra.comments) { - comment = source.slice(start + 2, index - 1); - loc.end = { - line: lineNumber, - column: index - lineStart - 1 - }; - addComment('Line', comment, start, index - 1, loc); - } - if (ch === 13 && source.charCodeAt(index) === 10) { - ++index; - } - ++lineNumber; - lineStart = index; - return; - } - } - - if (extra.comments) { - comment = source.slice(start + 2, index); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - addComment('Line', comment, start, index, loc); - } - } - - function skipMultiLineComment() { - var start, loc, ch, comment; - - if (extra.comments) { - start = index - 2; - loc = { - start: { - line: lineNumber, - column: index - lineStart - 2 - } - }; - } - - while (index < length) { - ch = source.charCodeAt(index); - if (isLineTerminator(ch)) { - if (ch === 13 && source.charCodeAt(index + 1) === 10) { - ++index; - } - ++lineNumber; - ++index; - lineStart = index; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else if (ch === 42) { - // Block comment ends with '*/' (char #42, char #47). - if (source.charCodeAt(index + 1) === 47) { - ++index; - ++index; - if (extra.comments) { - comment = source.slice(start + 2, index - 2); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - addComment('Block', comment, start, index, loc); - } - return; - } - ++index; - } else { - ++index; - } - } - - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - function skipComment() { - var ch; - - while (index < length) { - ch = source.charCodeAt(index); - - if (isWhiteSpace(ch)) { - ++index; - } else if (isLineTerminator(ch)) { - ++index; - if (ch === 13 && source.charCodeAt(index) === 10) { - ++index; - } - ++lineNumber; - lineStart = index; - } else if (ch === 47) { // 47 is '/' - ch = source.charCodeAt(index + 1); - if (ch === 47) { - ++index; - ++index; - skipSingleLineComment(); - } else if (ch === 42) { // 42 is '*' - ++index; - ++index; - skipMultiLineComment(); - } else { - break; - } - } else { - break; - } - } - } - - function scanHexEscape(prefix) { - var i, len, ch, code = 0; - - len = (prefix === 'u') ? 4 : 2; - for (i = 0; i < len; ++i) { - if (index < length && isHexDigit(source[index])) { - ch = source[index++]; - code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); - } else { - return ''; - } - } - return String.fromCharCode(code); - } - - function scanUnicodeCodePointEscape() { - var ch, code, cu1, cu2; - - ch = source[index]; - code = 0; - - // At least, one hex digit is required. - if (ch === '}') { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - while (index < length) { - ch = source[index++]; - if (!isHexDigit(ch)) { - break; - } - code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); - } - - if (code > 0x10FFFF || ch !== '}') { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - // UTF-16 Encoding - if (code <= 0xFFFF) { - return String.fromCharCode(code); - } - cu1 = ((code - 0x10000) >> 10) + 0xD800; - cu2 = ((code - 0x10000) & 1023) + 0xDC00; - return String.fromCharCode(cu1, cu2); - } - - function getEscapedIdentifier() { - var ch, id; - - ch = source.charCodeAt(index++); - id = String.fromCharCode(ch); - - // '\u' (char #92, char #117) denotes an escaped character. - if (ch === 92) { - if (source.charCodeAt(index) !== 117) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - ++index; - ch = scanHexEscape('u'); - if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - id = ch; - } - - while (index < length) { - ch = source.charCodeAt(index); - if (!isIdentifierPart(ch)) { - break; - } - ++index; - id += String.fromCharCode(ch); - - // '\u' (char #92, char #117) denotes an escaped character. - if (ch === 92) { - id = id.substr(0, id.length - 1); - if (source.charCodeAt(index) !== 117) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - ++index; - ch = scanHexEscape('u'); - if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - id += ch; - } - } - - return id; - } - - function getIdentifier() { - var start, ch; - - start = index++; - while (index < length) { - ch = source.charCodeAt(index); - if (ch === 92) { - // Blackslash (char #92) marks Unicode escape sequence. - index = start; - return getEscapedIdentifier(); - } - if (isIdentifierPart(ch)) { - ++index; - } else { - break; - } - } - - return source.slice(start, index); - } - - function scanIdentifier() { - var start, id, type; - - start = index; - - // Backslash (char #92) starts an escaped character. - id = (source.charCodeAt(index) === 92) ? getEscapedIdentifier() : getIdentifier(); - - // There is no keyword or literal with only one character. - // Thus, it must be an identifier. - if (id.length === 1) { - type = Token.Identifier; - } else if (isKeyword(id)) { - type = Token.Keyword; - } else if (id === 'null') { - type = Token.NullLiteral; - } else if (id === 'true' || id === 'false') { - type = Token.BooleanLiteral; - } else { - type = Token.Identifier; - } - - return { - type: type, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - - // 7.7 Punctuators - - function scanPunctuator() { - var start = index, - code = source.charCodeAt(index), - code2, - ch1 = source[index], - ch2, - ch3, - ch4; - - switch (code) { - // Check for most common single-character punctuators. - case 40: // ( open bracket - case 41: // ) close bracket - case 59: // ; semicolon - case 44: // , comma - case 123: // { open curly brace - case 125: // } close curly brace - case 91: // [ - case 93: // ] - case 58: // : - case 63: // ? - case 126: // ~ - ++index; - if (extra.tokenize) { - if (code === 40) { - extra.openParenToken = extra.tokens.length; - } else if (code === 123) { - extra.openCurlyToken = extra.tokens.length; - } - } - return { - type: Token.Punctuator, - value: String.fromCharCode(code), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - - default: - code2 = source.charCodeAt(index + 1); - - // '=' (char #61) marks an assignment or comparison operator. - if (code2 === 61) { - switch (code) { - case 37: // % - case 38: // & - case 42: // *: - case 43: // + - case 45: // - - case 47: // / - case 60: // < - case 62: // > - case 94: // ^ - case 124: // | - index += 2; - return { - type: Token.Punctuator, - value: String.fromCharCode(code) + String.fromCharCode(code2), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - - case 33: // ! - case 61: // = - index += 2; - - // !== and === - if (source.charCodeAt(index) === 61) { - ++index; - } - return { - type: Token.Punctuator, - value: source.slice(start, index), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - default: - break; - } - } - break; - } - - // Peek more characters. - - ch2 = source[index + 1]; - ch3 = source[index + 2]; - ch4 = source[index + 3]; - - // 4-character punctuator: >>>= - - if (ch1 === '>' && ch2 === '>' && ch3 === '>') { - if (ch4 === '=') { - index += 4; - return { - type: Token.Punctuator, - value: '>>>=', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - } - - // 3-character punctuators: === !== >>> <<= >>= - - if (ch1 === '>' && ch2 === '>' && ch3 === '>') { - index += 3; - return { - type: Token.Punctuator, - value: '>>>', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (ch1 === '<' && ch2 === '<' && ch3 === '=') { - index += 3; - return { - type: Token.Punctuator, - value: '<<=', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (ch1 === '>' && ch2 === '>' && ch3 === '=') { - index += 3; - return { - type: Token.Punctuator, - value: '>>=', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (ch1 === '.' && ch2 === '.' && ch3 === '.') { - index += 3; - return { - type: Token.Punctuator, - value: '...', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - // Other 2-character punctuators: ++ -- << >> && || - - if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { - index += 2; - return { - type: Token.Punctuator, - value: ch1 + ch2, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (ch1 === '=' && ch2 === '>') { - index += 2; - return { - type: Token.Punctuator, - value: '=>', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { - ++index; - return { - type: Token.Punctuator, - value: ch1, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (ch1 === '.') { - ++index; - return { - type: Token.Punctuator, - value: ch1, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - // 7.8.3 Numeric Literals - - function scanHexLiteral(start) { - var number = ''; - - while (index < length) { - if (!isHexDigit(source[index])) { - break; - } - number += source[index++]; - } - - if (number.length === 0) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - if (isIdentifierStart(source.charCodeAt(index))) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - return { - type: Token.NumericLiteral, - value: parseInt('0x' + number, 16), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - function scanBinaryLiteral(start) { - var ch, number; - - number = ''; - - while (index < length) { - ch = source[index]; - if (ch !== '0' && ch !== '1') { - break; - } - number += source[index++]; - } - - if (number.length === 0) { - // only 0b or 0B - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - if (index < length) { - ch = source.charCodeAt(index); - /* istanbul ignore else */ - if (isIdentifierStart(ch) || isDecimalDigit(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - - return { - type: Token.NumericLiteral, - value: parseInt(number, 2), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - function scanOctalLiteral(prefix, start) { - var number, octal; - - if (isOctalDigit(prefix)) { - octal = true; - number = '0' + source[index++]; - } else { - octal = false; - ++index; - number = ''; - } - - while (index < length) { - if (!isOctalDigit(source[index])) { - break; - } - number += source[index++]; - } - - if (!octal && number.length === 0) { - // only 0o or 0O - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - return { - type: Token.NumericLiteral, - value: parseInt(number, 8), - octal: octal, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - function scanNumericLiteral() { - var number, start, ch; - - ch = source[index]; - assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), - 'Numeric literal must start with a decimal digit or a decimal point'); - - start = index; - number = ''; - if (ch !== '.') { - number = source[index++]; - ch = source[index]; - - // Hex number starts with '0x'. - // Octal number starts with '0'. - // Octal number in ES6 starts with '0o'. - // Binary number in ES6 starts with '0b'. - if (number === '0') { - if (ch === 'x' || ch === 'X') { - ++index; - return scanHexLiteral(start); - } - if (ch === 'b' || ch === 'B') { - ++index; - return scanBinaryLiteral(start); - } - if (ch === 'o' || ch === 'O' || isOctalDigit(ch)) { - return scanOctalLiteral(ch, start); - } - // decimal number starts with '0' such as '09' is illegal. - if (ch && isDecimalDigit(ch.charCodeAt(0))) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - - while (isDecimalDigit(source.charCodeAt(index))) { - number += source[index++]; - } - ch = source[index]; - } - - if (ch === '.') { - number += source[index++]; - while (isDecimalDigit(source.charCodeAt(index))) { - number += source[index++]; - } - ch = source[index]; - } - - if (ch === 'e' || ch === 'E') { - number += source[index++]; - - ch = source[index]; - if (ch === '+' || ch === '-') { - number += source[index++]; - } - if (isDecimalDigit(source.charCodeAt(index))) { - while (isDecimalDigit(source.charCodeAt(index))) { - number += source[index++]; - } - } else { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - - if (isIdentifierStart(source.charCodeAt(index))) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - return { - type: Token.NumericLiteral, - value: parseFloat(number), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - // 7.8.4 String Literals - - function scanStringLiteral() { - var str = '', quote, start, ch, code, unescaped, restore, octal = false; - - quote = source[index]; - assert((quote === '\'' || quote === '"'), - 'String literal must starts with a quote'); - - start = index; - ++index; - - while (index < length) { - ch = source[index++]; - - if (ch === quote) { - quote = ''; - break; - } else if (ch === '\\') { - ch = source[index++]; - if (!ch || !isLineTerminator(ch.charCodeAt(0))) { - switch (ch) { - case 'n': - str += '\n'; - break; - case 'r': - str += '\r'; - break; - case 't': - str += '\t'; - break; - case 'u': - case 'x': - if (source[index] === '{') { - ++index; - str += scanUnicodeCodePointEscape(); - } else { - restore = index; - unescaped = scanHexEscape(ch); - if (unescaped) { - str += unescaped; - } else { - index = restore; - str += ch; - } - } - break; - case 'b': - str += '\b'; - break; - case 'f': - str += '\f'; - break; - case 'v': - str += '\x0B'; - break; - - default: - if (isOctalDigit(ch)) { - code = '01234567'.indexOf(ch); - - // \0 is not octal escape sequence - if (code !== 0) { - octal = true; - } - - /* istanbul ignore else */ - if (index < length && isOctalDigit(source[index])) { - octal = true; - code = code * 8 + '01234567'.indexOf(source[index++]); - - // 3 digits are only allowed when string starts - // with 0, 1, 2, 3 - if ('0123'.indexOf(ch) >= 0 && - index < length && - isOctalDigit(source[index])) { - code = code * 8 + '01234567'.indexOf(source[index++]); - } - } - str += String.fromCharCode(code); - } else { - str += ch; - } - break; - } - } else { - ++lineNumber; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - lineStart = index; - } - } else if (isLineTerminator(ch.charCodeAt(0))) { - break; - } else { - str += ch; - } - } - - if (quote !== '') { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - return { - type: Token.StringLiteral, - value: str, - octal: octal, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - function scanTemplate() { - var cooked = '', ch, start, terminated, tail, restore, unescaped, code, octal; - - terminated = false; - tail = false; - start = index; - - ++index; - - while (index < length) { - ch = source[index++]; - if (ch === '`') { - tail = true; - terminated = true; - break; - } else if (ch === '$') { - if (source[index] === '{') { - ++index; - terminated = true; - break; - } - cooked += ch; - } else if (ch === '\\') { - ch = source[index++]; - if (!isLineTerminator(ch.charCodeAt(0))) { - switch (ch) { - case 'n': - cooked += '\n'; - break; - case 'r': - cooked += '\r'; - break; - case 't': - cooked += '\t'; - break; - case 'u': - case 'x': - if (source[index] === '{') { - ++index; - cooked += scanUnicodeCodePointEscape(); - } else { - restore = index; - unescaped = scanHexEscape(ch); - if (unescaped) { - cooked += unescaped; - } else { - index = restore; - cooked += ch; - } - } - break; - case 'b': - cooked += '\b'; - break; - case 'f': - cooked += '\f'; - break; - case 'v': - cooked += '\v'; - break; - - default: - if (isOctalDigit(ch)) { - code = '01234567'.indexOf(ch); - - // \0 is not octal escape sequence - if (code !== 0) { - octal = true; - } - - /* istanbul ignore else */ - if (index < length && isOctalDigit(source[index])) { - octal = true; - code = code * 8 + '01234567'.indexOf(source[index++]); - - // 3 digits are only allowed when string starts - // with 0, 1, 2, 3 - if ('0123'.indexOf(ch) >= 0 && - index < length && - isOctalDigit(source[index])) { - code = code * 8 + '01234567'.indexOf(source[index++]); - } - } - cooked += String.fromCharCode(code); - } else { - cooked += ch; - } - break; - } - } else { - ++lineNumber; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - lineStart = index; - } - } else if (isLineTerminator(ch.charCodeAt(0))) { - ++lineNumber; - if (ch === '\r' && source[index] === '\n') { - ++index; - } - lineStart = index; - cooked += '\n'; - } else { - cooked += ch; - } - } - - if (!terminated) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - return { - type: Token.Template, - value: { - cooked: cooked, - raw: source.slice(start + 1, index - ((tail) ? 1 : 2)) - }, - tail: tail, - octal: octal, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - function scanTemplateElement(option) { - var startsWith, template; - - lookahead = null; - skipComment(); - - startsWith = (option.head) ? '`' : '}'; - - if (source[index] !== startsWith) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - - template = scanTemplate(); - - peek(); - - return template; - } - - function testRegExp(pattern, flags) { - var tmp = pattern, - value; - - if (flags.indexOf('u') >= 0) { - // Replace each astral symbol and every Unicode code point - // escape sequence with a single ASCII symbol to avoid throwing on - // regular expressions that are only valid in combination with the - // `/u` flag. - // Note: replacing with the ASCII symbol `x` might cause false - // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a - // perfectly valid pattern that is equivalent to `[a-b]`, but it - // would be replaced by `[x-b]` which throws an error. - tmp = tmp - .replace(/\\u\{([0-9a-fA-F]+)\}/g, function ($0, $1) { - if (parseInt($1, 16) <= 0x10FFFF) { - return 'x'; - } - throwError({}, Messages.InvalidRegExp); - }) - .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 'x'); - } - - // First, detect invalid regular expressions. - try { - value = new RegExp(tmp); - } catch (e) { - throwError({}, Messages.InvalidRegExp); - } - - // Return a regular expression object for this pattern-flag pair, or - // `null` in case the current environment doesn't support the flags it - // uses. - try { - return new RegExp(pattern, flags); - } catch (exception) { - return null; - } - } - - function scanRegExpBody() { - var ch, str, classMarker, terminated, body; - - ch = source[index]; - assert(ch === '/', 'Regular expression literal must start with a slash'); - str = source[index++]; - - classMarker = false; - terminated = false; - while (index < length) { - ch = source[index++]; - str += ch; - if (ch === '\\') { - ch = source[index++]; - // ECMA-262 7.8.5 - if (isLineTerminator(ch.charCodeAt(0))) { - throwError({}, Messages.UnterminatedRegExp); - } - str += ch; - } else if (isLineTerminator(ch.charCodeAt(0))) { - throwError({}, Messages.UnterminatedRegExp); - } else if (classMarker) { - if (ch === ']') { - classMarker = false; - } - } else { - if (ch === '/') { - terminated = true; - break; - } else if (ch === '[') { - classMarker = true; - } - } - } - - if (!terminated) { - throwError({}, Messages.UnterminatedRegExp); - } - - // Exclude leading and trailing slash. - body = str.substr(1, str.length - 2); - return { - value: body, - literal: str - }; - } - - function scanRegExpFlags() { - var ch, str, flags, restore; - - str = ''; - flags = ''; - while (index < length) { - ch = source[index]; - if (!isIdentifierPart(ch.charCodeAt(0))) { - break; - } - - ++index; - if (ch === '\\' && index < length) { - ch = source[index]; - if (ch === 'u') { - ++index; - restore = index; - ch = scanHexEscape('u'); - if (ch) { - flags += ch; - for (str += '\\u'; restore < index; ++restore) { - str += source[restore]; - } - } else { - index = restore; - flags += 'u'; - str += '\\u'; - } - throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL'); - } else { - str += '\\'; - throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - flags += ch; - str += ch; - } - } - - return { - value: flags, - literal: str - }; - } - - function scanRegExp() { - var start, body, flags, value; - - lookahead = null; - skipComment(); - start = index; - - body = scanRegExpBody(); - flags = scanRegExpFlags(); - value = testRegExp(body.value, flags.value); - - if (extra.tokenize) { - return { - type: Token.RegularExpression, - value: value, - regex: { - pattern: body.value, - flags: flags.value - }, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - return { - literal: body.literal + flags.literal, - value: value, - regex: { - pattern: body.value, - flags: flags.value - }, - range: [start, index] - }; - } - - function isIdentifierName(token) { - return token.type === Token.Identifier || - token.type === Token.Keyword || - token.type === Token.BooleanLiteral || - token.type === Token.NullLiteral; - } - - function advanceSlash() { - var prevToken, - checkToken; - // Using the following algorithm: - // https://github.com/mozilla/sweet.js/wiki/design - prevToken = extra.tokens[extra.tokens.length - 1]; - if (!prevToken) { - // Nothing before that: it cannot be a division. - return scanRegExp(); - } - if (prevToken.type === 'Punctuator') { - if (prevToken.value === ')') { - checkToken = extra.tokens[extra.openParenToken - 1]; - if (checkToken && - checkToken.type === 'Keyword' && - (checkToken.value === 'if' || - checkToken.value === 'while' || - checkToken.value === 'for' || - checkToken.value === 'with')) { - return scanRegExp(); - } - return scanPunctuator(); - } - if (prevToken.value === '}') { - // Dividing a function by anything makes little sense, - // but we have to check for that. - if (extra.tokens[extra.openCurlyToken - 3] && - extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') { - // Anonymous function. - checkToken = extra.tokens[extra.openCurlyToken - 4]; - if (!checkToken) { - return scanPunctuator(); - } - } else if (extra.tokens[extra.openCurlyToken - 4] && - extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') { - // Named function. - checkToken = extra.tokens[extra.openCurlyToken - 5]; - if (!checkToken) { - return scanRegExp(); - } - } else { - return scanPunctuator(); - } - // checkToken determines whether the function is - // a declaration or an expression. - if (FnExprTokens.indexOf(checkToken.value) >= 0) { - // It is an expression. - return scanPunctuator(); - } - // It is a declaration. - return scanRegExp(); - } - return scanRegExp(); - } - if (prevToken.type === 'Keyword' && prevToken.value !== 'this') { - return scanRegExp(); - } - return scanPunctuator(); - } - - function advance() { - var ch; - - skipComment(); - - if (index >= length) { - return { - type: Token.EOF, - lineNumber: lineNumber, - lineStart: lineStart, - range: [index, index] - }; - } - - ch = source.charCodeAt(index); - - // Very common: ( and ) and ; - if (ch === 40 || ch === 41 || ch === 58) { - return scanPunctuator(); - } - - // String literal starts with single quote (#39) or double quote (#34). - if (ch === 39 || ch === 34) { - return scanStringLiteral(); - } - - if (ch === 96) { - return scanTemplate(); - } - if (isIdentifierStart(ch)) { - return scanIdentifier(); - } - - // Dot (.) char #46 can also start a floating-point number, hence the need - // to check the next character. - if (ch === 46) { - if (isDecimalDigit(source.charCodeAt(index + 1))) { - return scanNumericLiteral(); - } - return scanPunctuator(); - } - - if (isDecimalDigit(ch)) { - return scanNumericLiteral(); - } - - // Slash (/) char #47 can also start a regex. - if (extra.tokenize && ch === 47) { - return advanceSlash(); - } - - return scanPunctuator(); - } - - function lex() { - var token; - - token = lookahead; - index = token.range[1]; - lineNumber = token.lineNumber; - lineStart = token.lineStart; - - lookahead = advance(); - - index = token.range[1]; - lineNumber = token.lineNumber; - lineStart = token.lineStart; - - return token; - } - - function peek() { - var pos, line, start; - - pos = index; - line = lineNumber; - start = lineStart; - lookahead = advance(); - index = pos; - lineNumber = line; - lineStart = start; - } - - function lookahead2() { - var adv, pos, line, start, result; - - // If we are collecting the tokens, don't grab the next one yet. - /* istanbul ignore next */ - adv = (typeof extra.advance === 'function') ? extra.advance : advance; - - pos = index; - line = lineNumber; - start = lineStart; - - // Scan for the next immediate token. - /* istanbul ignore if */ - if (lookahead === null) { - lookahead = adv(); - } - index = lookahead.range[1]; - lineNumber = lookahead.lineNumber; - lineStart = lookahead.lineStart; - - // Grab the token right after. - result = adv(); - index = pos; - lineNumber = line; - lineStart = start; - - return result; - } - - function markerCreate() { - if (!extra.loc && !extra.range) { - return undefined; - } - skipComment(); - return {offset: index, line: lineNumber, col: index - lineStart}; - } - - function processComment(node) { - var lastChild, - trailingComments, - bottomRight = extra.bottomRightStack, - last = bottomRight[bottomRight.length - 1]; - - if (node.type === Syntax.Program) { - /* istanbul ignore else */ - if (node.body.length > 0) { - return; - } - } - - if (extra.trailingComments.length > 0) { - if (extra.trailingComments[0].range[0] >= node.range[1]) { - trailingComments = extra.trailingComments; - extra.trailingComments = []; - } else { - extra.trailingComments.length = 0; - } - } else { - if (last && last.trailingComments && last.trailingComments[0].range[0] >= node.range[1]) { - trailingComments = last.trailingComments; - delete last.trailingComments; - } - } - - // Eating the stack. - if (last) { - while (last && last.range[0] >= node.range[0]) { - lastChild = last; - last = bottomRight.pop(); - } - } - - if (lastChild) { - if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) { - node.leadingComments = lastChild.leadingComments; - delete lastChild.leadingComments; - } - } else if (extra.leadingComments.length > 0 && extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) { - node.leadingComments = extra.leadingComments; - extra.leadingComments = []; - } - - if (trailingComments) { - node.trailingComments = trailingComments; - } - - bottomRight.push(node); - } - - function markerApply(marker, node) { - if (extra.range) { - node.range = [marker.offset, index]; - } - if (extra.loc) { - node.loc = { - start: { - line: marker.line, - column: marker.col - }, - end: { - line: lineNumber, - column: index - lineStart - } - }; - node = delegate.postProcess(node); - } - if (extra.attachComment) { - processComment(node); - } - return node; - } - - SyntaxTreeDelegate = { - - name: 'SyntaxTree', - - postProcess: function (node) { - return node; - }, - - createArrayExpression: function (elements) { - return { - type: Syntax.ArrayExpression, - elements: elements - }; - }, - - createAssignmentExpression: function (operator, left, right) { - return { - type: Syntax.AssignmentExpression, - operator: operator, - left: left, - right: right - }; - }, - - createBinaryExpression: function (operator, left, right) { - var type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : - Syntax.BinaryExpression; - return { - type: type, - operator: operator, - left: left, - right: right - }; - }, - - createBlockStatement: function (body) { - return { - type: Syntax.BlockStatement, - body: body - }; - }, - - createBreakStatement: function (label) { - return { - type: Syntax.BreakStatement, - label: label - }; - }, - - createCallExpression: function (callee, args) { - return { - type: Syntax.CallExpression, - callee: callee, - 'arguments': args - }; - }, - - createCatchClause: function (param, body) { - return { - type: Syntax.CatchClause, - param: param, - body: body - }; - }, - - createConditionalExpression: function (test, consequent, alternate) { - return { - type: Syntax.ConditionalExpression, - test: test, - consequent: consequent, - alternate: alternate - }; - }, - - createContinueStatement: function (label) { - return { - type: Syntax.ContinueStatement, - label: label - }; - }, - - createDebuggerStatement: function () { - return { - type: Syntax.DebuggerStatement - }; - }, - - createDoWhileStatement: function (body, test) { - return { - type: Syntax.DoWhileStatement, - body: body, - test: test - }; - }, - - createEmptyStatement: function () { - return { - type: Syntax.EmptyStatement - }; - }, - - createExpressionStatement: function (expression) { - return { - type: Syntax.ExpressionStatement, - expression: expression - }; - }, - - createForStatement: function (init, test, update, body) { - return { - type: Syntax.ForStatement, - init: init, - test: test, - update: update, - body: body - }; - }, - - createForInStatement: function (left, right, body) { - return { - type: Syntax.ForInStatement, - left: left, - right: right, - body: body, - each: false - }; - }, - - createForOfStatement: function (left, right, body) { - return { - type: Syntax.ForOfStatement, - left: left, - right: right, - body: body - }; - }, - - createFunctionDeclaration: function (id, params, defaults, body, rest, generator, expression) { - return { - type: Syntax.FunctionDeclaration, - id: id, - params: params, - defaults: defaults, - body: body, - rest: rest, - generator: generator, - expression: expression - }; - }, - - createFunctionExpression: function (id, params, defaults, body, rest, generator, expression) { - return { - type: Syntax.FunctionExpression, - id: id, - params: params, - defaults: defaults, - body: body, - rest: rest, - generator: generator, - expression: expression - }; - }, - - createIdentifier: function (name) { - return { - type: Syntax.Identifier, - name: name - }; - }, - - createIfStatement: function (test, consequent, alternate) { - return { - type: Syntax.IfStatement, - test: test, - consequent: consequent, - alternate: alternate - }; - }, - - createLabeledStatement: function (label, body) { - return { - type: Syntax.LabeledStatement, - label: label, - body: body - }; - }, - - createLiteral: function (token) { - var object = { - type: Syntax.Literal, - value: token.value, - raw: source.slice(token.range[0], token.range[1]) - }; - if (token.regex) { - object.regex = token.regex; - } - return object; - }, - - createMemberExpression: function (accessor, object, property) { - return { - type: Syntax.MemberExpression, - computed: accessor === '[', - object: object, - property: property - }; - }, - - createNewExpression: function (callee, args) { - return { - type: Syntax.NewExpression, - callee: callee, - 'arguments': args - }; - }, - - createObjectExpression: function (properties) { - return { - type: Syntax.ObjectExpression, - properties: properties - }; - }, - - createPostfixExpression: function (operator, argument) { - return { - type: Syntax.UpdateExpression, - operator: operator, - argument: argument, - prefix: false - }; - }, - - createProgram: function (body) { - return { - type: Syntax.Program, - body: body - }; - }, - - createProperty: function (kind, key, value, method, shorthand, computed) { - return { - type: Syntax.Property, - key: key, - value: value, - kind: kind, - method: method, - shorthand: shorthand, - computed: computed - }; - }, - - createReturnStatement: function (argument) { - return { - type: Syntax.ReturnStatement, - argument: argument - }; - }, - - createSequenceExpression: function (expressions) { - return { - type: Syntax.SequenceExpression, - expressions: expressions - }; - }, - - createSwitchCase: function (test, consequent) { - return { - type: Syntax.SwitchCase, - test: test, - consequent: consequent - }; - }, - - createSwitchStatement: function (discriminant, cases) { - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; - }, - - createThisExpression: function () { - return { - type: Syntax.ThisExpression - }; - }, - - createThrowStatement: function (argument) { - return { - type: Syntax.ThrowStatement, - argument: argument - }; - }, - - createTryStatement: function (block, guardedHandlers, handlers, finalizer) { - return { - type: Syntax.TryStatement, - block: block, - guardedHandlers: guardedHandlers, - handlers: handlers, - finalizer: finalizer - }; - }, - - createUnaryExpression: function (operator, argument) { - if (operator === '++' || operator === '--') { - return { - type: Syntax.UpdateExpression, - operator: operator, - argument: argument, - prefix: true - }; - } - return { - type: Syntax.UnaryExpression, - operator: operator, - argument: argument, - prefix: true - }; - }, - - createVariableDeclaration: function (declarations, kind) { - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: kind - }; - }, - - createVariableDeclarator: function (id, init) { - return { - type: Syntax.VariableDeclarator, - id: id, - init: init - }; - }, - - createWhileStatement: function (test, body) { - return { - type: Syntax.WhileStatement, - test: test, - body: body - }; - }, - - createWithStatement: function (object, body) { - return { - type: Syntax.WithStatement, - object: object, - body: body - }; - }, - - createTemplateElement: function (value, tail) { - return { - type: Syntax.TemplateElement, - value: value, - tail: tail - }; - }, - - createTemplateLiteral: function (quasis, expressions) { - return { - type: Syntax.TemplateLiteral, - quasis: quasis, - expressions: expressions - }; - }, - - createSpreadElement: function (argument) { - return { - type: Syntax.SpreadElement, - argument: argument - }; - }, - - createTaggedTemplateExpression: function (tag, quasi) { - return { - type: Syntax.TaggedTemplateExpression, - tag: tag, - quasi: quasi - }; - }, - - createArrowFunctionExpression: function (params, defaults, body, rest, expression) { - return { - type: Syntax.ArrowFunctionExpression, - id: null, - params: params, - defaults: defaults, - body: body, - rest: rest, - generator: false, - expression: expression - }; - }, - - createMethodDefinition: function (propertyType, kind, key, value, computed) { - return { - type: Syntax.MethodDefinition, - key: key, - value: value, - kind: kind, - 'static': propertyType === ClassPropertyType.static, - computed: computed - }; - }, - - createClassBody: function (body) { - return { - type: Syntax.ClassBody, - body: body - }; - }, - - createClassExpression: function (id, superClass, body) { - return { - type: Syntax.ClassExpression, - id: id, - superClass: superClass, - body: body - }; - }, - - createClassDeclaration: function (id, superClass, body) { - return { - type: Syntax.ClassDeclaration, - id: id, - superClass: superClass, - body: body - }; - }, - - createExportSpecifier: function (id, name) { - return { - type: Syntax.ExportSpecifier, - id: id, - name: name - }; - }, - - createExportBatchSpecifier: function () { - return { - type: Syntax.ExportBatchSpecifier - }; - }, - - createImportDefaultSpecifier: function (id) { - return { - type: Syntax.ImportDefaultSpecifier, - id: id - }; - }, - - createImportNamespaceSpecifier: function (id) { - return { - type: Syntax.ImportNamespaceSpecifier, - id: id - }; - }, - - createExportDeclaration: function (isDefault, declaration, specifiers, src) { - return { - type: Syntax.ExportDeclaration, - 'default': !!isDefault, - declaration: declaration, - specifiers: specifiers, - source: src - }; - }, - - createImportSpecifier: function (id, name) { - return { - type: Syntax.ImportSpecifier, - id: id, - name: name - }; - }, - - createImportDeclaration: function (specifiers, src) { - return { - type: Syntax.ImportDeclaration, - specifiers: specifiers, - source: src - }; - }, - - createYieldExpression: function (argument, dlg) { - return { - type: Syntax.YieldExpression, - argument: argument, - delegate: dlg - }; - }, - - createComprehensionExpression: function (filter, blocks, body) { - return { - type: Syntax.ComprehensionExpression, - filter: filter, - blocks: blocks, - body: body - }; - } - - }; - - // Return true if there is a line terminator before the next token. - - function peekLineTerminator() { - var pos, line, start, found; - - pos = index; - line = lineNumber; - start = lineStart; - skipComment(); - found = lineNumber !== line; - index = pos; - lineNumber = line; - lineStart = start; - - return found; - } - - // Throw an exception - - function throwError(token, messageFormat) { - var error, - args = Array.prototype.slice.call(arguments, 2), - msg = messageFormat.replace( - /%(\d)/g, - function (whole, idx) { - assert(idx < args.length, 'Message reference must be in range'); - return args[idx]; - } - ); - - if (typeof token.lineNumber === 'number') { - error = new Error('Line ' + token.lineNumber + ': ' + msg); - error.index = token.range[0]; - error.lineNumber = token.lineNumber; - error.column = token.range[0] - lineStart + 1; - } else { - error = new Error('Line ' + lineNumber + ': ' + msg); - error.index = index; - error.lineNumber = lineNumber; - error.column = index - lineStart + 1; - } - - error.description = msg; - throw error; - } - - function throwErrorTolerant() { - try { - throwError.apply(null, arguments); - } catch (e) { - if (extra.errors) { - extra.errors.push(e); - } else { - throw e; - } - } - } - - - // Throw an exception because of the token. - - function throwUnexpected(token) { - if (token.type === Token.EOF) { - throwError(token, Messages.UnexpectedEOS); - } - - if (token.type === Token.NumericLiteral) { - throwError(token, Messages.UnexpectedNumber); - } - - if (token.type === Token.StringLiteral) { - throwError(token, Messages.UnexpectedString); - } - - if (token.type === Token.Identifier) { - throwError(token, Messages.UnexpectedIdentifier); - } - - if (token.type === Token.Keyword) { - if (isFutureReservedWord(token.value)) { - throwError(token, Messages.UnexpectedReserved); - } else if (strict && isStrictModeReservedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictReservedWord); - return; - } - throwError(token, Messages.UnexpectedToken, token.value); - } - - if (token.type === Token.Template) { - throwError(token, Messages.UnexpectedTemplate, token.value.raw); - } - - // BooleanLiteral, NullLiteral, or Punctuator. - throwError(token, Messages.UnexpectedToken, token.value); - } - - // Expect the next token to match the specified punctuator. - // If not, an exception will be thrown. - - function expect(value) { - var token = lex(); - if (token.type !== Token.Punctuator || token.value !== value) { - throwUnexpected(token); - } - } - - // Expect the next token to match the specified keyword. - // If not, an exception will be thrown. - - function expectKeyword(keyword) { - var token = lex(); - if (token.type !== Token.Keyword || token.value !== keyword) { - throwUnexpected(token); - } - } - - // Return true if the next token matches the specified punctuator. - - function match(value) { - return lookahead.type === Token.Punctuator && lookahead.value === value; - } - - // Return true if the next token matches the specified keyword - - function matchKeyword(keyword) { - return lookahead.type === Token.Keyword && lookahead.value === keyword; - } - - - // Return true if the next token matches the specified contextual keyword - - function matchContextualKeyword(keyword) { - return lookahead.type === Token.Identifier && lookahead.value === keyword; - } - - // Return true if the next token is an assignment operator - - function matchAssign() { - var op; - - if (lookahead.type !== Token.Punctuator) { - return false; - } - op = lookahead.value; - return op === '=' || - op === '*=' || - op === '/=' || - op === '%=' || - op === '+=' || - op === '-=' || - op === '<<=' || - op === '>>=' || - op === '>>>=' || - op === '&=' || - op === '^=' || - op === '|='; - } - - function consumeSemicolon() { - var line, oldIndex = index, oldLineNumber = lineNumber, - oldLineStart = lineStart, oldLookahead = lookahead; - - // Catch the very common case first: immediately a semicolon (char #59). - if (source.charCodeAt(index) === 59) { - lex(); - return; - } - - line = lineNumber; - skipComment(); - if (lineNumber !== line) { - index = oldIndex; - lineNumber = oldLineNumber; - lineStart = oldLineStart; - lookahead = oldLookahead; - return; - } - - if (match(';')) { - lex(); - return; - } - - if (lookahead.type !== Token.EOF && !match('}')) { - throwUnexpected(lookahead); - } - } - - // Return true if provided expression is LeftHandSideExpression - - function isLeftHandSide(expr) { - return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression; - } - - function isAssignableLeftHandSide(expr) { - return isLeftHandSide(expr) || expr.type === Syntax.ObjectPattern || expr.type === Syntax.ArrayPattern; - } - - // 11.1.4 Array Initialiser - - function parseArrayInitialiser() { - var elements = [], blocks = [], filter = null, tmp, possiblecomprehension = true, - marker = markerCreate(); - - expect('['); - while (!match(']')) { - if (lookahead.value === 'for' && - lookahead.type === Token.Keyword) { - if (!possiblecomprehension) { - throwError({}, Messages.ComprehensionError); - } - matchKeyword('for'); - tmp = parseForStatement({ignoreBody: true}); - tmp.of = tmp.type === Syntax.ForOfStatement; - tmp.type = Syntax.ComprehensionBlock; - if (tmp.left.kind) { // can't be let or const - throwError({}, Messages.ComprehensionError); - } - blocks.push(tmp); - } else if (lookahead.value === 'if' && - lookahead.type === Token.Keyword) { - if (!possiblecomprehension) { - throwError({}, Messages.ComprehensionError); - } - expectKeyword('if'); - expect('('); - filter = parseExpression(); - expect(')'); - } else if (lookahead.value === ',' && - lookahead.type === Token.Punctuator) { - possiblecomprehension = false; // no longer allowed. - lex(); - elements.push(null); - } else { - tmp = parseSpreadOrAssignmentExpression(); - elements.push(tmp); - if (tmp && tmp.type === Syntax.SpreadElement) { - if (!match(']')) { - throwError({}, Messages.ElementAfterSpreadElement); - } - } else if (!(match(']') || matchKeyword('for') || matchKeyword('if'))) { - expect(','); // this lexes. - possiblecomprehension = false; - } - } - } - - expect(']'); - - if (filter && !blocks.length) { - throwError({}, Messages.ComprehensionRequiresBlock); - } - - if (blocks.length) { - if (elements.length !== 1) { - throwError({}, Messages.ComprehensionError); - } - return markerApply(marker, delegate.createComprehensionExpression(filter, blocks, elements[0])); - } - return markerApply(marker, delegate.createArrayExpression(elements)); - } - - // 11.1.5 Object Initialiser - - function parsePropertyFunction(options) { - var previousStrict, previousYieldAllowed, params, defaults, body, - marker = markerCreate(); - - previousStrict = strict; - previousYieldAllowed = state.yieldAllowed; - state.yieldAllowed = options.generator; - params = options.params || []; - defaults = options.defaults || []; - - body = parseConciseBody(); - if (options.name && strict && isRestrictedWord(params[0].name)) { - throwErrorTolerant(options.name, Messages.StrictParamName); - } - strict = previousStrict; - state.yieldAllowed = previousYieldAllowed; - - return markerApply(marker, delegate.createFunctionExpression( - null, - params, - defaults, - body, - options.rest || null, - options.generator, - body.type !== Syntax.BlockStatement - )); - } - - - function parsePropertyMethodFunction(options) { - var previousStrict, tmp, method; - - previousStrict = strict; - strict = true; - - tmp = parseParams(); - - if (tmp.stricted) { - throwErrorTolerant(tmp.stricted, tmp.message); - } - - - method = parsePropertyFunction({ - params: tmp.params, - defaults: tmp.defaults, - rest: tmp.rest, - generator: options.generator - }); - - strict = previousStrict; - - return method; - } - - - function parseObjectPropertyKey() { - var marker = markerCreate(), - token = lex(), - propertyKey, - result; - - // Note: This function is called only from parseObjectProperty(), where - // EOF and Punctuator tokens are already filtered out. - - if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) { - if (strict && token.octal) { - throwErrorTolerant(token, Messages.StrictOctalLiteral); - } - return markerApply(marker, delegate.createLiteral(token)); - } - - if (token.type === Token.Punctuator && token.value === '[') { - // For computed properties we should skip the [ and ], and - // capture in marker only the assignment expression itself. - marker = markerCreate(); - propertyKey = parseAssignmentExpression(); - result = markerApply(marker, propertyKey); - expect(']'); - return result; - } - - return markerApply(marker, delegate.createIdentifier(token.value)); - } - - function parseObjectProperty() { - var token, key, id, param, computed, - marker = markerCreate(); - - token = lookahead; - computed = (token.value === '[' && token.type === Token.Punctuator); - - if (token.type === Token.Identifier || computed) { - - id = parseObjectPropertyKey(); - - // Property Assignment: Getter and Setter. - - if (token.value === 'get' && !(match(':') || match('('))) { - computed = (lookahead.value === '['); - key = parseObjectPropertyKey(); - expect('('); - expect(')'); - return markerApply(marker, delegate.createProperty('get', key, parsePropertyFunction({ generator: false }), false, false, computed)); - } - if (token.value === 'set' && !(match(':') || match('('))) { - computed = (lookahead.value === '['); - key = parseObjectPropertyKey(); - expect('('); - token = lookahead; - param = [ parseVariableIdentifier() ]; - expect(')'); - return markerApply(marker, delegate.createProperty('set', key, parsePropertyFunction({ params: param, generator: false, name: token }), false, false, computed)); - } - if (match(':')) { - lex(); - return markerApply(marker, delegate.createProperty('init', id, parseAssignmentExpression(), false, false, computed)); - } - if (match('(')) { - return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: false }), true, false, computed)); - } - if (computed) { - // Computed properties can only be used with full notation. - throwUnexpected(lookahead); - } - return markerApply(marker, delegate.createProperty('init', id, id, false, true, false)); - } - if (token.type === Token.EOF || token.type === Token.Punctuator) { - if (!match('*')) { - throwUnexpected(token); - } - lex(); - - computed = (lookahead.type === Token.Punctuator && lookahead.value === '['); - - id = parseObjectPropertyKey(); - - if (!match('(')) { - throwUnexpected(lex()); - } - - return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: true }), true, false, computed)); - } - key = parseObjectPropertyKey(); - if (match(':')) { - lex(); - return markerApply(marker, delegate.createProperty('init', key, parseAssignmentExpression(), false, false, false)); - } - if (match('(')) { - return markerApply(marker, delegate.createProperty('init', key, parsePropertyMethodFunction({ generator: false }), true, false, false)); - } - throwUnexpected(lex()); - } - - function getFieldName(key) { - var toString = String; - if (key.type === Syntax.Identifier) { - return key.name; - } - return toString(key.value); - } - - function parseObjectInitialiser() { - var properties = [], property, name, kind, storedKind, map = new StringMap(), - marker = markerCreate(); - - expect('{'); - - while (!match('}')) { - property = parseObjectProperty(); - - if (!property.computed) { - name = getFieldName(property.key); - kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set; - - if (map.has(name)) { - storedKind = map.get(name); - if (storedKind === PropertyKind.Data) { - if (strict && kind === PropertyKind.Data) { - throwErrorTolerant({}, Messages.StrictDuplicateProperty); - } else if (kind !== PropertyKind.Data) { - throwErrorTolerant({}, Messages.AccessorDataProperty); - } - } else { - if (kind === PropertyKind.Data) { - throwErrorTolerant({}, Messages.AccessorDataProperty); - } else if (storedKind & kind) { - throwErrorTolerant({}, Messages.AccessorGetSet); - } - } - map.set(name, storedKind | kind); - } else { - map.set(name, kind); - } - } - - properties.push(property); - - if (!match('}')) { - expect(','); - } - } - - expect('}'); - - return markerApply(marker, delegate.createObjectExpression(properties)); - } - - function parseTemplateElement(option) { - var marker = markerCreate(), - token = scanTemplateElement(option); - if (strict && token.octal) { - throwError(token, Messages.StrictOctalLiteral); - } - return markerApply(marker, delegate.createTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail)); - } - - function parseTemplateLiteral() { - var quasi, quasis, expressions, marker = markerCreate(); - - quasi = parseTemplateElement({ head: true }); - quasis = [ quasi ]; - expressions = []; - - while (!quasi.tail) { - expressions.push(parseExpression()); - quasi = parseTemplateElement({ head: false }); - quasis.push(quasi); - } - - return markerApply(marker, delegate.createTemplateLiteral(quasis, expressions)); - } - - // 11.1.6 The Grouping Operator - - function parseGroupExpression() { - var expr; - - expect('('); - - ++state.parenthesizedCount; - - expr = parseExpression(); - - expect(')'); - - return expr; - } - - - // 11.1 Primary Expressions - - function parsePrimaryExpression() { - var marker, type, token, expr; - - type = lookahead.type; - - if (type === Token.Identifier) { - marker = markerCreate(); - return markerApply(marker, delegate.createIdentifier(lex().value)); - } - - if (type === Token.StringLiteral || type === Token.NumericLiteral) { - if (strict && lookahead.octal) { - throwErrorTolerant(lookahead, Messages.StrictOctalLiteral); - } - marker = markerCreate(); - return markerApply(marker, delegate.createLiteral(lex())); - } - - if (type === Token.Keyword) { - if (matchKeyword('this')) { - marker = markerCreate(); - lex(); - return markerApply(marker, delegate.createThisExpression()); - } - - if (matchKeyword('function')) { - return parseFunctionExpression(); - } - - if (matchKeyword('class')) { - return parseClassExpression(); - } - - if (matchKeyword('super')) { - marker = markerCreate(); - lex(); - return markerApply(marker, delegate.createIdentifier('super')); - } - } - - if (type === Token.BooleanLiteral) { - marker = markerCreate(); - token = lex(); - token.value = (token.value === 'true'); - return markerApply(marker, delegate.createLiteral(token)); - } - - if (type === Token.NullLiteral) { - marker = markerCreate(); - token = lex(); - token.value = null; - return markerApply(marker, delegate.createLiteral(token)); - } - - if (match('[')) { - return parseArrayInitialiser(); - } - - if (match('{')) { - return parseObjectInitialiser(); - } - - if (match('(')) { - return parseGroupExpression(); - } - - if (match('/') || match('/=')) { - marker = markerCreate(); - expr = delegate.createLiteral(scanRegExp()); - peek(); - return markerApply(marker, expr); - } - - if (type === Token.Template) { - return parseTemplateLiteral(); - } - - throwUnexpected(lex()); - } - - // 11.2 Left-Hand-Side Expressions - - function parseArguments() { - var args = [], arg; - - expect('('); - - if (!match(')')) { - while (index < length) { - arg = parseSpreadOrAssignmentExpression(); - args.push(arg); - - if (match(')')) { - break; - } else if (arg.type === Syntax.SpreadElement) { - throwError({}, Messages.ElementAfterSpreadElement); - } - - expect(','); - } - } - - expect(')'); - - return args; - } - - function parseSpreadOrAssignmentExpression() { - if (match('...')) { - var marker = markerCreate(); - lex(); - return markerApply(marker, delegate.createSpreadElement(parseAssignmentExpression())); - } - return parseAssignmentExpression(); - } - - function parseNonComputedProperty() { - var marker = markerCreate(), - token = lex(); - - if (!isIdentifierName(token)) { - throwUnexpected(token); - } - - return markerApply(marker, delegate.createIdentifier(token.value)); - } - - function parseNonComputedMember() { - expect('.'); - - return parseNonComputedProperty(); - } - - function parseComputedMember() { - var expr; - - expect('['); - - expr = parseExpression(); - - expect(']'); - - return expr; - } - - function parseNewExpression() { - var callee, args, marker = markerCreate(); - - expectKeyword('new'); - callee = parseLeftHandSideExpression(); - args = match('(') ? parseArguments() : []; - - return markerApply(marker, delegate.createNewExpression(callee, args)); - } - - function parseLeftHandSideExpressionAllowCall() { - var expr, args, marker = markerCreate(); - - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - - while (match('.') || match('[') || match('(') || lookahead.type === Token.Template) { - if (match('(')) { - args = parseArguments(); - expr = markerApply(marker, delegate.createCallExpression(expr, args)); - } else if (match('[')) { - expr = markerApply(marker, delegate.createMemberExpression('[', expr, parseComputedMember())); - } else if (match('.')) { - expr = markerApply(marker, delegate.createMemberExpression('.', expr, parseNonComputedMember())); - } else { - expr = markerApply(marker, delegate.createTaggedTemplateExpression(expr, parseTemplateLiteral())); - } - } - - return expr; - } - - function parseLeftHandSideExpression() { - var expr, marker = markerCreate(); - - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - - while (match('.') || match('[') || lookahead.type === Token.Template) { - if (match('[')) { - expr = markerApply(marker, delegate.createMemberExpression('[', expr, parseComputedMember())); - } else if (match('.')) { - expr = markerApply(marker, delegate.createMemberExpression('.', expr, parseNonComputedMember())); - } else { - expr = markerApply(marker, delegate.createTaggedTemplateExpression(expr, parseTemplateLiteral())); - } - } - - return expr; - } - - // 11.3 Postfix Expressions - - function parsePostfixExpression() { - var marker = markerCreate(), - expr = parseLeftHandSideExpressionAllowCall(), - token; - - if (lookahead.type !== Token.Punctuator) { - return expr; - } - - if ((match('++') || match('--')) && !peekLineTerminator()) { - // 11.3.1, 11.3.2 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant({}, Messages.StrictLHSPostfix); - } - - if (!isLeftHandSide(expr)) { - throwError({}, Messages.InvalidLHSInAssignment); - } - - token = lex(); - expr = markerApply(marker, delegate.createPostfixExpression(token.value, expr)); - } - - return expr; - } - - // 11.4 Unary Operators - - function parseUnaryExpression() { - var marker, token, expr; - - if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { - return parsePostfixExpression(); - } - - if (match('++') || match('--')) { - marker = markerCreate(); - token = lex(); - expr = parseUnaryExpression(); - // 11.4.4, 11.4.5 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant({}, Messages.StrictLHSPrefix); - } - - if (!isLeftHandSide(expr)) { - throwError({}, Messages.InvalidLHSInAssignment); - } - - return markerApply(marker, delegate.createUnaryExpression(token.value, expr)); - } - - if (match('+') || match('-') || match('~') || match('!')) { - marker = markerCreate(); - token = lex(); - expr = parseUnaryExpression(); - return markerApply(marker, delegate.createUnaryExpression(token.value, expr)); - } - - if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { - marker = markerCreate(); - token = lex(); - expr = parseUnaryExpression(); - expr = markerApply(marker, delegate.createUnaryExpression(token.value, expr)); - if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { - throwErrorTolerant({}, Messages.StrictDelete); - } - return expr; - } - - return parsePostfixExpression(); - } - - function binaryPrecedence(token, allowIn) { - var prec = 0; - - if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { - return 0; - } - - switch (token.value) { - case '||': - prec = 1; - break; - - case '&&': - prec = 2; - break; - - case '|': - prec = 3; - break; - - case '^': - prec = 4; - break; - - case '&': - prec = 5; - break; - - case '==': - case '!=': - case '===': - case '!==': - prec = 6; - break; - - case '<': - case '>': - case '<=': - case '>=': - case 'instanceof': - prec = 7; - break; - - case 'in': - prec = allowIn ? 7 : 0; - break; - - case '<<': - case '>>': - case '>>>': - prec = 8; - break; - - case '+': - case '-': - prec = 9; - break; - - case '*': - case '/': - case '%': - prec = 11; - break; - - default: - break; - } - - return prec; - } - - // 11.5 Multiplicative Operators - // 11.6 Additive Operators - // 11.7 Bitwise Shift Operators - // 11.8 Relational Operators - // 11.9 Equality Operators - // 11.10 Binary Bitwise Operators - // 11.11 Binary Logical Operators - - function parseBinaryExpression() { - var expr, token, prec, previousAllowIn, stack, right, operator, left, i, - marker, markers; - - previousAllowIn = state.allowIn; - state.allowIn = true; - - marker = markerCreate(); - left = parseUnaryExpression(); - - token = lookahead; - prec = binaryPrecedence(token, previousAllowIn); - if (prec === 0) { - return left; - } - token.prec = prec; - lex(); - - markers = [marker, markerCreate()]; - right = parseUnaryExpression(); - - stack = [left, token, right]; - - while ((prec = binaryPrecedence(lookahead, previousAllowIn)) > 0) { - - // Reduce: make a binary expression from the three topmost entries. - while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { - right = stack.pop(); - operator = stack.pop().value; - left = stack.pop(); - expr = delegate.createBinaryExpression(operator, left, right); - markers.pop(); - marker = markers.pop(); - markerApply(marker, expr); - stack.push(expr); - markers.push(marker); - } - - // Shift. - token = lex(); - token.prec = prec; - stack.push(token); - markers.push(markerCreate()); - expr = parseUnaryExpression(); - stack.push(expr); - } - - state.allowIn = previousAllowIn; - - // Final reduce to clean-up the stack. - i = stack.length - 1; - expr = stack[i]; - markers.pop(); - while (i > 1) { - expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr); - i -= 2; - marker = markers.pop(); - markerApply(marker, expr); - } - - return expr; - } - - - // 11.12 Conditional Operator - - function parseConditionalExpression() { - var expr, previousAllowIn, consequent, alternate, marker = markerCreate(); - expr = parseBinaryExpression(); - - if (match('?')) { - lex(); - previousAllowIn = state.allowIn; - state.allowIn = true; - consequent = parseAssignmentExpression(); - state.allowIn = previousAllowIn; - expect(':'); - alternate = parseAssignmentExpression(); - - expr = markerApply(marker, delegate.createConditionalExpression(expr, consequent, alternate)); - } - - return expr; - } - - // 11.13 Assignment Operators - - // 12.14.5 AssignmentPattern - - function reinterpretAsAssignmentBindingPattern(expr) { - var i, len, property, element; - - if (expr.type === Syntax.ObjectExpression) { - expr.type = Syntax.ObjectPattern; - for (i = 0, len = expr.properties.length; i < len; i += 1) { - property = expr.properties[i]; - if (property.kind !== 'init') { - throwError({}, Messages.InvalidLHSInAssignment); - } - reinterpretAsAssignmentBindingPattern(property.value); - } - } else if (expr.type === Syntax.ArrayExpression) { - expr.type = Syntax.ArrayPattern; - for (i = 0, len = expr.elements.length; i < len; i += 1) { - element = expr.elements[i]; - /* istanbul ignore else */ - if (element) { - reinterpretAsAssignmentBindingPattern(element); - } - } - } else if (expr.type === Syntax.Identifier) { - if (isRestrictedWord(expr.name)) { - throwError({}, Messages.InvalidLHSInAssignment); - } - } else if (expr.type === Syntax.SpreadElement) { - reinterpretAsAssignmentBindingPattern(expr.argument); - if (expr.argument.type === Syntax.ObjectPattern) { - throwError({}, Messages.ObjectPatternAsSpread); - } - } else { - /* istanbul ignore else */ - if (expr.type !== Syntax.MemberExpression && expr.type !== Syntax.CallExpression && expr.type !== Syntax.NewExpression) { - throwError({}, Messages.InvalidLHSInAssignment); - } - } - } - - // 13.2.3 BindingPattern - - function reinterpretAsDestructuredParameter(options, expr) { - var i, len, property, element; - - if (expr.type === Syntax.ObjectExpression) { - expr.type = Syntax.ObjectPattern; - for (i = 0, len = expr.properties.length; i < len; i += 1) { - property = expr.properties[i]; - if (property.kind !== 'init') { - throwError({}, Messages.InvalidLHSInFormalsList); - } - reinterpretAsDestructuredParameter(options, property.value); - } - } else if (expr.type === Syntax.ArrayExpression) { - expr.type = Syntax.ArrayPattern; - for (i = 0, len = expr.elements.length; i < len; i += 1) { - element = expr.elements[i]; - if (element) { - reinterpretAsDestructuredParameter(options, element); - } - } - } else if (expr.type === Syntax.Identifier) { - validateParam(options, expr, expr.name); - } else if (expr.type === Syntax.SpreadElement) { - // BindingRestElement only allows BindingIdentifier - if (expr.argument.type !== Syntax.Identifier) { - throwError({}, Messages.InvalidLHSInFormalsList); - } - validateParam(options, expr.argument, expr.argument.name); - } else { - throwError({}, Messages.InvalidLHSInFormalsList); - } - } - - function reinterpretAsCoverFormalsList(expressions) { - var i, len, param, params, defaults, defaultCount, options, rest; - - params = []; - defaults = []; - defaultCount = 0; - rest = null; - options = { - paramSet: new StringMap() - }; - - for (i = 0, len = expressions.length; i < len; i += 1) { - param = expressions[i]; - if (param.type === Syntax.Identifier) { - params.push(param); - defaults.push(null); - validateParam(options, param, param.name); - } else if (param.type === Syntax.ObjectExpression || param.type === Syntax.ArrayExpression) { - reinterpretAsDestructuredParameter(options, param); - params.push(param); - defaults.push(null); - } else if (param.type === Syntax.SpreadElement) { - assert(i === len - 1, 'It is guaranteed that SpreadElement is last element by parseExpression'); - if (param.argument.type !== Syntax.Identifier) { - throwError({}, Messages.InvalidLHSInFormalsList); - } - reinterpretAsDestructuredParameter(options, param.argument); - rest = param.argument; - } else if (param.type === Syntax.AssignmentExpression) { - params.push(param.left); - defaults.push(param.right); - ++defaultCount; - validateParam(options, param.left, param.left.name); - } else { - return null; - } - } - - if (options.message === Messages.StrictParamDupe) { - throwError( - strict ? options.stricted : options.firstRestricted, - options.message - ); - } - - if (defaultCount === 0) { - defaults = []; - } - - return { - params: params, - defaults: defaults, - rest: rest, - stricted: options.stricted, - firstRestricted: options.firstRestricted, - message: options.message - }; - } - - function parseArrowFunctionExpression(options, marker) { - var previousStrict, previousYieldAllowed, body; - - expect('=>'); - - previousStrict = strict; - previousYieldAllowed = state.yieldAllowed; - state.yieldAllowed = false; - body = parseConciseBody(); - - if (strict && options.firstRestricted) { - throwError(options.firstRestricted, options.message); - } - if (strict && options.stricted) { - throwErrorTolerant(options.stricted, options.message); - } - - strict = previousStrict; - state.yieldAllowed = previousYieldAllowed; - - return markerApply(marker, delegate.createArrowFunctionExpression( - options.params, - options.defaults, - body, - options.rest, - body.type !== Syntax.BlockStatement - )); - } - - function parseAssignmentExpression() { - var marker, expr, token, params, oldParenthesizedCount, - startsWithParen = false; - - // Note that 'yield' is treated as a keyword in strict mode, but a - // contextual keyword (identifier) in non-strict mode, so we need - // to use matchKeyword and matchContextualKeyword appropriately. - if ((state.yieldAllowed && matchContextualKeyword('yield')) || (strict && matchKeyword('yield'))) { - return parseYieldExpression(); - } - - oldParenthesizedCount = state.parenthesizedCount; - - marker = markerCreate(); - - if (match('(')) { - token = lookahead2(); - if ((token.type === Token.Punctuator && token.value === ')') || token.value === '...') { - params = parseParams(); - if (!match('=>')) { - throwUnexpected(lex()); - } - return parseArrowFunctionExpression(params, marker); - } - startsWithParen = true; - } - - token = lookahead; - expr = parseConditionalExpression(); - - if (match('=>') && - (state.parenthesizedCount === oldParenthesizedCount || - state.parenthesizedCount === (oldParenthesizedCount + 1))) { - if (expr.type === Syntax.Identifier) { - params = reinterpretAsCoverFormalsList([ expr ]); - } else if (expr.type === Syntax.AssignmentExpression || - expr.type === Syntax.ArrayExpression || - expr.type === Syntax.ObjectExpression) { - if (!startsWithParen) { - throwUnexpected(lex()); - } - params = reinterpretAsCoverFormalsList([ expr ]); - } else if (expr.type === Syntax.SequenceExpression) { - params = reinterpretAsCoverFormalsList(expr.expressions); - } - if (params) { - return parseArrowFunctionExpression(params, marker); - } - } - - if (matchAssign()) { - // 11.13.1 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant(token, Messages.StrictLHSAssignment); - } - - // ES.next draf 11.13 Runtime Semantics step 1 - if (match('=') && (expr.type === Syntax.ObjectExpression || expr.type === Syntax.ArrayExpression)) { - reinterpretAsAssignmentBindingPattern(expr); - } else if (!isLeftHandSide(expr)) { - throwError({}, Messages.InvalidLHSInAssignment); - } - - expr = markerApply(marker, delegate.createAssignmentExpression(lex().value, expr, parseAssignmentExpression())); - } - - return expr; - } - - // 11.14 Comma Operator - - function parseExpression() { - var marker, expr, expressions, sequence, spreadFound; - - marker = markerCreate(); - expr = parseAssignmentExpression(); - expressions = [ expr ]; - - if (match(',')) { - while (index < length) { - if (!match(',')) { - break; - } - - lex(); - expr = parseSpreadOrAssignmentExpression(); - expressions.push(expr); - - if (expr.type === Syntax.SpreadElement) { - spreadFound = true; - if (!match(')')) { - throwError({}, Messages.ElementAfterSpreadElement); - } - break; - } - } - - sequence = markerApply(marker, delegate.createSequenceExpression(expressions)); - } - - if (spreadFound && lookahead2().value !== '=>') { - throwError({}, Messages.IllegalSpread); - } - - return sequence || expr; - } - - // 12.1 Block - - function parseStatementList() { - var list = [], - statement; - - while (index < length) { - if (match('}')) { - break; - } - statement = parseSourceElement(); - if (typeof statement === 'undefined') { - break; - } - list.push(statement); - } - - return list; - } - - function parseBlock() { - var block, marker = markerCreate(); - - expect('{'); - - block = parseStatementList(); - - expect('}'); - - return markerApply(marker, delegate.createBlockStatement(block)); - } - - // 12.2 Variable Statement - - function parseVariableIdentifier() { - var marker = markerCreate(), - token = lex(); - - if (token.type !== Token.Identifier) { - throwUnexpected(token); - } - - return markerApply(marker, delegate.createIdentifier(token.value)); - } - - function parseVariableDeclaration(kind) { - var id, - marker = markerCreate(), - init = null; - if (match('{')) { - id = parseObjectInitialiser(); - reinterpretAsAssignmentBindingPattern(id); - } else if (match('[')) { - id = parseArrayInitialiser(); - reinterpretAsAssignmentBindingPattern(id); - } else { - /* istanbul ignore next */ - id = state.allowKeyword ? parseNonComputedProperty() : parseVariableIdentifier(); - // 12.2.1 - if (strict && isRestrictedWord(id.name)) { - throwErrorTolerant({}, Messages.StrictVarName); - } - } - - if (kind === 'const') { - if (!match('=')) { - throwError({}, Messages.NoUninitializedConst); - } - expect('='); - init = parseAssignmentExpression(); - } else if (match('=')) { - lex(); - init = parseAssignmentExpression(); - } - - return markerApply(marker, delegate.createVariableDeclarator(id, init)); - } - - function parseVariableDeclarationList(kind) { - var list = []; - - do { - list.push(parseVariableDeclaration(kind)); - if (!match(',')) { - break; - } - lex(); - } while (index < length); - - return list; - } - - function parseVariableStatement() { - var declarations, marker = markerCreate(); - - expectKeyword('var'); - - declarations = parseVariableDeclarationList(); - - consumeSemicolon(); - - return markerApply(marker, delegate.createVariableDeclaration(declarations, 'var')); - } - - // kind may be `const` or `let` - // Both are experimental and not in the specification yet. - // see http://wiki.ecmascript.org/doku.php?id=harmony:const - // and http://wiki.ecmascript.org/doku.php?id=harmony:let - function parseConstLetDeclaration(kind) { - var declarations, marker = markerCreate(); - - expectKeyword(kind); - - declarations = parseVariableDeclarationList(kind); - - consumeSemicolon(); - - return markerApply(marker, delegate.createVariableDeclaration(declarations, kind)); - } - - // people.mozilla.org/~jorendorff/es6-draft.html - - function parseModuleSpecifier() { - var marker = markerCreate(), - specifier; - - if (lookahead.type !== Token.StringLiteral) { - throwError({}, Messages.InvalidModuleSpecifier); - } - specifier = delegate.createLiteral(lex()); - return markerApply(marker, specifier); - } - - function parseExportBatchSpecifier() { - var marker = markerCreate(); - expect('*'); - return markerApply(marker, delegate.createExportBatchSpecifier()); - } - - function parseExportSpecifier() { - var id, name = null, marker = markerCreate(); - if (matchKeyword('default')) { - lex(); - id = markerApply(marker, delegate.createIdentifier('default')); - // export {default} from "something"; - } else { - id = parseVariableIdentifier(); - } - if (matchContextualKeyword('as')) { - lex(); - name = parseNonComputedProperty(); - } - - return markerApply(marker, delegate.createExportSpecifier(id, name)); - } - - function parseExportDeclaration() { - var declaration = null, - possibleIdentifierToken, sourceElement, - isExportFromIdentifier, - src = null, specifiers = [], - marker = markerCreate(); - - expectKeyword('export'); - - if (matchKeyword('default')) { - // covers: - // export default ... - lex(); - if (matchKeyword('function') || matchKeyword('class')) { - possibleIdentifierToken = lookahead2(); - if (isIdentifierName(possibleIdentifierToken)) { - // covers: - // export default function foo () {} - // export default class foo {} - sourceElement = parseSourceElement(); - return markerApply(marker, delegate.createExportDeclaration(true, sourceElement, [sourceElement.id], null)); - } - // covers: - // export default function () {} - // export default class {} - switch (lookahead.value) { - case 'class': - return markerApply(marker, delegate.createExportDeclaration(true, parseClassExpression(), [], null)); - case 'function': - return markerApply(marker, delegate.createExportDeclaration(true, parseFunctionExpression(), [], null)); - } - } - - if (matchContextualKeyword('from')) { - throwError({}, Messages.UnexpectedToken, lookahead.value); - } - - // covers: - // export default {}; - // export default []; - if (match('{')) { - declaration = parseObjectInitialiser(); - } else if (match('[')) { - declaration = parseArrayInitialiser(); - } else { - declaration = parseAssignmentExpression(); - } - consumeSemicolon(); - return markerApply(marker, delegate.createExportDeclaration(true, declaration, [], null)); - } - - // non-default export - if (lookahead.type === Token.Keyword) { - // covers: - // export var f = 1; - switch (lookahead.value) { - case 'let': - case 'const': - case 'var': - case 'class': - case 'function': - return markerApply(marker, delegate.createExportDeclaration(false, parseSourceElement(), specifiers, null)); - } - } - - if (match('*')) { - // covers: - // export * from "foo"; - specifiers.push(parseExportBatchSpecifier()); - - if (!matchContextualKeyword('from')) { - throwError({}, lookahead.value ? - Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); - } - lex(); - src = parseModuleSpecifier(); - consumeSemicolon(); - - return markerApply(marker, delegate.createExportDeclaration(false, null, specifiers, src)); - } - - expect('{'); - if (!match('}')) { - do { - isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default'); - specifiers.push(parseExportSpecifier()); - } while (match(',') && lex()); - } - expect('}'); - - if (matchContextualKeyword('from')) { - // covering: - // export {default} from "foo"; - // export {foo} from "foo"; - lex(); - src = parseModuleSpecifier(); - consumeSemicolon(); - } else if (isExportFromIdentifier) { - // covering: - // export {default}; // missing fromClause - throwError({}, lookahead.value ? - Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); - } else { - // cover - // export {foo}; - consumeSemicolon(); - } - return markerApply(marker, delegate.createExportDeclaration(false, declaration, specifiers, src)); - } - - - function parseImportSpecifier() { - // import {} ...; - var id, name = null, marker = markerCreate(); - - id = parseNonComputedProperty(); - if (matchContextualKeyword('as')) { - lex(); - name = parseVariableIdentifier(); - } - - return markerApply(marker, delegate.createImportSpecifier(id, name)); - } - - function parseNamedImports() { - var specifiers = []; - // {foo, bar as bas} - expect('{'); - if (!match('}')) { - do { - specifiers.push(parseImportSpecifier()); - } while (match(',') && lex()); - } - expect('}'); - return specifiers; - } - - function parseImportDefaultSpecifier() { - // import ...; - var id, marker = markerCreate(); - - id = parseNonComputedProperty(); - - return markerApply(marker, delegate.createImportDefaultSpecifier(id)); - } - - function parseImportNamespaceSpecifier() { - // import <* as foo> ...; - var id, marker = markerCreate(); - - expect('*'); - if (!matchContextualKeyword('as')) { - throwError({}, Messages.NoAsAfterImportNamespace); - } - lex(); - id = parseNonComputedProperty(); - - return markerApply(marker, delegate.createImportNamespaceSpecifier(id)); - } - - function parseImportDeclaration() { - var specifiers, src, marker = markerCreate(); - - expectKeyword('import'); - specifiers = []; - - if (lookahead.type === Token.StringLiteral) { - // covers: - // import "foo"; - src = parseModuleSpecifier(); - consumeSemicolon(); - return markerApply(marker, delegate.createImportDeclaration(specifiers, src)); - } - - if (!matchKeyword('default') && isIdentifierName(lookahead)) { - // covers: - // import foo - // import foo, ... - specifiers.push(parseImportDefaultSpecifier()); - if (match(',')) { - lex(); - } - } - if (match('*')) { - // covers: - // import foo, * as foo - // import * as foo - specifiers.push(parseImportNamespaceSpecifier()); - } else if (match('{')) { - // covers: - // import foo, {bar} - // import {bar} - specifiers = specifiers.concat(parseNamedImports()); - } - - if (!matchContextualKeyword('from')) { - throwError({}, lookahead.value ? - Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); - } - lex(); - src = parseModuleSpecifier(); - consumeSemicolon(); - - return markerApply(marker, delegate.createImportDeclaration(specifiers, src)); - } - - // 12.3 Empty Statement - - function parseEmptyStatement() { - var marker = markerCreate(); - expect(';'); - return markerApply(marker, delegate.createEmptyStatement()); - } - - // 12.4 Expression Statement - - function parseExpressionStatement() { - var marker = markerCreate(), expr = parseExpression(); - consumeSemicolon(); - return markerApply(marker, delegate.createExpressionStatement(expr)); - } - - // 12.5 If statement - - function parseIfStatement() { - var test, consequent, alternate, marker = markerCreate(); - - expectKeyword('if'); - - expect('('); - - test = parseExpression(); - - expect(')'); - - consequent = parseStatement(); - - if (matchKeyword('else')) { - lex(); - alternate = parseStatement(); - } else { - alternate = null; - } - - return markerApply(marker, delegate.createIfStatement(test, consequent, alternate)); - } - - // 12.6 Iteration Statements - - function parseDoWhileStatement() { - var body, test, oldInIteration, marker = markerCreate(); - - expectKeyword('do'); - - oldInIteration = state.inIteration; - state.inIteration = true; - - body = parseStatement(); - - state.inIteration = oldInIteration; - - expectKeyword('while'); - - expect('('); - - test = parseExpression(); - - expect(')'); - - if (match(';')) { - lex(); - } - - return markerApply(marker, delegate.createDoWhileStatement(body, test)); - } - - function parseWhileStatement() { - var test, body, oldInIteration, marker = markerCreate(); - - expectKeyword('while'); - - expect('('); - - test = parseExpression(); - - expect(')'); - - oldInIteration = state.inIteration; - state.inIteration = true; - - body = parseStatement(); - - state.inIteration = oldInIteration; - - return markerApply(marker, delegate.createWhileStatement(test, body)); - } - - function parseForVariableDeclaration() { - var marker = markerCreate(), - token = lex(), - declarations = parseVariableDeclarationList(); - - return markerApply(marker, delegate.createVariableDeclaration(declarations, token.value)); - } - - function parseForStatement(opts) { - var init, test, update, left, right, body, operator, oldInIteration, - marker = markerCreate(); - init = test = update = null; - expectKeyword('for'); - - // http://wiki.ecmascript.org/doku.php?id=proposals:iterators_and_generators&s=each - if (matchContextualKeyword('each')) { - throwError({}, Messages.EachNotAllowed); - } - - expect('('); - - if (match(';')) { - lex(); - } else { - if (matchKeyword('var') || matchKeyword('let') || matchKeyword('const')) { - state.allowIn = false; - init = parseForVariableDeclaration(); - state.allowIn = true; - - if (init.declarations.length === 1) { - if (matchKeyword('in') || matchContextualKeyword('of')) { - operator = lookahead; - if (!((operator.value === 'in' || init.kind !== 'var') && init.declarations[0].init)) { - lex(); - left = init; - right = parseExpression(); - init = null; - } - } - } - } else { - state.allowIn = false; - init = parseExpression(); - state.allowIn = true; - - if (matchContextualKeyword('of')) { - operator = lex(); - left = init; - right = parseExpression(); - init = null; - } else if (matchKeyword('in')) { - // LeftHandSideExpression - if (!isAssignableLeftHandSide(init)) { - throwError({}, Messages.InvalidLHSInForIn); - } - operator = lex(); - left = init; - right = parseExpression(); - init = null; - } - } - - if (typeof left === 'undefined') { - expect(';'); - } - } - - if (typeof left === 'undefined') { - - if (!match(';')) { - test = parseExpression(); - } - expect(';'); - - if (!match(')')) { - update = parseExpression(); - } - } - - expect(')'); - - oldInIteration = state.inIteration; - state.inIteration = true; - - if (!(opts !== undefined && opts.ignoreBody)) { - body = parseStatement(); - } - - state.inIteration = oldInIteration; - - if (typeof left === 'undefined') { - return markerApply(marker, delegate.createForStatement(init, test, update, body)); - } - - if (operator.value === 'in') { - return markerApply(marker, delegate.createForInStatement(left, right, body)); - } - return markerApply(marker, delegate.createForOfStatement(left, right, body)); - } - - // 12.7 The continue statement - - function parseContinueStatement() { - var label = null, marker = markerCreate(); - - expectKeyword('continue'); - - // Optimize the most common form: 'continue;'. - if (source.charCodeAt(index) === 59) { - lex(); - - if (!state.inIteration) { - throwError({}, Messages.IllegalContinue); - } - - return markerApply(marker, delegate.createContinueStatement(null)); - } - - if (peekLineTerminator()) { - if (!state.inIteration) { - throwError({}, Messages.IllegalContinue); - } - - return markerApply(marker, delegate.createContinueStatement(null)); - } - - if (lookahead.type === Token.Identifier) { - label = parseVariableIdentifier(); - - if (!state.labelSet.has(label.name)) { - throwError({}, Messages.UnknownLabel, label.name); - } - } - - consumeSemicolon(); - - if (label === null && !state.inIteration) { - throwError({}, Messages.IllegalContinue); - } - - return markerApply(marker, delegate.createContinueStatement(label)); - } - - // 12.8 The break statement - - function parseBreakStatement() { - var label = null, marker = markerCreate(); - - expectKeyword('break'); - - // Catch the very common case first: immediately a semicolon (char #59). - if (source.charCodeAt(index) === 59) { - lex(); - - if (!(state.inIteration || state.inSwitch)) { - throwError({}, Messages.IllegalBreak); - } - - return markerApply(marker, delegate.createBreakStatement(null)); - } - - if (peekLineTerminator()) { - if (!(state.inIteration || state.inSwitch)) { - throwError({}, Messages.IllegalBreak); - } - - return markerApply(marker, delegate.createBreakStatement(null)); - } - - if (lookahead.type === Token.Identifier) { - label = parseVariableIdentifier(); - - if (!state.labelSet.has(label.name)) { - throwError({}, Messages.UnknownLabel, label.name); - } - } - - consumeSemicolon(); - - if (label === null && !(state.inIteration || state.inSwitch)) { - throwError({}, Messages.IllegalBreak); - } - - return markerApply(marker, delegate.createBreakStatement(label)); - } - - // 12.9 The return statement - - function parseReturnStatement() { - var argument = null, marker = markerCreate(); - - expectKeyword('return'); - - if (!state.inFunctionBody) { - throwErrorTolerant({}, Messages.IllegalReturn); - } - - // 'return' followed by a space and an identifier is very common. - if (source.charCodeAt(index) === 32) { - if (isIdentifierStart(source.charCodeAt(index + 1))) { - argument = parseExpression(); - consumeSemicolon(); - return markerApply(marker, delegate.createReturnStatement(argument)); - } - } - - if (peekLineTerminator()) { - return markerApply(marker, delegate.createReturnStatement(null)); - } - - if (!match(';')) { - if (!match('}') && lookahead.type !== Token.EOF) { - argument = parseExpression(); - } - } - - consumeSemicolon(); - - return markerApply(marker, delegate.createReturnStatement(argument)); - } - - // 12.10 The with statement - - function parseWithStatement() { - var object, body, marker = markerCreate(); - - if (strict) { - throwErrorTolerant({}, Messages.StrictModeWith); - } - - expectKeyword('with'); - - expect('('); - - object = parseExpression(); - - expect(')'); - - body = parseStatement(); - - return markerApply(marker, delegate.createWithStatement(object, body)); - } - - // 12.10 The swith statement - - function parseSwitchCase() { - var test, - consequent = [], - sourceElement, - marker = markerCreate(); - - if (matchKeyword('default')) { - lex(); - test = null; - } else { - expectKeyword('case'); - test = parseExpression(); - } - expect(':'); - - while (index < length) { - if (match('}') || matchKeyword('default') || matchKeyword('case')) { - break; - } - sourceElement = parseSourceElement(); - if (typeof sourceElement === 'undefined') { - break; - } - consequent.push(sourceElement); - } - - return markerApply(marker, delegate.createSwitchCase(test, consequent)); - } - - function parseSwitchStatement() { - var discriminant, cases, clause, oldInSwitch, defaultFound, marker = markerCreate(); - - expectKeyword('switch'); - - expect('('); - - discriminant = parseExpression(); - - expect(')'); - - expect('{'); - - cases = []; - - if (match('}')) { - lex(); - return markerApply(marker, delegate.createSwitchStatement(discriminant, cases)); - } - - oldInSwitch = state.inSwitch; - state.inSwitch = true; - defaultFound = false; - - while (index < length) { - if (match('}')) { - break; - } - clause = parseSwitchCase(); - if (clause.test === null) { - if (defaultFound) { - throwError({}, Messages.MultipleDefaultsInSwitch); - } - defaultFound = true; - } - cases.push(clause); - } - - state.inSwitch = oldInSwitch; - - expect('}'); - - return markerApply(marker, delegate.createSwitchStatement(discriminant, cases)); - } - - // 12.13 The throw statement - - function parseThrowStatement() { - var argument, marker = markerCreate(); - - expectKeyword('throw'); - - if (peekLineTerminator()) { - throwError({}, Messages.NewlineAfterThrow); - } - - argument = parseExpression(); - - consumeSemicolon(); - - return markerApply(marker, delegate.createThrowStatement(argument)); - } - - // 12.14 The try statement - - function parseCatchClause() { - var param, body, marker = markerCreate(); - - expectKeyword('catch'); - - expect('('); - if (match(')')) { - throwUnexpected(lookahead); - } - - param = parseExpression(); - // 12.14.1 - if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) { - throwErrorTolerant({}, Messages.StrictCatchVariable); - } - - expect(')'); - body = parseBlock(); - return markerApply(marker, delegate.createCatchClause(param, body)); - } - - function parseTryStatement() { - var block, handlers = [], finalizer = null, marker = markerCreate(); - - expectKeyword('try'); - - block = parseBlock(); - - if (matchKeyword('catch')) { - handlers.push(parseCatchClause()); - } - - if (matchKeyword('finally')) { - lex(); - finalizer = parseBlock(); - } - - if (handlers.length === 0 && !finalizer) { - throwError({}, Messages.NoCatchOrFinally); - } - - return markerApply(marker, delegate.createTryStatement(block, [], handlers, finalizer)); - } - - // 12.15 The debugger statement - - function parseDebuggerStatement() { - var marker = markerCreate(); - expectKeyword('debugger'); - - consumeSemicolon(); - - return markerApply(marker, delegate.createDebuggerStatement()); - } - - // 12 Statements - - function parseStatement() { - var type = lookahead.type, - marker, - expr, - labeledBody; - - if (type === Token.EOF) { - throwUnexpected(lookahead); - } - - if (type === Token.Punctuator) { - switch (lookahead.value) { - case ';': - return parseEmptyStatement(); - case '{': - return parseBlock(); - case '(': - return parseExpressionStatement(); - default: - break; - } - } - - if (type === Token.Keyword) { - switch (lookahead.value) { - case 'break': - return parseBreakStatement(); - case 'continue': - return parseContinueStatement(); - case 'debugger': - return parseDebuggerStatement(); - case 'do': - return parseDoWhileStatement(); - case 'for': - return parseForStatement(); - case 'function': - return parseFunctionDeclaration(); - case 'class': - return parseClassDeclaration(); - case 'if': - return parseIfStatement(); - case 'return': - return parseReturnStatement(); - case 'switch': - return parseSwitchStatement(); - case 'throw': - return parseThrowStatement(); - case 'try': - return parseTryStatement(); - case 'var': - return parseVariableStatement(); - case 'while': - return parseWhileStatement(); - case 'with': - return parseWithStatement(); - default: - break; - } - } - - marker = markerCreate(); - expr = parseExpression(); - - // 12.12 Labelled Statements - if ((expr.type === Syntax.Identifier) && match(':')) { - lex(); - - if (state.labelSet.has(expr.name)) { - throwError({}, Messages.Redeclaration, 'Label', expr.name); - } - - state.labelSet.set(expr.name, true); - labeledBody = parseStatement(); - state.labelSet.delete(expr.name); - return markerApply(marker, delegate.createLabeledStatement(expr, labeledBody)); - } - - consumeSemicolon(); - - return markerApply(marker, delegate.createExpressionStatement(expr)); - } - - // 13 Function Definition - - function parseConciseBody() { - if (match('{')) { - return parseFunctionSourceElements(); - } - return parseAssignmentExpression(); - } - - function parseFunctionSourceElements() { - var sourceElement, sourceElements = [], token, directive, firstRestricted, - oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesizedCount, - marker = markerCreate(); - - expect('{'); - - while (index < length) { - if (lookahead.type !== Token.StringLiteral) { - break; - } - token = lookahead; - - sourceElement = parseSourceElement(); - sourceElements.push(sourceElement); - if (sourceElement.expression.type !== Syntax.Literal) { - // this is not directive - break; - } - directive = source.slice(token.range[0] + 1, token.range[1] - 1); - if (directive === 'use strict') { - strict = true; - if (firstRestricted) { - throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); - } - } else { - if (!firstRestricted && token.octal) { - firstRestricted = token; - } - } - } - - oldLabelSet = state.labelSet; - oldInIteration = state.inIteration; - oldInSwitch = state.inSwitch; - oldInFunctionBody = state.inFunctionBody; - oldParenthesizedCount = state.parenthesizedCount; - - state.labelSet = new StringMap(); - state.inIteration = false; - state.inSwitch = false; - state.inFunctionBody = true; - state.parenthesizedCount = 0; - - while (index < length) { - if (match('}')) { - break; - } - sourceElement = parseSourceElement(); - if (typeof sourceElement === 'undefined') { - break; - } - sourceElements.push(sourceElement); - } - - expect('}'); - - state.labelSet = oldLabelSet; - state.inIteration = oldInIteration; - state.inSwitch = oldInSwitch; - state.inFunctionBody = oldInFunctionBody; - state.parenthesizedCount = oldParenthesizedCount; - - return markerApply(marker, delegate.createBlockStatement(sourceElements)); - } - - function validateParam(options, param, name) { - if (strict) { - if (isRestrictedWord(name)) { - options.stricted = param; - options.message = Messages.StrictParamName; - } - if (options.paramSet.has(name)) { - options.stricted = param; - options.message = Messages.StrictParamDupe; - } - } else if (!options.firstRestricted) { - if (isRestrictedWord(name)) { - options.firstRestricted = param; - options.message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(name)) { - options.firstRestricted = param; - options.message = Messages.StrictReservedWord; - } else if (options.paramSet.has(name)) { - options.firstRestricted = param; - options.message = Messages.StrictParamDupe; - } - } - options.paramSet.set(name, true); - } - - function parseParam(options) { - var token, rest, param, def; - - token = lookahead; - if (token.value === '...') { - token = lex(); - rest = true; - } - - if (match('[')) { - param = parseArrayInitialiser(); - reinterpretAsDestructuredParameter(options, param); - } else if (match('{')) { - if (rest) { - throwError({}, Messages.ObjectPatternAsRestParameter); - } - param = parseObjectInitialiser(); - reinterpretAsDestructuredParameter(options, param); - } else { - param = parseVariableIdentifier(); - validateParam(options, token, token.value); - } - - if (match('=')) { - if (rest) { - throwErrorTolerant(lookahead, Messages.DefaultRestParameter); - } - lex(); - def = parseAssignmentExpression(); - ++options.defaultCount; - } - - if (rest) { - if (!match(')')) { - throwError({}, Messages.ParameterAfterRestParameter); - } - options.rest = param; - return false; - } - - options.params.push(param); - options.defaults.push(def); - return !match(')'); - } - - function parseParams(firstRestricted) { - var options, marker = markerCreate(); - - options = { - params: [], - defaultCount: 0, - defaults: [], - rest: null, - firstRestricted: firstRestricted - }; - - expect('('); - - if (!match(')')) { - options.paramSet = new StringMap(); - while (index < length) { - if (!parseParam(options)) { - break; - } - expect(','); - } - } - - expect(')'); - - if (options.defaultCount === 0) { - options.defaults = []; - } - - return markerApply(marker, options); - } - - function parseFunctionDeclaration() { - var id, body, token, tmp, firstRestricted, message, previousStrict, previousYieldAllowed, generator, - marker = markerCreate(); - - expectKeyword('function'); - - generator = false; - if (match('*')) { - lex(); - generator = true; - } - - token = lookahead; - - id = parseVariableIdentifier(); - - if (strict) { - if (isRestrictedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictFunctionName); - } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } - } - - tmp = parseParams(firstRestricted); - firstRestricted = tmp.firstRestricted; - if (tmp.message) { - message = tmp.message; - } - - previousStrict = strict; - previousYieldAllowed = state.yieldAllowed; - state.yieldAllowed = generator; - - body = parseFunctionSourceElements(); - - if (strict && firstRestricted) { - throwError(firstRestricted, message); - } - if (strict && tmp.stricted) { - throwErrorTolerant(tmp.stricted, message); - } - strict = previousStrict; - state.yieldAllowed = previousYieldAllowed; - - return markerApply(marker, delegate.createFunctionDeclaration(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false)); - } - - function parseFunctionExpression() { - var token, id = null, firstRestricted, message, tmp, body, previousStrict, previousYieldAllowed, generator, - marker = markerCreate(); - - expectKeyword('function'); - - generator = false; - - if (match('*')) { - lex(); - generator = true; - } - - if (!match('(')) { - token = lookahead; - id = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictFunctionName); - } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } - } - } - - tmp = parseParams(firstRestricted); - firstRestricted = tmp.firstRestricted; - if (tmp.message) { - message = tmp.message; - } - - previousStrict = strict; - previousYieldAllowed = state.yieldAllowed; - state.yieldAllowed = generator; - - body = parseFunctionSourceElements(); - - if (strict && firstRestricted) { - throwError(firstRestricted, message); - } - if (strict && tmp.stricted) { - throwErrorTolerant(tmp.stricted, message); - } - strict = previousStrict; - state.yieldAllowed = previousYieldAllowed; - - return markerApply(marker, delegate.createFunctionExpression(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false)); - } - - function parseYieldExpression() { - var yieldToken, delegateFlag, expr, marker = markerCreate(); - - yieldToken = lex(); - assert(yieldToken.value === 'yield', 'Called parseYieldExpression with non-yield lookahead.'); - - if (!state.yieldAllowed) { - throwErrorTolerant({}, Messages.IllegalYield); - } - - delegateFlag = false; - if (match('*')) { - lex(); - delegateFlag = true; - } - - expr = parseAssignmentExpression(); - - return markerApply(marker, delegate.createYieldExpression(expr, delegateFlag)); - } - - // 14 Functions and classes - - // 14.1 Functions is defined above (13 in ES5) - // 14.2 Arrow Functions Definitions is defined in (7.3 assignments) - - // 14.3 Method Definitions - // 14.3.7 - function specialMethod(methodDefinition) { - return methodDefinition.kind === 'get' || - methodDefinition.kind === 'set' || - methodDefinition.value.generator; - } - - function parseMethodDefinition() { - var token, key, param, propType, computed, - marker = markerCreate(); - - if (lookahead.value === 'static') { - propType = ClassPropertyType.static; - lex(); - } else { - propType = ClassPropertyType.prototype; - } - - if (match('*')) { - lex(); - computed = (lookahead.value === '['); - return markerApply(marker, delegate.createMethodDefinition( - propType, - '', - parseObjectPropertyKey(), - parsePropertyMethodFunction({ generator: true }), - computed - )); - } - - token = lookahead; - key = parseObjectPropertyKey(); - - if (token.value === 'get' && !match('(')) { - computed = (lookahead.value === '['); - key = parseObjectPropertyKey(); - - expect('('); - expect(')'); - return markerApply(marker, delegate.createMethodDefinition( - propType, - 'get', - key, - parsePropertyFunction({ generator: false }), - computed - )); - } - if (token.value === 'set' && !match('(')) { - computed = (lookahead.value === '['); - key = parseObjectPropertyKey(); - - expect('('); - token = lookahead; - param = [ parseVariableIdentifier() ]; - expect(')'); - return markerApply(marker, delegate.createMethodDefinition( - propType, - 'set', - key, - parsePropertyFunction({ params: param, generator: false, name: token }), - computed - )); - } - - computed = (token.value === '['); - - return markerApply(marker, delegate.createMethodDefinition( - propType, - '', - key, - parsePropertyMethodFunction({ generator: false }), - computed - )); - } - - // 14.5 Class Definitions - - function parseClassElement() { - if (match(';')) { - lex(); - } else { - return parseMethodDefinition(); - } - } - - function parseClassBody() { - var classElement, classElements = [], existingProps = {}, - marker = markerCreate(), propName, propType; - - existingProps[ClassPropertyType.static] = new StringMap(); - existingProps[ClassPropertyType.prototype] = new StringMap(); - - expect('{'); - - while (index < length) { - if (match('}')) { - break; - } - classElement = parseClassElement(existingProps); - - if (typeof classElement !== 'undefined') { - classElements.push(classElement); - - propName = !classElement.computed && getFieldName(classElement.key); - if (propName !== false) { - propType = classElement.static ? - ClassPropertyType.static : - ClassPropertyType.prototype; - - if (propName === 'constructor' && !classElement.static) { - if (specialMethod(classElement)) { - throwError(classElement, Messages.IllegalClassConstructorProperty); - } - if (existingProps[ClassPropertyType.prototype].has('constructor')) { - throwError(classElement.key, Messages.IllegalDuplicateClassProperty); - } - } - existingProps[propType].set(propName, true); - } - } - } - - expect('}'); - - return markerApply(marker, delegate.createClassBody(classElements)); - } - - function parseClassExpression() { - var id, previousYieldAllowed, superClass = null, marker = markerCreate(); - - expectKeyword('class'); - - if (!matchKeyword('extends') && !match('{')) { - id = parseVariableIdentifier(); - } - - if (matchKeyword('extends')) { - expectKeyword('extends'); - previousYieldAllowed = state.yieldAllowed; - state.yieldAllowed = false; - superClass = parseAssignmentExpression(); - state.yieldAllowed = previousYieldAllowed; - } - - return markerApply(marker, delegate.createClassExpression(id, superClass, parseClassBody())); - } - - function parseClassDeclaration() { - var id, previousYieldAllowed, superClass = null, marker = markerCreate(); - - expectKeyword('class'); - - id = parseVariableIdentifier(); - - if (matchKeyword('extends')) { - expectKeyword('extends'); - previousYieldAllowed = state.yieldAllowed; - state.yieldAllowed = false; - superClass = parseAssignmentExpression(); - state.yieldAllowed = previousYieldAllowed; - } - - return markerApply(marker, delegate.createClassDeclaration(id, superClass, parseClassBody())); - } - - // 15 Program - - function parseSourceElement() { - if (lookahead.type === Token.Keyword) { - switch (lookahead.value) { - case 'const': - case 'let': - return parseConstLetDeclaration(lookahead.value); - case 'function': - return parseFunctionDeclaration(); - case 'export': - throwErrorTolerant({}, Messages.IllegalExportDeclaration); - return parseExportDeclaration(); - case 'import': - throwErrorTolerant({}, Messages.IllegalImportDeclaration); - return parseImportDeclaration(); - default: - return parseStatement(); - } - } - - if (lookahead.type !== Token.EOF) { - return parseStatement(); - } - } - - function parseProgramElement() { - if (extra.isModule && lookahead.type === Token.Keyword) { - switch (lookahead.value) { - case 'export': - return parseExportDeclaration(); - case 'import': - return parseImportDeclaration(); - } - } - - return parseSourceElement(); - } - - function parseProgramElements() { - var sourceElement, sourceElements = [], token, directive, firstRestricted; - - while (index < length) { - token = lookahead; - if (token.type !== Token.StringLiteral) { - break; - } - - sourceElement = parseProgramElement(); - sourceElements.push(sourceElement); - if (sourceElement.expression.type !== Syntax.Literal) { - // this is not directive - break; - } - directive = source.slice(token.range[0] + 1, token.range[1] - 1); - if (directive === 'use strict') { - strict = true; - if (firstRestricted) { - throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); - } - } else { - if (!firstRestricted && token.octal) { - firstRestricted = token; - } - } - } - - while (index < length) { - sourceElement = parseProgramElement(); - if (typeof sourceElement === 'undefined') { - break; - } - sourceElements.push(sourceElement); - } - return sourceElements; - } - - function parseProgram() { - var body, marker = markerCreate(); - strict = !!extra.isModule; - peek(); - body = parseProgramElements(); - return markerApply(marker, delegate.createProgram(body)); - } - - function collectToken() { - var loc, token, range, value, entry; - - skipComment(); - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; - - token = extra.advance(); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - - if (token.type !== Token.EOF) { - range = [token.range[0], token.range[1]]; - value = source.slice(token.range[0], token.range[1]); - entry = { - type: TokenName[token.type], - value: value, - range: range, - loc: loc - }; - if (token.regex) { - entry.regex = { - pattern: token.regex.pattern, - flags: token.regex.flags - }; - } - extra.tokens.push(entry); - } - - return token; - } - - function collectRegex() { - var pos, loc, regex, token; - - skipComment(); - - pos = index; - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; - - regex = extra.scanRegExp(); - loc.end = { - line: lineNumber, - column: index - lineStart - }; - - if (!extra.tokenize) { - /* istanbul ignore next */ - // Pop the previous token, which is likely '/' or '/=' - if (extra.tokens.length > 0) { - token = extra.tokens[extra.tokens.length - 1]; - if (token.range[0] === pos && token.type === 'Punctuator') { - if (token.value === '/' || token.value === '/=') { - extra.tokens.pop(); - } - } - } - - extra.tokens.push({ - type: 'RegularExpression', - value: regex.literal, - regex: regex.regex, - range: [pos, index], - loc: loc - }); - } - - return regex; - } - - function filterTokenLocation() { - var i, entry, token, tokens = []; - - for (i = 0; i < extra.tokens.length; ++i) { - entry = extra.tokens[i]; - token = { - type: entry.type, - value: entry.value - }; - if (entry.regex) { - token.regex = { - pattern: entry.regex.pattern, - flags: entry.regex.flags - }; - } - if (extra.range) { - token.range = entry.range; - } - if (extra.loc) { - token.loc = entry.loc; - } - tokens.push(token); - } - - extra.tokens = tokens; - } - - function patch() { - if (typeof extra.tokens !== 'undefined') { - extra.advance = advance; - extra.scanRegExp = scanRegExp; - - advance = collectToken; - scanRegExp = collectRegex; - } - } - - function unpatch() { - if (typeof extra.scanRegExp === 'function') { - advance = extra.advance; - scanRegExp = extra.scanRegExp; - } - } - - // This is used to modify the delegate. - - function extend(object, properties) { - var entry, result = {}; - - for (entry in object) { - /* istanbul ignore else */ - if (object.hasOwnProperty(entry)) { - result[entry] = object[entry]; - } - } - - for (entry in properties) { - /* istanbul ignore else */ - if (properties.hasOwnProperty(entry)) { - result[entry] = properties[entry]; - } - } - - return result; - } - - function tokenize(code, options) { - var toString, - token, - tokens; - - toString = String; - if (typeof code !== 'string' && !(code instanceof String)) { - code = toString(code); - } - - delegate = SyntaxTreeDelegate; - source = code; - index = 0; - lineNumber = (source.length > 0) ? 1 : 0; - lineStart = 0; - length = source.length; - lookahead = null; - state = { - allowKeyword: true, - allowIn: true, - labelSet: new StringMap(), - inFunctionBody: false, - inIteration: false, - inSwitch: false, - lastCommentStart: -1 - }; - - extra = {}; - - // Options matching. - options = options || {}; - - // Of course we collect tokens here. - options.tokens = true; - extra.tokens = []; - extra.tokenize = true; - // The following two fields are necessary to compute the Regex tokens. - extra.openParenToken = -1; - extra.openCurlyToken = -1; - - extra.range = (typeof options.range === 'boolean') && options.range; - extra.loc = (typeof options.loc === 'boolean') && options.loc; - - if (typeof options.comment === 'boolean' && options.comment) { - extra.comments = []; - } - if (typeof options.tolerant === 'boolean' && options.tolerant) { - extra.errors = []; - } - - patch(); - - try { - peek(); - if (lookahead.type === Token.EOF) { - return extra.tokens; - } - - token = lex(); - while (lookahead.type !== Token.EOF) { - try { - token = lex(); - } catch (lexError) { - token = lookahead; - if (extra.errors) { - extra.errors.push(lexError); - // We have to break on the first error - // to avoid infinite loops. - break; - } else { - throw lexError; - } - } - } - - filterTokenLocation(); - tokens = extra.tokens; - if (typeof extra.comments !== 'undefined') { - tokens.comments = extra.comments; - } - if (typeof extra.errors !== 'undefined') { - tokens.errors = extra.errors; - } - } catch (e) { - throw e; - } finally { - unpatch(); - extra = {}; - } - return tokens; - } - - function parse(code, options) { - var program, toString; - - toString = String; - if (typeof code !== 'string' && !(code instanceof String)) { - code = toString(code); - } - - delegate = SyntaxTreeDelegate; - source = code; - index = 0; - lineNumber = (source.length > 0) ? 1 : 0; - lineStart = 0; - length = source.length; - lookahead = null; - state = { - allowKeyword: false, - allowIn: true, - labelSet: new StringMap(), - parenthesizedCount: 0, - inFunctionBody: false, - inIteration: false, - inSwitch: false, - lastCommentStart: -1, - yieldAllowed: false - }; - - extra = {}; - if (typeof options !== 'undefined') { - extra.range = (typeof options.range === 'boolean') && options.range; - extra.loc = (typeof options.loc === 'boolean') && options.loc; - extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment; - - if (extra.loc && options.source !== null && options.source !== undefined) { - delegate = extend(delegate, { - 'postProcess': function (node) { - node.loc.source = toString(options.source); - return node; - } - }); - } - - if (options.sourceType === 'module') { - extra.isModule = true; - } - if (typeof options.tokens === 'boolean' && options.tokens) { - extra.tokens = []; - } - if (typeof options.comment === 'boolean' && options.comment) { - extra.comments = []; - } - if (typeof options.tolerant === 'boolean' && options.tolerant) { - extra.errors = []; - } - if (extra.attachComment) { - extra.range = true; - extra.comments = []; - extra.bottomRightStack = []; - extra.trailingComments = []; - extra.leadingComments = []; - } - } - - patch(); - try { - program = parseProgram(); - if (typeof extra.comments !== 'undefined') { - program.comments = extra.comments; - } - if (typeof extra.tokens !== 'undefined') { - filterTokenLocation(); - program.tokens = extra.tokens; - } - if (typeof extra.errors !== 'undefined') { - program.errors = extra.errors; - } - } catch (e) { - throw e; - } finally { - unpatch(); - extra = {}; - } - - return program; - } - - // Sync with *.json manifests. - exports.version = '1.1.0-dev-harmony'; - - exports.tokenize = tokenize; - - exports.parse = parse; - - // Deep copy. - /* istanbul ignore next */ - exports.Syntax = (function () { - var name, types = {}; - - if (typeof Object.create === 'function') { - types = Object.create(null); - } - - for (name in Syntax) { - if (Syntax.hasOwnProperty(name)) { - types[name] = Syntax[name]; - } - } - - if (typeof Object.freeze === 'function') { - Object.freeze(types); - } - - return types; - }()); - -})); -/* vim: set sw=4 ts=4 et tw=80 : */ diff --git a/test/traverse.js b/test/traverse.js index b2a2f6c..c994f3a 100644 --- a/test/traverse.js +++ b/test/traverse.js @@ -23,7 +23,6 @@ import Dumper from './dumper'; import checkDump from './checkDump'; -import { expect } from 'chai'; import { traverse } from '..'; describe('object expression', function() { diff --git a/test/type.js b/test/type.js index 6d66344..342104f 100644 --- a/test/type.js +++ b/test/type.js @@ -22,7 +22,6 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import { expect } from 'chai'; import { traverse } from '..'; describe('type API', function() { From 44c2633d2ceadc3679f9e0415fe92b2480504ed9 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 29 Apr 2020 21:01:37 +0800 Subject: [PATCH 2/8] - Linting: Switch from JSHint to ESLint --- .eslintignore | 5 ++ .eslintrc.js | 48 +++++++++++++++++++ .jshintrc | 16 ------- estraverse.js | 120 ++++++++++++++++++++-------------------------- package.json | 4 +- test/checkDump.js | 12 ++--- test/dumper.js | 2 +- test/es6.js | 10 ++-- test/replace.js | 68 +++++++++++++------------- test/traverse.js | 24 +++++----- 10 files changed, 164 insertions(+), 145 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.js delete mode 100644 .jshintrc diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..42b0b2a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +node_modules +dist + +!.eslintrc.js +coverage diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..1507be3 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,48 @@ +'use strict'; +module.exports = { + env: { + browser: true, + commonjs: true, + es6: true, + node: true + }, + extends: 'eslint:recommended', + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly' + }, + overrides: [{ + files: '.eslintrc.js', + parserOptions: { + sourceType: 'script' + }, + rules: { + strict: 'error' + } + }, { + files: 'test/**', + globals: { + expect: true + }, + env: { + mocha: true + } + }], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018 + }, + rules: { + semi: ['error'], + indent: ['error', 4, { SwitchCase: 1 }], + 'prefer-const': ['error'], + 'no-var': ['error'], + 'prefer-destructuring': ['error'], + 'object-shorthand': ['error'], + 'object-curly-spacing': ['error', 'always'], + quotes: ['error', 'single'], + 'quote-props': ['error', 'as-needed'], + 'brace-style': ['error', '1tbs', { allowSingleLine: true }], + 'prefer-template': ['error'] + } +}; diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index f642dae..0000000 --- a/.jshintrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "curly": true, - "eqeqeq": true, - "immed": true, - "eqnull": true, - "latedef": true, - "noarg": true, - "noempty": true, - "quotmark": "single", - "undef": true, - "unused": true, - "strict": true, - "trailing": true, - - "node": true -} diff --git a/estraverse.js b/estraverse.js index f0d9af9..3e424eb 100644 --- a/estraverse.js +++ b/estraverse.js @@ -22,24 +22,15 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/*jslint vars:false, bitwise:true*/ -/*jshint indent:4*/ -/*global exports:true*/ + (function clone(exports) { 'use strict'; - var Syntax, - VisitorOption, - VisitorKeys, - BREAK, - SKIP, - REMOVE; - function deepCopy(obj) { - var ret = {}, key, val; - for (key in obj) { - if (obj.hasOwnProperty(key)) { - val = obj[key]; + const ret = {}; + for (const key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const val = obj[key]; if (typeof val === 'object' && val !== null) { ret[key] = deepCopy(val); } else { @@ -54,7 +45,7 @@ // MIT License function upperBound(array, func) { - var diff, len, i, current; + let diff, len, i, current; len = array.length; i = 0; @@ -72,7 +63,7 @@ return i; } - Syntax = { + const Syntax = { AssignmentExpression: 'AssignmentExpression', AssignmentPattern: 'AssignmentPattern', ArrayExpression: 'ArrayExpression', @@ -150,7 +141,7 @@ YieldExpression: 'YieldExpression' }; - VisitorKeys = { + const VisitorKeys = { AssignmentExpression: ['left', 'right'], AssignmentPattern: ['left', 'right'], ArrayExpression: ['elements'], @@ -229,11 +220,11 @@ }; // unique id - BREAK = {}; - SKIP = {}; - REMOVE = {}; + const BREAK = {}; + const SKIP = {}; + const REMOVE = {}; - VisitorOption = { + const VisitorOption = { Break: BREAK, Skip: SKIP, Remove: REMOVE @@ -270,7 +261,7 @@ // API: // return property path array from root to current node Controller.prototype.path = function path() { - var i, iz, j, jz, result, element; + let i, iz, j, jz, element; function addToPath(result, path) { if (Array.isArray(path)) { @@ -288,7 +279,7 @@ } // first node is sentinel, second node is root element - result = []; + const result = []; for (i = 2, iz = this.__leavelist.length; i < iz; ++i) { element = this.__leavelist[i]; addToPath(result, element.path); @@ -300,18 +291,16 @@ // API: // return type of current node Controller.prototype.type = function () { - var node = this.current(); + const node = this.current(); return node.type || this.__current.wrap; }; // API: // return array of parent elements Controller.prototype.parents = function parents() { - var i, iz, result; - // first node is sentinel - result = []; - for (i = 1, iz = this.__leavelist.length; i < iz; ++i) { + const result = []; + for (let i = 1, iz = this.__leavelist.length; i < iz; ++i) { result.push(this.__leavelist[i].node); } @@ -325,11 +314,11 @@ }; Controller.prototype.__execute = function __execute(callback, element) { - var previous, result; + let result; result = undefined; - previous = this.__current; + const previous = this.__current; this.__current = element; this.__state = null; if (callback) { @@ -394,9 +383,9 @@ function isProperty(nodeType, key) { return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key; } - + function candidateExistsInLeaveList(leavelist, candidate) { - for (var i = leavelist.length - 1; i >= 0; --i) { + for (let i = leavelist.length - 1; i >= 0; --i) { if (leavelist[i].node === candidate) { return true; } @@ -405,9 +394,7 @@ } Controller.prototype.traverse = function traverse(root, visitor) { - var worklist, - leavelist, - element, + let element, node, nodeType, ret, @@ -415,16 +402,15 @@ current, current2, candidates, - candidate, - sentinel; + candidate; this.__initialize(root, visitor); - sentinel = {}; + const sentinel = {}; // reference - worklist = this.__worklist; - leavelist = this.__leavelist; + const worklist = this.__worklist; + const leavelist = this.__leavelist; // initialize worklist.push(new Element(root, null, null, null)); @@ -459,14 +445,14 @@ continue; } - node = element.node; + ({ node } = element); nodeType = node.type || element.wrap; candidates = this.__keys[nodeType]; if (!candidates) { if (this.__fallback) { candidates = this.__fallback(node); } else { - throw new Error('Unknown node type ' + nodeType + '.'); + throw new Error(`Unknown node type ${ nodeType }.`); } } @@ -486,7 +472,7 @@ } if (candidateExistsInLeaveList(leavelist, candidate[current2])) { - continue; + continue; } if (isProperty(nodeType, candidates[current])) { @@ -500,7 +486,7 @@ } } else if (isNode(candidate)) { if (candidateExistsInLeaveList(leavelist, candidate)) { - continue; + continue; } worklist.push(new Element(candidate, key, null, null)); @@ -511,9 +497,7 @@ }; Controller.prototype.replace = function replace(root, visitor) { - var worklist, - leavelist, - node, + let node, nodeType, target, element, @@ -521,20 +505,17 @@ current2, candidates, candidate, - sentinel, - outer, key; function removeElem(element) { - var i, + let i, key, nextElem, parent; if (element.ref.remove()) { // When the reference is an element of an array. - key = element.ref.key; - parent = element.ref.parent; + ({ key, parent } = element.ref); // If removed from array, then decrease following items' keys. i = worklist.length; @@ -552,15 +533,15 @@ this.__initialize(root, visitor); - sentinel = {}; + const sentinel = {}; // reference - worklist = this.__worklist; - leavelist = this.__leavelist; + const worklist = this.__worklist; + const leavelist = this.__leavelist; // initialize - outer = { - root: root + const outer = { + root }; element = new Element(root, null, null, new Reference(outer, 'root')); worklist.push(element); @@ -611,7 +592,7 @@ } // node may be null - node = element.node; + ({ node } = element); if (!node) { continue; } @@ -629,7 +610,7 @@ if (this.__fallback) { candidates = this.__fallback(node); } else { - throw new Error('Unknown node type ' + nodeType + '.'); + throw new Error(`Unknown node type ${ nodeType }.`); } } @@ -666,17 +647,17 @@ }; function traverse(root, visitor) { - var controller = new Controller(); + const controller = new Controller(); return controller.traverse(root, visitor); } function replace(root, visitor) { - var controller = new Controller(); + const controller = new Controller(); return controller.replace(root, visitor); } function extendCommentRange(comment, tokens) { - var target; + let target; target = upperBound(tokens, function search(token) { return token.range[0] > comment.range[0]; @@ -685,12 +666,12 @@ comment.extendedRange = [comment.range[0], comment.range[1]]; if (target !== tokens.length) { - comment.extendedRange[1] = tokens[target].range[0]; + [comment.extendedRange[1]] = tokens[target].range; } target -= 1; if (target >= 0) { - comment.extendedRange[0] = tokens[target].range[1]; + [, comment.extendedRange[0]] = tokens[target].range; } return comment; @@ -698,7 +679,8 @@ function attachComments(tree, providedComments, tokens) { // At first, we should calculate extended comment ranges. - var comments = [], comment, len, i, cursor; + const comments = []; + let comment, len, i, cursor; if (!tree.range) { throw new Error('attachComments needs range information'); @@ -724,8 +706,8 @@ // This is based on John Freeman's implementation. cursor = 0; traverse(tree, { - enter: function (node) { - var comment; + enter (node) { + let comment; while (cursor < comments.length) { comment = comments[cursor]; @@ -757,8 +739,8 @@ cursor = 0; traverse(tree, { - leave: function (node) { - var comment; + leave (node) { + let comment; while (cursor < comments.length) { comment = comments[cursor]; diff --git a/package.json b/package.json index dccd687..958d157 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "@babel/preset-env": "^7.16.11", "@babel/register": "^7.17.0", "chai": "^4.3.6", + "eslint": "^6.8.0", "espree": "^9.3.1", "esprima": "^4.0.1", - "jshint": "^2.13.4", "mocha": "^9.2.1", "nyc": "^15.1.0" }, @@ -55,7 +55,7 @@ }, "scripts": { "test": "npm run lint && npm run unit-test", - "lint": "jshint estraverse.js", + "lint": "eslint .", "unit-test": "nyc mocha --require chai/register-expect.js --require @babel/register" } } diff --git a/test/checkDump.js b/test/checkDump.js index 964ddfc..21f0333 100644 --- a/test/checkDump.js +++ b/test/checkDump.js @@ -21,13 +21,13 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. export default function checkDump(dump, expected) { - expect(normalize(expected)).to.be.equal(normalize(dump)); + expect(normalize(expected)).to.be.equal(normalize(dump)); } function normalize(dump) { - return dump - .trim() - .split('\n') - .map(line => line.trim()) - .join('\n'); + return dump + .trim() + .split('\n') + .map(line => line.trim()) + .join('\n'); } diff --git a/test/dumper.js b/test/dumper.js index 2887308..7a7ee9b 100644 --- a/test/dumper.js +++ b/test/dumper.js @@ -56,4 +56,4 @@ export default class Dumper { return dumper.result(); } -}; +} diff --git a/test/es6.js b/test/es6.js index bd503f9..47e0fac 100644 --- a/test/es6.js +++ b/test/es6.js @@ -249,7 +249,7 @@ describe('export', function() { describe('import', function() { it('default specifier #1', function() { - const tree = espree(`import Cocoa from 'rabbit-house'`, { + const tree = espree('import Cocoa from \'rabbit-house\'', { ecmaVersion: 2015, sourceType: 'module' }); @@ -269,7 +269,7 @@ describe('import', function() { }); it('named specifier #1', function() { - const tree = espree(`import {Cocoa, Cappuccino as Chino} from 'rabbit-house'`, { + const tree = espree('import {Cocoa, Cappuccino as Chino} from \'rabbit-house\'', { ecmaVersion: 2015, sourceType: 'module' }); @@ -297,7 +297,7 @@ describe('import', function() { }); it('namespace specifier #1', function() { - const tree = espree(`import * as RabbitHouse from 'rabbit-house'`, { + const tree = espree('import * as RabbitHouse from \'rabbit-house\'', { ecmaVersion: 2015, sourceType: 'module' }); @@ -319,7 +319,7 @@ describe('import', function() { describe('dynamic import', function() { it('expression pattern #1', function() { - const tree = espree(`import('rabbit-house')`, { + const tree = espree('import(\'rabbit-house\')', { ecmaVersion: 2020 }); @@ -363,7 +363,7 @@ describe('pattern', function() { describe('super', function() { it('super expression#1', function() { - const tree = {type: 'Super'}; + const tree = { type: 'Super' }; checkDump(Dumper.dump(tree), ` enter - Super diff --git a/test/replace.js b/test/replace.js index fae2462..7b1bc13 100644 --- a/test/replace.js +++ b/test/replace.js @@ -87,10 +87,10 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 2}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 2 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] @@ -115,9 +115,9 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] @@ -136,10 +136,10 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 2}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 2 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] @@ -252,10 +252,10 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 2}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 2 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] @@ -408,10 +408,10 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 2}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 2 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] @@ -437,9 +437,9 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] @@ -458,25 +458,25 @@ describe('replace', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 2}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 2 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] }; expect(() => replace(tree, { - enter(node) { - if (node.type === 'Identifier' && node.name === 'a') { - this.remove(); - } - if (node.type === 'Literal' && node.value === 2) { - return VisitorOption.Remove; - } + enter(node) { + if (node.type === 'Identifier' && node.name === 'a') { + this.remove(); } - }) + if (node.type === 'Literal' && node.value === 2) { + return VisitorOption.Remove; + } + } + }) ).to.throw('Unknown node type XXXExpression.'); }); }); diff --git a/test/traverse.js b/test/traverse.js index c994f3a..69a2d14 100644 --- a/test/traverse.js +++ b/test/traverse.js @@ -93,7 +93,7 @@ describe('object expression', function() { }] }; - checkDump(Dumper.dump(tree, {CustomProperty: ['foo']}), ` + checkDump(Dumper.dump(tree, { CustomProperty: ['foo'] }), ` enter - ObjectExpression enter - CustomProperty enter - Identifier @@ -582,30 +582,30 @@ describe('no listed keys fallback', function() { value: { type: 'ArrayExpression', elements: [ - {type: 'Literal', value: 1}, - {type: 'Literal', value: 2}, - {type: 'Literal', value: 3}, - {type: 'Literal', value: 4} + { type: 'Literal', value: 1 }, + { type: 'Literal', value: 2 }, + { type: 'Literal', value: 3 }, + { type: 'Literal', value: 4 } ] } }] }; expect( - () => traverse(tree, { enter(node) {} }) + () => traverse(tree, { enter (/* node */) {} }) ).to.throw('Unknown node type XXXExpression.'); }); it('break loop', function () { const children = { - type: 'Children', - name: 'div' + type: 'Children', + name: 'div' }; const parent = { - type: 'Parent', - name: children, - parent: null, + type: 'Parent', + name: children, + parent: null, }; children.parent = parent; @@ -618,5 +618,5 @@ describe('no listed keys fallback', function() { leave - Children leave - Parent `); - }) + }); }); From fdc172a0758e1387b678d17c58718346e0774887 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 29 Apr 2020 21:09:29 +0800 Subject: [PATCH 3/8] - Refactoring: Move `let` closer to scope of use (or convert to `const`); prefer for-of - Refactoring: Use ES6 classes --- estraverse.js | 308 +++++++++++++++++++++++--------------------------- 1 file changed, 144 insertions(+), 164 deletions(-) diff --git a/estraverse.js b/estraverse.js index 3e424eb..98fcb32 100644 --- a/estraverse.js +++ b/estraverse.js @@ -45,14 +45,12 @@ // MIT License function upperBound(array, func) { - let diff, len, i, current; - - len = array.length; - i = 0; + let len = array.length; + let i = 0; while (len) { - diff = len >>> 1; - current = i + diff; + const diff = len >>> 1; + const current = i + diff; if (func(array[current])) { len = diff; } else { @@ -230,148 +228,149 @@ Remove: REMOVE }; - function Reference(parent, key) { - this.parent = parent; - this.key = key; - } - - Reference.prototype.replace = function replace(node) { - this.parent[this.key] = node; - }; + class Reference { + constructor (parent, key) { + this.parent = parent; + this.key = key; + } - Reference.prototype.remove = function remove() { - if (Array.isArray(this.parent)) { - this.parent.splice(this.key, 1); - return true; - } else { - this.replace(null); - return false; + replace (node) { + this.parent[this.key] = node; } - }; - function Element(node, path, wrap, ref) { - this.node = node; - this.path = path; - this.wrap = wrap; - this.ref = ref; + remove () { + if (Array.isArray(this.parent)) { + this.parent.splice(this.key, 1); + return true; + } else { + this.replace(null); + return false; + } + } } - function Controller() { } + class Element { + constructor (node, path, wrap, ref) { + this.node = node; + this.path = path; + this.wrap = wrap; + this.ref = ref; + } + } - // API: - // return property path array from root to current node - Controller.prototype.path = function path() { - let i, iz, j, jz, element; + class Controller { - function addToPath(result, path) { - if (Array.isArray(path)) { - for (j = 0, jz = path.length; j < jz; ++j) { - result.push(path[j]); + // API: + // return property path array from root to current node + path () { + function addToPath(result, path) { + if (Array.isArray(path)) { + for (const p of path) { + result.push(p); + } + } else { + result.push(path); } - } else { - result.push(path); } - } - // root node - if (!this.__current.path) { - return null; + // root node + if (!this.__current.path) { + return null; + } + + // first node is sentinel, second node is root element + const result = []; + for (let i = 2, iz = this.__leavelist.length; i < iz; ++i) { + const element = this.__leavelist[i]; + addToPath(result, element.path); + } + addToPath(result, this.__current.path); + return result; } - // first node is sentinel, second node is root element - const result = []; - for (i = 2, iz = this.__leavelist.length; i < iz; ++i) { - element = this.__leavelist[i]; - addToPath(result, element.path); + // API: + // return type of current node + type () { + const node = this.current(); + return node.type || this.__current.wrap; } - addToPath(result, this.__current.path); - return result; - }; - // API: - // return type of current node - Controller.prototype.type = function () { - const node = this.current(); - return node.type || this.__current.wrap; - }; + // API: + // return array of parent elements + parents () { + // first node is sentinel + const result = []; + for (let i = 1, iz = this.__leavelist.length; i < iz; ++i) { + result.push(this.__leavelist[i].node); + } - // API: - // return array of parent elements - Controller.prototype.parents = function parents() { - // first node is sentinel - const result = []; - for (let i = 1, iz = this.__leavelist.length; i < iz; ++i) { - result.push(this.__leavelist[i].node); + return result; } - return result; - }; - - // API: - // return current node - Controller.prototype.current = function current() { - return this.__current.node; - }; + // API: + // return current node + current () { + return this.__current.node; + } - Controller.prototype.__execute = function __execute(callback, element) { - let result; + __execute (callback, element) { + const previous = this.__current; + this.__current = element; + this.__state = null; - result = undefined; + let result = undefined; + if (callback) { + result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node); + } + this.__current = previous; - const previous = this.__current; - this.__current = element; - this.__state = null; - if (callback) { - result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node); + return result; } - this.__current = previous; - - return result; - }; - - // API: - // notify control skip / break - Controller.prototype.notify = function notify(flag) { - this.__state = flag; - }; - // API: - // skip child nodes of current node - Controller.prototype.skip = function () { - this.notify(SKIP); - }; + // API: + // notify control skip / break + notify (flag) { + this.__state = flag; + } - // API: - // break traversals - Controller.prototype['break'] = function () { - this.notify(BREAK); - }; + // API: + // skip child nodes of current node + skip () { + this.notify(SKIP); + } - // API: - // remove node - Controller.prototype.remove = function () { - this.notify(REMOVE); - }; + // API: + // break traversals + break () { + this.notify(BREAK); + } - Controller.prototype.__initialize = function(root, visitor) { - this.visitor = visitor; - this.root = root; - this.__worklist = []; - this.__leavelist = []; - this.__current = null; - this.__state = null; - this.__fallback = null; - if (visitor.fallback === 'iteration') { - this.__fallback = Object.keys; - } else if (typeof visitor.fallback === 'function') { - this.__fallback = visitor.fallback; + // API: + // remove node + remove () { + this.notify(REMOVE); } - this.__keys = VisitorKeys; - if (visitor.keys) { - this.__keys = Object.assign(Object.create(this.__keys), visitor.keys); + __initialize (root, visitor) { + this.visitor = visitor; + this.root = root; + this.__worklist = []; + this.__leavelist = []; + this.__current = null; + this.__state = null; + this.__fallback = null; + if (visitor.fallback === 'iteration') { + this.__fallback = Object.keys; + } else if (typeof visitor.fallback === 'function') { + this.__fallback = visitor.fallback; + } + + this.__keys = VisitorKeys; + if (visitor.keys) { + this.__keys = Object.assign(Object.create(this.__keys), visitor.keys); + } } - }; + } function isNode(node) { if (node == null) { @@ -394,16 +393,6 @@ } Controller.prototype.traverse = function traverse(root, visitor) { - let element, - node, - nodeType, - ret, - key, - current, - current2, - candidates, - candidate; - this.__initialize(root, visitor); const sentinel = {}; @@ -417,8 +406,9 @@ leavelist.push(new Element(null, null, null, null)); while (worklist.length) { - element = worklist.pop(); + let element = worklist.pop(); + let ret; if (element === sentinel) { element = leavelist.pop(); @@ -445,9 +435,9 @@ continue; } - ({ node } = element); - nodeType = node.type || element.wrap; - candidates = this.__keys[nodeType]; + const { node } = element; + const nodeType = node.type || element.wrap; + let candidates = this.__keys[nodeType]; if (!candidates) { if (this.__fallback) { candidates = this.__fallback(node); @@ -456,16 +446,16 @@ } } - current = candidates.length; + let current = candidates.length; while ((current -= 1) >= 0) { - key = candidates[current]; - candidate = node[key]; + const key = candidates[current]; + const candidate = node[key]; if (!candidate) { continue; } if (Array.isArray(candidate)) { - current2 = candidate.length; + let current2 = candidate.length; while ((current2 -= 1) >= 0) { if (!candidate[current2]) { continue; @@ -497,16 +487,6 @@ }; Controller.prototype.replace = function replace(root, visitor) { - let node, - nodeType, - target, - element, - current, - current2, - candidates, - candidate, - key; - function removeElem(element) { let i, key, @@ -543,7 +523,7 @@ const outer = { root }; - element = new Element(root, null, null, new Reference(outer, 'root')); + let element = new Element(root, null, null, new Reference(outer, 'root')); worklist.push(element); leavelist.push(element); @@ -553,7 +533,7 @@ if (element === sentinel) { element = leavelist.pop(); - target = this.__execute(visitor.leave, element); + const target = this.__execute(visitor.leave, element); // node may be replaced with null, // so distinguish between undefined and null in this place @@ -572,7 +552,7 @@ continue; } - target = this.__execute(visitor.enter, element); + const target = this.__execute(visitor.enter, element); // node may be replaced with null, // so distinguish between undefined and null in this place @@ -592,7 +572,7 @@ } // node may be null - ({ node } = element); + const { node } = element; if (!node) { continue; } @@ -604,8 +584,8 @@ continue; } - nodeType = node.type || element.wrap; - candidates = this.__keys[nodeType]; + const nodeType = node.type || element.wrap; + let candidates = this.__keys[nodeType]; if (!candidates) { if (this.__fallback) { candidates = this.__fallback(node); @@ -614,16 +594,16 @@ } } - current = candidates.length; + let current = candidates.length; while ((current -= 1) >= 0) { - key = candidates[current]; - candidate = node[key]; + const key = candidates[current]; + const candidate = node[key]; if (!candidate) { continue; } if (Array.isArray(candidate)) { - current2 = candidate.length; + let current2 = candidate.length; while ((current2 -= 1) >= 0) { if (!candidate[current2]) { continue; @@ -679,8 +659,8 @@ function attachComments(tree, providedComments, tokens) { // At first, we should calculate extended comment ranges. - const comments = []; - let comment, len, i, cursor; + const comments = []; + let comment, cursor; if (!tree.range) { throw new Error('attachComments needs range information'); @@ -689,8 +669,8 @@ // tokens array is empty, we attach comments to tree as 'leadingComments' if (!tokens.length) { if (providedComments.length) { - for (i = 0, len = providedComments.length; i < len; i += 1) { - comment = deepCopy(providedComments[i]); + for (const providedComment of providedComments) { + comment = deepCopy(providedComment); comment.extendedRange = [0, tree.range[0]]; comments.push(comment); } @@ -699,8 +679,8 @@ return tree; } - for (i = 0, len = providedComments.length; i < len; i += 1) { - comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens)); + for (const providedComment of providedComments) { + comments.push(extendCommentRange(deepCopy(providedComment), tokens)); } // This is based on John Freeman's implementation. From a53a0122f1856a5c52ad762bcf5fbea1c65d196e Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 29 Apr 2020 21:24:15 +0800 Subject: [PATCH 4/8] - Enhancement: Provide ESM distribution pointed to by `module` - Refactoring: Switch to ESM source, using Rollup + Babel/Terser - Refactoring: Further ES6 class --- .gitignore | 2 + package.json | 9 +- rollup.config.js | 45 ++++ estraverse.js => src/estraverse.js | 396 +++++++++++++++-------------- 4 files changed, 256 insertions(+), 196 deletions(-) create mode 100644 rollup.config.js rename estraverse.js => src/estraverse.js (77%) diff --git a/.gitignore b/.gitignore index 5b78fb8..4bb0d5b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ coverage/ npm-debug.log .vimrc.local + +dist diff --git a/package.json b/package.json index 958d157..ec2b7bc 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "description": "ECMAScript JS AST traversal functions", "homepage": "https://github.com/estools/estraverse", "bugs": "https://github.com/estools/estraverse/issues", - "main": "estraverse.js", + "main": "dist/estraverse.min.js", + "module": "dist/estraverse.esm.min.js", "version": "5.3.0", "engines": { "node": ">=4.0" @@ -32,12 +33,15 @@ "@babel/core": "^7.17.5", "@babel/preset-env": "^7.16.11", "@babel/register": "^7.17.0", + "@rollup/plugin-babel": "^5.0.0", "chai": "^4.3.6", "eslint": "^6.8.0", "espree": "^9.3.1", "esprima": "^4.0.1", "mocha": "^9.2.1", - "nyc": "^15.1.0" + "nyc": "^15.1.0", + "rollup": "^2.7.3", + "rollup-plugin-terser": "^5.3.0" }, "license": "BSD-2-Clause", "nyc": { @@ -54,6 +58,7 @@ ] }, "scripts": { + "build": "rollup -c", "test": "npm run lint && npm run unit-test", "lint": "eslint .", "unit-test": "nyc mocha --require chai/register-expect.js --require @babel/register" diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..e38bc1b --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,45 @@ +import { terser } from 'rollup-plugin-terser'; + +import babel from '@rollup/plugin-babel'; + +/** + * @external RollupConfig + * @type {PlainObject} + * @see {@link https://rollupjs.org/guide/en#big-list-of-options} + */ + +/** + * @param {PlainObject} [config= {}] + * @param {boolean} [config.minifying=false] + * @param {string} [config.format='umd'] + * @returns {external:RollupConfig} + */ +function getRollupObject ({ minifying, format = 'umd' } = {}) { + const nonMinified = { + input: 'src/estraverse.js', + output: { + format, + sourcemap: minifying, + file: `dist/estraverse${ + format === 'umd' ? '' : `.${format}` + }${minifying ? '.min' : ''}.js`, + name: 'estraverse' + }, + plugins: [ + babel({ + babelHelpers: 'bundled' + }) + ] + }; + if (minifying) { + nonMinified.plugins.push(terser()); + } + return nonMinified; +} + +export default [ + getRollupObject({ minifying: true, format: 'umd' }), + getRollupObject({ minifying: false, format: 'umd' }), + getRollupObject({ minifying: true, format: 'esm' }), + getRollupObject({ minifying: false, format: 'esm' }) +]; diff --git a/estraverse.js b/src/estraverse.js similarity index 77% rename from estraverse.js rename to src/estraverse.js index 98fcb32..f6d048d 100644 --- a/estraverse.js +++ b/src/estraverse.js @@ -23,9 +23,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -(function clone(exports) { - 'use strict'; - +function clone(exports) { function deepCopy(obj) { const ret = {}; for (const key in obj) { @@ -217,7 +215,7 @@ YieldExpression: ['argument'] }; - // unique id + // unique id (convert to symbols once targeted versions supporting?) const BREAK = {}; const SKIP = {}; const REMOVE = {}; @@ -258,6 +256,26 @@ } } + function isNode(node) { + if (node == null) { + return false; + } + return typeof node === 'object' && typeof node.type === 'string'; + } + + function isProperty(nodeType, key) { + return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key; + } + + function candidateExistsInLeaveList(leavelist, candidate) { + for (let i = leavelist.length - 1; i >= 0; --i) { + if (leavelist[i].node === candidate) { + return true; + } + } + return false; + } + class Controller { // API: @@ -370,261 +388,241 @@ this.__keys = Object.assign(Object.create(this.__keys), visitor.keys); } } - } - - function isNode(node) { - if (node == null) { - return false; - } - return typeof node === 'object' && typeof node.type === 'string'; - } - - function isProperty(nodeType, key) { - return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key; - } - function candidateExistsInLeaveList(leavelist, candidate) { - for (let i = leavelist.length - 1; i >= 0; --i) { - if (leavelist[i].node === candidate) { - return true; - } - } - return false; - } + traverse (root, visitor) { + this.__initialize(root, visitor); - Controller.prototype.traverse = function traverse(root, visitor) { - this.__initialize(root, visitor); + const sentinel = {}; - const sentinel = {}; + // reference + const worklist = this.__worklist; + const leavelist = this.__leavelist; - // reference - const worklist = this.__worklist; - const leavelist = this.__leavelist; + // initialize + worklist.push(new Element(root, null, null, null)); + leavelist.push(new Element(null, null, null, null)); - // initialize - worklist.push(new Element(root, null, null, null)); - leavelist.push(new Element(null, null, null, null)); + while (worklist.length) { + let element = worklist.pop(); - while (worklist.length) { - let element = worklist.pop(); + let ret; + if (element === sentinel) { + element = leavelist.pop(); - let ret; - if (element === sentinel) { - element = leavelist.pop(); + ret = this.__execute(visitor.leave, element); - ret = this.__execute(visitor.leave, element); - - if (this.__state === BREAK || ret === BREAK) { - return; + if (this.__state === BREAK || ret === BREAK) { + return; + } + continue; } - continue; - } - - if (element.node) { - ret = this.__execute(visitor.enter, element); + if (element.node) { - if (this.__state === BREAK || ret === BREAK) { - return; - } + ret = this.__execute(visitor.enter, element); - worklist.push(sentinel); - leavelist.push(element); + if (this.__state === BREAK || ret === BREAK) { + return; + } - if (this.__state === SKIP || ret === SKIP) { - continue; - } + worklist.push(sentinel); + leavelist.push(element); - const { node } = element; - const nodeType = node.type || element.wrap; - let candidates = this.__keys[nodeType]; - if (!candidates) { - if (this.__fallback) { - candidates = this.__fallback(node); - } else { - throw new Error(`Unknown node type ${ nodeType }.`); + if (this.__state === SKIP || ret === SKIP) { + continue; } - } - let current = candidates.length; - while ((current -= 1) >= 0) { - const key = candidates[current]; - const candidate = node[key]; - if (!candidate) { - continue; + const { node } = element; + const nodeType = node.type || element.wrap; + let candidates = this.__keys[nodeType]; + if (!candidates) { + if (this.__fallback) { + candidates = this.__fallback(node); + } else { + throw new Error(`Unknown node type ${ nodeType }.`); + } } - if (Array.isArray(candidate)) { - let current2 = candidate.length; - while ((current2 -= 1) >= 0) { - if (!candidate[current2]) { - continue; - } + let current = candidates.length; + while ((current -= 1) >= 0) { + const key = candidates[current]; + const candidate = node[key]; + if (!candidate) { + continue; + } - if (candidateExistsInLeaveList(leavelist, candidate[current2])) { - continue; + if (Array.isArray(candidate)) { + let current2 = candidate.length; + while ((current2 -= 1) >= 0) { + if (!candidate[current2]) { + continue; + } + + if (candidateExistsInLeaveList(leavelist, candidate[current2])) { + continue; + } + + if (isProperty(nodeType, candidates[current])) { + element = new Element(candidate[current2], [key, current2], 'Property', null); + } else if (isNode(candidate[current2])) { + element = new Element(candidate[current2], [key, current2], null, null); + } else { + continue; + } + worklist.push(element); } - - if (isProperty(nodeType, candidates[current])) { - element = new Element(candidate[current2], [key, current2], 'Property', null); - } else if (isNode(candidate[current2])) { - element = new Element(candidate[current2], [key, current2], null, null); - } else { + } else if (isNode(candidate)) { + if (candidateExistsInLeaveList(leavelist, candidate)) { continue; } - worklist.push(element); - } - } else if (isNode(candidate)) { - if (candidateExistsInLeaveList(leavelist, candidate)) { - continue; - } - worklist.push(new Element(candidate, key, null, null)); + worklist.push(new Element(candidate, key, null, null)); + } } } } } - }; - Controller.prototype.replace = function replace(root, visitor) { - function removeElem(element) { - let i, - key, - nextElem, - parent; - - if (element.ref.remove()) { - // When the reference is an element of an array. - ({ key, parent } = element.ref); - - // If removed from array, then decrease following items' keys. - i = worklist.length; - while (i--) { - nextElem = worklist[i]; - if (nextElem.ref && nextElem.ref.parent === parent) { - if (nextElem.ref.key < key) { - break; + replace (root, visitor) { + function removeElem(element) { + let i, + key, + nextElem, + parent; + + if (element.ref.remove()) { + // When the reference is an element of an array. + ({ key, parent } = element.ref); + + // If removed from array, then decrease following items' keys. + i = worklist.length; + while (i--) { + nextElem = worklist[i]; + if (nextElem.ref && nextElem.ref.parent === parent) { + if (nextElem.ref.key < key) { + break; + } + --nextElem.ref.key; } - --nextElem.ref.key; } } } - } - this.__initialize(root, visitor); + this.__initialize(root, visitor); - const sentinel = {}; + const sentinel = {}; - // reference - const worklist = this.__worklist; - const leavelist = this.__leavelist; + // reference + const worklist = this.__worklist; + const leavelist = this.__leavelist; - // initialize - const outer = { - root - }; - let element = new Element(root, null, null, new Reference(outer, 'root')); - worklist.push(element); - leavelist.push(element); + // initialize + const outer = { + root + }; + let element = new Element(root, null, null, new Reference(outer, 'root')); + worklist.push(element); + leavelist.push(element); + + while (worklist.length) { + element = worklist.pop(); + + if (element === sentinel) { + element = leavelist.pop(); + + const target = this.__execute(visitor.leave, element); + + // node may be replaced with null, + // so distinguish between undefined and null in this place + if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) { + // replace + element.ref.replace(target); + } - while (worklist.length) { - element = worklist.pop(); + if (this.__state === REMOVE || target === REMOVE) { + removeElem(element); + } - if (element === sentinel) { - element = leavelist.pop(); + if (this.__state === BREAK || target === BREAK) { + return outer.root; + } + continue; + } - const target = this.__execute(visitor.leave, element); + const target = this.__execute(visitor.enter, element); // node may be replaced with null, // so distinguish between undefined and null in this place if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) { // replace element.ref.replace(target); + element.node = target; } if (this.__state === REMOVE || target === REMOVE) { removeElem(element); + element.node = null; } if (this.__state === BREAK || target === BREAK) { return outer.root; } - continue; - } - - const target = this.__execute(visitor.enter, element); - - // node may be replaced with null, - // so distinguish between undefined and null in this place - if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) { - // replace - element.ref.replace(target); - element.node = target; - } - - if (this.__state === REMOVE || target === REMOVE) { - removeElem(element); - element.node = null; - } - - if (this.__state === BREAK || target === BREAK) { - return outer.root; - } - - // node may be null - const { node } = element; - if (!node) { - continue; - } - worklist.push(sentinel); - leavelist.push(element); + // node may be null + const { node } = element; + if (!node) { + continue; + } - if (this.__state === SKIP || target === SKIP) { - continue; - } + worklist.push(sentinel); + leavelist.push(element); - const nodeType = node.type || element.wrap; - let candidates = this.__keys[nodeType]; - if (!candidates) { - if (this.__fallback) { - candidates = this.__fallback(node); - } else { - throw new Error(`Unknown node type ${ nodeType }.`); + if (this.__state === SKIP || target === SKIP) { + continue; } - } - let current = candidates.length; - while ((current -= 1) >= 0) { - const key = candidates[current]; - const candidate = node[key]; - if (!candidate) { - continue; + const nodeType = node.type || element.wrap; + let candidates = this.__keys[nodeType]; + if (!candidates) { + if (this.__fallback) { + candidates = this.__fallback(node); + } else { + throw new Error(`Unknown node type ${ nodeType }.`); + } } - if (Array.isArray(candidate)) { - let current2 = candidate.length; - while ((current2 -= 1) >= 0) { - if (!candidate[current2]) { - continue; - } - if (isProperty(nodeType, candidates[current])) { - element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2)); - } else if (isNode(candidate[current2])) { - element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2)); - } else { - continue; + let current = candidates.length; + while ((current -= 1) >= 0) { + const key = candidates[current]; + const candidate = node[key]; + if (!candidate) { + continue; + } + + if (Array.isArray(candidate)) { + let current2 = candidate.length; + while ((current2 -= 1) >= 0) { + if (!candidate[current2]) { + continue; + } + if (isProperty(nodeType, candidates[current])) { + element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2)); + } else if (isNode(candidate[current2])) { + element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2)); + } else { + continue; + } + worklist.push(element); } - worklist.push(element); + } else if (isNode(candidate)) { + worklist.push(new Element(candidate, key, null, new Reference(node, key))); } - } else if (isNode(candidate)) { - worklist.push(new Element(candidate, key, null, new Reference(node, key))); } } - } - return outer.root; - }; + return outer.root; + } + } function traverse(root, visitor) { const controller = new Controller(); @@ -763,5 +761,15 @@ exports.cloneEnvironment = function () { return clone({}); }; return exports; -}(exports)); +} /* vim: set sw=4 ts=4 et tw=80 : */ + +const { + Syntax, traverse, replace, attachComments, + VisitorKeys, VisitorOption, Controller, cloneEnvironment +} = clone({}); + +export { + Syntax, traverse, replace, attachComments, + VisitorKeys, VisitorOption, Controller, cloneEnvironment +}; From 4c737cf4bd8c8ccb35852d81ec6985ea5c3821bf Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 29 Apr 2020 23:15:40 +0800 Subject: [PATCH 5/8] - Update tests to check against source --- test/controller.js | 2 +- test/dumper.js | 2 +- test/replace.js | 2 +- test/traverse.js | 2 +- test/type.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/controller.js b/test/controller.js index aadc51f..a96e2bd 100644 --- a/test/controller.js +++ b/test/controller.js @@ -21,7 +21,7 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import { Controller } from '../'; +import { Controller } from '../src/estraverse.js'; import Dumper from './dumper'; import checkDump from './checkDump'; diff --git a/test/dumper.js b/test/dumper.js index 7a7ee9b..c8eccba 100644 --- a/test/dumper.js +++ b/test/dumper.js @@ -21,7 +21,7 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import { traverse, VisitorOption } from '..'; +import { traverse, VisitorOption } from '../src/estraverse.js'; export default class Dumper { constructor() { diff --git a/test/replace.js b/test/replace.js index 7b1bc13..2af6bf8 100644 --- a/test/replace.js +++ b/test/replace.js @@ -21,7 +21,7 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import { replace, VisitorOption } from '..'; +import { replace, VisitorOption } from '../src/estraverse.js'; describe('replace', function() { it('can simplify expressions', function() { diff --git a/test/traverse.js b/test/traverse.js index 69a2d14..ae1323c 100644 --- a/test/traverse.js +++ b/test/traverse.js @@ -23,7 +23,7 @@ import Dumper from './dumper'; import checkDump from './checkDump'; -import { traverse } from '..'; +import { traverse } from '../src/estraverse.js'; describe('object expression', function() { it('properties', function() { diff --git a/test/type.js b/test/type.js index 342104f..db1a3e5 100644 --- a/test/type.js +++ b/test/type.js @@ -22,7 +22,7 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import { traverse } from '..'; +import { traverse } from '../src/estraverse.js'; describe('type API', function() { it('for Property', function() { From 3f2f96af853205a6dbcd7fc375db4ce7e7380981 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 30 Apr 2020 15:46:01 +0800 Subject: [PATCH 6/8] - Use file extensions --- test/controller.js | 4 ++-- test/es6.js | 4 ++-- test/traverse.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/controller.js b/test/controller.js index a96e2bd..62b3e43 100644 --- a/test/controller.js +++ b/test/controller.js @@ -22,8 +22,8 @@ import { Controller } from '../src/estraverse.js'; -import Dumper from './dumper'; -import checkDump from './checkDump'; +import Dumper from './dumper.js'; +import checkDump from './checkDump.js'; describe('controller', function() { it('traverse', function() { diff --git a/test/es6.js b/test/es6.js index 47e0fac..e3ce920 100644 --- a/test/es6.js +++ b/test/es6.js @@ -22,8 +22,8 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import Dumper from './dumper'; -import checkDump from './checkDump'; +import Dumper from './dumper.js'; +import checkDump from './checkDump.js'; import { parse as esprima } from 'esprima'; import { parse as espree } from 'espree'; diff --git a/test/traverse.js b/test/traverse.js index ae1323c..827a3db 100644 --- a/test/traverse.js +++ b/test/traverse.js @@ -21,8 +21,8 @@ // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import Dumper from './dumper'; -import checkDump from './checkDump'; +import Dumper from './dumper.js'; +import checkDump from './checkDump.js'; import { traverse } from '../src/estraverse.js'; describe('object expression', function() { From 82b6a7dc1c857301c33da1b55f0cfef333d03c39 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Fri, 30 Apr 2021 10:06:21 +0800 Subject: [PATCH 7/8] Update devDeps. --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ec2b7bc..74b08a9 100644 --- a/package.json +++ b/package.json @@ -33,15 +33,15 @@ "@babel/core": "^7.17.5", "@babel/preset-env": "^7.16.11", "@babel/register": "^7.17.0", - "@rollup/plugin-babel": "^5.0.0", + "@rollup/plugin-babel": "^5.3.1", "chai": "^4.3.6", - "eslint": "^6.8.0", + "eslint": "^8.10.0", "espree": "^9.3.1", "esprima": "^4.0.1", "mocha": "^9.2.1", "nyc": "^15.1.0", - "rollup": "^2.7.3", - "rollup-plugin-terser": "^5.3.0" + "rollup": "^2.70.0", + "rollup-plugin-terser": "^7.0.2" }, "license": "BSD-2-Clause", "nyc": { From f9e04bbbecd29b6467ae8a08985fa2f6c67cd539 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 10 Mar 2022 17:59:47 +0800 Subject: [PATCH 8/8] - Switch to native ESM --- .eslintrc.js => .eslintrc.cjs | 0 package.json | 14 +++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) rename .eslintrc.js => .eslintrc.cjs (100%) diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 100% rename from .eslintrc.js rename to .eslintrc.cjs diff --git a/package.json b/package.json index 74b08a9..a0385c3 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,12 @@ "description": "ECMAScript JS AST traversal functions", "homepage": "https://github.com/estools/estraverse", "bugs": "https://github.com/estools/estraverse/issues", - "main": "dist/estraverse.min.js", - "module": "dist/estraverse.esm.min.js", + "type": "module", + "main": "./dist/estraverse.min.js", + "exports": { + "import": "./dist/estraverse.esm.min.js", + "require": "./dist/estraverse.min.js" + }, "version": "5.3.0", "engines": { "node": ">=4.0" @@ -34,17 +38,17 @@ "@babel/preset-env": "^7.16.11", "@babel/register": "^7.17.0", "@rollup/plugin-babel": "^5.3.1", + "c8": "^7.11.0", "chai": "^4.3.6", "eslint": "^8.10.0", "espree": "^9.3.1", "esprima": "^4.0.1", "mocha": "^9.2.1", - "nyc": "^15.1.0", "rollup": "^2.70.0", "rollup-plugin-terser": "^7.0.2" }, "license": "BSD-2-Clause", - "nyc": { + "c8": { "branches": 100, "lines": 100, "functions": 100, @@ -61,6 +65,6 @@ "build": "rollup -c", "test": "npm run lint && npm run unit-test", "lint": "eslint .", - "unit-test": "nyc mocha --require chai/register-expect.js --require @babel/register" + "unit-test": "c8 mocha --require chai/register-expect.js --require @babel/register" } }