Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions tko.io/public/agents/soul.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,36 @@ script tag, verify your tests pass, ship. From there, adopting modern
features (TSX, native providers, modular packages) is incremental and
optional.

The two builds wire the same core differently. Concretely, comparing
`builds/knockout/src/index.ts` and `builds/reference/src/index.ts`:

- **Providers.** Both ship `Component`, `DataBind`, `Virtual`, and
`Attribute`. `reference` additionally enables `Native`,
`AttributeMustache`, and `TextMustache`. `knockout` does not. Provider
order is resolution precedence and differs too:
- `knockout`: `Component` -> `DataBind` -> `Virtual` -> `Attribute`
- `reference`: `Component` -> `Native` -> `AttributeMustache` ->
`TextMustache` -> `DataBind` -> `Virtual` -> `Attribute`
- **Equality.** `reference` sets `options.strictEquality = true`, so `==`
and `!=` in binding expressions evaluate as `===` / `!==`. `knockout`
leaves the legacy lax behavior on.
- **Globals in bindings.** `knockout` sets
`bindingGlobals: options.global`, exposing the global scope inside
binding expressions. `reference` does not.
- **Inline-function rewrite.** `knockout` registers
`functionRewrite` from `@tko/utils.functionrewrite` as a binding-string
preparser, so legacy inline `function (...) { ... }` expressions in
`data-bind` still work. `reference` has no `functionRewrite` preparser.
- **`foreach` binding target.** `knockout` resolves
`data-bind="foreach: ..."` to `TemplateForEachBindingHandler` (legacy
template-engine path), while `reference` resolves it to modern
`ForEachBinding` from `@tko/binding.foreach`. Both resolve
`data-bind="each: ..."` to `ForEachBinding`.
- **Legacy surface.** `knockout` exposes a
`ko.expressionRewriting.preProcessBindings` shim for KO 3 plugins.
`reference` exposes `ko.jsx = { createElement, Fragment, render }` for
TSX. Neither has the other.

TKO aims for stability, not strict backwards compatibility at all costs.
If a change improves correctness, security, or maintainability and the
migration path is clear, it's worth making. But gratuitous breakage is
Expand Down
51 changes: 49 additions & 2 deletions tko.io/src/content/docs/3to4.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ TKO keeps the familiar Knockout programming model, but changes the packaging, bu
## Start with the right package

- **`@tko/build.knockout`**
Recommended for most migrations. It is the compatibility-focused build and the closest match for a traditional Knockout application.
Recommended for most migrations. Closest to a traditional Knockout 3.x application: lax `==`/`!=` in expressions, globals visible inside bindings, the legacy `function (...) { ... }` rewrite for inline `data-bind` callbacks, and a `ko.expressionRewriting.preProcessBindings` shim for plugins that reach for it.
- **`@tko/build.reference`**
Better for custom or modular setups where you want to compose providers and features more explicitly, and the build used by the TSX / `ko-*` docs path.
The modern, modular path used by the TSX / `ko-*` docs. Adds the native, text-mustache, and attribute-mustache providers; switches `==`/`!=` to strict equality (`options.strictEquality`); has no legacy `functionRewrite` preparser and no `expressionRewriting` shim; and exposes `ko.jsx` for TSX rendering.

## The biggest changes

Expand Down Expand Up @@ -80,6 +80,53 @@ TKO is designed for stricter Content Security Policies and avoids the older `eva
| CSP-friendly binding parser | No | Yes | Yes |
| Legacy compatibility workarounds | More common | Reduced | Reduced |

### Build deltas in detail

Both TKO builds share the same foundational packages (`@tko/builder`,
observables, computeds, core/template/if/component bindings, and the punches
filter syntax). They differ in provider order, which options default
differently, which `foreach` handler wins, and which surface APIs they expose.

#### Providers (binding-resolution strategies)

| Provider | What it recognizes | `@tko/build.knockout` | `@tko/build.reference` |
| --- | --- | --- | --- |
| `ComponentProvider` | `<custom-element>` components | Yes | Yes |
| `DataBindProvider` | `data-bind="..."` strings | Yes | Yes |
| `VirtualProvider` | `<!-- ko ... -->` comments | Yes | Yes |
| `AttributeProvider` | `ko-foo="expression"` string attributes | Yes | Yes |
| `NativeProvider` | `el.bindings = {...}` set in JS / TSX | No | Yes |
| `AttributeMustacheProvider` | `attr="prefix {{expr}} suffix"` interpolation | No | Yes |
| `TextMustacheProvider` | `{{ expr }}` in text nodes | No | Yes |

Provider order is resolution precedence, and it differs:

- `@tko/build.knockout`: `ComponentProvider` -> `DataBindProvider` -> `VirtualProvider` -> `AttributeProvider`
- `@tko/build.reference`: `ComponentProvider` -> `NativeProvider` -> `AttributeMustacheProvider` -> `TextMustacheProvider` -> `DataBindProvider` -> `VirtualProvider` -> `AttributeProvider`

#### Binding-expression behavior

| Behavior | `@tko/build.knockout` | `@tko/build.reference` |
| --- | --- | --- |
| `==` / `!=` evaluated as `===` / `!==` (`options.strictEquality`) | No | Yes |
| Bindings see globals (`options.bindingGlobals`) | Yes (`options.global`) | No |
| Inline `function (...) { ... }` rewritten in `data-bind` (`functionRewrite` preparser) | Yes | No |
| `ko.expressionRewriting.preProcessBindings(s)` legacy stub | Yes | No |
| `data-bind="foreach: ..."` resolves to | `TemplateForEachBindingHandler` (legacy template-engine path) | `ForEachBinding` (modern, from `@tko/binding.foreach`) |
| `data-bind="each: ..."` resolves to | `ForEachBinding` | `ForEachBinding` |

#### Surface APIs

| API | `@tko/build.knockout` | `@tko/build.reference` |
| --- | --- | --- |
| `ko.observable` / `ko.computed` / `ko.applyBindings` / components | Yes | Yes |
| `ko.expressionRewriting` (legacy KO 3 plugin shim) | Yes | No |
| `ko.jsx.createElement` / `Fragment` / `render(jsx)` | No | Yes |

The reference build is what the TSX / `ko-*` examples in this docs set rely on,
because TSX needs `NativeProvider` (to read `el.bindings`) and the `ko.jsx`
render helper.

## When not to migrate yet

- Your templates rely heavily on legacy inline binding expressions you cannot easily change.
Expand Down
Loading