Skip to content

Commit ac7b14a

Browse files
committed
INCOMPLETE:
0. depends on acorn and acorn-jsx being updated per PRs a. acornjs/acorn#1104 (merged but not yet released) b. acornjs/acorn#1105 (merged but not yet released) c. acornjs/acorn-jsx#130 1. Refactor to avoid `typedef` shortcuts when not desired for export 2. Avoid dummy class, so parse JavaScript with https://github.com/es-joy/jsdoc-eslint-parser feat: Adds JSDoc-based TypeScript declaration file Also: 1. chore: adds `editorconfig` 2. refactor: Removes unused esprima 3. refactor: changes to force EspreeParser constructor to convert `new String` to plain string (for typing) 4. refactor: switches to `Object.keys` to avoid `hasOwnProperty` and easier for typing 5. refactor: drops a use of `acorn.Parser.extend` for typing purposes 6. refactor: checks for existence of `tokens` in `tokenize` (as may be absent) 7. refactor: checks for existence of `firstNode.range` and `firstNode.loc` in `parse` (as may be absent) 8. refactor: checks for existence of `extra.lastToken.range` and `extra.lastToken.loc` in `parse` (as may be absent) 7. feat: throws specific error if `jsx_readString` superclass undefined 8. refactor: checks for existence of `lastTemplateToken.loc` and `lastTemplateToken.range` in `token-translator.js` (as may be absent)
1 parent 5beccb4 commit ac7b14a

File tree

8 files changed

+695
-290
lines changed

8 files changed

+695
-290
lines changed

.editorconfig

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
; EditorConfig file: https://EditorConfig.org
2+
; Install the "EditorConfig" plugin into your editor to use
3+
4+
root = true
5+
6+
[*]
7+
charset = utf-8
8+
end_of_line = lf
9+
insert_final_newline = true
10+
indent_style = space
11+
indent_size = 4
12+
trim_trailing_whitespace = true
13+
14+
[*.md]
15+
indent_size = 2

espree.js

+77-15
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,50 @@
5656
*/
5757
/* eslint no-undefined:0, no-use-before-define: 0 */
5858

59+
/**
60+
* @typedef {import('acorn')} acorn
61+
* @typedef {typeof import('acorn-jsx').AcornJsxParser} AcornJsxParser
62+
* @typedef {import('./lib/espree').EnhancedSyntaxError} EnhancedSyntaxError
63+
* @typedef {typeof import('./lib/espree').EspreeParser} IEspreeParser
64+
* @typedef {acorn.ecmaVersion} ecmaVersion
65+
*/
66+
/**
67+
* `jsx.Options` gives us 2 optional properties, so extend it
68+
*
69+
* `allowReserved`, `ranges`, `locations`, `allowReturnOutsideFunction`,
70+
* `onToken`, and `onComment` are as in `acorn.Options`
71+
*
72+
* `ecmaVersion` as in `acorn.Options` though optional
73+
*
74+
* `sourceType` as in `acorn.Options` but also allows `commonjs`
75+
*
76+
* `ecmaFeatures`, `range`, `loc`, `tokens` are not in `acorn.Options`
77+
*
78+
* `comment` is not in `acorn.Options` and doesn't err without it, but is used
79+
* @typedef {{
80+
* allowReserved?: boolean | "never",
81+
* ranges?: boolean,
82+
* locations?: boolean,
83+
* allowReturnOutsideFunction?: boolean,
84+
* onToken?: ((token: acorn.Token) => any) | acorn.Token[]
85+
* onComment?: ((
86+
* isBlock: boolean, text: string, start: number, end: number, startLoc?: acorn.Position,
87+
* endLoc?: acorn.Position
88+
* ) => void) | acorn.Comment[]
89+
* ecmaVersion?: ecmaVersion,
90+
* sourceType?: "script"|"module"|"commonjs",
91+
* ecmaFeatures?: {
92+
* jsx?: boolean,
93+
* globalReturn?: boolean,
94+
* impliedStrict?: boolean
95+
* },
96+
* range?: boolean,
97+
* loc?: boolean,
98+
* tokens?: boolean | null
99+
* comment?: boolean,
100+
* } & jsx.Options} ParserOptions
101+
*/
102+
59103
import * as acorn from "acorn";
60104
import jsx from "acorn-jsx";
61105
import espree from "./lib/espree.js";
@@ -66,23 +110,43 @@ import { getLatestEcmaVersion, getSupportedEcmaVersions } from "./lib/options.js
66110

67111
// To initialize lazily.
68112
const parsers = {
69-
_regular: null,
70-
_jsx: null,
113+
_regular: /** @type {IEspreeParser|null} */ (null),
114+
_jsx: /** @type {IEspreeParser|null} */ (null),
71115

116+
/**
117+
* Returns regular Parser
118+
* @returns {IEspreeParser} Regular Acorn parser
119+
*/
72120
get regular() {
73121
if (this._regular === null) {
74-
this._regular = acorn.Parser.extend(espree());
122+
const espreeParserFactory = espree();
123+
124+
// Cast the `acorn.Parser` to our own for required properties not specified in *.d.ts
125+
this._regular = espreeParserFactory(/** @type {AcornJsxParser} */ (acorn.Parser));
75126
}
76127
return this._regular;
77128
},
78129

130+
/**
131+
* Returns JSX Parser
132+
* @returns {IEspreeParser} JSX Acorn parser
133+
*/
79134
get jsx() {
80135
if (this._jsx === null) {
81-
this._jsx = acorn.Parser.extend(jsx(), espree());
136+
const espreeParserFactory = espree();
137+
const jsxFactory = jsx();
138+
139+
// Cast the `acorn.Parser` to our own for required properties not specified in *.d.ts
140+
this._jsx = espreeParserFactory(/** @type {AcornJsxParser} */ (jsxFactory(acorn.Parser)));
82141
}
83142
return this._jsx;
84143
},
85144

145+
/**
146+
* Returns Regular or JSX Parser
147+
* @param {ParserOptions} options Parser options
148+
* @returns {IEspreeParser} Regular or JSX Acorn parser
149+
*/
86150
get(options) {
87151
const useJsx = Boolean(
88152
options &&
@@ -101,9 +165,9 @@ const parsers = {
101165
/**
102166
* Tokenizes the given code.
103167
* @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.
168+
* @param {ParserOptions} options Options defining how to tokenize.
169+
* @returns {acorn.Token[]|null} An array of tokens.
170+
* @throws {EnhancedSyntaxError} If the input code is invalid.
107171
* @private
108172
*/
109173
export function tokenize(code, options) {
@@ -124,9 +188,9 @@ export function tokenize(code, options) {
124188
/**
125189
* Parses the given code.
126190
* @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.
191+
* @param {ParserOptions} options Options defining how to tokenize.
192+
* @returns {acorn.Node} The "Program" AST node.
193+
* @throws {EnhancedSyntaxError} If the input code is invalid.
130194
*/
131195
export function parse(code, options) {
132196
const Parser = parsers.get(options);
@@ -148,17 +212,15 @@ export const VisitorKeys = (function() {
148212
// Derive node types from VisitorKeys
149213
/* istanbul ignore next */
150214
export const Syntax = (function() {
151-
let name,
215+
let /** @type {Object<string,string>} */
152216
types = {};
153217

154218
if (typeof Object.create === "function") {
155219
types = Object.create(null);
156220
}
157221

158-
for (name in VisitorKeys) {
159-
if (Object.hasOwnProperty.call(VisitorKeys, name)) {
160-
types[name] = name;
161-
}
222+
for (const name of Object.keys(VisitorKeys)) {
223+
types[name] = name;
162224
}
163225

164226
if (typeof Object.freeze === "function") {

0 commit comments

Comments
 (0)