Skip to content

Commit 4d7faed

Browse files
committed
fix: warnings in dev mode instead of slient failing in pass() and
dependency resolution, dynamic element updates in createEventsSensor, full script cloning in dangerouslySetInnerHTML()
1 parent d0a4e93 commit 4d7faed

File tree

120 files changed

+711
-1021
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+711
-1021
lines changed

ARCHITECTURE.md

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ src/
2222
class.ts toggleClass
2323
event.ts on() — event listener effect
2424
html.ts dangerouslySetInnerHTML
25-
method.ts callMethod, focus
2625
pass.ts pass() — inter-component reactive property binding
2726
property.ts setProperty, show
2827
style.ts setStyle
@@ -46,7 +45,7 @@ scheduler.ts ──── (leaf, no internal imports) │
4645
parsers.ts ─────── ui.ts (types only) │
4746
parsers/* ──────── parsers.ts, ui.ts (types) │
4847
49-
internal.ts ────── (leaf, WeakMap for signal storage) │
48+
internal.ts ────── (leaf, signal storage)
5049
ui.ts ──────────── errors.ts, util.ts │
5150
5251
effects.ts ─────── component.ts (types), errors.ts, │
@@ -141,7 +140,7 @@ updateElement(reactive, { op, name, read, update, delete? })
141140
└─ createEffect(() => {
142141
value = resolveReactive(reactive) ← auto-tracks signal deps
143142
if value === RESET → use fallback
144-
if value === null → delete(target) if available, else use fallback
143+
if value === null → delete(target) if available, else use fallback
145144
if value !== current → update(target, value)
146145
})
147146
```
@@ -169,8 +168,6 @@ The `Reactive<T>` type is a union of three forms:
169168
| `show(reactive)` | `p` | Controls `el.hidden` |
170169
| `setStyle(prop, reactive?)` | `s` | Sets/removes an inline style |
171170
| `dangerouslySetInnerHTML(reactive, opts?)` | `h` | Sets innerHTML, optionally in a shadow root |
172-
| `callMethod(name, reactive, args?)` | `m` | Calls a method when reactive is truthy |
173-
| `focus(reactive)` | `m` | Calls `el.focus()` when truthy |
174171

175172
All default their `reactive` parameter to the effect name (e.g., `setAttribute('href')` reads `host.href`).
176173

@@ -266,22 +263,6 @@ The distinction between Parser (≥2 params) and Reader (1 param) is detected at
266263

267264
The dependency resolution catches all errors and runs the callback anyway. The `.catch(() => { callback() })` pattern means even unexpected errors (not just timeouts) are silently swallowed. The `DependencyTimeoutError` is constructed and passed to `reject`, but the actual logging happens... nowhere visible. The error is created inside a `new Promise((_, reject) => { reject(new DependencyTimeoutError(...)) })`, which rejects the race, but the `.catch` just calls `callback()` without logging the error.
268265

269-
### `CircularMutationError` is defined but never thrown
270-
271-
`CircularMutationError` is exported from `errors.ts` and from `index.ts`, but no code in the repository actually throws it. Is this dead code left from a previous implementation, or is it intended for future use?
272-
273-
### `callMethod` reads `() => null` as fallback
274-
275-
`callMethod` uses `read: () => null` — it never reads the current state. This means it always runs `update()` when the reactive is truthy, even if the method was already called. For idempotent methods like `focus()` this is fine, but for methods with side effects it could cause redundant calls. The `focus` effect partially addresses this with `read: el => el === document.activeElement`, but this pattern isn't consistent.
276-
277-
### Commented-out code in effects.ts
278-
279-
`effects.ts` contains a large commented-out `insertOrRemoveElement` function with its `ElementInserter` type. This suggests an in-progress or abandoned feature. Should it be removed, or is it actively being worked on?
280-
281-
### `on()` event handler can't access component UI
282-
283-
The `on()` effect handler receives only the event — it cannot access the component's `ui` object (unlike `createEventsSensor` handlers, which receive `{ event, ui, target, prev }`). This means `on()` handlers can't easily read from other UI elements. Is this intentional to keep `on()` simple, or is it a limitation that should be addressed?
284-
285266
### `createEventsSensor` captures `targets` once
286267

287268
In `createEventsSensor`, the `targets` array is computed once at sensor creation time from the current state of the `Memo`. If the collection changes later (elements added/removed), the sensor won't pick up new targets. For `Memo`-based collections that are specifically designed to be dynamic, this seems like a gap.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
### Fixed
3131

3232
- **`pass()` no longer requires `configurable` property descriptors** on the target element and no longer leaks state on cleanup.
33+
- **`pass()` now warns in dev mode** when a property doesn't exist on the target (likely a typo) or has no Slot (non-Le Truc element), instead of silently doing nothing.
34+
- **`dangerouslySetInnerHTML` script cloning** now copies all functional and security-hardening attributes (`src`, `async`, `defer`, `nomodule`, `crossorigin`, `integrity`, `referrerpolicy`, `fetchpriority`) instead of only `type`. External scripts with `src` no longer become empty inline scripts.
35+
- **`createEventsSensor` now reacts to collection changes**: For `Memo`-backed element collections, `getTarget()` reads the current elements on each event instead of a stale snapshot captured at sensor creation time. Static single-element targets use a fast path with no array overhead.
36+
- **Dependency resolution no longer swallows errors silently**: `DependencyTimeoutError` is now logged via `console.warn` in dev mode. Previously, the `.catch` handler discarded all errors without any output.
37+
- **Dependency resolution filters out already-defined components**: A microtask defer before `Promise.race` filters out components that were defined synchronously after queries ran (e.g. co-bundled components), avoiding unnecessary waits.
38+
- **Dependency timeout increased from 50ms to 200ms**: Now that structural (CSS-only) custom elements and co-bundled components are filtered out, the timeout only applies to genuinely pending async dependencies and gives them a more realistic window.
3339
- **`module-dialog` effect cleanup** no longer resets `host.open` to `false`, which was causing all dialog tests to fail.
3440
- **`form-listbox` keyboard navigation** now uses direct `querySelectorAll` instead of a watched `Memo` that never activated its `MutationObserver` outside reactive contexts.
3541

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
Version 0.16.0
44

5-
**Le Truc - the thing for type-safe reactive web components**
5+
**Le Truc - the thing for type-safe reactive Web Components**
66

7-
Le Truc helps you create reusable, interactive web components that work with any backend or static site generator. Build once, use everywhere.
7+
Le Truc helps you create reusable, interactive Web Components that work with any backend or static site generator. Build once, use everywhere.
88

99
Le Truc is a set of functions to build reusable, loosely coupled Web Components with reactive properties. It provides structure through components and simplifies state management and DOM synchronization using signals and effects, leading to more organized and maintainable code without a steep learning curve.
1010

@@ -53,7 +53,7 @@ defineComponent(
5353
- 🧩 **Function Composition**: Declare component behavior by composing small, reusable functions (parsers and effects).
5454
- 🛠️ **Customizable**: Le Truc is designed to be easily customizable and extensible. Create your own custom parsers and effects to suit your specific needs.
5555
- 🌐 **Context Support**: Share global states across components without prop drilling or tightly coupling logic.
56-
- 🪶 **Tiny footprint**: Minimal core (~8kB gzipped) with tree-shaking support, minimizing JavaScript bundle size.
56+
- 🪶 **Tiny footprint**: Minimal core (~10kB gzipped) with tree-shaking support, minimizing JavaScript bundle size.
5757
- 🛡️ **Type Safety**: Early warnings when types don't match improve code quality and reduce bugs.
5858

5959
Le Truc uses [Cause & Effect](https://github.com/zeixcom/cause-effect) internally for state management with signals and glitch-free DOM updates. If wanted, you could fork Le Truc and replace Cause & Effect with a different state management library without changes to the user-facing `defineComponent()` API.

0 commit comments

Comments
 (0)