Skip to content

Commit e4441a3

Browse files
authored
Merge pull request #321 from knockout/docs/legacy-content
Add foundational docs: Getting Started, API Reference, History
2 parents bd644ad + 5425ba0 commit e4441a3

11 files changed

Lines changed: 784 additions & 29 deletions

File tree

plans/legacy-docs.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Plan: Write fresh docs for critical gaps
2+
3+
**Risk class:** LOW — new documentation pages only, no code changes
4+
**Owner:** brianmhunt
5+
6+
## Context
7+
8+
TKO's Starlight docs cover bindings, observables, computed, components, and binding context thoroughly — but lack foundational pages that new users need: installation, API reference, browser support, utility functions, and data serialization. Legacy Knockout docs covered these topics but are outdated. We're writing fresh content informed by the legacy structure.
9+
10+
## New pages (5 files)
11+
12+
### 1. `tko.io/src/content/docs/getting-started/index.md` — Installation & Setup
13+
- Sidebar: `label: Overview, order: 0`
14+
- CDN (ES module + classic script) for `build.reference`
15+
- Package manager install (npm/bun/pnpm/yarn tabs)
16+
- `build.knockout` as migration option with link to `/3to4/`
17+
- First binding example (standalone HTML)
18+
- TypeScript setup notes
19+
- Landing page quick start stays as-is (quick taste vs full guide)
20+
21+
### 2. `tko.io/src/content/docs/getting-started/browser-support.md` — Browser Support
22+
- Modern browser engine coverage (Chromium, WebKit, Gecko)
23+
- ES module support requirements
24+
- Classic script fallback for older environments
25+
- How TKO is tested (Vitest + Playwright, 3 engines in CI)
26+
27+
### 3. `tko.io/src/content/docs/observables/utilities.md` — Utility Functions
28+
- `ko.toJS` / `ko.toJSON` serialization
29+
- `ko.unwrap` / `ko.isObservable` / `ko.isWritableObservable` / `ko.isComputed`
30+
- `.fn` extensibility (`ko.observable.fn`, `ko.observableArray.fn`, etc.)
31+
- Type hierarchy: subscribable → observable → observableArray/computed
32+
- Working examples
33+
34+
### 4. `tko.io/src/content/docs/observables/json-data.md` — Loading & Saving Data
35+
- Serializing view models with `ko.toJS` / `ko.toJSON`
36+
- Loading data into observables (manual assignment patterns)
37+
- Fetch API examples (replacing legacy jQuery patterns)
38+
- Debugging: rendering JSON in the UI
39+
40+
### 5. `tko.io/src/content/docs/api.md` — API Reference
41+
- Root-level page (add to sidebar config)
42+
- Index/lookup table format: function name, one-liner, link to detailed page
43+
- Organized by category: Observables, Computed, Components, Bindings, Utilities
44+
- No full signatures — just names + links for quick navigation
45+
46+
## Sidebar config update
47+
48+
Edit `tko.io/astro.config.mjs` to add:
49+
```js
50+
{ label: 'Getting Started', autogenerate: { directory: 'getting-started' } },
51+
// ... existing sections ...
52+
{ label: 'API Reference', slug: 'api' },
53+
```
54+
55+
Place "Getting Started" right after Introduction. Place "API Reference" before "Knockout 3 → 4 Guide".
56+
57+
## Conventions to follow
58+
- Frontmatter: `title` required, `description` + `sidebar` on index pages
59+
- Filenames: kebab-case `.md`
60+
- Code examples: use `<Tabs>` from `@astrojs/starlight/components` for multi-variant snippets (`.mdx` extension when using imports)
61+
- Link to existing docs pages with relative paths
62+
- Keep pages concise — reference detailed pages rather than duplicating
63+
64+
## Files to modify
65+
- `tko.io/astro.config.mjs` — sidebar config
66+
- 5 new files as listed above
67+
68+
## Verification
69+
1. Run `bun run dev` in `tko.io/` and check all new pages render
70+
2. Verify sidebar navigation order is correct
71+
3. Check all internal links resolve
72+
4. Review on mobile viewport

tko.io/astro.config.mjs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,17 @@ export default defineConfig({
2828
},
2929
sidebar: [
3030
{ label: 'Introduction', slug: 'index' },
31+
{ label: 'Getting Started', autogenerate: { directory: 'getting-started' } },
3132
{ label: 'Examples', slug: 'examples' },
32-
{ label: 'Bindings', autogenerate: { directory: 'bindings' } },
33-
{ label: 'Observables', autogenerate: { directory: 'observables' } },
34-
{ label: 'Computed', autogenerate: { directory: 'computed' } },
35-
{ label: 'Components', autogenerate: { directory: 'components' } },
36-
{ label: 'Binding Context', autogenerate: { directory: 'binding-context' } },
37-
{ label: 'Advanced', autogenerate: { directory: 'advanced' } },
38-
{ label: 'Knockout 3 → 4 Guide', slug: '3to4' }
33+
{ label: 'Bindings', collapsed: true, autogenerate: { directory: 'bindings' } },
34+
{ label: 'Observables', collapsed: true, autogenerate: { directory: 'observables' } },
35+
{ label: 'Computed', collapsed: true, autogenerate: { directory: 'computed' } },
36+
{ label: 'Components', collapsed: true, autogenerate: { directory: 'components' } },
37+
{ label: 'Binding Context', collapsed: true, autogenerate: { directory: 'binding-context' } },
38+
{ label: 'Advanced', collapsed: true, autogenerate: { directory: 'advanced' } },
39+
{ label: 'API Reference', slug: 'api' },
40+
{ label: 'Knockout 3 → 4 Guide', slug: '3to4' },
41+
{ label: 'History', slug: 'history' }
3942
]
4043
})
4144
]

tko.io/src/content/docs/api.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
title: API Reference
3+
description: Quick-lookup index of TKO's public API.
4+
---
5+
6+
# API Reference
7+
8+
Quick-lookup table for TKO's public API. Each entry links to its detailed documentation.
9+
10+
## Observables
11+
12+
| Function | Description |
13+
|----------|-------------|
14+
| `ko.observable(value?)` | Create a reactive value. [Docs](/observables/) |
15+
| `ko.observableArray(array?)` | Observable wrapper around an array with mutation methods. [Docs](/observables/observablearrays/) |
16+
| `ko.isObservable(value)` | Check if a value is an observable. [Docs](/observables/utilities/) |
17+
| `ko.isObservableArray(value)` | Check if a value is an observable array. [Docs](/observables/utilities/) |
18+
| `ko.isWritableObservable(value)` | Check if a value is a writable observable. [Docs](/observables/utilities/) |
19+
| `ko.isSubscribable(value)` | Check if a value is any subscribable type. [Docs](/observables/utilities/) |
20+
| `ko.peek(value)` | Read an observable's value without creating a dependency. |
21+
| `ko.unwrap(value)` | Read an observable's value, or return a plain value as-is. [Docs](/observables/utilities/) |
22+
| `ko.toJS(object)` | Clone an object tree, replacing observables with their values. [Docs](/observables/utilities/) |
23+
| `ko.toJSON(object, replacer?, space?)` | `ko.toJS` + `JSON.stringify`. [Docs](/observables/utilities/) |
24+
25+
## Computed
26+
27+
| Function | Description |
28+
|----------|-------------|
29+
| `ko.computed(evaluatorOrOptions, owner?, options?)` | Create a value that depends on other observables. Accepts a function or `{ read, write }` options object. [Docs](/computed/computedobservables/) |
30+
| `ko.pureComputed(evaluator, owner?)` | Computed that sleeps when it has no subscribers. [Docs](/computed/computed-pure/) |
31+
| `ko.isComputed(value)` | Check if a value is a computed observable. [Docs](/observables/utilities/) |
32+
| `ko.isPureComputed(value)` | Check if a value is a pure computed. |
33+
| `ko.ignoreDependencies(callback, owner?, args?)` | Run a function without tracking dependencies. |
34+
| `ko.when(predicate, callback?, context?)` | Resolve when the predicate becomes truthy. Returns a promise, or calls callback if provided. [Docs](/observables/) |
35+
36+
## Subscribable instance methods
37+
38+
Every observable, computed, and observable array inherits these from `subscribable.fn`:
39+
40+
| Method | Description |
41+
|--------|-------------|
42+
| `.subscribe(callback, target?, event?)` | Register a callback for changes. [Docs](/observables/) |
43+
| `.when(testOrValue, returnValue?)` | Promise that resolves when the test passes. [Docs](/observables/) |
44+
| `.yet(testOrValue, returnValue?)` | Promise that resolves when the test *fails* (negated `.when`). [Docs](/observables/) |
45+
| `.next()` | Promise that resolves on the next value change. [Docs](/observables/) |
46+
| `.once(callback)` | Call the callback on the next change, then auto-dispose. [Docs](/observables/) |
47+
| `.peek()` | Read the current value without creating a dependency. |
48+
| `.dispose()` | Tear down all subscriptions. |
49+
50+
## Extenders
51+
52+
| Function | Description |
53+
|----------|-------------|
54+
| `observable.extend(extenders)` | Apply extenders to an observable or computed. [Docs](/observables/extenders/) |
55+
| `rateLimit` | Throttle change notifications. [Docs](/observables/ratelimit-observable/) |
56+
| `notify: 'always'` | Force notification even when value hasn't changed. [Docs](/observables/) |
57+
58+
## Bindings
59+
60+
| Function | Description |
61+
|----------|-------------|
62+
| `ko.applyBindings(viewModel, rootNode?)` | Activate bindings on a DOM subtree. [Docs](/observables/#activating-knockout) |
63+
| `ko.applyBindingsToNode(node, bindings, viewModelOrContext?)` | Apply bindings to a single node programmatically. |
64+
| `ko.applyBindingsToDescendants(viewModelOrContext, rootNode)` | Apply bindings to descendants only (used in custom bindings). [Docs](/binding-context/custom-bindings-controlling-descendant-bindings/) |
65+
| `ko.contextFor(node)` | Get the binding context for a DOM node. |
66+
| `ko.dataFor(node)` | Get the view model bound to a DOM node. |
67+
| `ko.cleanNode(node)` | Remove all TKO data and bindings from a node. |
68+
| `ko.bindingHandlers` | Registry of built-in and custom binding handlers. [Docs](/binding-context/custom-bindings/) |
69+
| `ko.bindingEvent` | Binding lifecycle event constants (e.g., `childrenComplete`, `descendantsComplete`). |
70+
| `ko.BindingHandler` | Base class for class-based custom binding handlers. [Docs](/binding-context/custom-bindings/) |
71+
| `ko.AsyncBindingHandler` | Async variant of `BindingHandler` for bindings that load resources. [Docs](/binding-context/custom-bindings/) |
72+
73+
### Built-in bindings
74+
75+
**Text & HTML:** [`text`](/bindings/text-binding/), [`html`](/bindings/html-binding/), [`textInput`](/bindings/textinput-binding/), [`value`](/bindings/value-binding/)
76+
77+
**Appearance:** [`visible`](/bindings/visible-binding/), `hidden`, [`css`](/bindings/css-binding/) (alias: `class`), [`style`](/bindings/style-binding/), [`attr`](/bindings/attr-binding/)
78+
79+
**Control flow:** [`if`](/bindings/if-binding/), [`ifnot`](/bindings/ifnot-binding/) (alias: `unless`), `else`, `elseif`, [`foreach`](/bindings/foreach-binding/) (alias: `each`), [`with`](/bindings/with-binding/), [`template`](/bindings/template-binding/)
80+
81+
**Context:** `let`, `using`
82+
83+
**Form:** [`click`](/bindings/click-binding/), [`event`](/bindings/event-binding/) (alias: `on`), [`submit`](/bindings/submit-binding/), [`enable`](/bindings/enable-binding/), [`disable`](/bindings/disable-binding/), [`checked`](/bindings/checked-binding/), `checkedValue`, [`options`](/bindings/options-binding/), [`selectedOptions`](/bindings/selectedoptions-binding/), [`hasfocus`](/bindings/hasfocus-binding/), [`uniqueName`](/bindings/uniquename-binding/)
84+
85+
**Components:** `component`, `slot`
86+
87+
**Lifecycle:** `descendantsComplete`
88+
89+
## Components
90+
91+
| Function | Description |
92+
|----------|-------------|
93+
| `ko.components.register(name, config)` | Register a component. [Docs](/components/component-registration/) |
94+
| `ko.components.get(name)` | Retrieve a registered component definition. [Docs](/components/component-loaders/) |
95+
| `ko.components.isRegistered(name)` | Check if a component name is registered. |
96+
| `ko.components.unregister(name)` | Remove a component registration. |
97+
| `ko.components.clearCachedDefinition(name)` | Clear a cached component definition. |
98+
| `ko.Component` | Base class for class-based components (`ComponentABC`). [Docs](/components/) |
99+
100+
## JSX (build.reference only)
101+
102+
| Function | Description |
103+
|----------|-------------|
104+
| `ko.jsx.createElement(tag, props, ...children)` | Create a JSX element. Used as the JSX factory. |
105+
| `ko.jsx.Fragment` | Fragment component for grouping elements without a wrapper node. |
106+
| `ko.jsx.render(jsx)` | Render JSX to DOM nodes. Returns `{ node, dispose }`. |
107+
108+
## DOM disposal
109+
110+
| Function | Description |
111+
|----------|-------------|
112+
| `ko.domNodeDisposal.addDisposeCallback(node, callback)` | Run a callback when a DOM node is removed by TKO. |
113+
| `ko.domNodeDisposal.removeDisposeCallback(node, callback)` | Remove a previously registered disposal callback. |
114+
115+
## Extensibility
116+
117+
| Function | Description |
118+
|----------|-------------|
119+
| `ko.subscribable.fn` | Prototype for all subscribables. [Docs](/observables/utilities/) |
120+
| `ko.observable.fn` | Prototype for all observables — add methods here. [Docs](/observables/utilities/) |
121+
| `ko.observableArray.fn` | Prototype for observable arrays (`remove`, `replace`, etc.). [Docs](/observables/utilities/) |
122+
| `ko.computed.fn` | Prototype for all computeds. [Docs](/observables/utilities/) |
123+
| `ko.tasks` | Microtask scheduler for batching async work. |
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
title: Browser Support
3+
---
4+
5+
TKO targets modern browsers — any browser that supports ES2020 and `<script type="module">`.
6+
7+
## Supported engines
8+
9+
| Engine | Browsers | Tested in CI |
10+
|--------|----------|:---:|
11+
| Chromium | Chrome, Edge, Opera, Brave, Arc | Yes |
12+
| WebKit | Safari (macOS, iOS) | Yes |
13+
| Gecko | Firefox | Yes |
14+
15+
These three engines cover effectively all modern browsers.
16+
17+
## What we test
18+
19+
Every pull request runs the full test suite (2700+ tests) across all three engines using [Vitest](https://vitest.dev) browser mode with [Playwright](https://playwright.dev). The three engines run as parallel CI jobs.
20+
21+
CI tests against the **latest stable** version of each engine. We do not currently test against specific older browser versions, so minimum version support is not precisely known. If you discover a compatibility issue with a particular browser version, please [open an issue](https://github.com/knockout/tko/issues).
22+
23+
The original Knockout supported browsers back to IE6. TKO's modernized codebase uses ES2020+ features (optional chaining, nullish coalescing, etc.), so IE is no longer supported. Exact minimum versions for Chrome, Safari, and Firefox have not been established.
24+
25+
## Loading TKO
26+
27+
The recommended way to load TKO is as an ES module:
28+
29+
```html
30+
<script type="module">
31+
import ko from 'https://esm.run/@tko/build.reference'
32+
</script>
33+
```
34+
35+
An IIFE build is also available for classic `<script>` tag loading:
36+
37+
```html
38+
<script src="https://cdn.jsdelivr.net/npm/@tko/build.reference/dist/browser.min.js"></script>
39+
<script>
40+
const ko = globalThis.tko
41+
</script>
42+
```
43+
44+
## Server-side / Node.js
45+
46+
TKO's observable and computed primitives have no DOM dependencies and can run in Node.js, Bun, or Deno. Binding and template features require a DOM environment.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
---
2+
title: Deploy in Seconds
3+
description: Deploy a TKO app to static hosting — no build step required.
4+
---
5+
6+
A TKO app can be as simple as a single HTML file. No bundler, no server runtime, no build step — just upload to any static hosting provider and you're live.
7+
8+
## The simplest deploy
9+
10+
Save this as `index.html`:
11+
12+
```html
13+
<!doctype html>
14+
<html>
15+
<body>
16+
<div id="app">
17+
<input data-bind="textInput: name" />
18+
<p>Hello, <strong data-bind="text: name"></strong>.</p>
19+
</div>
20+
<script src="https://cdn.jsdelivr.net/npm/@tko/build.reference/dist/browser.min.js"></script>
21+
<script>
22+
const ko = globalThis.tko
23+
ko.applyBindings({ name: ko.observable('TKO') }, document.getElementById('app'))
24+
</script>
25+
</body>
26+
</html>
27+
```
28+
29+
Or as an ES module (no IIFE needed):
30+
31+
```html
32+
<script type="module">
33+
import ko from 'https://esm.run/@tko/build.reference'
34+
ko.applyBindings({ name: ko.observable('TKO') }, document.getElementById('app'))
35+
</script>
36+
```
37+
38+
Upload that single file to any of the platforms below. That's it — a live, reactive web UI.
39+
40+
## GitHub Pages
41+
42+
Free, deploys from a git push.
43+
44+
1. Create a repo and add your `index.html`
45+
2. Go to **Settings → Pages → Source** and select your branch
46+
3. Your site is live at `https://username.github.io/repo/`
47+
48+
Or use the `gh` CLI:
49+
50+
```sh
51+
gh repo create my-app --public --clone
52+
# add index.html
53+
git add index.html && git commit -m "init" && git push
54+
gh browse --settings # enable Pages under Settings → Pages
55+
```
56+
57+
## Cloudflare Pages
58+
59+
Free tier, global CDN, automatic HTTPS.
60+
61+
1. Push your files to a GitHub or GitLab repo
62+
2. Connect the repo at [dash.cloudflare.com](https://dash.cloudflare.com)**Pages → Create**
63+
3. No build command needed — just set the output directory to `/` (or wherever your HTML lives)
64+
65+
Or deploy directly from the CLI:
66+
67+
```sh
68+
npx wrangler pages deploy . --project-name my-app
69+
```
70+
71+
## Google Cloud Storage
72+
73+
Good for projects already on GCP.
74+
75+
```sh
76+
gcloud storage buckets create gs://my-app.example.com
77+
gcloud storage buckets update gs://my-app.example.com --web-main-page-suffix=index.html
78+
gcloud storage cp index.html gs://my-app.example.com/
79+
```
80+
81+
Add a load balancer or use [Firebase Hosting](https://firebase.google.com/docs/hosting) for automatic HTTPS and CDN.
82+
83+
## Firebase Hosting
84+
85+
Free tier, automatic HTTPS, global CDN.
86+
87+
```sh
88+
npm install -g firebase-tools
89+
firebase init hosting # select your project, set public dir to "."
90+
firebase deploy
91+
```
92+
93+
## Netlify
94+
95+
Free tier, drag-and-drop or git-based deploys.
96+
97+
1. Go to [app.netlify.com/drop](https://app.netlify.com/drop)
98+
2. Drag your project folder onto the page
99+
3. Live in seconds
100+
101+
Or via CLI:
102+
103+
```sh
104+
npx netlify-cli deploy --dir . --prod
105+
```
106+
107+
## Why this works
108+
109+
TKO loads from a CDN (`esm.run` or `jsdelivr`). Your app is just HTML + the browser's ES module loader. No server-side rendering, no Node.js, no build artifacts. The entire deploy is one file.
110+
111+
As your app grows you can add a bundler, but you don't *have* to. Many production TKO apps are a handful of HTML files and a CSS stylesheet.

0 commit comments

Comments
 (0)