Skip to content

Commit 5887c4d

Browse files
brettz9fasttimelumirlumir
authored
feat: TypeScript support for Espree (#705)
* feat: typescript Also: - test: complete coverage * Update packages/espree/package.json Co-authored-by: Francesco Trotta <github@fasttime.org> * rename ParserOptions -> Options * rename ecmaVersion -> EcmaVersion; remove unused imports * cast lastTemplateToken as (defined) acorn.Token * add tsd tests and inspired fixes (making arguments optional) * add further public exports * tsd fixes * Update packages/espree/package.json Co-authored-by: Francesco Trotta <github@fasttime.org> * assume loc and range are defined * liberalize version string type * remove unused import * moving definitions to main export file; attempt to avoid lib folder * Revert "moving definitions to main export file; attempt to avoid lib folder" but keep exported types within espree.js This reverts commit d558469. * inline BaseEsprimaToken * delete dist/lib at end of build * check dist file types; make eslint-visitor-keys explicit to avoid broken type dependency * remove dist/lib with evaluated script * revert to stable acorn-jsx * inline acorn-jsx types * Update packages/espree/lib/espree.js Co-authored-by: 루밀LuMir <rpfos@naver.com> * switch from Integer to number * liberalize version * Update packages/espree/tsconfig.json Co-authored-by: 루밀LuMir <rpfos@naver.com> * Update packages/espree/tsconfig-cjs.json Co-authored-by: 루밀LuMir <rpfos@naver.com> * use bare import specifier * set `getLatestEcmaVersion` to last `const` value in union * narrow parse return type to acorn.Program * reuse existing import for types * double quotes * Update packages/espree/lib/espree.js Co-authored-by: 루밀LuMir <rpfos@naver.com> * Update packages/espree/espree.js Co-authored-by: 루밀LuMir <rpfos@naver.com> * Update packages/espree/lib/options.js Co-authored-by: 루밀LuMir <rpfos@naver.com> * use `@import` * Update packages/espree/espree.js Co-authored-by: 루밀LuMir <rpfos@naver.com> * normalizedEcmaVersion -> NormalizedEcmaVersion * infer _R -> unknown[] Co-authored-by: Francesco Trotta <github@fasttime.org> * move JSDoc types after imports * use EcmaVersion internally to options.js function * revert EcmaVersion values and add need for updating to CONTRIBUTING --------- Co-authored-by: Francesco Trotta <github@fasttime.org> Co-authored-by: 루밀LuMir <rpfos@naver.com>
1 parent 010de9c commit 5887c4d

12 files changed

Lines changed: 917 additions & 308 deletions

File tree

packages/espree/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Our full contribution guidelines can be found at:
1010
# How to upgrade `acorn` to support new syntax
1111

1212
1. `npm install acorn@latest`
13-
1. If a new `ecmaVersion` value is added, update `SUPPORTED_VERSIONS` constant in `lib/options.js` and tests in `tests/lib/supported-ecmaversions.js`.
13+
1. If a new `ecmaVersion` value is added, update the `SUPPORTED_VERSIONS` constant in `lib/options.js`, the tests in `tests/lib/supported-ecmaversions.js`, and the `EcmaVersion` type.
1414
1. If new token types are added, update `lib/token-translator.js` file to translate the tokens.
1515
1. Add tests in `tests/fixtures/ecma-version/<ecma-vesion>/`.
1616
- Add a directory named the new syntax name.

packages/espree/espree.cts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import * as espree from "./espree.js";
2+
export = espree;

packages/espree/espree.js

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

58-
5958
import * as acorn from "acorn";
6059
import jsx from "acorn-jsx";
6160
import espree from "./lib/espree.js";
6261
import * as visitorKeys from "eslint-visitor-keys";
6362
import { getLatestEcmaVersion, getSupportedEcmaVersions } from "./lib/options.js";
6463

64+
/**
65+
* @import { EspreeParserCtor, EspreeParserJsxCtor } from "./lib/types.js";
66+
*/
67+
68+
// ----------------------------------------------------------------------------
69+
// Types exported from file
70+
// ----------------------------------------------------------------------------
71+
/**
72+
* @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
73+
*/
74+
75+
/**
76+
* @typedef {{
77+
* type: string;
78+
* value: any;
79+
* start?: number;
80+
* end?: number;
81+
* loc?: acorn.SourceLocation;
82+
* range?: [number, number];
83+
* regex?: {flags: string, pattern: string};
84+
* }} EspreeToken
85+
*/
86+
87+
/**
88+
* @typedef {{
89+
* type: "Block" | "Hashbang" | "Line",
90+
* value: string,
91+
* range?: [number, number],
92+
* start?: number,
93+
* end?: number,
94+
* loc?: {
95+
* start: acorn.Position | undefined,
96+
* end: acorn.Position | undefined
97+
* }
98+
* }} EspreeComment
99+
*/
100+
101+
/**
102+
* @typedef {{
103+
* comments?: EspreeComment[]
104+
* } & EspreeToken[]} EspreeTokens
105+
*/
106+
107+
/**
108+
* `allowReserved` is as in `acorn.Options`
109+
*
110+
* `ecmaVersion` currently as in `acorn.Options` though optional
111+
*
112+
* `sourceType` as in `acorn.Options` but also allows `commonjs`
113+
*
114+
* `ecmaFeatures`, `range`, `loc`, `tokens` are not in `acorn.Options`
115+
*
116+
* `comment` is not in `acorn.Options` and doesn't err without it, but is used
117+
*/
118+
/**
119+
* @typedef {{
120+
* allowReserved?: boolean,
121+
* ecmaVersion?: EcmaVersion,
122+
* sourceType?: "script"|"module"|"commonjs",
123+
* ecmaFeatures?: {
124+
* jsx?: boolean,
125+
* globalReturn?: boolean,
126+
* impliedStrict?: boolean
127+
* },
128+
* range?: boolean,
129+
* loc?: boolean,
130+
* tokens?: boolean,
131+
* comment?: boolean,
132+
* }} Options
133+
*/
65134

66135
// To initialize lazily.
67136
const parsers = {
137+
138+
/** @type {EspreeParserCtor|null} */
68139
_regular: null,
140+
141+
/** @type {EspreeParserJsxCtor|null} */
69142
_jsx: null,
70143

144+
/**
145+
* Returns regular Parser
146+
* @returns {EspreeParserCtor} Regular Acorn parser
147+
*/
71148
get regular() {
72149
if (this._regular === null) {
73-
this._regular = acorn.Parser.extend(espree());
150+
const espreeParserFactory = /** @type {unknown} */ (espree());
151+
152+
this._regular = /** @type {EspreeParserCtor} */ (
153+
// Without conversion, types are incompatible, as
154+
// acorn's has a protected constructor
155+
/** @type {unknown} */
156+
(acorn.Parser.extend(
157+
/**
158+
* @type {(
159+
* BaseParser: typeof acorn.Parser
160+
* ) => typeof acorn.Parser}
161+
*/ (espreeParserFactory)
162+
))
163+
);
74164
}
75165
return this._regular;
76166
},
77167

168+
/**
169+
* Returns JSX Parser
170+
* @returns {EspreeParserJsxCtor} JSX Acorn parser
171+
*/
78172
get jsx() {
79173
if (this._jsx === null) {
80-
this._jsx = acorn.Parser.extend(jsx(), espree());
174+
const espreeParserFactory = /** @type {unknown} */ (espree());
175+
const jsxFactory = jsx();
176+
177+
this._jsx = /** @type {EspreeParserJsxCtor} */ (
178+
// Without conversion, types are incompatible, as
179+
// acorn's has a protected constructor
180+
/** @type {unknown} */
181+
(acorn.Parser.extend(
182+
jsxFactory,
183+
184+
/** @type {(BaseParser: typeof acorn.Parser) => typeof acorn.Parser} */
185+
(espreeParserFactory)
186+
))
187+
);
81188
}
82189
return this._jsx;
83190
},
84191

192+
/**
193+
* Gets the parser object based on the supplied options.
194+
* @param {Options} [options] The parser options.
195+
* @returns {EspreeParserJsxCtor|EspreeParserCtor} Regular or JSX Acorn parser
196+
*/
85197
get(options) {
86198
const useJsx = Boolean(
87199
options &&
@@ -100,9 +212,9 @@ const parsers = {
100212
/**
101213
* Tokenizes the given code.
102214
* @param {string} code The code to tokenize.
103-
* @param {Object} options Options defining how to tokenize.
104-
* @returns {Token[]} An array of tokens.
105-
* @throws {SyntaxError} If the input code is invalid.
215+
* @param {Options} [options] Options defining how to tokenize.
216+
* @returns {EspreeTokens} An array of tokens.
217+
* @throws {EnhancedSyntaxError} If the input code is invalid.
106218
* @private
107219
*/
108220
export function tokenize(code, options) {
@@ -113,7 +225,7 @@ export function tokenize(code, options) {
113225
options = Object.assign({}, options, { tokens: true }); // eslint-disable-line no-param-reassign -- stylistic choice
114226
}
115227

116-
return new Parser(options, code).tokenize();
228+
return /** @type {EspreeTokens} */ (new Parser(options, code).tokenize());
117229
}
118230

119231
//------------------------------------------------------------------------------
@@ -123,9 +235,9 @@ export function tokenize(code, options) {
123235
/**
124236
* Parses the given code.
125237
* @param {string} code The code to tokenize.
126-
* @param {Object} options Options defining how to tokenize.
127-
* @returns {ASTNode} The "Program" AST node.
128-
* @throws {SyntaxError} If the input code is invalid.
238+
* @param {Options} [options] Options defining how to tokenize.
239+
* @returns {acorn.Program} The "Program" AST node.
240+
* @throws {EnhancedSyntaxError} If the input code is invalid.
129241
*/
130242
export function parse(code, options) {
131243
const Parser = parsers.get(options);
@@ -137,10 +249,14 @@ export function parse(code, options) {
137249
// Public
138250
//------------------------------------------------------------------------------
139251

252+
/** @type {string} */
140253
export const version = "11.0.0"; // x-release-please-version
141254
export const name = "espree";
142255

143256
/* istanbul ignore next */
257+
/**
258+
* @type {visitorKeys.VisitorKeys}
259+
*/
144260
export const VisitorKeys = (function() {
145261
return visitorKeys.KEYS;
146262
}());
@@ -149,6 +265,8 @@ export const VisitorKeys = (function() {
149265
/* istanbul ignore next */
150266
export const Syntax = (function() {
151267
let key,
268+
269+
/** @type {Record<string,string>} */
152270
types = {};
153271

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

0 commit comments

Comments
 (0)