Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/espree/espree.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import * as espree from "./espree.js";
export = espree;
116 changes: 107 additions & 9 deletions packages/espree/espree.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,59 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
* @import { EnhancedSyntaxError } from "./lib/espree.js";
* @import { EspreeParserCtor, EspreeParserJsxCtor } from "./lib/types.js";
*/

// ----------------------------------------------------------------------------
// Types exported from file
// ----------------------------------------------------------------------------
/**
* @typedef {3|5|6|7|8|9|10|11|12|13|14|15|16|17|2015|2016|2017|2018|2019|2020|2021|2022|2023|2024|2025|2026|'latest'} EcmaVersion
*/

/**
* @typedef {import('./lib/token-translator.js').EsprimaToken} EspreeToken
*/

/**
* @typedef {import('./lib/espree.js').EsprimaComment} EspreeComment
*/

/**
* @typedef {{
* comments?: EspreeComment[]
* } & EspreeToken[]} EspreeTokens
*/

/**
* `allowReserved` is as in `acorn.Options`
*
* `ecmaVersion` currently as in `acorn.Options` though optional
*
* `sourceType` as in `acorn.Options` but also allows `commonjs`
*
* `ecmaFeatures`, `range`, `loc`, `tokens` are not in `acorn.Options`
*
* `comment` is not in `acorn.Options` and doesn't err without it, but is used
*/
/**
* @typedef {{
* allowReserved?: boolean,
* ecmaVersion?: EcmaVersion,
* sourceType?: "script"|"module"|"commonjs",
* ecmaFeatures?: {
* jsx?: boolean,
* globalReturn?: boolean,
* impliedStrict?: boolean
* },
* range?: boolean,
* loc?: boolean,
* tokens?: boolean,
* comment?: boolean,
* }} Options
*/

import * as acorn from "acorn";
import jsx from "acorn-jsx";
Expand All @@ -66,23 +119,66 @@ import { getLatestEcmaVersion, getSupportedEcmaVersions } from "./lib/options.js

// To initialize lazily.
const parsers = {

/** @type {EspreeParserCtor|null} */
_regular: null,

/** @type {EspreeParserJsxCtor|null} */
_jsx: null,

/**
* Returns regular Parser
* @returns {EspreeParserCtor} Regular Acorn parser
*/
get regular() {
if (this._regular === null) {
this._regular = acorn.Parser.extend(espree());
const espreeParserFactory = /** @type {unknown} */ (espree());

this._regular = /** @type {EspreeParserCtor} */ (
// Without conversion, types are incompatible, as
// acorn's has a protected constructor
/** @type {unknown} */
(acorn.Parser.extend(
/**
* @type {(
* BaseParser: typeof acorn.Parser
* ) => typeof acorn.Parser}
*/ (espreeParserFactory)
))
);
}
return this._regular;
},

/**
* Returns JSX Parser
* @returns {EspreeParserJsxCtor} JSX Acorn parser
*/
get jsx() {
if (this._jsx === null) {
this._jsx = acorn.Parser.extend(jsx(), espree());
const espreeParserFactory = /** @type {unknown} */ (espree());
const jsxFactory = jsx();

this._jsx = /** @type {EspreeParserJsxCtor} */ (
// Without conversion, types are incompatible, as
// acorn's has a protected constructor
/** @type {unknown} */
(acorn.Parser.extend(
jsxFactory,

/** @type {(BaseParser: typeof acorn.Parser) => typeof acorn.Parser} */
(espreeParserFactory)
))
);
}
return this._jsx;
},

/**
* Gets the parser object based on the supplied options.
* @param {Options} [options] The parser options.
* @returns {EspreeParserJsxCtor|EspreeParserCtor} Regular or JSX Acorn parser
*/
get(options) {
const useJsx = Boolean(
options &&
Expand All @@ -101,9 +197,9 @@ const parsers = {
/**
* Tokenizes the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {Token[]} An array of tokens.
* @throws {SyntaxError} If the input code is invalid.
* @param {Options} [options] Options defining how to tokenize.
* @returns {EspreeTokens} An array of tokens.
* @throws {EnhancedSyntaxError} If the input code is invalid.
* @private
*/
export function tokenize(code, options) {
Expand All @@ -114,7 +210,7 @@ export function tokenize(code, options) {
options = Object.assign({}, options, { tokens: true }); // eslint-disable-line no-param-reassign -- stylistic choice
}

return new Parser(options, code).tokenize();
return /** @type {EspreeTokens} */ (new Parser(options, code).tokenize());
}

//------------------------------------------------------------------------------
Expand All @@ -124,9 +220,9 @@ export function tokenize(code, options) {
/**
* Parses the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {ASTNode} The "Program" AST node.
* @throws {SyntaxError} If the input code is invalid.
* @param {Options} [options] Options defining how to tokenize.
* @returns {acorn.Node} The "Program" AST node.
* @throws {EnhancedSyntaxError} If the input code is invalid.
*/
export function parse(code, options) {
const Parser = parsers.get(options);
Expand All @@ -150,6 +246,8 @@ export const VisitorKeys = (function() {
/* istanbul ignore next */
export const Syntax = (function() {
let key,

/** @type {Record<string,string>} */
types = {};

if (typeof Object.create === "function") {
Expand Down
Loading