@tsrx/core 0.1.42 → 0.1.43

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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core compiler infrastructure for TSRX syntax",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.1.42",
6
+ "version": "0.1.43",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -58,6 +58,18 @@ export function is_jsdoc_ts_annotation(comment) {
58
58
  return tsAnnotations.some((annotation) => comment.value.includes(annotation));
59
59
  }
60
60
 
61
+ /**
62
+ * Check if a comment is a TypeScript JSX pragma (`@jsxImportSource`,
63
+ * `@jsxRuntime`, `@jsxFrag`, `@jsx`). TS reads these from a file's LEADING
64
+ * comments to pick that file's JSX type source/factory, overriding tsconfig —
65
+ * dropping one silently retypes every JSX expression in the file.
66
+ * @param {AST.CommentWithLocation} comment
67
+ * @returns {boolean}
68
+ */
69
+ export function is_jsx_pragma(comment) {
70
+ return /@jsx(ImportSource|Runtime|Frag)?\b/.test(comment.value);
71
+ }
72
+
61
73
  /**
62
74
  * Check if a comment should be preserved in to_ts mode
63
75
  * @param {AST.CommentWithLocation} comment
@@ -65,7 +77,10 @@ export function is_jsdoc_ts_annotation(comment) {
65
77
  */
66
78
  export function should_preserve_comment(comment) {
67
79
  return (
68
- is_ts_pragma(comment) || is_triple_slash_directive(comment) || is_jsdoc_ts_annotation(comment)
80
+ is_ts_pragma(comment) ||
81
+ is_triple_slash_directive(comment) ||
82
+ is_jsdoc_ts_annotation(comment) ||
83
+ is_jsx_pragma(comment)
69
84
  );
70
85
  }
71
86
 
@@ -2,6 +2,7 @@
2
2
  /** @import { Visitors } from 'zimmerframe' */
3
3
 
4
4
  import tsx from 'esrap/languages/tsx';
5
+ import { should_preserve_comment, format_comment } from '../../comment-utils.js';
5
6
 
6
7
  /**
7
8
  * Zimmerframe provides `path` as the ancestor chain. A native template node in
@@ -68,10 +69,32 @@ export function set_node_path_metadata(node, path) {
68
69
  * (structural tokens carry one-character source locations). typeOnly/volar
69
70
  * prints opt in — their maps are consumed positionally by the language
70
71
  * tooling and never shipped; build prints stay sparse.
72
+ * @param {AST.CommentWithLocation[]} [comments] Source comments; the ones
73
+ * `should_preserve_comment` classifies as semantic-to-TS (`@ts-nocheck`,
74
+ * `@jsxImportSource`, triple-slash references, …) and that LEAD the program
75
+ * are re-emitted at the top of the printed output. The generated TSX is real
76
+ * TS input — dropping a leading pragma changes how the whole file checks.
71
77
  */
72
- export function tsx_with_ts_locations(boundary_tokens = false) {
78
+ export function tsx_with_ts_locations(boundary_tokens = false, comments = undefined) {
73
79
  const base = /** @type {any} */ (tsx({ boundaryTokens: boundary_tokens }));
74
80
 
81
+ const leading_preserved = (/** @type {any} */ program) => {
82
+ if (!comments?.length) return [];
83
+ // Injected statements (dynamic-import/try-import prepends) carry no
84
+ // loc; anchor "leading" on the first statement that maps to source,
85
+ // else every preserved comment in the file would hoist to the top.
86
+ const first = program.body?.find((/** @type {any} */ node) => node.loc);
87
+ return comments.filter(
88
+ (/** @type {any} */ comment) =>
89
+ should_preserve_comment(comment) &&
90
+ (first?.loc == null ||
91
+ (comment.loc &&
92
+ (comment.loc.end.line < first.loc.start.line ||
93
+ (comment.loc.end.line === first.loc.start.line &&
94
+ comment.loc.end.column <= first.loc.start.column)))),
95
+ );
96
+ };
97
+
75
98
  /**
76
99
  * @param {any} node
77
100
  * @param {any} context
@@ -89,6 +112,15 @@ export function tsx_with_ts_locations(boundary_tokens = false) {
89
112
 
90
113
  /** @type {Record<string, (node: any, context: any) => void>} */
91
114
  const wrappers = {
115
+ Program: (node, context) => {
116
+ for (const comment of leading_preserved(node)) {
117
+ if (comment.loc) context.location(comment.loc.start.line, comment.loc.start.column);
118
+ context.write(format_comment(comment));
119
+ if (comment.loc) context.location(comment.loc.end.line, comment.loc.end.column);
120
+ context.newline();
121
+ }
122
+ base.Program(node, context);
123
+ },
92
124
  ArrayPattern: (node, context) => {
93
125
  base.ArrayPattern(node, context);
94
126
  if (node.typeAnnotation) {
@@ -841,10 +841,20 @@ export function createJsxTransform(platform) {
841
841
  : apply_lazy_transforms(/** @type {any} */ (lowered_program), new Map())
842
842
  );
843
843
 
844
- const result = print(final_program, tsx_with_ts_locations(transform_context.typeOnly), {
845
- sourceMapSource: filename,
846
- sourceMapContent: source,
847
- });
844
+ const result = print(
845
+ final_program,
846
+ // typeOnly output is real TS input: re-emit preserved leading comments
847
+ // (@jsxImportSource / @ts-nocheck / triple-slash references) so TS
848
+ // semantics survive the comment-stripping print.
849
+ tsx_with_ts_locations(
850
+ transform_context.typeOnly,
851
+ transform_context.typeOnly ? transform_context.comments : undefined,
852
+ ),
853
+ {
854
+ sourceMapSource: filename,
855
+ sourceMapContent: source,
856
+ },
857
+ );
848
858
 
849
859
  const { css, cssHash } = render_css_result(/** @type {any} */ (stylesheets));
850
860