@tsrx/core 0.1.62 → 0.1.64

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.62",
6
+ "version": "0.1.64",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -79,7 +79,7 @@
79
79
  "is-reference": "^3.0.3",
80
80
  "magic-string": "^0.30.18",
81
81
  "zimmerframe": "^1.1.2",
82
- "@tsrx/runtime": "0.1.2"
82
+ "@tsrx/runtime": "0.1.3"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/node": "^24.3.0",
@@ -7,30 +7,75 @@ import { walk } from 'zimmerframe';
7
7
  import {
8
8
  is_code_block_function_body,
9
9
  is_statement_position,
10
+ is_supported_lazy_assignment_position,
11
+ is_transparent_expression_wrapper,
10
12
  is_tsrx_render_output_node,
11
13
  } from '../utils/ast.js';
12
- import { validate_forgotten_statement_container } from './validation.js';
14
+ import {
15
+ validate_forgotten_statement_container,
16
+ validate_unsupported_lazy_assignment_position,
17
+ } from './validation.js';
13
18
 
14
19
  /**
15
- * Wrappers that preserve an expression's value and therefore do not turn a
16
- * template into a used value on their own. Looking through them keeps strict
17
- * builds and `preserveParens` type-only builds on the same diagnostic path.
20
+ * Find the first authored lazy pattern along an assignment target's binding
21
+ * edges. Default-value expressions and computed keys are evaluated
22
+ * expressions, so nested assignments there own their own diagnostics.
18
23
  *
19
- * @param {AST.Node} parent
20
- * @param {AST.Node} child
21
- * @returns {boolean}
24
+ * @param {AST.Node} node
25
+ * @returns {import('../../types/index').LazyPattern | null}
22
26
  */
23
- function is_transparent_expression_wrapper(parent, child) {
24
- return (
25
- (parent.type === 'ParenthesizedExpression' ||
26
- parent.type === 'TSAsExpression' ||
27
- parent.type === 'TSSatisfiesExpression' ||
28
- parent.type === 'TSNonNullExpression' ||
29
- parent.type === 'TSInstantiationExpression' ||
30
- parent.type === 'TSTypeAssertion' ||
31
- parent.type === 'ChainExpression') &&
32
- /** @type {{ expression?: AST.Node }} */ (parent).expression === child
33
- );
27
+ function find_first_lazy_pattern(node) {
28
+ if ((node.type === 'ObjectPattern' || node.type === 'ArrayPattern') && node.lazy) {
29
+ return node;
30
+ }
31
+
32
+ switch (node.type) {
33
+ case 'AssignmentPattern':
34
+ return find_first_lazy_pattern(node.left);
35
+ case 'RestElement':
36
+ return find_first_lazy_pattern(node.argument);
37
+ case 'ObjectPattern':
38
+ for (const property of node.properties) {
39
+ const lazy =
40
+ property.type === 'Property'
41
+ ? find_first_lazy_pattern(property.value)
42
+ : find_first_lazy_pattern(property.argument);
43
+ if (lazy) return lazy;
44
+ }
45
+ return null;
46
+ case 'ArrayPattern':
47
+ for (const element of node.elements) {
48
+ if (!element) continue;
49
+ const lazy = find_first_lazy_pattern(element);
50
+ if (lazy) return lazy;
51
+ }
52
+ return null;
53
+ default: {
54
+ const expression = /** @type {{ expression?: AST.Node }} */ (node).expression;
55
+ return expression && is_transparent_expression_wrapper(node, expression)
56
+ ? find_first_lazy_pattern(expression)
57
+ : null;
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @param {AST.AssignmentExpression} node
64
+ * @param {{ next: () => unknown, path: AST.Node[], state: TSRXAnalysisState }} context
65
+ */
66
+ function visit_assignment_expression(node, { next, path, state }) {
67
+ const lazy = find_first_lazy_pattern(node.left);
68
+
69
+ if (lazy && !is_supported_lazy_assignment_position(node, path)) {
70
+ validate_unsupported_lazy_assignment_position(
71
+ lazy,
72
+ state.filename,
73
+ state.collect ? state.errors : undefined,
74
+ state.comments,
75
+ );
76
+ }
77
+
78
+ next();
34
79
  }
35
80
 
36
81
  /**
@@ -126,6 +171,8 @@ function visit_class(_node, { next, state }) {
126
171
  }
127
172
 
128
173
  const visitors = {
174
+ AssignmentExpression: visit_assignment_expression,
175
+
129
176
  FunctionDeclaration: visit_function,
130
177
  FunctionExpression: visit_function,
131
178
  ArrowFunctionExpression: visit_function,
@@ -30,6 +30,8 @@ export const TSRX_DO_WHILE_STATEMENT_ERROR =
30
30
  'Do...while loops are not supported in TSRX templates. Move the do...while loop into a function.';
31
31
  export const TSRX_FORGOTTEN_STATEMENT_CONTAINER_ERROR =
32
32
  "This TSRX template output is unused. Return it, assign it to a value that is rendered, or make it part of the rendered output of a function '@{...}' body.";
33
+ export const TSRX_UNSUPPORTED_LAZY_ASSIGNMENT_POSITION_ERROR =
34
+ 'Lazy destructuring assignments require a directly lazy target as a standalone statement inside a program, block, TSRX code block, or switch case.';
33
35
 
34
36
  const invalid_nestings = {
35
37
  // <p> cannot contain block-level elements
@@ -220,6 +222,23 @@ export function validate_forgotten_statement_container(node, filename, errors, c
220
222
  );
221
223
  }
222
224
 
225
+ /**
226
+ * @param {import('../../types/index').LazyPattern} node
227
+ * @param {string | null | undefined} filename
228
+ * @param {CompileError[]} [errors]
229
+ * @param {AST.CommentWithLocation[]} [comments]
230
+ */
231
+ export function validate_unsupported_lazy_assignment_position(node, filename, errors, comments) {
232
+ error(
233
+ TSRX_UNSUPPORTED_LAZY_ASSIGNMENT_POSITION_ERROR,
234
+ filename ?? null,
235
+ node,
236
+ errors,
237
+ comments,
238
+ DIAGNOSTIC_CODES.UNSUPPORTED_LAZY_ASSIGNMENT_POSITION,
239
+ );
240
+ }
241
+
223
242
  /**
224
243
  * @param {AST.ReturnStatement} node
225
244
  * @param {string | null | undefined} filename
@@ -5,4 +5,5 @@ export const DIAGNOSTIC_CODES = {
5
5
  TEMPLATE_EXPRESSION_TRAILING_SEMICOLON: 'tsrx-template-expression-trailing-semicolon',
6
6
  TEMPLATE_RETURN_STATEMENT: 'tsrx-template-return-statement',
7
7
  FORGOTTEN_STATEMENT_CONTAINER: 'tsrx-forgotten-statement-container',
8
+ UNSUPPORTED_LAZY_ASSIGNMENT_POSITION: 'tsrx-unsupported-lazy-assignment-position',
8
9
  };
package/src/index.js CHANGED
@@ -94,6 +94,7 @@ export {
94
94
  is_template_directive as isTemplateDirective,
95
95
  is_tsrx_render_output_node as isTsrxRenderOutputNode,
96
96
  is_code_block_function_body as isCodeBlockFunctionBody,
97
+ is_statement_list_item as isStatementListItem,
97
98
  is_statement_position as isStatementPosition,
98
99
  } from './utils/ast.js';
99
100
 
@@ -262,6 +263,7 @@ export {
262
263
  TSRX_LOOP_CONTINUE_ERROR,
263
264
  TSRX_LOOP_RETURN_ERROR,
264
265
  TSRX_RETURN_STATEMENT_ERROR,
266
+ TSRX_UNSUPPORTED_LAZY_ASSIGNMENT_POSITION_ERROR,
265
267
  TSRX_WHILE_STATEMENT_ERROR,
266
268
  get_return_keyword_node as getReturnKeywordNode,
267
269
  get_statement_keyword_node as getStatementKeywordNode,
@@ -272,6 +274,7 @@ export {
272
274
  validate_tsrx_loop_continue_statement as validateTsrxLoopContinueStatement,
273
275
  validate_tsrx_loop_return_statement as validateTsrxLoopReturnStatement,
274
276
  validate_tsrx_return_statement as validateTsrxReturnStatement,
277
+ validate_unsupported_lazy_assignment_position as validateUnsupportedLazyAssignmentPosition,
275
278
  validate_tsrx_unsupported_loop_statement as validateTsrxUnsupportedLoopStatement,
276
279
  validate_forgotten_statement_container as validateForgottenStatementContainer,
277
280
  validate_nesting as validateNesting,