@tsrx/core 0.0.19 → 0.0.21

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/README.md CHANGED
@@ -35,194 +35,19 @@ const ast = parseModule(source, 'App.tsrx');
35
35
  The parser produces an ESTree-compatible AST, augmented with the TSRX node types
36
36
  listed below. Framework compilers walk this AST to emit their own output.
37
37
 
38
- ## TSRX Specification (draft)
38
+ ## Language docs
39
39
 
40
- TSRX is a superset of TypeScript. All valid TypeScript is valid TSRX. TSRX adds
41
- the following productions.
40
+ The TSRX website is the canonical source for language documentation:
42
41
 
43
- ### 1. `component` declarations
42
+ - [Getting Started](https://tsrx.dev/getting-started) install TSRX for React,
43
+ Preact, Solid, Vue, or Ripple and configure editor/AI tooling.
44
+ - [Features](https://tsrx.dev/features) — examples of components, statement
45
+ templates, control flow, scoped styles, server blocks, and lazy destructuring.
46
+ - [Specification](https://tsrx.dev/specification) — the current grammar and
47
+ parser-level semantics.
44
48
 
45
- A `component` is a new top-level and expression-level declaration form. It has the
46
- same shape as a function declaration, but is a distinct AST node (`Component`) so
47
- that framework compilers can treat it specially.
48
-
49
- ```tsx
50
- component Button(props: Props) {
51
- <button>{props.label}</button>
52
- }
53
- ```
54
-
55
- - `component` may be used wherever `function` may be used (declaration,
56
- expression, default export).
57
- - The body of a `component` may contain JSX-like elements as statements — see §3.
58
- - `component` is a contextual keyword. Use as an identifier is preserved in
59
- non-declaration positions.
60
-
61
- ### 2. JSX-as-statements
62
-
63
- Inside a `component` body, JSX elements are valid _statement_ forms. They describe
64
- rendered output and are not expressions — they have no value. Static text may be
65
- written as a direct double-quoted child; dynamic values and other JavaScript
66
- expressions stay inside `{}`.
67
-
68
- ```tsx
69
- component Greeting() {
70
- <h1>"Hello"</h1>
71
- <p>"Welcome"</p>
72
- }
73
- ```
74
-
75
- Only double quotes have direct-child text meaning. Single-quoted strings and
76
- template literals remain JavaScript expressions and must be written inside `{}`.
77
-
78
- Elsewhere (outside a `component` body), JSX remains an expression, as in standard
79
- JSX.
80
-
81
- ### 4. Control-flow statements in `component` bodies
82
-
83
- Inside a `component` body, the standard JavaScript control-flow keywords `if`,
84
- `else`, `for`, `switch`, and `try` gain an additional role: their branches may
85
- contain JSX-as-statements (§2) describing conditionally- or repeatedly-rendered
86
- output. The keywords retain their usual JavaScript syntax — no new grammar is
87
- introduced — but framework compilers treat them as _reactive_ boundaries.
88
-
89
- ```tsx
90
- component List(props: { items: Item[]; showHeader: boolean }) {
91
- if (props.showHeader) {
92
- <h1>"Items"</h1>
93
- } else {
94
- <h2>"(no header)"</h2>
95
- }
96
-
97
- for (const item of props.items) {
98
- <li>{item.name}</li>
99
- }
100
-
101
- switch (props.items.length) {
102
- case 0:
103
- <p>"empty"</p>
104
- break;
105
- default:
106
- <p>"has items"</p>
107
- }
108
-
109
- try {
110
- <AsyncThing />
111
- } catch (e) {
112
- <pre>{String(e)}</pre>
113
- }
114
- }
115
- ```
116
-
117
- **Early returns.** A bare `return;` (or `return` at the end of a branch) is a
118
- valid statement inside a `component` body and short-circuits any remaining
119
- rendering in the current branch. This composes naturally with the control-flow
120
- forms above:
121
-
122
- ```tsx
123
- component Page(props: { user: User | null }) {
124
- if (props.user == null) {
125
- <LoginPrompt />
126
- return;
127
- }
128
-
129
- <Dashboard user={props.user} />
130
- }
131
- ```
132
-
133
- Because a `component` body does not produce a value, `return` never carries an
134
- expression — it only marks a rendering short-circuit.
135
-
136
- **Nesting inside elements.** Control-flow statements may appear directly as
137
- children of a JSX element, not only at the top level of the component body. Their
138
- branches contribute children to the enclosing element in source order:
139
-
140
- ```tsx
141
- component Menu(props: { items: Item[]; loading: boolean }) {
142
- <ul>
143
- if (props.loading) {
144
- <li>{'loading…'}</li>
145
- } else {
146
- for (const item of props.items) {
147
- <li>
148
- <a href={item.href}>{item.label}</a>
149
- if (item.badge) {
150
- <span class="badge">{item.badge}</span>
151
- }
152
- </li>
153
- }
154
- }
155
- </ul>
156
- }
157
- ```
158
-
159
- Any control-flow form that is legal at the component-body level is also legal as a
160
- child of a JSX element, and may be nested to arbitrary depth.
161
-
162
- TSRX only describes what is syntactically permitted. The reactive semantics
163
- (dependency tracking, list reconciliation, error boundaries, suspense) are the
164
- responsibility of the framework compiler.
165
-
166
- ### 5. JSX escape hatch: `<tsx>...</tsx>`
167
-
168
- Because JSX inside a `component` body is a _statement_ (§2), the element itself
169
- has no value. To embed regular _expression_-form JSX — e.g. when a third-party
170
- library accepts a JSX tree as a value — wrap it in the reserved `<tsx>` element.
171
- Its children are parsed as standard JSX expressions and the whole form evaluates
172
- to the JSX expression value (or an array of values if there are multiple
173
- children).
174
-
175
- ```tsx
176
- component Page() {
177
- const header = <tsx><h1>Hello</h1></tsx>;
178
- renderSomewhereElse(header);
179
- }
180
- ```
181
-
182
- `<tsx>` is a reserved tag name in TSRX. It has no runtime representation of its
183
- own — the framework compiler unwraps it into the underlying JSX expression.
184
-
185
- ### 6. Lazy destructuring: `&[]` and `&{}`
186
-
187
- Two new destructuring forms prefixed with `&` bind by _reference_ rather than by
188
- value. Each bound name compiles to a lazy property lookup on the source, so reads
189
- and writes are deferred to the use-site.
190
-
191
- ```tsx
192
- let &[count] = source; // array-style lazy destructure
193
- let &{ name, age } = props; // object-style lazy destructure
194
- ```
195
-
196
- Semantics are provided by the framework compiler. TSRX only defines the syntax and
197
- the AST shape (`kind: 'lazy'` binding patterns).
198
-
199
- ### 7. `#server` blocks
200
-
201
- A `#server { ... }` block marks a lexical region whose contents are intended for
202
- the server compile target. TSRX parses the block and records its exports;
203
- framework compilers decide how to emit or strip it per target.
204
-
205
- ```ts
206
- #server {
207
- export async function load() { /* ... */ }
208
- }
209
- ```
210
-
211
- ### 8. `#style` identifier
212
-
213
- `#style` is a reserved identifier that refers, at compile time, to the set of
214
- scoped CSS classes declared in the current module. It is legal only in positions
215
- where the framework compiler expects a class-name value.
216
-
217
- ```tsx
218
- <div class={#style.card} />
219
- ```
220
-
221
- ### 9. Scoped CSS blocks
222
-
223
- A `component` may contain a trailing CSS block (delimited by the framework
224
- compiler's chosen grammar). The block is parsed into a `CSS.StyleSheet` AST node
225
- and hashed for scoping.
49
+ Keeping the language reference on the website avoids duplicating the specification
50
+ here and keeps package docs focused on the core parser API.
226
51
 
227
52
  ## What `@tsrx/core` provides
228
53
 
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.0.19",
6
+ "version": "0.0.21",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -0,0 +1,7 @@
1
+ export const DIAGNOSTIC_CODES = {
2
+ JSX_EXPRESSION_VALUE: 'tsrx-jsx-expression-value',
3
+ JSX_RETURN_IN_COMPONENT: 'tsrx-jsx-return-in-component',
4
+ FUNCTION_COMPONENT_SYNTAX: 'tsrx-function-component-syntax',
5
+ UNCLOSED_TAG: 'tsrx-unclosed-tag',
6
+ MISMATCHED_CLOSING_TAG: 'tsrx-mismatched-closing-tag',
7
+ };
package/src/errors.js CHANGED
@@ -10,9 +10,10 @@
10
10
  * @param {AST.Node | AST.NodeWithLocation} node
11
11
  * @param {CompileError[]} [errors]
12
12
  * @param {AST.CommentWithLocation[]} [comments]
13
+ * @param {string} [code]
13
14
  * @returns {void}
14
15
  */
15
- export function error(message, filename, node, errors, comments) {
16
+ export function error(message, filename, node, errors, comments, code) {
16
17
  if (errors && comments && is_error_suppressed(node, comments)) {
17
18
  return;
18
19
  }
@@ -25,6 +26,7 @@ export function error(message, filename, node, errors, comments) {
25
26
 
26
27
  // custom properties
27
28
  error.fileName = filename;
29
+ error.code = code;
28
30
  error.end = node.end ?? undefined;
29
31
  error.loc = !node.loc
30
32
  ? undefined
package/src/index.js CHANGED
@@ -24,6 +24,7 @@ export { create_scopes as createScopes, ScopeRoot, Scope } from './scope.js';
24
24
 
25
25
  // Errors
26
26
  export { error } from './errors.js';
27
+ export { DIAGNOSTIC_CODES } from './diagnostics.js';
27
28
 
28
29
  // Constants
29
30
  export {
@@ -201,7 +201,8 @@ export function createParser(...plugins) {
201
201
  return function parse(source, filename, options) {
202
202
  /** @type {AST.CommentWithLocation[]} */
203
203
  const comments = [];
204
- const output_comments = options?.comments;
204
+ const collect = !!(options?.collect || options?.loose);
205
+ const output_comments = collect ? options?.comments : undefined;
205
206
 
206
207
  const { onComment, add_comments } = get_comment_handlers(source, comments);
207
208
  /** @type {AST.Program} */
@@ -216,7 +217,8 @@ export function createParser(...plugins) {
216
217
  onComment,
217
218
  tsrxOptions: {
218
219
  filename,
219
- errors: options?.errors ?? [],
220
+ collect,
221
+ errors: collect ? (options?.errors ?? []) : undefined,
220
222
  loose: options?.loose || false,
221
223
  },
222
224
  });