Skip to content

Commit 241fd44

Browse files
committed
feat: typescript
Also: - test: complete coverage
1 parent 229ebda commit 241fd44

File tree

8 files changed

+786
-323
lines changed

8 files changed

+786
-323
lines changed

packages/espree/espree.js

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,59 @@
5555
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5656
*/
5757

58+
/**
59+
* @import { EnhancedSyntaxError } from "./lib/espree.js";
60+
* @import { EspreeParserCtor, EspreeParserJsxCtor } from "./lib/types.js";
61+
*/
62+
63+
// ----------------------------------------------------------------------------
64+
// Types exported from file
65+
// ----------------------------------------------------------------------------
66+
/**
67+
* @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
68+
*/
69+
70+
/**
71+
* @typedef {import('./lib/token-translator.js').EsprimaToken} EspreeToken
72+
*/
73+
74+
/**
75+
* @typedef {import('./lib/espree.js').EsprimaComment} EspreeComment
76+
*/
77+
78+
/**
79+
* @typedef {{
80+
* comments?: EspreeComment[]
81+
* } & EspreeToken[]} EspreeTokens
82+
*/
83+
84+
/**
85+
* `allowReserved` is as in `acorn.Options`
86+
*
87+
* `ecmaVersion` currently as in `acorn.Options` though optional
88+
*
89+
* `sourceType` as in `acorn.Options` but also allows `commonjs`
90+
*
91+
* `ecmaFeatures`, `range`, `loc`, `tokens` are not in `acorn.Options`
92+
*
93+
* `comment` is not in `acorn.Options` and doesn't err without it, but is used
94+
*/
95+
/**
96+
* @typedef {{
97+
* allowReserved?: boolean,
98+
* ecmaVersion?: ecmaVersion,
99+
* sourceType?: "script"|"module"|"commonjs",
100+
* ecmaFeatures?: {
101+
* jsx?: boolean,
102+
* globalReturn?: boolean,
103+
* impliedStrict?: boolean
104+
* },
105+
* range?: boolean,
106+
* loc?: boolean,
107+
* tokens?: boolean,
108+
* comment?: boolean,
109+
* }} ParserOptions
110+
*/
58111

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

67120
// To initialize lazily.
68121
const parsers = {
122+
123+
/** @type {EspreeParserCtor|null} */
69124
_regular: null,
125+
126+
/** @type {EspreeParserJsxCtor|null} */
70127
_jsx: null,
71128

129+
/**
130+
* Returns regular Parser
131+
* @returns {EspreeParserCtor} Regular Acorn parser
132+
*/
72133
get regular() {
73134
if (this._regular === null) {
74-
this._regular = acorn.Parser.extend(espree());
135+
const espreeParserFactory = /** @type {unknown} */ (espree());
136+
137+
this._regular = /** @type {EspreeParserCtor} */ (
138+
// Without conversion, types are incompatible, as
139+
// acorn's has a protected constructor
140+
/** @type {unknown} */
141+
(acorn.Parser.extend(
142+
/**
143+
* @type {(
144+
* BaseParser: typeof acorn.Parser
145+
* ) => typeof acorn.Parser}
146+
*/ (espreeParserFactory)
147+
))
148+
);
75149
}
76150
return this._regular;
77151
},
78152

153+
/**
154+
* Returns JSX Parser
155+
* @returns {EspreeParserJsxCtor} JSX Acorn parser
156+
*/
79157
get jsx() {
80158
if (this._jsx === null) {
81-
this._jsx = acorn.Parser.extend(jsx(), espree());
159+
const espreeParserFactory = /** @type {unknown} */ (espree());
160+
const jsxFactory = jsx();
161+
162+
this._jsx = /** @type {EspreeParserJsxCtor} */ (
163+
// Without conversion, types are incompatible, as
164+
// acorn's has a protected constructor
165+
/** @type {unknown} */
166+
(acorn.Parser.extend(
167+
jsxFactory,
168+
169+
/** @type {(BaseParser: typeof acorn.Parser) => typeof acorn.Parser} */
170+
(espreeParserFactory)
171+
))
172+
);
82173
}
83174
return this._jsx;
84175
},
85176

177+
/**
178+
* Gets the parser object based on the supplied options.
179+
* @param {ParserOptions} options The parser options.
180+
* @returns {EspreeParserJsxCtor|EspreeParserCtor} Regular or JSX Acorn parser
181+
*/
86182
get(options) {
87183
const useJsx = Boolean(
88184
options &&
@@ -101,9 +197,9 @@ const parsers = {
101197
/**
102198
* Tokenizes the given code.
103199
* @param {string} code The code to tokenize.
104-
* @param {Object} options Options defining how to tokenize.
105-
* @returns {Token[]} An array of tokens.
106-
* @throws {SyntaxError} If the input code is invalid.
200+
* @param {ParserOptions} options Options defining how to tokenize.
201+
* @returns {EspreeTokens} An array of tokens.
202+
* @throws {EnhancedSyntaxError} If the input code is invalid.
107203
* @private
108204
*/
109205
export function tokenize(code, options) {
@@ -114,7 +210,7 @@ export function tokenize(code, options) {
114210
options = Object.assign({}, options, { tokens: true }); // eslint-disable-line no-param-reassign -- stylistic choice
115211
}
116212

117-
return new Parser(options, code).tokenize();
213+
return /** @type {EspreeTokens} */ (new Parser(options, code).tokenize());
118214
}
119215

120216
//------------------------------------------------------------------------------
@@ -124,9 +220,9 @@ export function tokenize(code, options) {
124220
/**
125221
* Parses the given code.
126222
* @param {string} code The code to tokenize.
127-
* @param {Object} options Options defining how to tokenize.
128-
* @returns {ASTNode} The "Program" AST node.
129-
* @throws {SyntaxError} If the input code is invalid.
223+
* @param {ParserOptions} options Options defining how to tokenize.
224+
* @returns {acorn.Node} The "Program" AST node.
225+
* @throws {EnhancedSyntaxError} If the input code is invalid.
130226
*/
131227
export function parse(code, options) {
132228
const Parser = parsers.get(options);
@@ -150,6 +246,8 @@ export const VisitorKeys = (function() {
150246
/* istanbul ignore next */
151247
export const Syntax = (function() {
152248
let key,
249+
250+
/** @type {Record<string,string>} */
153251
types = {};
154252

155253
if (typeof Object.create === "function") {

0 commit comments

Comments
 (0)