svelte-origin 0.0.0 → 1.0.0-next.15
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.
Potentially problematic release.
This version of svelte-origin might be problematic. Click here for more details.
- package/LLM.md +754 -0
- package/README.md +412 -1
- package/dist/aliases.d.ts +44 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +3440 -0
- package/dist/generate-dts.d.ts +31 -0
- package/dist/globals.d.ts +576 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +6685 -0
- package/dist/plugin.d.ts +143 -0
- package/dist/plugin.js +3467 -0
- package/dist/post-process.d.ts +27 -0
- package/dist/post-process.js +3071 -0
- package/dist/preprocess.d.ts +73 -0
- package/dist/preprocess.js +3360 -0
- package/dist/runtime/attrs.d.ts +52 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +3665 -0
- package/dist/runtime/origin.d.ts +144 -0
- package/dist/runtime/types.d.ts +173 -0
- package/dist/transform/attrs-for-transform.d.ts +24 -0
- package/dist/transform/attrs-origin-transform.d.ts +62 -0
- package/dist/transform/attrs-schema.d.ts +52 -0
- package/dist/transform/codegen.d.ts +15 -0
- package/dist/transform/core.d.ts +62 -0
- package/dist/transform/declaration-parser.d.ts +77 -0
- package/dist/transform/element-types.d.ts +10 -0
- package/dist/transform/origin-transform.d.ts +51 -0
- package/dist/transform/patterns.d.ts +82 -0
- package/dist/transform/schema.d.ts +116 -0
- package/dist/transform/standalone-attrs-transform.d.ts +36 -0
- package/dist/vite-dts.d.ts +17 -0
- package/dist/vite-dts.js +606 -0
- package/package.json +73 -3
package/LLM.md
ADDED
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
# svelte-origin - AI/LLM Reference
|
|
2
|
+
|
|
3
|
+
This document provides comprehensive documentation for AI coding assistants working with `svelte-origin`, a compiler-assisted state and prop ergonomics library for Svelte 5.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`svelte-origin` provides compile-time macros that transform into standard Svelte 5 code:
|
|
8
|
+
|
|
9
|
+
- **`$origin({ ... })`** - Define reusable state + props + methods in `.svelte.ts` files
|
|
10
|
+
- **`$attrs({ ... })`** - Define the props schema inside an origin
|
|
11
|
+
- **`$bindable(value)`** - Mark a prop as bindable (two-way binding)
|
|
12
|
+
- **`$attrs.origin(Factory)`** - Create an origin instance from component props
|
|
13
|
+
- **`$attrs.for(Factory)`** - Forward props to child components
|
|
14
|
+
- **`$attrs.Of<typeof Factory>`** - Type helper to extract component props
|
|
15
|
+
|
|
16
|
+
## Quick Reference
|
|
17
|
+
|
|
18
|
+
### Origin Definition (`.svelte.ts` file)
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// counter.svelte.ts
|
|
22
|
+
export const Counter = $origin({
|
|
23
|
+
// Props schema - these become component props
|
|
24
|
+
props: $attrs({
|
|
25
|
+
label: 'Counter', // Default value: 'Counter'
|
|
26
|
+
count: $bindable(0), // Bindable with default 0
|
|
27
|
+
step: 1 as number, // Typed with default
|
|
28
|
+
optional: undefined as string | undefined // Optional prop
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
// Private state (prefixed with _)
|
|
32
|
+
_clickCount: $state(0),
|
|
33
|
+
|
|
34
|
+
// Derived values (getters with $derived)
|
|
35
|
+
get double() {
|
|
36
|
+
return $derived(this.props.count * 2)
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Methods
|
|
40
|
+
increment() {
|
|
41
|
+
this._clickCount++
|
|
42
|
+
this.props.count += this.props.step
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
decrement() {
|
|
46
|
+
this.props.count -= this.props.step
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
reset() {
|
|
50
|
+
this.props.count = 0
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Component Usage (`.svelte` file)
|
|
56
|
+
|
|
57
|
+
```svelte
|
|
58
|
+
<script lang="ts">
|
|
59
|
+
import { Counter } from './counter.svelte'
|
|
60
|
+
|
|
61
|
+
// Optional: Explicit $$Props for svelte-check compatibility
|
|
62
|
+
type $$Props = $attrs.Of<typeof Counter>
|
|
63
|
+
|
|
64
|
+
// Create instance from component props
|
|
65
|
+
let counter = $attrs.origin(Counter)
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<h3>{counter.props.label}</h3>
|
|
69
|
+
<p>Count: {counter.props.count} (Double: {counter.double})</p>
|
|
70
|
+
<button onclick={() => counter.increment()}>+{counter.props.step}</button>
|
|
71
|
+
<button onclick={() => counter.decrement()}>-{counter.props.step}</button>
|
|
72
|
+
<button onclick={() => counter.reset()}>Reset</button>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Parent Component Using Child
|
|
76
|
+
|
|
77
|
+
```svelte
|
|
78
|
+
<script lang="ts">
|
|
79
|
+
import CounterComponent from './CounterComponent.svelte'
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<!-- All props from Counter origin are available -->
|
|
83
|
+
<CounterComponent label="My Counter" count={10} step={5} />
|
|
84
|
+
|
|
85
|
+
<!-- Two-way binding works for $bindable props -->
|
|
86
|
+
<CounterComponent bind:count={myCount} />
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## API Reference
|
|
90
|
+
|
|
91
|
+
### `$origin(definition)`
|
|
92
|
+
|
|
93
|
+
Creates an origin factory from a definition object.
|
|
94
|
+
|
|
95
|
+
**Signature:**
|
|
96
|
+
```ts
|
|
97
|
+
function $origin<T>(definition: T): OriginFactory<T>
|
|
98
|
+
function $origin<Parents extends OriginFactory[], T>(parents: Parents, definition: T): OriginFactory<Merged<Parents, T>>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Definition Object Properties:**
|
|
102
|
+
|
|
103
|
+
| Property | Description |
|
|
104
|
+
|----------|-------------|
|
|
105
|
+
| `props: $attrs({...})` | Props schema (becomes component props) |
|
|
106
|
+
| `attrs: $attrs({...})` | Alternative name for props schema |
|
|
107
|
+
| `_privateField` | Private state/method (not exposed on instance) |
|
|
108
|
+
| `get propertyName()` | Derived value (use `$derived` inside) |
|
|
109
|
+
| `methodName()` | Method accessible on the instance |
|
|
110
|
+
|
|
111
|
+
**Context (`this`) inside definition:**
|
|
112
|
+
- `this.props` - Access props (reactive)
|
|
113
|
+
- `this.super` - Parent instance (when extending)
|
|
114
|
+
- `this._privateField` - Access private state
|
|
115
|
+
|
|
116
|
+
### `$attrs(schema)`
|
|
117
|
+
|
|
118
|
+
Defines the props schema inside an origin.
|
|
119
|
+
|
|
120
|
+
**Example:**
|
|
121
|
+
```ts
|
|
122
|
+
props: $attrs({
|
|
123
|
+
// With default value (optional prop)
|
|
124
|
+
label: 'Default Label',
|
|
125
|
+
|
|
126
|
+
// Bindable with default
|
|
127
|
+
count: $bindable(0),
|
|
128
|
+
|
|
129
|
+
// Bindable without default (required)
|
|
130
|
+
value: $bindable(),
|
|
131
|
+
|
|
132
|
+
// Type annotation with default
|
|
133
|
+
step: 1 as number,
|
|
134
|
+
|
|
135
|
+
// Optional prop (no default)
|
|
136
|
+
optional: undefined as string | undefined,
|
|
137
|
+
|
|
138
|
+
// Required prop (typed, no default)
|
|
139
|
+
required: '' as string,
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `$bindable(defaultValue?)`
|
|
144
|
+
|
|
145
|
+
Marks a prop as bindable for two-way binding.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
props: $attrs({
|
|
149
|
+
// Bindable with default - optional prop
|
|
150
|
+
count: $bindable(0),
|
|
151
|
+
|
|
152
|
+
// Bindable without default - required prop
|
|
153
|
+
value: $bindable(),
|
|
154
|
+
|
|
155
|
+
// Bindable with typed default
|
|
156
|
+
text: $bindable('') as string,
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `$attrs.origin(Factory)`
|
|
161
|
+
|
|
162
|
+
Creates an origin instance from component props. Use inside `.svelte` components.
|
|
163
|
+
|
|
164
|
+
**Example:**
|
|
165
|
+
```svelte
|
|
166
|
+
<script lang="ts">
|
|
167
|
+
import { Counter } from './counter.svelte'
|
|
168
|
+
|
|
169
|
+
let counter = $attrs.origin(Counter)
|
|
170
|
+
</script>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Transformation:**
|
|
174
|
+
```svelte
|
|
175
|
+
<!-- Before (user writes) -->
|
|
176
|
+
let counter = $attrs.origin(Counter)
|
|
177
|
+
|
|
178
|
+
<!-- After (plugin transforms to) -->
|
|
179
|
+
let { label = 'Counter', count = $bindable(0), step = 1 } = $props()
|
|
180
|
+
let __attrs = { get label() { return label }, get count() { return count }, set count(v) { count = v }, get step() { return step } }
|
|
181
|
+
let counter = Counter(__attrs)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### `$attrs.for(Factory)`
|
|
185
|
+
|
|
186
|
+
Forwards props for child origin components.
|
|
187
|
+
|
|
188
|
+
```svelte
|
|
189
|
+
<script lang="ts">
|
|
190
|
+
import { ChildOrigin } from './child.svelte'
|
|
191
|
+
import ChildComponent from './ChildComponent.svelte'
|
|
192
|
+
|
|
193
|
+
let childProps = $attrs.for(ChildOrigin)
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
<ChildComponent {...childProps} />
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `$attrs.for('element')`
|
|
200
|
+
|
|
201
|
+
Gets typed rest props for HTML element wrappers.
|
|
202
|
+
|
|
203
|
+
```svelte
|
|
204
|
+
<script lang="ts">
|
|
205
|
+
let inputProps = $attrs.for('input')
|
|
206
|
+
</script>
|
|
207
|
+
|
|
208
|
+
<input {...inputProps} />
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `$attrs.Of<typeof Factory>`
|
|
212
|
+
|
|
213
|
+
Type helper to extract component props from an origin factory.
|
|
214
|
+
|
|
215
|
+
```svelte
|
|
216
|
+
<script lang="ts">
|
|
217
|
+
import { Counter } from './counter.svelte'
|
|
218
|
+
|
|
219
|
+
// Explicit $$Props declaration for svelte-check
|
|
220
|
+
type $$Props = $attrs.Of<typeof Counter>
|
|
221
|
+
|
|
222
|
+
let counter = $attrs.origin(Counter)
|
|
223
|
+
</script>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Origin Inheritance
|
|
227
|
+
|
|
228
|
+
Origins can extend other origins using array syntax:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// base.svelte.ts
|
|
232
|
+
export const Counter = $origin({
|
|
233
|
+
props: $attrs({
|
|
234
|
+
label: 'Counter',
|
|
235
|
+
count: $bindable(0),
|
|
236
|
+
step: 1
|
|
237
|
+
}),
|
|
238
|
+
|
|
239
|
+
increment() {
|
|
240
|
+
this.props.count += this.props.step
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
decrement() {
|
|
244
|
+
this.props.count -= this.props.step
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// extended.svelte.ts
|
|
249
|
+
import { Counter } from './base.svelte'
|
|
250
|
+
|
|
251
|
+
export const ExtendedCounter = $origin([Counter], {
|
|
252
|
+
props: $attrs({
|
|
253
|
+
bonus: 10 // Additional prop
|
|
254
|
+
}),
|
|
255
|
+
|
|
256
|
+
// Override method - call parent with this.super
|
|
257
|
+
increment() {
|
|
258
|
+
this.super.increment()
|
|
259
|
+
console.log('Incremented!')
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
// New method using parent
|
|
263
|
+
incrementWithBonus() {
|
|
264
|
+
this.super.increment()
|
|
265
|
+
this.super.props.count += this.props.bonus
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
// Delegate derived value
|
|
269
|
+
get double() {
|
|
270
|
+
return $derived(this.super.double)
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Inheritance Rules:**
|
|
276
|
+
- Child inherits all parent props (merged with child's own props)
|
|
277
|
+
- Access parent instance via `this.super`
|
|
278
|
+
- Access parent props via `this.super.props`
|
|
279
|
+
- Child props are on `this.props`
|
|
280
|
+
- Override methods by redefining them
|
|
281
|
+
- Call parent method with `this.super.methodName()`
|
|
282
|
+
|
|
283
|
+
## Init Callbacks
|
|
284
|
+
|
|
285
|
+
Origins can have an optional init callback that runs when the instance is created:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
export const Timer = $origin({
|
|
289
|
+
props: $attrs({
|
|
290
|
+
interval: 1000
|
|
291
|
+
}),
|
|
292
|
+
|
|
293
|
+
_count: $state(0),
|
|
294
|
+
|
|
295
|
+
get count() {
|
|
296
|
+
return $derived(this._count)
|
|
297
|
+
}
|
|
298
|
+
}, function() {
|
|
299
|
+
// Runs when the component is created
|
|
300
|
+
const id = setInterval(() => {
|
|
301
|
+
this._count++
|
|
302
|
+
}, this.props.interval)
|
|
303
|
+
|
|
304
|
+
// Return cleanup function (runs on unmount)
|
|
305
|
+
return () => clearInterval(id)
|
|
306
|
+
})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Init Callback Rules:**
|
|
310
|
+
- Second argument to `$origin()` (or third when extending)
|
|
311
|
+
- Runs after the instance is created
|
|
312
|
+
- Has access to `this` (full instance including private `_` prefixed members)
|
|
313
|
+
- Can return a cleanup function that runs when the component is destroyed
|
|
314
|
+
- Useful for side effects, subscriptions, or DOM interactions
|
|
315
|
+
|
|
316
|
+
## Attachments
|
|
317
|
+
|
|
318
|
+
Origins can define **attachment methods** that run when an element is attached to the DOM. These are methods prefixed with `$` that receive the element and optionally return a cleanup function.
|
|
319
|
+
|
|
320
|
+
### Defining Attachment Methods
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
// widget.svelte.ts
|
|
324
|
+
export const Widget = $origin({
|
|
325
|
+
props: $attrs({
|
|
326
|
+
color: 'blue',
|
|
327
|
+
tooltipText: 'Hello'
|
|
328
|
+
}),
|
|
329
|
+
|
|
330
|
+
/** Attachment: adds a tooltip to an element */
|
|
331
|
+
$tooltip(element: Element) {
|
|
332
|
+
const tooltip = document.createElement('div')
|
|
333
|
+
tooltip.className = 'tooltip'
|
|
334
|
+
tooltip.textContent = this.props.tooltipText
|
|
335
|
+
element.appendChild(tooltip)
|
|
336
|
+
|
|
337
|
+
// Return cleanup function (runs on unmount or re-attach)
|
|
338
|
+
return () => tooltip.remove()
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
/** Attachment: highlights the element with the color prop */
|
|
342
|
+
$highlight(element: Element) {
|
|
343
|
+
const el = element as HTMLElement
|
|
344
|
+
const original = el.style.backgroundColor
|
|
345
|
+
el.style.backgroundColor = this.props.color
|
|
346
|
+
return () => { el.style.backgroundColor = original }
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Key Points:**
|
|
352
|
+
- Attachment methods are prefixed with `$` (e.g., `$tooltip`, `$highlight`)
|
|
353
|
+
- They receive the DOM `Element` as their first argument
|
|
354
|
+
- They can access `this.props`, `this.super`, and other instance members
|
|
355
|
+
- Return a cleanup function to run on unmount or when the attachment re-runs
|
|
356
|
+
- They are NOT exposed on the public instance type
|
|
357
|
+
|
|
358
|
+
### Using `$attachments`
|
|
359
|
+
|
|
360
|
+
Every origin instance has a `$attachments` getter that returns an object suitable for spreading onto elements:
|
|
361
|
+
|
|
362
|
+
```svelte
|
|
363
|
+
<script lang="ts">
|
|
364
|
+
import { Widget } from './widget.svelte'
|
|
365
|
+
let widget = $attrs.origin(Widget)
|
|
366
|
+
</script>
|
|
367
|
+
|
|
368
|
+
<!-- All attachment methods ($tooltip, $highlight) are applied -->
|
|
369
|
+
<div {...widget.$attachments}>
|
|
370
|
+
Content with tooltip and highlight
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
<!-- Can also apply to multiple elements -->
|
|
374
|
+
<button {...widget.$attachments}>Button with attachments</button>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Forwarding Incoming Attachments
|
|
378
|
+
|
|
379
|
+
When a parent component uses Svelte's `{@attach ...}` directive on your component, those attachments are automatically merged into `$attachments`:
|
|
380
|
+
|
|
381
|
+
```svelte
|
|
382
|
+
<!-- Parent.svelte -->
|
|
383
|
+
<script lang="ts">
|
|
384
|
+
function logElement(el: Element) {
|
|
385
|
+
console.log('Attached to:', el)
|
|
386
|
+
return () => console.log('Detached from:', el)
|
|
387
|
+
}
|
|
388
|
+
</script>
|
|
389
|
+
|
|
390
|
+
<WidgetComponent {@attach logElement} color="red" />
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```svelte
|
|
394
|
+
<!-- WidgetComponent.svelte -->
|
|
395
|
+
<script lang="ts">
|
|
396
|
+
import { Widget } from './widget.svelte'
|
|
397
|
+
let widget = $attrs.origin(Widget)
|
|
398
|
+
</script>
|
|
399
|
+
|
|
400
|
+
<!-- Both origin-defined ($tooltip, $highlight) AND incoming (logElement) attachments are applied -->
|
|
401
|
+
<div {...widget.$attachments}>...</div>
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**How it works:**
|
|
405
|
+
1. Incoming attachments from `{@attach ...}` are passed as Symbol-keyed props
|
|
406
|
+
2. The `$attachments` getter collects both origin-defined and incoming attachments
|
|
407
|
+
3. Each attachment gets a unique Symbol key (via `createAttachmentKey()` from Svelte)
|
|
408
|
+
4. When spread onto an element, Svelte's runtime registers all attachments
|
|
409
|
+
|
|
410
|
+
### Attachment Type
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
type AttachmentFn = (this: any, element: Element) => void | (() => void)
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
The `$attachments` getter returns:
|
|
417
|
+
```ts
|
|
418
|
+
type SvelteOriginAttachments = {
|
|
419
|
+
[key: symbol]: (element: Element) => void | (() => void)
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Project Setup
|
|
424
|
+
|
|
425
|
+
### Installation
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
npm install svelte-origin
|
|
429
|
+
# or
|
|
430
|
+
bun add svelte-origin
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Vite Configuration
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
// vite.config.ts
|
|
437
|
+
import { sveltekit } from '@sveltejs/kit/vite'
|
|
438
|
+
import { svelteOrigin } from 'svelte-origin/plugin'
|
|
439
|
+
|
|
440
|
+
export default {
|
|
441
|
+
plugins: [
|
|
442
|
+
svelteOrigin(), // MUST come BEFORE sveltekit/svelte
|
|
443
|
+
sveltekit()
|
|
444
|
+
]
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Svelte Configuration
|
|
449
|
+
|
|
450
|
+
```js
|
|
451
|
+
// svelte.config.js
|
|
452
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
453
|
+
import { svelteOriginPreprocess } from 'svelte-origin/preprocess'
|
|
454
|
+
|
|
455
|
+
export default {
|
|
456
|
+
preprocess: [
|
|
457
|
+
svelteOriginPreprocess(), // MUST come BEFORE vitePreprocess
|
|
458
|
+
vitePreprocess()
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### TypeScript Configuration
|
|
464
|
+
|
|
465
|
+
```json
|
|
466
|
+
{
|
|
467
|
+
"compilerOptions": {
|
|
468
|
+
"types": ["svelte-origin/globals"]
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## File Naming Conventions
|
|
474
|
+
|
|
475
|
+
| File Pattern | Description |
|
|
476
|
+
|--------------|-------------|
|
|
477
|
+
| `*.svelte.ts` | Origin definition files (runes enabled) |
|
|
478
|
+
| `*.svelte` | Svelte components using origins |
|
|
479
|
+
| `*.origin.svelte.ts` | Alternative pattern for origin files |
|
|
480
|
+
|
|
481
|
+
## Type System
|
|
482
|
+
|
|
483
|
+
### Core Types
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
import type {
|
|
487
|
+
SvelteOriginFactory,
|
|
488
|
+
SvelteOriginInstance,
|
|
489
|
+
SvelteOriginBindable,
|
|
490
|
+
SvelteOriginDefinition,
|
|
491
|
+
SvelteOriginComponentProps,
|
|
492
|
+
} from 'svelte-origin'
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Factory Type
|
|
496
|
+
|
|
497
|
+
```ts
|
|
498
|
+
interface SvelteOriginFactory<TDef, TAllAttrs> {
|
|
499
|
+
(inputAttrs: TAllAttrs): SvelteOriginInstance<TDef>
|
|
500
|
+
readonly __origin: true
|
|
501
|
+
readonly __attrSchema: AttrSchema
|
|
502
|
+
readonly __parents: SvelteOriginFactory[]
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Instance Type
|
|
507
|
+
|
|
508
|
+
```ts
|
|
509
|
+
type SvelteOriginInstance<T> = {
|
|
510
|
+
props: ReactiveAttrs<T['props']> // Or 'attrs' if that's what you named it
|
|
511
|
+
super?: ParentInstance
|
|
512
|
+
$attachments: SvelteOriginAttachments
|
|
513
|
+
} & PublicMembers<T>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Note:** The `props`/`attrs` property name matches what you use in your origin definition.
|
|
517
|
+
|
|
518
|
+
## Common Patterns
|
|
519
|
+
|
|
520
|
+
### Counter with Two-Way Binding
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
// counter.svelte.ts
|
|
524
|
+
export const Counter = $origin({
|
|
525
|
+
props: $attrs({
|
|
526
|
+
count: $bindable(0)
|
|
527
|
+
}),
|
|
528
|
+
|
|
529
|
+
increment() { this.props.count++ },
|
|
530
|
+
decrement() { this.props.count-- }
|
|
531
|
+
})
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
```svelte
|
|
535
|
+
<!-- CounterComponent.svelte -->
|
|
536
|
+
<script lang="ts">
|
|
537
|
+
import { Counter } from './counter.svelte'
|
|
538
|
+
type $$Props = $attrs.Of<typeof Counter>
|
|
539
|
+
let counter = $attrs.origin(Counter)
|
|
540
|
+
</script>
|
|
541
|
+
|
|
542
|
+
<button onclick={() => counter.increment()}>{counter.props.count}</button>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
```svelte
|
|
546
|
+
<!-- Parent using CounterComponent -->
|
|
547
|
+
<script lang="ts">
|
|
548
|
+
let count = $state(0)
|
|
549
|
+
</script>
|
|
550
|
+
|
|
551
|
+
<CounterComponent bind:count />
|
|
552
|
+
<p>Count in parent: {count}</p>
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Form Input Wrapper
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
// input.svelte.ts
|
|
559
|
+
export const TextInput = $origin({
|
|
560
|
+
props: $attrs({
|
|
561
|
+
value: $bindable(''),
|
|
562
|
+
label: '',
|
|
563
|
+
placeholder: ''
|
|
564
|
+
}),
|
|
565
|
+
|
|
566
|
+
handleInput(e: Event) {
|
|
567
|
+
this.props.value = (e.target as HTMLInputElement).value
|
|
568
|
+
}
|
|
569
|
+
})
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
```svelte
|
|
573
|
+
<!-- TextInput.svelte -->
|
|
574
|
+
<script lang="ts">
|
|
575
|
+
import { TextInput } from './input.svelte'
|
|
576
|
+
type $$Props = $attrs.Of<typeof TextInput>
|
|
577
|
+
let input = $attrs.origin(TextInput)
|
|
578
|
+
</script>
|
|
579
|
+
|
|
580
|
+
<label>
|
|
581
|
+
{input.props.label}
|
|
582
|
+
<input
|
|
583
|
+
type="text"
|
|
584
|
+
value={input.props.value}
|
|
585
|
+
placeholder={input.props.placeholder}
|
|
586
|
+
oninput={(e) => input.handleInput(e)}
|
|
587
|
+
/>
|
|
588
|
+
</label>
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Toggle with Derived State
|
|
592
|
+
|
|
593
|
+
```ts
|
|
594
|
+
// toggle.svelte.ts
|
|
595
|
+
export const Toggle = $origin({
|
|
596
|
+
props: $attrs({
|
|
597
|
+
checked: $bindable(false),
|
|
598
|
+
label: 'Toggle'
|
|
599
|
+
}),
|
|
600
|
+
|
|
601
|
+
get labelText() {
|
|
602
|
+
return $derived(this.props.checked ? 'On' : 'Off')
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
toggle() {
|
|
606
|
+
this.props.checked = !this.props.checked
|
|
607
|
+
}
|
|
608
|
+
})
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### List with Generic Types
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
// list.svelte.ts
|
|
615
|
+
export const List = <T>() => $origin({
|
|
616
|
+
props: $attrs({
|
|
617
|
+
items: [] as T[],
|
|
618
|
+
selected: $bindable<T | null>(null)
|
|
619
|
+
}),
|
|
620
|
+
|
|
621
|
+
get isEmpty() {
|
|
622
|
+
return $derived(this.props.items.length === 0)
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
select(item: T) {
|
|
626
|
+
this.props.selected = item
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
clear() {
|
|
630
|
+
this.props.selected = null
|
|
631
|
+
}
|
|
632
|
+
})
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
```svelte
|
|
636
|
+
<!-- Usage with generics -->
|
|
637
|
+
<script lang="ts">
|
|
638
|
+
import { List } from './list.svelte'
|
|
639
|
+
|
|
640
|
+
type Item = { id: number; name: string }
|
|
641
|
+
const ItemList = List<Item>
|
|
642
|
+
|
|
643
|
+
type $$Props = $attrs.Of<ReturnType<typeof ItemList>>
|
|
644
|
+
let list = $attrs.origin(ItemList())
|
|
645
|
+
</script>
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## Known Limitations
|
|
649
|
+
|
|
650
|
+
### svelte-check Type Inference
|
|
651
|
+
|
|
652
|
+
The `svelte-check` CLI runs `svelte2tsx` on the **original** source, not the transformed output. This means:
|
|
653
|
+
|
|
654
|
+
- Components using `$attrs.origin()` may show type errors
|
|
655
|
+
- Runtime behavior is always correct
|
|
656
|
+
- Two-way binding and reactivity work as expected
|
|
657
|
+
|
|
658
|
+
**Workarounds:**
|
|
659
|
+
1. Declare `type $$Props = $attrs.Of<typeof Factory>` explicitly
|
|
660
|
+
2. Use `// @ts-expect-error` comments when needed
|
|
661
|
+
3. Enable `outputTransformed: true` in plugin options for debugging
|
|
662
|
+
|
|
663
|
+
### File Requirements
|
|
664
|
+
|
|
665
|
+
- Origin definitions must be in `.svelte.ts` files (runes enabled)
|
|
666
|
+
- Components using origins must import from `.svelte` or `.svelte.ts` extensions
|
|
667
|
+
- Plugin must be placed **before** svelte/sveltekit plugins
|
|
668
|
+
|
|
669
|
+
## Debugging
|
|
670
|
+
|
|
671
|
+
### Enable Debug Output
|
|
672
|
+
|
|
673
|
+
```ts
|
|
674
|
+
// vite.config.ts
|
|
675
|
+
svelteOrigin({
|
|
676
|
+
debug: true,
|
|
677
|
+
outputTransformed: true // Writes .transformed.txt files
|
|
678
|
+
})
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### View Transformed Code
|
|
682
|
+
|
|
683
|
+
With `outputTransformed: true`, the plugin writes `*.transformed.txt` files showing the macro expansion results.
|
|
684
|
+
|
|
685
|
+
## CLI Commands
|
|
686
|
+
|
|
687
|
+
The `svelte-origin` CLI provides utilities for library authors and type generation.
|
|
688
|
+
|
|
689
|
+
### generate-dts
|
|
690
|
+
|
|
691
|
+
Generate `.svelte.d.ts` declaration files for svelte-check compatibility:
|
|
692
|
+
|
|
693
|
+
```bash
|
|
694
|
+
# Generate .d.ts files for all components using $attrs.origin()
|
|
695
|
+
svelte-origin generate-dts [src-dir]
|
|
696
|
+
|
|
697
|
+
# Show what would be generated without writing
|
|
698
|
+
svelte-origin generate-dts --dry-run
|
|
699
|
+
|
|
700
|
+
# Remove generated .d.ts files
|
|
701
|
+
svelte-origin generate-dts --clean
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### post-process
|
|
705
|
+
|
|
706
|
+
Transform macros in svelte-package output for library distribution:
|
|
707
|
+
|
|
708
|
+
```bash
|
|
709
|
+
# Run after svelte-package to transform macros in dist/
|
|
710
|
+
svelte-package && svelte-origin post-process [dist-dir]
|
|
711
|
+
|
|
712
|
+
# Preview what would be transformed
|
|
713
|
+
svelte-origin post-process --dry-run --verbose
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
## Package Exports
|
|
717
|
+
|
|
718
|
+
```ts
|
|
719
|
+
// Main entry
|
|
720
|
+
import { transformScript, svelteOriginDts } from 'svelte-origin'
|
|
721
|
+
|
|
722
|
+
// Runtime functions
|
|
723
|
+
import { __createOrigin, bindable, __attrsFor } from 'svelte-origin/runtime'
|
|
724
|
+
|
|
725
|
+
// Vite/Rollup plugin
|
|
726
|
+
import { svelteOrigin } from 'svelte-origin/plugin'
|
|
727
|
+
|
|
728
|
+
// Svelte preprocessor
|
|
729
|
+
import { svelteOriginPreprocess } from 'svelte-origin/preprocess'
|
|
730
|
+
|
|
731
|
+
// Type globals (use in tsconfig.json)
|
|
732
|
+
// "types": ["svelte-origin/globals"]
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## Architecture
|
|
736
|
+
|
|
737
|
+
```
|
|
738
|
+
User Code Transformation Output
|
|
739
|
+
───────── ────────────── ──────
|
|
740
|
+
|
|
741
|
+
$origin({ Vite Plugin __createOrigin({
|
|
742
|
+
props: $attrs({...}), ─────────────► __attrSchema: {...},
|
|
743
|
+
... __create: (attrs) => {...}
|
|
744
|
+
}) })
|
|
745
|
+
|
|
746
|
+
$attrs.origin(Factory) Plugin + Preprocessor let {...} = $props()
|
|
747
|
+
─────────────────────► let counter = Factory(...)
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
The transformation ensures:
|
|
751
|
+
1. Origins compile to factory functions with schema metadata
|
|
752
|
+
2. Components use standard `$props()` for proper Svelte integration
|
|
753
|
+
3. Runtime behavior matches Svelte 5 expectations
|
|
754
|
+
4. Two-way binding works through `$bindable()` markers
|