@tsrx/core 0.1.49 → 0.1.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,17 +2,14 @@
2
2
  @import * as AST from 'estree'
3
3
  @import * as ESTreeJSX from 'estree-jsx'
4
4
  @import { Parse } from '../../types/parse'
5
+ @import { BaseNodeMetaData } from '../../types/index'
5
6
  @import { NonEmptyString } from '../../types/helpers'
6
7
  */
7
8
 
8
9
  import * as acorn from 'acorn';
9
10
  import { tsPlugin } from '@sveltejs/acorn-typescript';
10
11
  import { walk } from 'zimmerframe';
11
- import { has_location } from '../utils/ast.js';
12
-
13
- /**
14
- * @typedef {(BaseParser: typeof acorn.Parser) => typeof acorn.Parser} AcornPlugin
15
- */
12
+ import { has_location, node_children } from '../utils/ast.js';
16
13
 
17
14
  /** @type {Parse.BindingType} */
18
15
  export const BINDING_TYPES = {
@@ -87,14 +84,14 @@ export function isWhitespaceTextNode(node) {
87
84
  }
88
85
 
89
86
  /**
90
- * @type {AcornPlugin}
87
+ * @type {Parse.AcornPlugin}
91
88
  */
92
89
  function elementTemplateClosingTagPlugin(Base) {
93
- const jsxTagStart = /** @type {any} */ (Base).acornTypeScript?.tokTypes?.jsxTagStart;
90
+ const jsxTagStart = Base.acornTypeScript?.tokTypes?.jsxTagStart;
94
91
  if (!jsxTagStart) return Base;
95
92
 
96
93
  /**
97
- * @param {any} parser
94
+ * @param {Parse.Parser} parser
98
95
  */
99
96
  function inElementTemplateBodyDirect(parser) {
100
97
  const stack = parser.context;
@@ -104,7 +101,7 @@ function elementTemplateClosingTagPlugin(Base) {
104
101
  }
105
102
 
106
103
  /**
107
- * @param {any} parser
104
+ * @param {Parse.Parser} parser
108
105
  */
109
106
  function inElementTemplateBodyAnywhere(parser) {
110
107
  const stack = parser.context;
@@ -122,7 +119,7 @@ function elementTemplateClosingTagPlugin(Base) {
122
119
  }
123
120
 
124
121
  /**
125
- * @param {any} parser
122
+ * @param {Parse.Parser} parser
126
123
  */
127
124
  function isOpeningTagAfterReturnKeyword(parser) {
128
125
  if (parser.input.charCodeAt(parser.start + 1) === 47 /* '/' */) return false;
@@ -153,32 +150,26 @@ function elementTemplateClosingTagPlugin(Base) {
153
150
 
154
151
  return class extends Base {
155
152
  /** @param {number} code */
156
- // @ts-ignore — extending acorn's Parser with internal hooks
157
153
  getTokenFromCode(code) {
158
- if (code === 60 /* '<' */ && !(/** @type {any} */ (this).inType)) {
159
- const self = /** @type {any} */ (this);
154
+ if (code === 60 /* '<' */ && !this.inType) {
160
155
  const nextChar =
161
- self.pos + 1 < self.input.length ? self.input.charCodeAt(self.pos + 1) : -1;
162
- if (nextChar === 47 /* '/' */ && inElementTemplateBodyDirect(self)) {
163
- ++self.pos;
164
- return self.finishToken(jsxTagStart);
156
+ this.pos + 1 < this.input.length ? this.input.charCodeAt(this.pos + 1) : -1;
157
+ if (nextChar === 47 /* '/' */ && inElementTemplateBodyDirect(this)) {
158
+ ++this.pos;
159
+ return this.finishToken(jsxTagStart);
165
160
  }
166
161
  }
167
- // @ts-ignore — super dispatches to next layer in the plugin chain
168
162
  return super.getTokenFromCode(code);
169
163
  }
170
164
 
171
- // @ts-ignore — extending acorn's Parser with internal hooks
172
165
  canInsertSemicolon() {
173
- const self = /** @type {any} */ (this);
174
166
  if (
175
- self.type === jsxTagStart &&
176
- inElementTemplateBodyAnywhere(self) &&
177
- !isOpeningTagAfterReturnKeyword(self)
167
+ this.type === jsxTagStart &&
168
+ inElementTemplateBodyAnywhere(this) &&
169
+ !isOpeningTagAfterReturnKeyword(this)
178
170
  ) {
179
171
  return true;
180
172
  }
181
- // @ts-ignore
182
173
  return super.canInsertSemicolon();
183
174
  }
184
175
  };
@@ -190,16 +181,20 @@ function elementTemplateClosingTagPlugin(Base) {
190
181
  * This is the core factory for building tsrx-based parsers. Framework plugins (like TSRXPlugin)
191
182
  * extend the base parser with framework-specific syntax.
192
183
  *
193
- * @param {...(AcornPlugin | Function)} plugins - Framework parser plugins to compose
194
- * @returns {<T extends string>(source: string, filename: NonEmptyString<T>, options?: any) => AST.Program} A parse function
184
+ * @param {...Parse.AcornPlugin} plugins - Framework parser plugins to compose
185
+ * @returns {<T extends string>(source: string, filename: NonEmptyString<T>, options?: Parse.ParseFunctionOptions) => AST.Program} A parse function
195
186
  */
196
187
  export function createParser(...plugins) {
197
188
  const parser = /** @type {Parse.ParserConstructor} */ (
198
189
  /** @type {unknown} */ (
199
190
  acorn.Parser.extend(
200
191
  tsPlugin({ jsx: true }),
201
- ...plugins.map((p) => /** @type {AcornPlugin} */ (/** @type {unknown} */ (p))),
202
- elementTemplateClosingTagPlugin,
192
+ .../** @type {Array<(BaseParser: typeof acorn.Parser) => typeof acorn.Parser>} */ (
193
+ /** @type {unknown} */ (plugins)
194
+ ),
195
+ /** @type {(BaseParser: typeof acorn.Parser) => typeof acorn.Parser} */ (
196
+ /** @type {unknown} */ (elementTemplateClosingTagPlugin)
197
+ ),
203
198
  )
204
199
  )
205
200
  );
@@ -207,7 +202,7 @@ export function createParser(...plugins) {
207
202
  /**
208
203
  * @param {string} source
209
204
  * @param {string} filename
210
- * @param {any} [options]
205
+ * @param {Parse.ParseFunctionOptions} [options]
211
206
  * @returns {AST.Program}
212
207
  */
213
208
  return function parse(source, filename, options) {
@@ -225,20 +220,19 @@ export function createParser(...plugins) {
225
220
  // node records. The tokenizer is the only correct source — offset
226
221
  // arithmetic breaks on extra whitespace, and text search breaks on
227
222
  // comments (`async /* function */ function`).
228
- /** @type {Array<{ value: string, start: number, end: number, loc: AST.SourceLocation }> | undefined} */
223
+ /** @type {Parse.KeywordToken[] | undefined} */
229
224
  const keyword_tokens = options?.keywordTokens ? [] : undefined;
230
225
  /** @type {Parse.Options['onToken'] | undefined} */
231
226
  const onToken = keyword_tokens
232
227
  ? (token) => {
233
- const t = /** @type {any} */ (token);
234
- const is_function_keyword = t.type?.keyword === 'function';
235
- const is_async_name = t.type?.label === 'name' && t.value === 'async';
236
- if (is_function_keyword || is_async_name) {
228
+ const is_function_keyword = token.type.keyword === 'function';
229
+ const is_async_name = token.type.label === 'name' && token.value === 'async';
230
+ if ((is_function_keyword || is_async_name) && token.loc) {
237
231
  keyword_tokens.push({
238
232
  value: is_function_keyword ? 'function' : 'async',
239
- start: t.start,
240
- end: t.end,
241
- loc: t.loc,
233
+ start: token.start,
234
+ end: token.end,
235
+ loc: token.loc,
242
236
  });
243
237
  }
244
238
  }
@@ -273,7 +267,7 @@ export function createParser(...plugins) {
273
267
  add_comments(ast);
274
268
 
275
269
  if (keyword_tokens) {
276
- /** @type {any} */ (ast).tsrx_keyword_tokens = keyword_tokens;
270
+ ast.tsrx_keyword_tokens = keyword_tokens;
277
271
  }
278
272
 
279
273
  return ast;
@@ -305,80 +299,68 @@ export function get_comment_handlers(source, comments, index = 0) {
305
299
  }
306
300
 
307
301
  /**
308
- * @param {any} node
309
- * @returns {node is (ESTreeJSX.JSXElement | ESTreeJSX.JSXFragment) & AST.NodeWithLocation}
302
+ * @param {AST.Node | AST.CSS.Node | null | undefined} node
303
+ * @returns {node is AST.NativeTSRXTemplateNode & AST.NodeWithLocation}
310
304
  */
311
305
  function isNativeTemplateNode(node) {
312
306
  return (
313
307
  (node?.type === 'JSXElement' ||
314
308
  node?.type === 'JSXFragment' ||
315
309
  node?.type === 'JSXStyleElement') &&
316
- node.metadata?.native_tsrx
310
+ node.metadata?.native_tsrx === true
317
311
  );
318
312
  }
319
313
 
320
314
  /**
321
- * @param {any} node
322
- * @returns {node is (ESTreeJSX.JSXElement | AST.JSXStyleElement) & AST.NodeWithLocation}
315
+ * @param {AST.Node | AST.CSS.Node | null | undefined} node
316
+ * @returns {node is AST.TSRXElementNode & AST.NodeWithLocation}
323
317
  */
324
318
  function isNativeTemplateElement(node) {
325
319
  return (
326
320
  (node?.type === 'JSXElement' || node?.type === 'JSXStyleElement') &&
327
- node.metadata?.native_tsrx
321
+ node.metadata?.native_tsrx === true
328
322
  );
329
323
  }
330
324
 
331
325
  /**
332
- * @param {any} node
333
- * @returns {AST.Node[]}
334
- */
335
- function getTemplateChildren(node) {
336
- return Array.isArray(node?.children)
337
- ? /** @type {AST.Node[]} */ (/** @type {unknown} */ (node.children))
338
- : [];
339
- }
340
-
341
- /**
342
- * @param {any} node
343
- * @returns {node is (ESTreeJSX.JSXElement | ESTreeJSX.JSXFragment) & AST.NodeWithLocation}
326
+ * @param {AST.Node | AST.CSS.Node | null | undefined} node
327
+ * @returns {node is AST.NativeTSRXTemplateNode & AST.NodeWithLocation}
344
328
  */
345
329
  function isEmptyTemplateNode(node) {
346
- return isNativeTemplateNode(node) && getTemplateChildren(node).length === 0;
330
+ return isNativeTemplateNode(node) && node.children.length === 0;
347
331
  }
348
332
 
349
333
  /**
350
- * @param {any} node
351
- * @returns {any}
334
+ * @param {AST.Node} node
335
+ * @returns {BaseNodeMetaData}
352
336
  */
353
337
  function getNodeMetadata(node) {
354
- const target = /** @type {AST.Node} */ (/** @type {unknown} */ (node));
355
- target.metadata ??= { path: [] };
356
- return target.metadata;
338
+ node.metadata ??= { path: [] };
339
+ return node.metadata;
357
340
  }
358
341
 
359
342
  /**
360
- * @param {any} node
343
+ * @param {AST.NodeWithMaybeComments} node
361
344
  * @param {AST.CommentWithLocation} comment
362
345
  */
363
346
  function pushInnerComment(node, comment) {
364
- const target = /** @type {any} */ (node);
365
- (target.innerComments ||= []).push(comment);
347
+ (node.innerComments ||= []).push(comment);
366
348
  }
367
349
 
368
350
  /**
369
- * @param {any} node
351
+ * @param {AST.NodeWithMaybeComments} node
370
352
  * @returns {boolean}
371
353
  */
372
354
  function hasInnerComments(node) {
373
- return !!(/** @type {any} */ (node).innerComments?.length);
355
+ return !!node.innerComments?.length;
374
356
  }
375
357
 
376
358
  /**
377
- * @param {ESTreeJSX.JSXElement | AST.JSXStyleElement} node
359
+ * @param {AST.TSRXElementNode} node
378
360
  * @returns {string | null}
379
361
  */
380
362
  function getJSXElementName(node) {
381
- const name = node.openingElement?.name;
363
+ const name = node.openingElement.name;
382
364
  if (!name) return null;
383
365
  if (name.type === 'JSXIdentifier') return name.name;
384
366
  if (name.type === 'JSXNamespacedName') return `${name.namespace.name}:${name.name.name}`;
@@ -478,7 +460,7 @@ export function get_comment_handlers(source, comments, index = 0) {
478
460
 
479
461
  /**
480
462
  * @param {AST.CommentWithLocation} comment
481
- * @returns {((ESTreeJSX.JSXElement | ESTreeJSX.JSXFragment) & AST.NodeWithLocation) | null}
463
+ * @returns {(AST.NativeTSRXTemplateNode & AST.NodeWithLocation) | null}
482
464
  */
483
465
  function getEmptyElementInnerCommentTarget(comment) {
484
466
  const element = path.findLast((ancestor) => isNativeTemplateNode(ancestor));
@@ -505,15 +487,12 @@ export function get_comment_handlers(source, comments, index = 0) {
505
487
  // parent <style> element's content range so they don't leak to
506
488
  // subsequent JS nodes.
507
489
  if (node.type === 'StyleSheet') {
508
- const styleElement =
509
- /** @type {(ESTreeJSX.JSXElement & AST.NodeWithLocation) | undefined} */ (
510
- path.findLast(
511
- (ancestor) =>
512
- isNativeTemplateElement(ancestor) && getJSXElementName(ancestor) === 'style',
513
- )
514
- );
515
- if (styleElement) {
516
- const cssStart = styleElement.openingElement?.end ?? styleElement.start;
490
+ const styleElement = path.findLast(
491
+ (ancestor) =>
492
+ isNativeTemplateElement(ancestor) && getJSXElementName(ancestor) === 'style',
493
+ );
494
+ if (isNativeTemplateElement(styleElement)) {
495
+ const cssStart = styleElement.openingElement.end ?? styleElement.start;
517
496
  const cssEnd = styleElement.closingElement?.start ?? styleElement.end;
518
497
  while (comments[0] && comments[0].start >= cssStart && comments[0].end <= cssEnd) {
519
498
  comments.shift();
@@ -540,7 +519,7 @@ export function get_comment_handlers(source, comments, index = 0) {
540
519
  // before the child element is pushed to the parser's #path, causing
541
520
  // comments inside the child to get the parent's containerId.
542
521
  const commentStart = comments[0].start;
543
- const isInsideChildElement = getTemplateChildren(node).some(
522
+ const isInsideChildElement = node_children(node).some(
544
523
  (child) =>
545
524
  child &&
546
525
  child.start !== undefined &&