@jay-framework/compiler-jay-html 0.5.0

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.
@@ -0,0 +1,145 @@
1
+ # Jay Compiler Patterns
2
+
3
+ Jay Compiler Patterns are used by jay security module for detecting safe code patterns that can be extracted from
4
+ a component file and run in the main context, while the rest of the component runs in a sandbox.
5
+
6
+ A simple example is the following event handler:
7
+
8
+ ```typescript
9
+ refs.cycles.oninput(({ event }: JayEvent<Event, MainViewState>) => {
10
+ const cycles = (event.target as HTMLInputElement).value;
11
+ setCycles(Number(cycles));
12
+ });
13
+ ```
14
+
15
+ As the event handler can run in a sandbox environment that does not have access to the browser native event,
16
+ the code above will break unless is running in the main context `(event.target as HTMLInputElement).value`.
17
+
18
+ Given the compiler pattern
19
+
20
+ ```typescript
21
+ import { JayEvent } from '@jay-framework/runtime';
22
+ function inputValuePattern({ event }: JayEvent<any, any>) {
23
+ return event.target.value;
24
+ }
25
+ ```
26
+
27
+ The event handler is rewritten into
28
+
29
+ ```typescript
30
+ refs.cycles.oninput$(handler$('1')).then(({ event }: JayEvent<any, MainViewState>) => {
31
+ setCycles(Number(event.$0));
32
+ });
33
+ ```
34
+
35
+ while the main context gains
36
+
37
+ ```typescript
38
+ const funcRepository: FunctionsRepository = {
39
+ '1': ({ event }: JayEvent<any, any>) => {
40
+ const cycles = (event.target as HTMLInputElement).value;
41
+ return { $0: event.target.value };
42
+ },
43
+ };
44
+ ```
45
+
46
+ The full example can be found at [mini-benchmark](../../../../examples/jay/mini-benchmark)
47
+
48
+ ## Writing compiler patterns
49
+
50
+ Jay compiler patterns are written as TS functions. However, those functions are not run directly, instead, they are
51
+ compiled into AST patterns that are matched against AST of event handlers and `exec$` APIs.
52
+
53
+ Pattern matching is checking
54
+
55
+ 1. the access path used for values
56
+ 2. the input parameter types
57
+ 3. the output type
58
+ 4. Compiler patterns are designed to be simple, one liner expressions.
59
+ 5. A function can contain multiple lines, at which each line is considered a different expression.
60
+ 6. the `@JayPattern` can be used to force a pattern to only be valid in a certain environment.
61
+
62
+ Jay supports a number of types of patterns:
63
+
64
+ ## return patterns
65
+
66
+ A return pattern is a pattern that returns a value, which can be used by the component code (as in the example above).
67
+
68
+ A return pattern is written as a function that returns a value.
69
+
70
+ ```typescript
71
+ import { JayEvent } from '@jay-framework/runtime';
72
+
73
+ function inputValuePattern(handler: JayEvent<any, any>): string {
74
+ return handler.event.target.value;
75
+ }
76
+ ```
77
+
78
+ - A return pattern must have a type
79
+ - A pattern must have parameter types
80
+
81
+ > Note: it is not important if we write the pattern using `handler.event.target.value` or
82
+ > deconstruct the handler with `{ event }` and write the pattern as `event.target.value` - the AST matching is the same.
83
+
84
+ ## call patterns
85
+
86
+ A call pattern is a pattern that matches a call to a function.
87
+
88
+ ```typescript
89
+ import {JayEvent} from '@jay-framework/runtime';
90
+
91
+ @JayPattern(JayTargetEnv.main)
92
+ function eventPreventDefault(handler: JayEvent<any, any>) {
93
+ handler.event.preventDefault();
94
+ }
95
+ ```
96
+
97
+ The pattern above allows the call of the native `event.preventDefault()` in the main context.
98
+
99
+ ## chainable call patterns
100
+
101
+ The combination of a call pattern and a return pattern, allowing to match a sequence of return and call functions.
102
+
103
+ An example is the string replace function which can run in any environment.
104
+
105
+ ```typescript
106
+ @JayPattern(JayTargetEnv.any)
107
+ function stringReplace(value: string, regex: RegExp, replacement: string): string {
108
+ return value.replace(regex, replacement)
109
+ }
110
+ ```
111
+
112
+ > Note: the matching is only on the function body `value.replace(regex, replacement)`. The parameters and return
113
+ > are just indications of types and inputs / outputs.
114
+
115
+ ## assignment pattern
116
+
117
+ Assignment pattern allows to support assignment of values directly in the main context.
118
+
119
+ An example is the assignment of a string value to an input.
120
+
121
+ ```typescript
122
+ import {JayEvent} from '@jay-framework/runtime';
123
+
124
+ @JayPattern(JayTargetEnv.main)
125
+ function setInputValue(handler: JayEvent<any, any>, value: string) {
126
+ handler.event.target.value = value;
127
+ }
128
+ ```
129
+
130
+ In most cases, the same can be achieved using state management. However, consider the following event handler
131
+
132
+ ```typescript
133
+ refs.input.onClick(({ event }) => {
134
+ const inputValue = event.target.value;
135
+ const validValue = inputValue.replace(/[^A-Za-z0-9]+/g, '');
136
+ event.target.value = validValue;
137
+ });
138
+ ```
139
+
140
+ The combination of assignment, read and chainable call patterns allows to push all of the event handler to the main
141
+ context for sync input validation.
142
+
143
+ ## constants
144
+
145
+ Constants are considered "safe" be definition and if used in patterns are also extracted to the main context.
@@ -0,0 +1,297 @@
1
+ # Contract File Format
2
+
3
+ Contract files (`.jay-contract`) define the interface between headless components and their UI implementations. They specify the data structure, interactive elements, and component composition using a YAML-based format.
4
+
5
+ ## Basic Structure
6
+
7
+ A contract file consists of a `name` and a list of `tags`:
8
+
9
+ ```yaml
10
+ name: component-name
11
+ tags:
12
+ - tag: tagName
13
+ type: tagType
14
+ # additional properties based on type
15
+ ```
16
+
17
+ ## Tag Types
18
+
19
+ ### Data Tags
20
+
21
+ Data tags define the component's view state properties.
22
+
23
+ ```yaml
24
+ - tag: count
25
+ type: data
26
+ dataType: number
27
+ required: true
28
+ description: The current count value
29
+ ```
30
+
31
+ **Properties:**
32
+
33
+ - `type: data` - Identifies this as a data property
34
+ - `dataType` - The data type (string, number, boolean, enum)
35
+ - `required` - (Optional) Whether this property is required
36
+ - `description` - (Optional) Documentation for the property
37
+
38
+ ### Interactive Tags
39
+
40
+ Interactive tags define elements that can be interacted with programmatically.
41
+
42
+ ```yaml
43
+ - tag: submitButton
44
+ type: interactive
45
+ elementType: HTMLButtonElement
46
+ description: Button to submit the form
47
+ ```
48
+
49
+ **Properties:**
50
+
51
+ - `type: interactive` - Identifies this as an interactive element
52
+ - `elementType` - The HTML element type (required)
53
+ - `description` - (Optional) Documentation for the element
54
+
55
+ ### Variant Tags
56
+
57
+ Variant tags define design variations or states.
58
+
59
+ ```yaml
60
+ - tag: status
61
+ type: variant
62
+ dataType: enum (active | inactive | pending)
63
+ description: The current status of the component
64
+ ```
65
+
66
+ **Properties:**
67
+
68
+ - `type: variant` - Identifies this as a variant
69
+ - `dataType` - Must be an enum type (required)
70
+ - `description` - (Optional) Documentation for the variant
71
+
72
+ ### Sub-Contract Tags
73
+
74
+ Sub-contract tags define nested component structures.
75
+
76
+ ```yaml
77
+ - tag: items
78
+ type: sub-contract
79
+ repeated: true
80
+ tags:
81
+ - tag: title
82
+ type: data
83
+ dataType: string
84
+ - tag: completed
85
+ type: data
86
+ dataType: boolean
87
+ ```
88
+
89
+ **Properties:**
90
+
91
+ - `type: sub-contract` - Identifies this as a nested contract
92
+ - `repeated` - (Optional) Whether this represents an array of items
93
+ - `tags` - Nested tag definitions
94
+ - `link` - (Alternative to tags) Reference to another contract file
95
+
96
+ ## Linked Contracts
97
+
98
+ Linked contracts allow you to reference external contract files, enabling component reuse and modular design. Instead of defining nested tags inline, you can link to a separate contract file.
99
+
100
+ ### Basic Linked Contract
101
+
102
+ ```yaml
103
+ - tag: discount
104
+ type: sub-contract
105
+ link: ./discount
106
+ ```
107
+
108
+ This references a `discount.jay-contract` file in the same directory.
109
+
110
+ ### Linked Contract with Repeated Items
111
+
112
+ ```yaml
113
+ - tag: media
114
+ type: sub-contract
115
+ tags:
116
+ - tag: items
117
+ type: sub-contract
118
+ link: ./media-item
119
+ repeated: true
120
+ ```
121
+
122
+ This creates an array of items, each following the structure defined in `media-item.jay-contract`.
123
+
124
+ ### Benefits of Linked Contracts
125
+
126
+ 1. **Reusability** - Define a contract once and use it across multiple components
127
+ 2. **Maintainability** - Changes to the linked contract automatically apply everywhere
128
+ 3. **Modularity** - Break complex components into smaller, focused contracts
129
+ 4. **Consistency** - Ensure consistent structure across related components
130
+
131
+ ### File Resolution
132
+
133
+ - **Relative paths** - `./component` or `../components/button`
134
+ - **Module imports** - `<npm-module-name>/<path-to-contract>`
135
+ - **File extension** - The `.jay-contract` extension is automatically appended
136
+ - **Directory structure** - Contracts can be organized in subdirectories
137
+
138
+ ### Example: E-commerce Product Structure
139
+
140
+ ```yaml
141
+ name: product-page
142
+ tags:
143
+ - tag: name
144
+ type: data
145
+ dataType: string
146
+ - tag: price
147
+ type: data
148
+ dataType: number
149
+ - tag: discount
150
+ type: sub-contract
151
+ link: ./discount
152
+ - tag: media
153
+ type: sub-contract
154
+ tags:
155
+ - tag: items
156
+ type: sub-contract
157
+ link: ./media-item
158
+ repeated: true
159
+ - tag: mainMedia
160
+ type: sub-contract
161
+ link: ./media-item
162
+ - tag: addToCart
163
+ type: interactive
164
+ elementType: HTMLButtonElement
165
+ ```
166
+
167
+ In this example:
168
+
169
+ - `discount` links to a reusable discount contract
170
+ - `media.items` creates an array of media items using the linked contract
171
+ - `media.mainMedia` uses the same contract for a single item
172
+
173
+ ### Best Practices for Linked Contracts
174
+
175
+ 1. **Use descriptive names** for contract files that indicate their purpose
176
+ 2. **Keep contracts focused** - Each linked contract should represent a single concept
177
+ 3. **Organize by feature** - Group related contracts in feature-specific directories
178
+ 4. **Document dependencies** - Make it clear which contracts are required
179
+ 5. **Version contracts** - Consider versioning for breaking changes
180
+
181
+ ## Data Types
182
+
183
+ ### Basic Types
184
+
185
+ | Type | Example | Description |
186
+ | --------- | ------------------- | ----------------- |
187
+ | `string` | `dataType: string` | Text values |
188
+ | `number` | `dataType: number` | Numeric values |
189
+ | `boolean` | `dataType: boolean` | True/false values |
190
+
191
+ ### Enum Types
192
+
193
+ ```yaml
194
+ dataType: enum (option1 | option2 | option3)
195
+ ```
196
+
197
+ Enums define a set of allowed values separated by `|`.
198
+
199
+ ## Examples
200
+
201
+ ### Simple Counter Component
202
+
203
+ ```yaml
204
+ name: counter
205
+ tags:
206
+ - tag: count
207
+ type: data
208
+ dataType: number
209
+ required: true
210
+ - tag: add
211
+ type: interactive
212
+ elementType: HTMLButtonElement
213
+ - tag: subtract
214
+ type: interactive
215
+ elementType: HTMLButtonElement
216
+ ```
217
+
218
+ ### Form Component with Nested Structure
219
+
220
+ ```yaml
221
+ name: userForm
222
+ tags:
223
+ - tag: submitButton
224
+ type: interactive
225
+ elementType: HTMLButtonElement
226
+ - tag: personalInfo
227
+ type: sub-contract
228
+ tags:
229
+ - tag: sectionTitle
230
+ type: data
231
+ dataType: string
232
+ - tag: nameFields
233
+ type: sub-contract
234
+ tags:
235
+ - tag: firstName
236
+ type: [data, interactive]
237
+ dataType: string
238
+ elementType: HTMLInputElement
239
+ - tag: lastName
240
+ type: [data, interactive]
241
+ dataType: string
242
+ elementType: HTMLInputElement
243
+ ```
244
+
245
+ ### Product Component with External References
246
+
247
+ ```yaml
248
+ name: product-page
249
+ tags:
250
+ - tag: name
251
+ type: data
252
+ dataType: string
253
+ - tag: price
254
+ type: data
255
+ dataType: number
256
+ - tag: discount
257
+ type: sub-contract
258
+ link: ./discount
259
+ - tag: media
260
+ type: sub-contract
261
+ tags:
262
+ - tag: items
263
+ type: sub-contract
264
+ link: ./media-item
265
+ repeated: true
266
+ - tag: addToCart
267
+ type: interactive
268
+ elementType: HTMLButtonElement
269
+ ```
270
+
271
+ ## Validation Rules
272
+
273
+ ### Required Properties
274
+
275
+ - **Variant tags** must have a `dataType` (enum)
276
+ - **Interactive tags** must have an `elementType`
277
+ - **Sub-contract tags** must have either `tags` or `link`
278
+
279
+ ### Type Restrictions
280
+
281
+ - **Sub-contract tags** cannot be combined with other types
282
+ - **Sub-contract tags** cannot have `dataType` or `elementType`
283
+ - **Unknown tag types** will cause validation errors
284
+
285
+ ### Defaults
286
+
287
+ - If `type` is not specified, defaults to `data`
288
+ - If `dataType` is not specified for data tags, defaults to `string`
289
+
290
+ ## Best Practices
291
+
292
+ 1. **Use descriptive names** for tags that clearly indicate their purpose
293
+ 2. **Add descriptions** to document complex or non-obvious tags
294
+ 3. **Use enums** for variant tags to ensure type safety
295
+ 4. **Structure nested contracts** logically to match your component hierarchy
296
+ 5. **Reference external contracts** for reusable sub-components
297
+ 6. **Mark required properties** explicitly for better validation
@@ -0,0 +1,178 @@
1
+ # Jay-HTML Syntax Reference
2
+
3
+ Jay-HTML extends standard HTML with several features for component-based development. This document covers the syntax elements that distinguish Jay-HTML from regular HTML.
4
+
5
+ A Jay-HTML file must contain one `application/jay-data` script and can include several unique Jay directives.
6
+
7
+ ## Data Contract Definition
8
+
9
+ The data contract defines the `ViewState` - the input data structure for the component.
10
+ The ViewState is defined as a YAML script with `data:` as the root element.
11
+ Each property in the YAML becomes a property of the component's view state, supporting nested objects and arrays.
12
+
13
+ ### Supported Data Types
14
+
15
+ | Type | Example | Description |
16
+ | --------------- | ---------------------------------------------------------------------------- | ------------------------------------ |
17
+ | `string` | `text: string` | Text values |
18
+ | `number` | `count: number` | Numeric values |
19
+ | `boolean` | `isVisible: boolean` | True/false values |
20
+ | `object` | <code>user: </br>&nbsp;&nbsp;name: string</br>&nbsp;&nbsp;age: number</code> | Nested object structures |
21
+ | `array` | <code>items: </br>-&nbsp;name: string</br>&nbsp;&nbsp;value: number</code> | Collections of items |
22
+ | `enum` | `status: enum(active \| inactive \| pending)` | Enumerated values |
23
+ | `imported type` | `config: ImportedConfig` | Types imported from other components |
24
+
25
+ ### View State Context
26
+
27
+ Jay-HTML treats the view state as the **current context** that gets bound to components and elements. The `forEach` directive changes this context from an object to individual array items.
28
+
29
+ #### Example: Context Switching with forEach
30
+
31
+ ```yaml
32
+ data:
33
+ users:
34
+ - id: string
35
+ - name: string
36
+ - email: string
37
+ ```
38
+
39
+ ```html
40
+ <!-- Current context: users array -->
41
+ <div forEach="users" trackBy="id">
42
+ <!-- Current context: individual user object -->
43
+ <span>{name}</span>
44
+ <span>{email}</span>
45
+ </div>
46
+ ```
47
+
48
+ In this example, the context switches from the `users` array to individual `user` objects within the forEach loop.
49
+
50
+ ## Component Usage
51
+
52
+ Jay-HTML allows you to use components as part of the HTML structure. Components must be imported before they can be used.
53
+
54
+ ### Importing Components
55
+
56
+ ```html
57
+ <script
58
+ type="application/jay-headfull"
59
+ src="./my-component.ts"
60
+ names="MyComponent"
61
+ sandbox="false"
62
+ ></script>
63
+ ```
64
+
65
+ ### Using Components
66
+
67
+ ```html
68
+ <MyComponent title="Welcome" count="{itemCount}"></MyComponent>
69
+ ```
70
+
71
+ ### Property Binding Options
72
+
73
+ Components accept properties in three ways:
74
+
75
+ 1. **Static values**: `title="Welcome"`
76
+ 2. **View state bindings**: `count="{itemCount}"` - binds to a specific property
77
+ 3. **Context binding**: `data="{.}"` - binds the entire current context
78
+
79
+ > **Note**: Jay currently doesn't support passing children as properties to components. This feature is planned for future releases.
80
+
81
+ ## Element References
82
+
83
+ The `ref` attribute creates references to HTML elements or components that your component code can interact with.
84
+
85
+ ### Creating References
86
+
87
+ ```html
88
+ <div ref="mainContainer">{content}</div>
89
+ <Counter ref="counterComponent" initialValue="{startCount}" />
90
+ ```
91
+
92
+ For each element with a `ref` attribute, Jay generates a corresponding member in the component's refs type, enabling programmatic interaction.
93
+
94
+ Reference types are defined in the `@jay-framework/runtime` library - see [refs.md](../../../runtime/runtime/docs/refs.md) for details.
95
+
96
+ ## Data Binding
97
+
98
+ The `{}` syntax enables dynamic data binding to the current view state context. This syntax supports property access, conditional expressions, and simple operations.
99
+
100
+ ### Basic Binding
101
+
102
+ ```html
103
+ <div>{title}</div>
104
+ <div>Hello, {user.name}!</div>
105
+ <div>Count: {items.length}</div>
106
+ ```
107
+
108
+ ### Conditional Expressions
109
+
110
+ ```html
111
+ <div>{isVisible ? 'Visible' : 'Hidden'}</div>
112
+ <div>{hasName ? name : 'Anonymous'}</div>
113
+ <div>{!isLoading ? 'Ready' : 'Loading...'}</div>
114
+ ```
115
+
116
+ ### Enum Comparisons
117
+
118
+ ```html
119
+ <div>{status === 'active' ? 'Online' : 'Offline'}</div>
120
+ <div>{role !== 'admin' ? 'User' : 'Administrator'}</div>
121
+ ```
122
+
123
+ ### Class Binding
124
+
125
+ Class bindings support dynamic and conditional class inclusion:
126
+
127
+ ```html
128
+ <div class="{status}">Status indicator</div>
129
+ <div class="{isActive ? 'active' : 'inactive'}">Toggle state</div>
130
+ <div class="{isPrimary ? 'primary' : 'secondary'} button">Button</div>
131
+ ```
132
+
133
+ ## Conditional Rendering
134
+
135
+ The `if` directive conditionally renders elements based on expressions.
136
+
137
+ ### Basic Conditional Rendering
138
+
139
+ ```html
140
+ <div if="isVisible">
141
+ <p>This content is only shown when isVisible is true</p>
142
+ </div>
143
+ ```
144
+
145
+ > **Note**: The `if` directive automatically evaluates expressions, so `{}` binding syntax is not needed.
146
+
147
+ ## List Rendering
148
+
149
+ The `forEach` and `trackBy` directives enable rendering lists of items.
150
+
151
+ ### Basic List Rendering
152
+
153
+ ```html
154
+ <ul>
155
+ <li forEach="users" trackBy="id">
156
+ <span>{name}</span>
157
+ <span>{email}</span>
158
+ </li>
159
+ </ul>
160
+ ```
161
+
162
+ ### How It Works
163
+
164
+ - `forEach="users"` - iterates over the `users` array
165
+ - `trackBy="id"` - uses the `id` property to track items for efficient DOM updates
166
+ - Within the loop, the context becomes individual array items
167
+
168
+ Since Jay uses immutable view state, the `trackBy` attribute is essential for proper item tracking and DOM optimization.
169
+
170
+ ### Example with Component
171
+
172
+ ```html
173
+ <div forEach="products" trackBy="sku">
174
+ <ProductCard product="{.}" />
175
+ </div>
176
+ ```
177
+
178
+ In this example, each `ProductCard` component receives the entire product object as its context.
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@jay-framework/compiler-jay-html",
3
+ "version": "0.5.0",
4
+ "description": "",
5
+ "license": "Apache-2.0",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "directories": {
9
+ "lib": "lib"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "docs",
14
+ "readme.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "npm run build:pegjs && npm run build:js && npm run build:types",
18
+ "build:watch": "npm run build:js -- --watch & npm run build:types -- --watch",
19
+ "build:pegjs": "pegjs --allowed-start-rules dynamicAttribute,booleanAttribute,dynamicText,dynamicProperty,conditionFunc,accessor,Identifier,classExpression,dynamicComponentProp,importNames,enum,is_enum,reactDynamicText,reactDynamicProperty,reactClassExpression,condition -o lib/expressions/expression-parser.cjs lib/expressions/expression-parser.pegjs",
20
+ "build:js": "vite build",
21
+ "build:types": "tsup lib/index.ts --dts-only --format cjs",
22
+ "build:check-types": "tsc",
23
+ "clean": "rimraf dist",
24
+ "confirm": "npm run clean && npm run build && npm run build:check-types && npm run test",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest"
27
+ },
28
+ "author": "",
29
+ "dependencies": {
30
+ "@jay-framework/compiler-analyze-exported-types": "workspace:^",
31
+ "@jay-framework/compiler-shared": "workspace:^",
32
+ "@jay-framework/component": "workspace:^",
33
+ "@jay-framework/runtime": "workspace:^",
34
+ "@jay-framework/secure": "workspace:^",
35
+ "@types/js-yaml": "^4.0.9",
36
+ "change-case": "^4.1.2",
37
+ "js-yaml": "^4.1.0",
38
+ "node-html-parser": "^6.1.12",
39
+ "pegjs": "^0.10.0",
40
+ "pluralize": "^8.0.0",
41
+ "style-to-object": "^1.0.8",
42
+ "typescript": "^5.3.3"
43
+ },
44
+ "devDependencies": {
45
+ "@caiogondim/strip-margin": "^1.0.0",
46
+ "@jay-framework/4-react": "workspace:^",
47
+ "@jay-framework/dev-environment": "workspace:^",
48
+ "@testing-library/jest-dom": "^6.2.0",
49
+ "@types/js-beautify": "^1",
50
+ "@types/node": "^20.11.5",
51
+ "@types/react": "^18.2.0",
52
+ "jest-diff": "^29.7.0",
53
+ "jest-matcher-utils": "^29.7.0",
54
+ "js-beautify": "^1.14.11",
55
+ "react": "^18.2.0",
56
+ "react-dom": "^18.2.0",
57
+ "rimraf": "^5.0.5",
58
+ "tsup": "^8.0.1",
59
+ "vite": "^5.0.11",
60
+ "vitest": "^1.2.1"
61
+ }
62
+ }