Skip to content

Commit 6c147d6

Browse files
authored
Merge pull request #11 from valtiojs/global
Global
2 parents 89fe422 + b7d4922 commit 6c147d6

File tree

7 files changed

+563
-1
lines changed

7 files changed

+563
-1
lines changed

examples/README.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Valtio Plugin Examples
2+
3+
This directory contains examples demonstrating how to use the Valtio Plugin System.
4+
5+
## Examples
6+
7+
### 1. Logging Plugin (`logging-plugin.ts`)
8+
9+
A comprehensive example showing:
10+
11+
-**Global Plugin Registration**: Register plugins that affect all proxy instances
12+
-**TypeScript Support**: Full autocomplete and type safety when importing from 'valtio'
13+
-**Plugin API Access**: Access plugin methods via `proxy.pluginId`
14+
-**Lifecycle Hooks**: Implement `onInit`, `beforeChange`, `afterChange` hooks
15+
-**Method Chaining**: Use `proxy.use().method()` pattern
16+
-**Instance Creation**: Create independent proxy instances with `proxy.createInstance()`
17+
18+
## Running the Examples
19+
20+
### Prerequisites
21+
22+
Make sure you have the plugin built:
23+
24+
```bash
25+
npm run build
26+
```
27+
28+
### Quick Start
29+
30+
```bash
31+
# Run the JavaScript example (easiest)
32+
node examples/logging-example.js
33+
34+
# Verify TypeScript compiles correctly
35+
npx tsc --noEmit examples/typescript-example.ts
36+
37+
# Run TypeScript example with tsx (if you have it installed)
38+
npx tsx examples/typescript-example.ts
39+
```
40+
41+
### Available Examples
42+
43+
1. **`logging-example.js`** - JavaScript version, runs immediately
44+
2. **`logging-plugin.ts`** - Full TypeScript version with detailed documentation
45+
3. **`typescript-example.ts`** - Focused on TypeScript autocomplete features
46+
47+
## Key Features Demonstrated
48+
49+
### TypeScript Module Augmentation
50+
51+
When you import any export from this package, the `proxy` from 'valtio' automatically gets enhanced:
52+
53+
```typescript
54+
import { proxy } from 'valtio'
55+
import { ValtioPlugin } from 'valtio-plugin' // This enables augmentation
56+
57+
// Now you have full TypeScript support:
58+
proxy.use(myPlugin) // ✅ Autocomplete
59+
proxy.clearPlugins() // ✅ Autocomplete
60+
proxy.createInstance() // ✅ Autocomplete
61+
proxy.getPlugins() // ✅ Autocomplete
62+
```
63+
64+
### Plugin API Access
65+
66+
Access your plugins as properties with full typing:
67+
68+
```typescript
69+
const loggingPlugin = createLoggingPlugin()
70+
proxy.use(loggingPlugin)
71+
72+
// Access with TypeScript support
73+
const logger = proxy.logger as LoggingPlugin
74+
logger.info('Hello world!') // ✅ Autocomplete
75+
logger.getLogCount() // ✅ Autocomplete
76+
```
77+
78+
### Global vs Instance Plugins
79+
80+
```typescript
81+
// Global plugins affect ALL proxy instances
82+
proxy.use(globalLoggingPlugin)
83+
84+
const store1 = proxy({ count: 0 }) // Logging enabled
85+
const store2 = proxy({ name: 'John' }) // Logging enabled
86+
87+
// Instance plugins only affect that instance
88+
const instance = proxy.createInstance()
89+
instance.use(instanceOnlyPlugin)
90+
91+
const store3 = instance({ value: 'test' }) // Both global AND instance plugins
92+
const store4 = proxy({ other: 'data' }) // Only global plugins
93+
```
94+
95+
## Creating Your Own Plugins
96+
97+
### Basic Plugin Structure
98+
99+
```typescript
100+
import { ValtioPlugin } from 'valtio-plugin'
101+
102+
const myPlugin: ValtioPlugin = {
103+
id: 'my-plugin', // Required: unique identifier
104+
name: 'My Plugin', // Optional: display name
105+
106+
// Lifecycle hooks (all optional)
107+
onInit: () => {
108+
console.log('Plugin initialized')
109+
},
110+
111+
onAttach: (proxyFactory) => {
112+
console.log('Plugin attached to factory')
113+
},
114+
115+
beforeChange: (path, newValue, oldValue, state) => {
116+
console.log(`Changing ${path.join('.')}`)
117+
return true // Return false to prevent the change
118+
},
119+
120+
afterChange: (path, newValue, state) => {
121+
console.log(`Changed ${path.join('.')}`)
122+
},
123+
124+
onSubscribe: (proxyObject, callback) => {
125+
console.log('New subscription created')
126+
},
127+
128+
alterSnapshot: (snapshot) => {
129+
// Modify snapshot before it's returned
130+
return { ...snapshot, _modified: true }
131+
},
132+
133+
// Custom plugin methods
134+
myMethod: () => 'Hello from plugin!',
135+
customAction: (data: any) => { /* do something */ }
136+
}
137+
```
138+
139+
### Plugin with TypeScript Interface
140+
141+
```typescript
142+
interface MyPlugin extends ValtioPlugin {
143+
myMethod: () => string
144+
customAction: (data: any) => void
145+
}
146+
147+
const createMyPlugin = (): MyPlugin => ({
148+
id: 'my-plugin',
149+
myMethod: () => 'Hello!',
150+
customAction: (data) => console.log(data)
151+
})
152+
153+
// Usage with typing
154+
proxy.use(createMyPlugin())
155+
const plugin = proxy['my-plugin'] as MyPlugin
156+
plugin.myMethod() // ✅ Full TypeScript support
157+
```
158+
159+
## Best Practices
160+
161+
1. **Use unique plugin IDs** to avoid conflicts
162+
2. **Implement TypeScript interfaces** for better developer experience
163+
3. **Handle errors gracefully** in plugin hooks
164+
4. **Use global plugins sparingly** - they affect all proxy instances
165+
5. **Prefer instance plugins** for application-specific functionality
166+
6. **Document your plugin APIs** for other developers

examples/autocomplete-demo.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* TypeScript Autocomplete Demo
3+
*
4+
* Open this file in VS Code or your TypeScript-enabled editor
5+
* to see the autocomplete in action!
6+
*/
7+
8+
import { proxy } from 'valtio'
9+
import { ValtioPlugin } from '../src/index'
10+
11+
// Simple plugin for demonstration
12+
const demoPlugin: ValtioPlugin = {
13+
id: 'demo',
14+
sayHello: () => 'Hello from plugin!'
15+
}
16+
17+
// Register the plugin
18+
proxy.use(demoPlugin)
19+
20+
// 🎯 TRY THIS: Place your cursor after the dot and see the autocomplete!
21+
22+
// Global proxy methods (these should all have autocomplete):
23+
proxy.use // ✅ Should show: (pluginOrPlugins: ValtioPlugin | ValtioPlugin[]) => typeof proxy
24+
proxy.clearPlugins // ✅ Should show: () => void
25+
proxy.getPlugins // ✅ Should show: () => readonly ValtioPlugin[]
26+
proxy.removePlugin // ✅ Should show: (pluginId: string) => boolean
27+
proxy.createInstance // ✅ Should show: () => ProxyFactory
28+
proxy.subscribe // ✅ Should show: subscribe function
29+
proxy.snapshot // ✅ Should show: snapshot function
30+
31+
// Plugin access (these should work too):
32+
const plugin = (proxy as any).demo // Should be accessible
33+
34+
// Instance methods (these should have autocomplete):
35+
const instance = proxy.createInstance()
36+
instance.use // ✅ Should show: (pluginOrPlugins: ValtioPlugin | ValtioPlugin[]) => ProxyFactory
37+
instance.dispose // ✅ Should show: () => void
38+
instance.subscribe // ✅ Should show: subscribe function
39+
instance.snapshot // ✅ Should show: snapshot function
40+
41+
console.log('✅ If you see autocomplete for all the methods above, the TypeScript augmentation is working!')
42+
43+
export {}

examples/logging-example.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Simple Logging Plugin Example (JavaScript)
3+
*
4+
* This demonstrates the logging plugin functionality using the built dist files.
5+
*/
6+
7+
import { proxy } from '../dist/index.js'
8+
9+
// Create the logging plugin
10+
const createLoggingPlugin = () => {
11+
let logCount = 0
12+
13+
const log = (level, message) => {
14+
const timestamp = new Date().toISOString()
15+
console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`)
16+
logCount++
17+
}
18+
19+
return {
20+
id: 'logger',
21+
name: 'Logging Plugin',
22+
23+
// Lifecycle hooks
24+
onInit: () => {
25+
log('info', '🚀 Logging plugin initialized')
26+
},
27+
28+
beforeChange: (path, newValue, oldValue) => {
29+
log('debug', `📝 Changing ${path.join('.')} from ${JSON.stringify(oldValue)} to ${JSON.stringify(newValue)}`)
30+
return true // Allow the change
31+
},
32+
33+
afterChange: (path, newValue) => {
34+
log('info', `✅ Changed ${path.join('.')} to ${JSON.stringify(newValue)}`)
35+
},
36+
37+
// Plugin API methods
38+
debug: (message) => log('debug', message),
39+
info: (message) => log('info', message),
40+
warn: (message) => log('warn', message),
41+
error: (message) => log('error', message),
42+
getLogCount: () => logCount
43+
}
44+
}
45+
46+
// Example usage
47+
console.log('=== Valtio Plugin System - Logging Example ===\n')
48+
49+
// 1. Create and register the global logging plugin
50+
const loggingPlugin = createLoggingPlugin()
51+
proxy.use(loggingPlugin)
52+
53+
// 2. Access plugin methods
54+
const logger = proxy.logger
55+
56+
// 3. Use plugin methods directly
57+
logger.info('Logger is ready! 🎉')
58+
logger.debug('This is a debug message')
59+
60+
// 4. Create a store - the plugin will automatically log changes
61+
const userStore = proxy({
62+
name: 'John Doe',
63+
age: 30,
64+
preferences: {
65+
theme: 'dark',
66+
notifications: true
67+
}
68+
})
69+
70+
// 5. Make changes - watch the logging in action
71+
logger.info('Making changes to user store...')
72+
73+
userStore.name = 'Jane Smith'
74+
userStore.age = 25
75+
userStore.preferences.theme = 'light'
76+
userStore.preferences.notifications = false
77+
78+
// 6. Add new properties
79+
logger.info('Adding new properties...')
80+
userStore.email = '[email protected]'
81+
82+
// 7. Show log statistics
83+
console.log(`\n📊 Total log entries: ${logger.getLogCount()}`)
84+
85+
// 8. Demonstrate other global proxy methods
86+
logger.info('Demonstrating other proxy methods...')
87+
88+
const allPlugins = proxy.getPlugins()
89+
console.log(`📦 Registered plugins: ${allPlugins.map(p => p.name || p.id).join(', ')}`)
90+
91+
// 9. Create an instance to show the difference
92+
const instance = proxy.createInstance()
93+
const instanceStore = instance({ count: 0 })
94+
95+
logger.info('Created instance store - this change will be logged by global plugin')
96+
instanceStore.count = 42
97+
98+
console.log('\n✅ Example completed! Check the logs above to see the plugin in action.')
99+
100+
export { createLoggingPlugin }

0 commit comments

Comments
 (0)