diff --git a/acorn-loose/src/statement.js b/acorn-loose/src/statement.js index ac208424e..a5b574044 100644 --- a/acorn-loose/src/statement.js +++ b/acorn-loose/src/statement.js @@ -10,9 +10,7 @@ lp.parseTopLevel = function() { while (this.tok.type !== tt.eof) node.body.push(this.parseStatement()) this.toks.adaptDirectivePrologue(node.body) this.last = this.tok - if (this.options.ecmaVersion >= 6) { - node.sourceType = this.options.sourceType - } + node.sourceType = this.options.sourceType return this.finishNode(node, "Program") } diff --git a/acorn/CHANGELOG.md b/acorn/CHANGELOG.md index f2cd6a4b6..1438da677 100644 --- a/acorn/CHANGELOG.md +++ b/acorn/CHANGELOG.md @@ -1,3 +1,15 @@ +## 6.4.0 (2019-11-26) + +### New features + +Add a static `acorn` property to the `Parser` class that contains the entire module interface, to allow plugins to access the instance of the library that they are acting on. + +## 6.3.0 (2019-08-12) + +### New features + +`sourceType: "module"` can now be used even when `ecmaVersion` is less than 6, to parse module-style code that otherwise conforms to an older standard. + ## 6.2.1 (2019-07-21) ### Bug fixes diff --git a/acorn/README.md b/acorn/README.md index fa372ee68..bcf85ccba 100644 --- a/acorn/README.md +++ b/acorn/README.md @@ -64,6 +64,9 @@ an object containing any of these fields: either `"script"` or `"module"`. This influences global strict mode and parsing of `import` and `export` declarations. + **NOTE**: If set to `"module"`, then static `import` / `export` syntax + will be valid, even if `ecmaVersion` is less than 6. + - **onInsertedSemicolon**: If given a callback, that callback will be called whenever a missing semicolon is inserted by the parser. The callback will be given the character offset of the point where the diff --git a/acorn/dist/acorn.d.ts b/acorn/dist/acorn.d.ts index 9d6bb784f..c68e23912 100644 --- a/acorn/dist/acorn.d.ts +++ b/acorn/dist/acorn.d.ts @@ -36,14 +36,14 @@ declare namespace acorn { class Parser { constructor(options: Options, input: string, startPos?: number) - parse(): Node - static parse(input: string, options?: Options): Node - static parseExpressionAt(input: string, pos: number, options?: Options): Node - static tokenizer(input: string, options?: Options): { + parse(this: Parser): Node + static parse(this: typeof Parser, input: string, options?: Options): Node + static parseExpressionAt(this: typeof Parser, input: string, pos: number, options?: Options): Node + static tokenizer(this: typeof Parser, input: string, options?: Options): { getToken(): Token [Symbol.iterator](): Iterator } - static extend(...plugins: ((BaseParser: typeof Parser) => typeof Parser)[]): typeof Parser + static extend(this: typeof Parser, ...plugins: ((BaseParser: typeof Parser) => typeof Parser)[]): typeof Parser } interface Position { line: number; column: number; offset: number } diff --git a/acorn/package.json b/acorn/package.json index c6f812313..318bed138 100644 --- a/acorn/package.json +++ b/acorn/package.json @@ -4,7 +4,7 @@ "homepage": "https://github.com/acornjs/acorn", "main": "dist/acorn.js", "module": "dist/acorn.mjs", - "version": "6.2.1", + "version": "6.4.1", "engines": {"node": ">=0.4.0"}, "maintainers": [ { diff --git a/acorn/src/expression.js b/acorn/src/expression.js index 7a4aed93b..b338141e8 100644 --- a/acorn/src/expression.js +++ b/acorn/src/expression.js @@ -272,7 +272,7 @@ pp.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow) if (computed || this.eat(tt.dot)) { let node = this.startNodeAt(startPos, startLoc) node.object = base - node.property = computed ? this.parseExpression() : this.parseIdent(true) + node.property = computed ? this.parseExpression() : this.parseIdent(this.options.allowReserved !== "never") node.computed = !!computed if (computed) this.expect(tt.bracketR) base = this.finishNode(node, "MemberExpression") @@ -734,7 +734,7 @@ pp.parsePropertyName = function(prop) { prop.computed = false } } - return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom() : this.parseIdent(true) + return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== "never") } // Initialize empty function node. @@ -902,7 +902,6 @@ pp.checkUnreserved = function({start, end, name}) { pp.parseIdent = function(liberal, isBinding) { let node = this.startNode() - if (liberal && this.options.allowReserved === "never") liberal = false if (this.type === tt.name) { node.name = this.value } else if (this.type.keyword) { diff --git a/acorn/src/identifier.js b/acorn/src/identifier.js index c768a2d79..339066260 100644 --- a/acorn/src/identifier.js +++ b/acorn/src/identifier.js @@ -14,6 +14,7 @@ const ecma5AndLessKeywords = "break case catch continue debugger default do else export const keywords = { 5: ecma5AndLessKeywords, + "5module": ecma5AndLessKeywords + " export import", 6: ecma5AndLessKeywords + " const class extends export import super" } diff --git a/acorn/src/index.js b/acorn/src/index.js index 1fb6877ff..506a989dd 100644 --- a/acorn/src/index.js +++ b/acorn/src/index.js @@ -22,17 +22,58 @@ import "./expression" import "./location" import "./scope" -export {Parser} from "./state" -export {defaultOptions} from "./options" -export {Position, SourceLocation, getLineInfo} from "./locutil" -export {Node} from "./node" -export {TokenType, types as tokTypes, keywords as keywordTypes} from "./tokentype" -export {TokContext, types as tokContexts} from "./tokencontext" -export {isIdentifierChar, isIdentifierStart} from "./identifier" -export {Token} from "./tokenize" -export {isNewLine, lineBreak, lineBreakG, nonASCIIwhitespace} from "./whitespace" - -export const version = "6.2.1" +import {defaultOptions} from "./options" +import {Position, SourceLocation, getLineInfo} from "./locutil" +import {Node} from "./node" +import {TokenType, types as tokTypes, keywords as keywordTypes} from "./tokentype" +import {TokContext, types as tokContexts} from "./tokencontext" +import {isIdentifierChar, isIdentifierStart} from "./identifier" +import {Token} from "./tokenize" +import {isNewLine, lineBreak, lineBreakG, nonASCIIwhitespace} from "./whitespace" + +export const version = "6.4.0" +export { + Parser, + defaultOptions, + Position, + SourceLocation, + getLineInfo, + Node, + TokenType, + tokTypes, + keywordTypes, + TokContext, + tokContexts, + isIdentifierChar, + isIdentifierStart, + Token, + isNewLine, + lineBreak, + lineBreakG, + nonASCIIwhitespace +} + +Parser.acorn = { + Parser, + version, + defaultOptions, + Position, + SourceLocation, + getLineInfo, + Node, + TokenType, + tokTypes, + keywordTypes, + TokContext, + tokContexts, + isIdentifierChar, + isIdentifierStart, + Token, + isNewLine, + lineBreak, + lineBreakG, + nonASCIIwhitespace +} // The main exported interface (under `self.acorn` when in the // browser) is a `parse` function that takes a code string and diff --git a/acorn/src/regexp.js b/acorn/src/regexp.js index ee19bcf55..2fe832b4b 100644 --- a/acorn/src/regexp.js +++ b/acorn/src/regexp.js @@ -50,7 +50,8 @@ export class RegExpValidationState { if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) { return c } - return (c << 10) + s.charCodeAt(i + 1) - 0x35FDC00 + const next = s.charCodeAt(i + 1) + return next >= 0xDC00 && next <= 0xDFFF ? (c << 10) + next - 0x35FDC00 : c } nextIndex(i) { @@ -59,8 +60,9 @@ export class RegExpValidationState { if (i >= l) { return l } - const c = s.charCodeAt(i) - if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) { + let c = s.charCodeAt(i), next + if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l || + (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) { return i + 1 } return i + 2 diff --git a/acorn/src/state.js b/acorn/src/state.js index 73e49b9f3..eb4e0e120 100644 --- a/acorn/src/state.js +++ b/acorn/src/state.js @@ -9,7 +9,7 @@ export class Parser { constructor(options, input, startPos) { this.options = options = getOptions(options) this.sourceFile = options.sourceFile - this.keywords = wordsRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5]) + this.keywords = wordsRegexp(keywords[options.ecmaVersion >= 6 ? 6 : options.sourceType === "module" ? "5module" : 5]) let reserved = "" if (options.allowReserved !== true) { for (let v = options.ecmaVersion;; v--) diff --git a/acorn/src/statement.js b/acorn/src/statement.js index d423b5910..8cadb5118 100644 --- a/acorn/src/statement.js +++ b/acorn/src/statement.js @@ -27,9 +27,7 @@ pp.parseTopLevel = function(node) { this.raiseRecoverable(this.undefinedExports[name].start, `Export '${name}' is not defined`) this.adaptDirectivePrologue(node.body) this.next() - if (this.options.ecmaVersion >= 6) { - node.sourceType = this.options.sourceType - } + node.sourceType = this.options.sourceType return this.finishNode(node, "Program") } diff --git a/test/tests-regexp.js b/test/tests-regexp.js index 6c4719486..804e00a59 100644 --- a/test/tests-regexp.js +++ b/test/tests-regexp.js @@ -1049,6 +1049,7 @@ test("/[\\d][\\12-\\14]{1,}[^\\d]/", {}, { ecmaVersion: 2015 }) testFail("/[\\d][\\12-\\14]{1,}[^\\d]/u", "Invalid regular expression flag (1:1)", { ecmaVersion: 5 }) testFail("/[\\d][\\12-\\14]{1,}[^\\d]/u", "Invalid regular expression: /[\\d][\\12-\\14]{1,}[^\\d]/: Invalid class escape (1:1)", { ecmaVersion: 2015 }) test("/([a ]\\b)*\\b/", {}, { ecmaVersion: 5 }) +test("/[x-*]/u".replace("*", String.fromCharCode(0xd800)), {}, {ecmaVersion: 6}) /* // This is test case generator. diff --git a/test/tests.js b/test/tests.js index ca07b3a71..35fe29ca5 100644 --- a/test/tests.js +++ b/test/tests.js @@ -7,6 +7,62 @@ if (typeof exports != "undefined") { var acorn = require("../acorn"); } +test("import ''", { + type: "Program", + start: 0, + end: 9, + body: [ + { + type: "ImportDeclaration", + start: 0, + end: 9, + specifiers: [], + source: { + type: "Literal", + start: 7, + end: 9, + value: "", + raw: "''" + } + } + ] +}, { + ecmaVersion: 5, + sourceType: "module" +}); + +testFail("import('')", "Unexpected token (1:6)", { + ecmaVersion: 5, + sourceType: "module" +}); + +test("new Object", { + type: "Program", + start: 0, + end: 10, + body: [ + { + type: "ExpressionStatement", + start: 0, + end: 10, + expression: { + type: "NewExpression", + start: 0, + end: 10, + callee: { + type: "Identifier", + start: 4, + end: 10, + name: "Object" + }, + arguments: [] + } + } + ] +}, { + allowReserved: "never" +}); + test("this\n", { type: "Program", body: [