mancha 0.18.2 → 0.18.4
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/.github/workflows/ci.yml +3 -0
- package/README.md +19 -477
- package/biome.json +46 -0
- package/dist/browser.d.ts +39 -33
- package/dist/browser.js +13 -1
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +37 -6
- package/dist/cli.js.map +1 -1
- package/dist/css_gen_basic.d.ts +1 -1
- package/dist/css_gen_basic.js.map +1 -1
- package/dist/dome.d.ts +3 -3
- package/dist/dome.js +26 -16
- package/dist/dome.js.map +1 -1
- package/dist/expressions/ast.test.js +5 -5
- package/dist/expressions/ast.test.js.map +1 -1
- package/dist/expressions/ast_factory.d.ts +1 -1
- package/dist/expressions/ast_factory.test.js.map +1 -1
- package/dist/expressions/constants.test.js +1 -1
- package/dist/expressions/constants.test.js.map +1 -1
- package/dist/expressions/eval.d.ts +2 -2
- package/dist/expressions/eval.js +19 -9
- package/dist/expressions/eval.js.map +1 -1
- package/dist/expressions/eval.test.js +23 -0
- package/dist/expressions/eval.test.js.map +1 -1
- package/dist/expressions/parser.d.ts +2 -2
- package/dist/expressions/parser.js +13 -9
- package/dist/expressions/parser.js.map +1 -1
- package/dist/expressions/parser.test.js +2 -1
- package/dist/expressions/parser.test.js.map +1 -1
- package/dist/expressions/tokenizer.js +17 -17
- package/dist/expressions/tokenizer.js.map +1 -1
- package/dist/expressions/tokenizer.test.js +1 -1
- package/dist/expressions/tokenizer.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces-Dizh9utI.d.ts +220 -0
- package/dist/interfaces.d.ts +1 -1
- package/dist/mancha.d.ts +5 -2
- package/dist/mancha.js +57 -1
- package/dist/mancha.js.map +1 -1
- package/dist/plugins.js +39 -24
- package/dist/plugins.js.map +1 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.js +1 -1
- package/dist/query.js.map +1 -1
- package/dist/renderer-B3R6_o-2.js +30 -0
- package/dist/renderer.d.ts +3 -3
- package/dist/renderer.js +4 -3
- package/dist/renderer.js.map +1 -1
- package/dist/safe_browser.d.ts +15 -13
- package/dist/safe_browser.js +1 -1
- package/dist/store.d.ts +2 -2
- package/dist/store.js +6 -2
- package/dist/store.js.map +1 -1
- package/dist/test_utils.d.ts +8 -7
- package/dist/test_utils.js +8 -3
- package/dist/test_utils.js.map +1 -1
- package/dist/type_checker.js +37 -30
- package/dist/type_checker.js.map +1 -1
- package/dist/worker.d.ts +5 -5
- package/dist/worker.js +5 -5
- package/dist/worker.js.map +1 -1
- package/docs/components.md +199 -0
- package/docs/quickstart.md +18 -701
- package/docs/reactivity.md +135 -0
- package/docs/ssr.md +78 -0
- package/docs/syntax.md +123 -0
- package/docs/testing.md +66 -0
- package/docs/typescript.md +167 -0
- package/gulpfile.ts +25 -22
- package/package.json +9 -7
- package/scripts/generate-css-docs.ts +6 -9
- package/tsconfig.json +3 -10
- package/tsdown.config.ts +32 -0
- package/.prettierrc +0 -3
- package/webpack.config.esmodule.ts +0 -26
- package/webpack.config.ts +0 -21
package/.github/workflows/ci.yml
CHANGED
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# mancha
|
|
2
2
|
|
|
3
|
-
`mancha` is a simple HTML templating and reactivity library for simple people. It works on the
|
|
4
|
-
browser or the server. It can be used as a command-line tool, or imported as a Javascript module.
|
|
3
|
+
`mancha` is a simple HTML templating and reactivity library for simple people. It works on the browser or the server. It can be used as a command-line tool, or imported as a Javascript module.
|
|
5
4
|
|
|
6
5
|
Here's a small sample of the things that you can do with `mancha`:
|
|
7
6
|
|
|
@@ -35,496 +34,39 @@ Here's a small sample of the things that you can do with `mancha`:
|
|
|
35
34
|
|
|
36
35
|
## Why another front-end Javascript library?
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- [Google's Svelte](https://svelte.dev)
|
|
41
|
-
- [Meta's React](https://react.dev)
|
|
42
|
-
- [Vue.js](https://vuejs.org) and [petite-vue](https://github.com/vuejs/petite-vue)
|
|
43
|
-
- [Alpine.js](https://alpinejs.dev)
|
|
37
|
+
`mancha` is great for:
|
|
44
38
|
|
|
45
|
-
|
|
39
|
+
- **prototyping**, just plop a script tag in your HTML and off you go
|
|
40
|
+
- **testing**, individual components can be rendered and tested outside the browser
|
|
41
|
+
- **progressive enhancement**, from simple templating and basic reactivity to a full-blown app
|
|
46
42
|
|
|
47
43
|
| Feature | mancha | Svelte | React.js | Vue.js | petite-vue | Alpine.js |
|
|
48
44
|
| --------------------- | ------ | ------ | -------- | ------ | ---------- | --------- |
|
|
49
45
|
| Simple to learn | ✔️ | ❌ | ❌ | ❌ | ✔️ | ✔️ |
|
|
50
|
-
| <
|
|
46
|
+
| < 16kb compressed | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ |
|
|
51
47
|
| Custom web components | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
|
|
52
48
|
| Client-side rendering | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ✔️ |
|
|
53
49
|
| Server-side rendering | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- **prototyping**, just plop a script tag in your HTML and off you go
|
|
58
|
-
- **testing**, individual components can be rendered and tested outside the browser
|
|
59
|
-
- **progressive enhancement**, from simple templating and basic reactivity to a full-blown app
|
|
60
|
-
|
|
61
|
-
A core benefit of using `mancha` is that it allows you to compartmentalize the complexity of
|
|
62
|
-
front-end development. Whether you decide to break up your app into reusable partial sections via
|
|
63
|
-
`<include>` or create custom web components, you can write HTML as if your mother was watching.
|
|
64
|
-
|
|
65
|
-
`mancha` implements its own reactivity engine, so the bundled browser module contains no external
|
|
66
|
-
dependencies with the exception of [`jexpr`][jexpr] for safe expression evaluation (see the
|
|
67
|
-
[dependencies section](#dependencies)).
|
|
68
|
-
|
|
69
|
-
## Preprocessing
|
|
70
|
-
|
|
71
|
-
As part of the rendering lifecycle, `mancha` first preprocesses the HTML. The two main stages of
|
|
72
|
-
preprocessing consist of:
|
|
73
|
-
|
|
74
|
-
- Resolution of `<include>` tags
|
|
75
|
-
|
|
76
|
-
```html
|
|
77
|
-
<!-- ./button.tpl.html -->
|
|
78
|
-
<button>Click Me</button>
|
|
79
|
-
|
|
80
|
-
<!-- ./index.html -->
|
|
81
|
-
<div>
|
|
82
|
-
<include src="button.tpl.html"></include>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<!-- Result after rendering `index.html`. -->
|
|
86
|
-
<div>
|
|
87
|
-
<button>Click Me</button>
|
|
88
|
-
</div>
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
- Registration and resolution of all custom web components
|
|
92
|
-
|
|
93
|
-
```html
|
|
94
|
-
<!-- Use <template is="my-component-name"> to register a component. -->
|
|
95
|
-
<template is="my-red-button">
|
|
96
|
-
<button style="background-color: red;">
|
|
97
|
-
<slot></slot>
|
|
98
|
-
</button>
|
|
99
|
-
</template>
|
|
100
|
-
|
|
101
|
-
<!-- Any node traversed after registration can use the component. -->
|
|
102
|
-
<my-red-button :on:click="console.log('clicked')">
|
|
103
|
-
<!-- The contents within will replace the `<slot></slot>` tag. -->
|
|
104
|
-
Click Me
|
|
105
|
-
</my-red-button>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Rendering
|
|
109
|
-
|
|
110
|
-
Once the HTML has been preprocessed, it is rendered by traversing every node in the DOM and applying
|
|
111
|
-
a series of plugins. Each plugin is only applied if specific conditions are met such as the HTML
|
|
112
|
-
element tag or attributes match a specific criteria. Here's the list of attributes handled:
|
|
113
|
-
|
|
114
|
-
- `:data` provides scoped variables to all subnodes, evaluated using `jexpr`
|
|
115
|
-
```html
|
|
116
|
-
<div :data="{ name: 'Stranger' }"></div>
|
|
117
|
-
```
|
|
118
|
-
- `:for` clones the node and repeats it
|
|
119
|
-
```html
|
|
120
|
-
<div :for="item in ['a', 'b', 'c']">{{ item }}</div>
|
|
121
|
-
```
|
|
122
|
-
- `:text` sets the `textContent` value of a node
|
|
123
|
-
```html
|
|
124
|
-
<div :data="{foo: 'bar'}" :text="foo"></div>
|
|
125
|
-
```
|
|
126
|
-
- `:html` sets the `innerHTML` value of a node
|
|
127
|
-
```html
|
|
128
|
-
<div :html="<span>Hello World</span>"></div>
|
|
129
|
-
```
|
|
130
|
-
- `:show` toggles `$elem.style.display` to `none`
|
|
131
|
-
```html
|
|
132
|
-
<div :data="{foo: false}" :show="foo"></div>
|
|
133
|
-
```
|
|
134
|
-
- `:if` conditionally renders the element (removes it from the DOM when false). **Note**: `:else` is not currently supported.
|
|
135
|
-
```html
|
|
136
|
-
<div :data="{visible: true}" :if="visible">Content</div>
|
|
137
|
-
```
|
|
138
|
-
- `:class` appends rendered text to existing class attribute
|
|
139
|
-
```html
|
|
140
|
-
<span :class="error ? 'red' : 'blue'" class="text-xl">...</span>
|
|
141
|
-
```
|
|
142
|
-
- `:bind` binds (two-way) a variable to the `value` or `checked` property of the element
|
|
143
|
-
```html
|
|
144
|
-
<div :data="{ name: 'Stranger' }">
|
|
145
|
-
<input type="text" :bind="name" />
|
|
146
|
-
</div>
|
|
147
|
-
```
|
|
148
|
-
- `:on:{event}` adds an event listener for `event` to the node
|
|
149
|
-
```html
|
|
150
|
-
<button :on:click="console.log('clicked')"></button>
|
|
151
|
-
```
|
|
152
|
-
- `:on:{event}.prevent` calls `event.preventDefault()` in the event handler
|
|
153
|
-
```html
|
|
154
|
-
<a href="#" :on:click.prevent="console.log('clicked')"></a>
|
|
155
|
-
```
|
|
156
|
-
- `:attr:{name}` sets the corresponding attribute for `name` in the node
|
|
157
|
-
```html
|
|
158
|
-
<a :attr:href="buildUrl()"></a>
|
|
159
|
-
```
|
|
160
|
-
- `:prop:{name}` sets the corresponding property for (camel-case converted) `name` in the node
|
|
161
|
-
```html
|
|
162
|
-
<video :prop:src="buildSrc()"></video>
|
|
163
|
-
```
|
|
164
|
-
- `:render` links an element to a JavaScript ES module for initialization
|
|
165
|
-
```html
|
|
166
|
-
<canvas :render="./chart-init.js"></canvas>
|
|
167
|
-
```
|
|
168
|
-
The module's default export is called with the element and renderer:
|
|
169
|
-
```js
|
|
170
|
-
// chart-init.js
|
|
171
|
-
export default function (elem, renderer) {
|
|
172
|
-
new Chart(elem, { type: "bar" });
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
- `{{ value }}` replaces `value` in text nodes
|
|
176
|
-
```html
|
|
177
|
-
<button :data="{label: 'Click Me'}">{{ label }}</button>
|
|
178
|
-
```
|
|
179
|
-
- `:types` **(experimental)** declares TypeScript types for static type checking
|
|
180
|
-
```html
|
|
181
|
-
<div :types='{"user": "@import:./types/user.ts:User"}'>
|
|
182
|
-
<span>{{ user.name.toUpperCase() }}</span>
|
|
183
|
-
</div>
|
|
184
|
-
```
|
|
185
|
-
The value of `:types` is parsed with `jexpr` during static analysis. It must evaluate to a plain
|
|
186
|
-
object whose values are strings containing TypeScript snippets. When you need quotes inside a type,
|
|
187
|
-
either escape them in a double-quoted attribute (e.g.
|
|
188
|
-
`<div :types="{\"status\": \"'active' | 'inactive'\"}">`) or, preferably, move the definition into a
|
|
189
|
-
TypeScript module and reference it via `@import`.
|
|
190
|
-
See the [Type Checking section in the quickstart guide](./docs/quickstart.md#type-checking-experimental) for more details.
|
|
191
|
-
|
|
192
|
-
## Evaluation
|
|
193
|
-
|
|
194
|
-
To avoid violation of Content Security Policy (CSP) that forbids the use of `eval()`, `Mancha`
|
|
195
|
-
evaluates all expressions using a safe expression parser. This means that only simple expressions are
|
|
196
|
-
allowed, but it supports many modern JavaScript features, including optional chaining, the spread
|
|
197
|
-
operator, and arrow functions. For example:
|
|
198
|
-
|
|
199
|
-
```html
|
|
200
|
-
<!-- Valid expression: string concatenation -->
|
|
201
|
-
<body :data="{ pos: 1 }">
|
|
202
|
-
<p :text="'you are number ' + pos + ' in the queue'"></p>
|
|
203
|
-
</body>
|
|
204
|
-
|
|
205
|
-
<!-- Valid expression: optional chaining -->
|
|
206
|
-
<body :data="{ user: null }">
|
|
207
|
-
<p :text="user?.name ?? 'Anonymous'"></p>
|
|
208
|
-
</body>
|
|
209
|
-
|
|
210
|
-
<!-- Valid expression: spread operator -->
|
|
211
|
-
<body :data="{ list: [1, 2], extra: 3 }">
|
|
212
|
-
<div :for="item in [...list, extra]">{{ item }}</div>
|
|
213
|
-
</body>
|
|
214
|
-
|
|
215
|
-
<!-- Valid expression: arrow functions (e.g. in map) -->
|
|
216
|
-
<body :data="{ items: [1, 2, 3] }">
|
|
217
|
-
<div :for="n in items.map((x) => x * 2)">{{ n }}</div>
|
|
218
|
-
</body>
|
|
219
|
-
|
|
220
|
-
<!-- Valid expression: boolean logic -->
|
|
221
|
-
<body :data="{ pos: 1, finished: false }">
|
|
222
|
-
<p :show="pos >= 1 && !finished">you are number {{ pos }} in the queue</p>
|
|
223
|
-
</body>
|
|
224
|
-
|
|
225
|
-
<!-- Valid expression: ternary operators -->
|
|
226
|
-
<body :data="{ pos: 1 }">
|
|
227
|
-
<p :text="pos % 2 == 0 ? 'even' : 'odd'"></p>
|
|
228
|
-
</body>
|
|
229
|
-
|
|
230
|
-
<!-- Valid expression: function calling -->
|
|
231
|
-
<body :data="{ pos : 1 }">
|
|
232
|
-
<p :text="buildQueueMessage()"></p>
|
|
233
|
-
<script>
|
|
234
|
-
const { $ } = Mancha;
|
|
235
|
-
$.buildQueueMessage = function () {
|
|
236
|
-
return "you are number " + this.pos + " in the queue";
|
|
237
|
-
};
|
|
238
|
-
// Alternatively, anonymous functions without `this`:
|
|
239
|
-
// $.buildQueueMessage = () => 'you are number ' + $.pos + ' in the queue';
|
|
240
|
-
</script>
|
|
241
|
-
</body>
|
|
242
|
-
|
|
243
|
-
<!-- Valid expression: simple assignment -->
|
|
244
|
-
<body :data="{ pos: 1 }">
|
|
245
|
-
<p :text="'you are number ' + pos + ' in the queue'"></p>
|
|
246
|
-
<button :on:click="pos = pos + 1">Click to get there faster</button>
|
|
247
|
-
</body>
|
|
248
|
-
|
|
249
|
-
<!-- Invalid expression: multiple statements -->
|
|
250
|
-
<button :on:click="console.log('yes'); answer = 'no'"></button>
|
|
251
|
-
|
|
252
|
-
<!-- Invalid expression: function definition (top-level) -->
|
|
253
|
-
<body :data="{ foo: function() { return 'yes'; } }">
|
|
254
|
-
<p :text="foo()"></p>
|
|
255
|
-
</body>
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Variable Scoping
|
|
259
|
-
|
|
260
|
-
Contents of the `:data` attribute are only available to subnodes in the HTML tree. This is better
|
|
261
|
-
illustrated with an example:
|
|
262
|
-
|
|
263
|
-
```html
|
|
264
|
-
<body :data="{ name: 'stranger', key: '1234' }">
|
|
265
|
-
<!-- Hello, stranger -->
|
|
266
|
-
<h1>Hello, {{ name }}</h1>
|
|
267
|
-
|
|
268
|
-
<!-- Initially "undefined", but reactive to later changes -->
|
|
269
|
-
<span>{{ message }}</span>
|
|
270
|
-
|
|
271
|
-
<!-- How are you, danger? The secret message is "secret" and the key is "1234" -->
|
|
272
|
-
<p :data="{ name: 'danger', message: 'secret' }">
|
|
273
|
-
How are you, {{ name }}? The secret message is "{{ message }}" and the key is "{{ key }}"
|
|
274
|
-
</p>
|
|
275
|
-
</body>
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
By default, the target root element is the `body` tag. So, any variables defined in the body's
|
|
279
|
-
`:data` attribute are available to the main renderer.
|
|
280
|
-
|
|
281
|
-
In the example above, the `<span>` references `message` which is not defined in the body's `:data`.
|
|
282
|
-
This auto-initializes `message` to `undefined` and attaches an observer, so setting `$.message`
|
|
283
|
-
later will update the `<span>` content. The `<p>` tag has its own local `message` variable which
|
|
284
|
-
shadows any parent value. Since the variables are not accessible via the global object, you'll need
|
|
285
|
-
to retrieve the renderer from the element's properties:
|
|
286
|
-
|
|
287
|
-
```js
|
|
288
|
-
// Explicitly render the body, so we can await it and then modify variables.
|
|
289
|
-
const { $ } = Mancha;
|
|
290
|
-
await $.mount(document.body);
|
|
291
|
-
|
|
292
|
-
// This modifies the `name` variable in all the renderer contexts.
|
|
293
|
-
$.name = "world";
|
|
294
|
-
|
|
295
|
-
// This updates the `<span>` content to "bandit" because `message` was
|
|
296
|
-
// auto-initialized when the template referenced it. However, the `<p>` tag
|
|
297
|
-
// still shows "secret" because it has its own local `message` variable.
|
|
298
|
-
$.message = "bandit";
|
|
299
|
-
|
|
300
|
-
// We extract the subrenderer from the element's properties. Only elements
|
|
301
|
-
// with `:data` attribute have a `renderer` property.
|
|
302
|
-
const subrenderer = document.querySelector("p").renderer;
|
|
303
|
-
|
|
304
|
-
// This modifies the `message` variable only in the `<p>` tag.
|
|
305
|
-
subrenderer.$.message = "banana";
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
When accessing variables, `mancha` searches the current renderer first, then the parent, the
|
|
309
|
-
parent's parent, and so forth until the root renderer is reached. If the requested variable is not
|
|
310
|
-
found in the current renderer or any of the ancestor renderers, then `undefined` is returned:
|
|
311
|
-
|
|
312
|
-
```html
|
|
313
|
-
<body :data="{ name: 'stranger' }">
|
|
314
|
-
<!-- Hello, stranger! -->
|
|
315
|
-
<p :data="{}">Hello, {{ name }}!</p>
|
|
316
|
-
</body>
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Reactive Undefined Variables
|
|
51
|
+
## Documentation
|
|
320
52
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
53
|
+
- **[Quick Start](./docs/quickstart.md)**: Get up and running in minutes.
|
|
54
|
+
- **[Syntax](./docs/syntax.md)**: Reference for attributes and expressions.
|
|
55
|
+
- **[Reactivity](./docs/reactivity.md)**: How variables, scoping, and URL binding work.
|
|
56
|
+
- **[Components](./docs/components.md)**: Creating reusable components and includes.
|
|
57
|
+
- **[CSS](./docs/css.md)**: Built-in CSS utilities.
|
|
58
|
+
- **[Server-Side Rendering](./docs/ssr.md)**: Using Mancha on the server (Node, Workers).
|
|
59
|
+
- **[TypeScript](./docs/typescript.md)**: Type safety and checking.
|
|
60
|
+
- **[Testing](./docs/testing.md)**: Testing your UI.
|
|
324
61
|
|
|
325
|
-
|
|
326
|
-
<body>
|
|
327
|
-
<!-- Initially shows "undefined", but updates reactively when `message` is set -->
|
|
328
|
-
<p>{{ message }}</p>
|
|
329
|
-
</body>
|
|
330
|
-
<script type="module">
|
|
331
|
-
const { $ } = Mancha;
|
|
332
|
-
await $.mount(document.body);
|
|
333
|
-
|
|
334
|
-
// The template initially renders with `message` as undefined.
|
|
335
|
-
// Setting it now will trigger a reactive update.
|
|
336
|
-
$.message = "Hello, World!";
|
|
337
|
-
</script>
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
This behavior is particularly useful with the `:render` attribute, where a JavaScript module can
|
|
341
|
-
set variables that are already referenced in the template:
|
|
342
|
-
|
|
343
|
-
```html
|
|
344
|
-
<div :render="./init.js">
|
|
345
|
-
<!-- These variables are set by the :render callback -->
|
|
346
|
-
<span>{{ title }}</span>
|
|
347
|
-
<ul :for="item in items">
|
|
348
|
-
<li>{{ item }}</li>
|
|
349
|
-
</ul>
|
|
350
|
-
</div>
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
```js
|
|
354
|
-
// init.js
|
|
355
|
-
export default async function (elem, renderer) {
|
|
356
|
-
await renderer.set("title", "My List");
|
|
357
|
-
await renderer.set("items", ["a", "b", "c"]);
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
The auto-initialization only happens when variables are accessed during an effect (such as template
|
|
362
|
-
rendering). Accessing a variable outside of an effect context will return `undefined` without
|
|
363
|
-
creating an observer.
|
|
364
|
-
|
|
365
|
-
When setting a variable, there are 3 possible cases:
|
|
366
|
-
|
|
367
|
-
1. The variable has already been defined in the current renderer. Then it gets updated in the
|
|
368
|
-
current renderer.
|
|
369
|
-
1. The variable is undefined in the current renderer but has already been defined in an ancestor
|
|
370
|
-
renderer. Then it gets updated in the corresponding ancestor renderer.
|
|
371
|
-
1. The variable is not defined in the current renderer or any ancestor renderers. Then it is set in
|
|
372
|
-
the current renderer.
|
|
373
|
-
|
|
374
|
-
NOTE: This does not apply to variables defined via `:data` attribute, which always set a new
|
|
375
|
-
variable for the newly created renderer.
|
|
376
|
-
|
|
377
|
-
Renderers also have a `$parent`, `$rootRenderer` and `$rootNode` attributes. The `$parent` attribute
|
|
378
|
-
references the immediate ancestor renderer if any, or it's `null` otherwise. The `$rootRenderer`
|
|
379
|
-
attribute references the root renderer where `mancha` was mounted, which could be a self-reference.
|
|
380
|
-
Finally, the `$rootNode` attribute references the HTML node where `mancha` was mounted.
|
|
381
|
-
|
|
382
|
-
While evaluating expressions, there will also be an `$elem` attribute which references the current
|
|
383
|
-
element being rendered or, in the case of events, the element dispatching the event as well as the
|
|
384
|
-
corresponding `$event` attribute.
|
|
385
|
-
|
|
386
|
-
### URL Query Parameter Binding
|
|
387
|
-
|
|
388
|
-
`mancha` provides a convenient way to establish a two-way binding between the state of your application and the URL query parameters. This is useful for preserving state across page reloads and for sharing links that restore the application to a specific state.
|
|
389
|
-
|
|
390
|
-
This feature is enabled automatically. When a renderer is mounted to a DOM element, any variable in its store prefixed with `$$` will be automatically synchronized with the URL query parameters.
|
|
391
|
-
|
|
392
|
-
- **Store to URL**: When you set a variable like `$.$$page = 2` (where `$` is the renderer's reactive proxy), the URL will be updated to `/?page=2`. If you set the value to a falsy value (e.g., `null`, `undefined`, `false`, `0`, or `''`), the parameter will be removed from the URL.
|
|
393
|
-
|
|
394
|
-
- **URL to Store**: When the page loads or the user navigates using the browser's back/forward buttons, the store is automatically updated from the current URL's query parameters. For example, if the URL is `/?search=mancha`, the store variable `$$search` will be set to `"mancha"`.
|
|
395
|
-
|
|
396
|
-
This allows you to react to URL changes declaratively within your components:
|
|
397
|
-
|
|
398
|
-
```html
|
|
399
|
-
<body :data="{ $$search: '' }">
|
|
400
|
-
<input type="text" :bind="$$search" placeholder="Search..." />
|
|
401
|
-
<div :show="$$search">Searching for: {{ $$search }}</div>
|
|
402
|
-
</body>
|
|
403
|
-
<script type="module">
|
|
404
|
-
import { Mancha } from "//unpkg.com/mancha";
|
|
405
|
-
await Mancha.mount(document.body);
|
|
406
|
-
</script>
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
## Styling
|
|
410
|
-
|
|
411
|
-
Some basic styling rules are built into the library and can be optionally used. The styling
|
|
412
|
-
component was designed to be used in the browser, and it's enabled by adding a `css` attribute
|
|
413
|
-
to the `<script>` tag that loads `mancha`. The supported rulesets are:
|
|
414
|
-
|
|
415
|
-
- `basic`: inspired by [these rules](https://www.swyx.io/css-100-bytes), the full CSS can be found
|
|
416
|
-
[here](./src/css_gen_basic.ts).
|
|
417
|
-
- `utils`: utility classes inspired by [tailwindcss](https://tailwindcss.com), the resulting CSS is
|
|
418
|
-
a drop-in replacement for a subset of the classes provided by `tailwindcss` with the main
|
|
419
|
-
exception of the color palette which is borrowed from
|
|
420
|
-
[material design](https://www.materialpalette.com/colors).
|
|
421
|
-
|
|
422
|
-
## Usage
|
|
423
|
-
|
|
424
|
-
### Client Side Rendering (CSR)
|
|
62
|
+
## AI Agents
|
|
425
63
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
```html
|
|
429
|
-
<body :data="{ name: 'John' }">
|
|
430
|
-
<span>Hello, {{ name }}!</span>
|
|
431
|
-
</body>
|
|
432
|
-
|
|
433
|
-
<script src="//unpkg.com/mancha" target="body" css="basic+utils" init></script>
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
Script tag attributes:
|
|
437
|
-
|
|
438
|
-
- `init`: whether to automatically render upon script load
|
|
439
|
-
- `target`: document elements separated by `+` to render e.g. "body" or "head+body" (defaults to
|
|
440
|
-
"body")
|
|
441
|
-
- `css`: inject predefined CSS rulesets into the `<head>` element, see the
|
|
442
|
-
[styling section](#styling) for more details.
|
|
443
|
-
|
|
444
|
-
For a more complete example, see [examples/browser](./examples/browser).
|
|
445
|
-
|
|
446
|
-
### Compile Time Server Side Rendering (SSR)
|
|
447
|
-
|
|
448
|
-
To use `mancha` on the server at compile time, you can use the `npx mancha` command. For example,
|
|
449
|
-
if this is your project structure:
|
|
450
|
-
|
|
451
|
-
```
|
|
452
|
-
src/
|
|
453
|
-
├─ components/
|
|
454
|
-
| ├─ main.tpl.html
|
|
455
|
-
| ├─ footer.tpl.html
|
|
456
|
-
├─ index.html
|
|
457
|
-
├─ vars.json
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
You can run the following command to compile the site into a `public` folder:
|
|
64
|
+
If you are an AI agent building with `mancha`, you can dump all the documentation in a single concatenated output by running:
|
|
461
65
|
|
|
462
66
|
```bash
|
|
463
|
-
npx mancha
|
|
67
|
+
npx mancha docs
|
|
464
68
|
```
|
|
465
69
|
|
|
466
|
-
For a more complete example, see [examples/compiled](./examples/compiled).
|
|
467
|
-
|
|
468
|
-
### On Demand Server Side Rendering (SSR)
|
|
469
|
-
|
|
470
|
-
You can also use `mancha` as part of your server's request handling. Assuming a similar folder
|
|
471
|
-
structure as described in the previous section, the following `express` node server would render
|
|
472
|
-
the HTML code on demand for each incoming request:
|
|
473
|
-
|
|
474
|
-
```js
|
|
475
|
-
import express from "express";
|
|
476
|
-
import { Renderer } from "mancha";
|
|
477
|
-
import vars from "./vars.json";
|
|
478
|
-
|
|
479
|
-
const app = express();
|
|
480
|
-
|
|
481
|
-
app.get("/", async (req, res) => {
|
|
482
|
-
const name = req.query.name || "Stranger";
|
|
483
|
-
// Instantiate a new renderer.
|
|
484
|
-
const renderer = new Renderer({ name, ...vars });
|
|
485
|
-
// Preprocess input HTML from a local file path.
|
|
486
|
-
const fragment = await renderer.preprocessLocal("src/index.html");
|
|
487
|
-
// Render and serialize output HTML.
|
|
488
|
-
const html = renderer.serializeHTML(await renderer.renderNode(fragment));
|
|
489
|
-
// Send it to the client.
|
|
490
|
-
res.set("Content-Type", "text/html");
|
|
491
|
-
res.send(html);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
app.listen(process.env.PORT || 8080);
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
For a more complete example, see [examples/express](./examples/express).
|
|
498
|
-
|
|
499
|
-
### Web Worker Runtime Server Side Rendering (SSR)
|
|
500
|
-
|
|
501
|
-
For servers hosted as worker runtimes, such as `Cloudflare Workers`, you will need to import a
|
|
502
|
-
stripped down version of `mancha` that does not have the ability to read local files.
|
|
503
|
-
|
|
504
|
-
```js
|
|
505
|
-
import { Renderer } from "mancha/dist/worker";
|
|
506
|
-
import htmlIndex from "./index.html";
|
|
507
|
-
import vars from "./vars.json";
|
|
508
|
-
|
|
509
|
-
self.addEventListener("fetch", async (event) => {
|
|
510
|
-
// Instantiate a new renderer.
|
|
511
|
-
const renderer = new Renderer({ ...vars });
|
|
512
|
-
// Preprocess input HTML from a string.
|
|
513
|
-
const fragment = await renderer.preprocessString(htmlIndex);
|
|
514
|
-
// Render and serialize output HTML.
|
|
515
|
-
const html = renderer.serializeHTML(await renderer.renderNode(fragment));
|
|
516
|
-
// Send it to the client.
|
|
517
|
-
event.respondWith(new Response(content, { headers: { "Content-Type": "text/html" } }));
|
|
518
|
-
});
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
To meet the size requirements of popular worker runtimes, the worker version of `mancha` uses
|
|
522
|
-
`htmlparser2` instead of `jsdom` for the underlying HTML and DOM manipulation. This keeps the
|
|
523
|
-
footprint of `mancha` and its dependencies under 100kb.
|
|
524
|
-
|
|
525
|
-
For a more complete example, see [examples/wrangler](./examples/wrangler).
|
|
526
|
-
|
|
527
70
|
## Dependencies
|
|
528
71
|
|
|
529
|
-
The browser bundle contains no external dependencies. The unbundled version
|
|
530
|
-
can use `htmlparser2`, which is compatible with web workers, or `jsdom`.
|
|
72
|
+
The browser bundle contains no external dependencies. The unbundled version can use `htmlparser2`, which is compatible with web workers, or `jsdom`.
|
package/biome.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": true,
|
|
10
|
+
"includes": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.json"]
|
|
11
|
+
},
|
|
12
|
+
"formatter": {
|
|
13
|
+
"enabled": true,
|
|
14
|
+
"formatWithErrors": false,
|
|
15
|
+
"indentStyle": "tab",
|
|
16
|
+
"indentWidth": 2,
|
|
17
|
+
"lineEnding": "lf",
|
|
18
|
+
"lineWidth": 100,
|
|
19
|
+
"attributePosition": "auto"
|
|
20
|
+
},
|
|
21
|
+
"linter": {
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"rules": {
|
|
24
|
+
"recommended": true,
|
|
25
|
+
"complexity": {
|
|
26
|
+
"noUselessTypeConstraint": "error"
|
|
27
|
+
},
|
|
28
|
+
"style": {
|
|
29
|
+
"noNonNullAssertion": "error",
|
|
30
|
+
"useConst": "error",
|
|
31
|
+
"useImportType": "error"
|
|
32
|
+
},
|
|
33
|
+
"suspicious": {
|
|
34
|
+
"noExplicitAny": "error",
|
|
35
|
+
"useIterableCallbackReturn": "error"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"javascript": {
|
|
40
|
+
"formatter": {
|
|
41
|
+
"quoteStyle": "double",
|
|
42
|
+
"trailingCommas": "all",
|
|
43
|
+
"semicolons": "always"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,48 +1,52 @@
|
|
|
1
|
-
import { IRenderer } from "./
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { a as StoreState, i as IRenderer, n as RenderParams, r as RendererPlugin, t as ParserParams } from "./interfaces-Dizh9utI.js";
|
|
2
|
+
import { SafeStyleSheet } from "safevalues";
|
|
3
|
+
|
|
4
|
+
//#region src/css_gen_basic.d.ts
|
|
5
|
+
declare function rules(): SafeStyleSheet;
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/css_gen_utils.d.ts
|
|
8
|
+
|
|
9
|
+
declare function rules$1(): string;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/browser.d.ts
|
|
12
|
+
declare class Renderer<T extends StoreState = StoreState> extends IRenderer<T> {
|
|
13
|
+
readonly impl = "browser";
|
|
14
|
+
protected readonly dirpath: string;
|
|
15
|
+
parseHTML(content: string, params?: ParserParams): Document | DocumentFragment;
|
|
16
|
+
serializeHTML(root: Node | DocumentFragment): string;
|
|
17
|
+
preprocessLocal(fpath: string, params?: RenderParams & ParserParams): Promise<Document | DocumentFragment>;
|
|
18
|
+
createElement(tag: string, owner?: Document | null): Element;
|
|
19
|
+
createComment(content: string, owner?: Document | null): Node;
|
|
20
|
+
textContent(node: Node, content: string): void;
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
declare const Mancha: Renderer<StoreState>;
|
|
19
23
|
/** Options for CSS injection. */
|
|
20
|
-
|
|
24
|
+
type CssName = "basic" | "utils";
|
|
21
25
|
/**
|
|
22
26
|
* Injects CSS rules into the document head.
|
|
23
27
|
* @param names - Array of CSS names to inject ("basic", "utils").
|
|
24
28
|
*/
|
|
25
|
-
|
|
29
|
+
declare function injectCss(names: CssName[]): void;
|
|
26
30
|
/**
|
|
27
31
|
* Injects the basic CSS rules into the document head.
|
|
28
32
|
*/
|
|
29
|
-
|
|
33
|
+
declare function injectBasicCss(): void;
|
|
30
34
|
/**
|
|
31
35
|
* Injects the utils CSS rules into the document head.
|
|
32
36
|
*/
|
|
33
|
-
|
|
37
|
+
declare function injectUtilsCss(): void;
|
|
34
38
|
/** Options for initializing Mancha. */
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
interface InitManchaOptions {
|
|
40
|
+
/** CSS styles to inject before mounting. */
|
|
41
|
+
css?: CssName[];
|
|
42
|
+
/** Target selector(s) to mount the renderer to. */
|
|
43
|
+
target?: string | string[];
|
|
44
|
+
/** Enable debug mode. */
|
|
45
|
+
debug?: boolean;
|
|
46
|
+
/** Cache policy for fetch requests. */
|
|
47
|
+
cache?: RequestCache;
|
|
48
|
+
/** Initial state to set before mounting. */
|
|
49
|
+
state?: Record<string, unknown>;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* Initializes Mancha with the provided options.
|
|
@@ -51,4 +55,6 @@ export interface InitManchaOptions {
|
|
|
51
55
|
* @param options - Initialization options.
|
|
52
56
|
* @returns A promise that resolves to the Renderer instance.
|
|
53
57
|
*/
|
|
54
|
-
|
|
58
|
+
declare function initMancha<T extends StoreState = StoreState>(options?: InitManchaOptions): Promise<Renderer<T>>;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { CssName, IRenderer, InitManchaOptions, Mancha, type ParserParams, type RenderParams, Renderer, type RendererPlugin, rules as basicCssRules, initMancha, injectBasicCss, injectCss, injectUtilsCss, rules$1 as utilsCssRules };
|