silmaril

Limits

The compiler follows reads in the setup it compiles. Some reads are out of its reach. This page lists them, so you know when a change does not run your code.

The compiler reads one module

silmaril decides what a write runs when the module compiles. It sees only reads in the same setup tree. A read it cannot see is not tracked, and a change to that value runs nothing.

Functions the compiler cannot follow

A call tracks the reads of the function when the function is local. A local function is a function declaration, or a let, const or var with a function or arrow initializer, declared inside the same setup. The binding must never be written and must have no slot.

Every other call is opaque, and the reads inside it are not tracked:

To track a value, pass it in as an argument or read it in the setup:

import { read } from './other.js';

$$(() => {
  let count = 0;

  // Not tracked: read() reads `count` in another module.
  $sync(console.log(read()));

  // Tracked: the argument is read in the setup.
  $sync(console.log(read(count)));
});

A function initializer that reads a reactive value is a computed variable, not an opaque helper. const handler = () => count has a slot, and a call to it reads that slot.

Reads inside nested plain functions are followed, as in $sync(() => [1].map(() => a)). Reads inside $skip(...), nested entries and nested setups are not part of the outer entry.

Mutation in place

Mutating an object in place is not a write. A reactive variable changes only when you assign it.

limits-in-place.js

No logs.

To react to a change, assign a new value: user = { ...user, name: 'Grace' }.

Defaults in declaration patterns

A read in a default of a declaration pattern does not make a computed variable. Only the initializer counts. In this sample b reads a once, when the line runs, and does not follow a:

limits-pattern-default.js

No logs.

Read the variable in the initializer to track it, as in const b = a.

typeof queries

type T = typeof n keeps naming the binding n, also when n has a slot. A type is erased at compile time, so it is not a read and adds no edge to the graph.

The loop guard

An entry that writes what it reads runs again until the value settles. A cycle that settles is not an error:

limits-settled-loop.js

No logs.

A cycle that never settles would run forever. One write that reaches more than 10,000 entries in one drain throws Error('Update loop'). The error reaches the caller of the write, or of $$ for a setup. The root is disposed when its setup throws.

limits-update-loop.js

No logs.

Details:

Other limits