create-lego-one 2.0.12 → 2.0.14
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.
- package/dist/index.cjs +150 -15
- package/dist/index.cjs.map +1 -1
- package/package.json +1 -1
- package/template/.cursor/rules/rules.mdc +639 -0
- package/template/.dockerignore +58 -0
- package/template/.env.example +18 -0
- package/template/.eslintignore +5 -0
- package/template/.eslintrc.js +28 -0
- package/template/.prettierignore +6 -0
- package/template/.prettierrc +11 -0
- package/template/CLAUDE.md +634 -0
- package/template/Dockerfile +67 -0
- package/template/PROMPT.md +457 -0
- package/template/README.md +325 -0
- package/template/docker-compose.yml +48 -0
- package/template/docker-entrypoint.sh +23 -0
- package/template/docs/checkpoints/.template.md +64 -0
- package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
- package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
- package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
- package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
- package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
- package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
- package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
- package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
- package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
- package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
- package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
- package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
- package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
- package/template/docs/framework/plans/00-index.md +164 -0
- package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
- package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
- package/template/docs/framework/plans/03-host-kernel.md +1518 -0
- package/template/docs/framework/plans/04-auth-system.md +1466 -0
- package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
- package/template/docs/framework/plans/06-ui-components.md +1478 -0
- package/template/docs/framework/plans/07-communication-system.md +1106 -0
- package/template/docs/framework/plans/08-plugin-system.md +1179 -0
- package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
- package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
- package/template/docs/framework/plans/11-testing.md +935 -0
- package/template/docs/framework/plans/12-deployment.md +896 -0
- package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
- package/template/docs/framework/research/00-modernjs-audit.md +488 -0
- package/template/docs/framework/research/01-system-blueprint.md +721 -0
- package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
- package/template/docs/framework/research/03-host-setup.md +714 -0
- package/template/docs/framework/research/04-plugin-architecture.md +645 -0
- package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
- package/template/docs/framework/research/06-cli-strategy.md +615 -0
- package/template/docs/framework/research/07-deployment.md +629 -0
- package/template/docs/framework/research/README.md +282 -0
- package/template/docs/framework/setup/00-index.md +210 -0
- package/template/docs/framework/setup/01-framework-structure.md +308 -0
- package/template/docs/framework/setup/02-development-workflow.md +405 -0
- package/template/docs/framework/setup/03-environment-setup.md +215 -0
- package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
- package/template/docs/framework/setup/05-plugin-system.md +620 -0
- package/template/docs/framework/setup/06-communication-patterns.md +451 -0
- package/template/docs/framework/setup/07-plugin-development.md +582 -0
- package/template/docs/framework/setup/08-component-library.md +658 -0
- package/template/docs/framework/setup/09-data-integration.md +609 -0
- package/template/docs/framework/setup/10-auth-rbac.md +497 -0
- package/template/docs/framework/setup/11-hooks-api.md +393 -0
- package/template/docs/framework/setup/12-components-api.md +665 -0
- package/template/docs/framework/setup/13-deployment-guide.md +566 -0
- package/template/docs/framework/setup/README.md +548 -0
- package/template/host/package.json +1 -1
- package/template/nginx.conf +72 -0
- package/template/package.json +1 -1
- package/template/packages/plugins/@lego/plugin-dashboard/package.json +1 -1
- package/template/packages/plugins/@lego/plugin-todo/package.json +1 -1
- package/template/pocketbase/CHANGELOG.md +911 -0
- package/template/pocketbase/LICENSE.md +17 -0
- package/template/scripts/create-plugin.js +221 -0
- package/template/scripts/deploy.sh +56 -0
- package/template/tsconfig.base.json +26 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
# Plugin Architecture: Hello World & Registration
|
|
2
|
+
|
|
3
|
+
**Project:** Lego-One (Modern.js SaaS OS)
|
|
4
|
+
**Document:** 04 - Plugin Architecture
|
|
5
|
+
**Status:** Research Phase
|
|
6
|
+
|
|
7
|
+
## Executive Summary
|
|
8
|
+
|
|
9
|
+
This document explains how to create a "Hello World" plugin as a standalone Modern.js sub-app and register it with the Host (Kernel). Plugins are independently deployable Modern.js applications that can run standalone or be loaded by the host.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Plugin Structure Overview
|
|
14
|
+
|
|
15
|
+
### 1.1 Directory Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
lego-one/
|
|
19
|
+
├── host/ # Kernel application
|
|
20
|
+
├── packages/
|
|
21
|
+
│ └── plugins/
|
|
22
|
+
│ └── @lego/
|
|
23
|
+
│ └── plugin-hello/ # Hello World plugin
|
|
24
|
+
│ ├── modern.config.ts
|
|
25
|
+
│ ├── package.json
|
|
26
|
+
│ ├── plugin.config.ts
|
|
27
|
+
│ ├── tsconfig.json
|
|
28
|
+
│ └── src/
|
|
29
|
+
│ ├── App.tsx
|
|
30
|
+
│ ├── main.tsx
|
|
31
|
+
│ ├── pages/
|
|
32
|
+
│ │ └── HelloPage.tsx
|
|
33
|
+
│ ├── components/
|
|
34
|
+
│ │ └── Greeting.tsx
|
|
35
|
+
│ ├── hooks/
|
|
36
|
+
│ │ ├── useKernelState.ts
|
|
37
|
+
│ │ └── useKernelBus.ts
|
|
38
|
+
│ └── lib/
|
|
39
|
+
│ └── utils.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 1.2 Plugin Types
|
|
43
|
+
|
|
44
|
+
| Type | Description | Example |
|
|
45
|
+
|------|-------------|---------|
|
|
46
|
+
| **Feature Plugin** | Standalone business functionality | Loan Calculator, Inventory |
|
|
47
|
+
| **Integration Plugin** | Third-party service integration | Stripe, Snowflake, SendGrid |
|
|
48
|
+
| **UI Extension** | Extends host UI (slots) | Dashboard widget, Sidebar link |
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. Create Hello World Plugin
|
|
53
|
+
|
|
54
|
+
### 2.1 Initialize Plugin Project
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cd lego-one/packages/plugins/@lego
|
|
58
|
+
|
|
59
|
+
# Create plugin using Modern.js
|
|
60
|
+
npx @modern-js/create@latest plugin-hello
|
|
61
|
+
|
|
62
|
+
cd plugin-hello
|
|
63
|
+
|
|
64
|
+
# Enable Micro Frontend
|
|
65
|
+
pnpm run new
|
|
66
|
+
# Select: Enable features -> Micro Front-end Feature
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2.2 Configure Package.json
|
|
70
|
+
|
|
71
|
+
**File:** `packages/plugins/@lego/plugin-hello/package.json`
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"name": "@lego/plugin-hello",
|
|
76
|
+
"version": "1.0.0",
|
|
77
|
+
"private": true,
|
|
78
|
+
"type": "module",
|
|
79
|
+
"exports": {
|
|
80
|
+
".": "./src/main.tsx",
|
|
81
|
+
"./plugin.config": "./plugin.config.ts"
|
|
82
|
+
},
|
|
83
|
+
"scripts": {
|
|
84
|
+
"dev": "modern dev",
|
|
85
|
+
"build": "modern build",
|
|
86
|
+
"preview": "modern preview",
|
|
87
|
+
"lint": "modern lint"
|
|
88
|
+
},
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"@modern-js/runtime": "^2.0.0",
|
|
91
|
+
"react": "^18.3.0",
|
|
92
|
+
"react-dom": "^18.3.0"
|
|
93
|
+
},
|
|
94
|
+
"devDependencies": {
|
|
95
|
+
"@modern-js/app-tools": "^2.0.0",
|
|
96
|
+
"@modern-js/plugin-garfish": "^2.0.0",
|
|
97
|
+
"@types/react": "^18.3.0",
|
|
98
|
+
"@types/react-dom": "^18.3.0",
|
|
99
|
+
"typescript": "^5.5.0"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2.3 Configure Modern.js
|
|
105
|
+
|
|
106
|
+
**File:** `packages/plugins/@lego/plugin-hello/modern.config.ts`
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { appTools, defineConfig } from '@modern-js/app-tools';
|
|
110
|
+
import { garfishPlugin } from '@modern-js/plugin-garfish';
|
|
111
|
+
|
|
112
|
+
export default defineConfig({
|
|
113
|
+
// Unique dev port for each plugin
|
|
114
|
+
dev: {
|
|
115
|
+
port: 3001,
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Enable routing
|
|
119
|
+
runtime: {
|
|
120
|
+
router: true,
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// CRITICAL: Mark as micro-frontend sub-app
|
|
124
|
+
deploy: {
|
|
125
|
+
microFrontend: true,
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
plugins: [appTools(), garfishPlugin()],
|
|
129
|
+
|
|
130
|
+
// Path aliases
|
|
131
|
+
source: {
|
|
132
|
+
alias: {
|
|
133
|
+
'@': './src',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 2.4 Create Plugin Config
|
|
140
|
+
|
|
141
|
+
**File:** `packages/plugins/@lego/plugin-hello/plugin.config.ts`
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { definePluginConfig } from '@lego/kernel/plugin-config';
|
|
145
|
+
|
|
146
|
+
export default definePluginConfig({
|
|
147
|
+
name: '@lego/plugin-hello',
|
|
148
|
+
version: '1.0.0',
|
|
149
|
+
displayName: 'Hello World',
|
|
150
|
+
description: 'A simple Hello World plugin for demonstration',
|
|
151
|
+
|
|
152
|
+
// Plugin exports available to host
|
|
153
|
+
exports: {
|
|
154
|
+
components: {
|
|
155
|
+
Greeting: './src/components/Greeting',
|
|
156
|
+
},
|
|
157
|
+
hooks: {
|
|
158
|
+
useHello: './src/hooks/useHello',
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// Slot injections
|
|
163
|
+
slots: {
|
|
164
|
+
'sidebar:nav': {
|
|
165
|
+
component: './src/components/SidebarLink',
|
|
166
|
+
order: 100,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
// Routes this plugin handles
|
|
171
|
+
routes: {
|
|
172
|
+
basePath: '/hello',
|
|
173
|
+
children: ['/', '/greeting'],
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 2.5 Create Plugin Entry Point
|
|
179
|
+
|
|
180
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/App.tsx`
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { BrowserRouter, Routes, Route } from '@modern-js/runtime/router';
|
|
184
|
+
import { useGarfish } from '@garfish/hooks';
|
|
185
|
+
import { useEffect, useState } from 'react';
|
|
186
|
+
import { HelloPage } from './pages/HelloPage';
|
|
187
|
+
import { GreetingPage } from './pages/GreetingPage';
|
|
188
|
+
import pluginConfig from '../../plugin.config';
|
|
189
|
+
import { registerSlots } from './lib/slot-registration';
|
|
190
|
+
|
|
191
|
+
interface PluginProps {
|
|
192
|
+
basename?: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default function PluginApp(props: PluginProps) {
|
|
196
|
+
const { appInfo } = useGarfish();
|
|
197
|
+
const basename = props.basename || '/hello';
|
|
198
|
+
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
console.log(`[${pluginConfig.name}] Plugin loaded with appInfo:`, appInfo);
|
|
201
|
+
|
|
202
|
+
// Register slot injections with host
|
|
203
|
+
registerSlots(pluginConfig.slots);
|
|
204
|
+
}, [appInfo]);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<BrowserRouter basename={basename}>
|
|
208
|
+
<Routes>
|
|
209
|
+
<Route index element={<HelloPage />} />
|
|
210
|
+
<Route path="greeting" element={<GreetingPage />} />
|
|
211
|
+
</Routes>
|
|
212
|
+
</BrowserRouter>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 2.6 Create Pages
|
|
218
|
+
|
|
219
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/pages/HelloPage.tsx`
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { useKernelState } from '../hooks/useKernelState';
|
|
223
|
+
import { useKernelBus } from '../hooks/useKernelBus';
|
|
224
|
+
import { Button } from '../components/ui/button';
|
|
225
|
+
|
|
226
|
+
export function HelloPage() {
|
|
227
|
+
const kernelState = useKernelState();
|
|
228
|
+
const bus = useKernelBus();
|
|
229
|
+
|
|
230
|
+
const handleGreet = () => {
|
|
231
|
+
// Emit event to host for toast notification
|
|
232
|
+
bus.emit({
|
|
233
|
+
type: 'toast:show',
|
|
234
|
+
payload: { message: 'Hello from the plugin!', variant: 'success' },
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<div className="p-8">
|
|
240
|
+
<h1 className="text-3xl font-bold mb-4">Hello World Plugin</h1>
|
|
241
|
+
|
|
242
|
+
<div className="space-y-4">
|
|
243
|
+
<p className="text-muted-foreground">
|
|
244
|
+
This is a standalone Modern.js application running as a micro-frontend.
|
|
245
|
+
</p>
|
|
246
|
+
|
|
247
|
+
<div className="bg-muted p-4 rounded-lg">
|
|
248
|
+
<h2 className="font-semibold mb-2">Shared State from Kernel:</h2>
|
|
249
|
+
<ul className="list-disc list-inside space-y-1">
|
|
250
|
+
<li>Theme: {kernelState?.theme}</li>
|
|
251
|
+
<li>Sidebar: {kernelState?.sidebarOpen ? 'Open' : 'Closed'}</li>
|
|
252
|
+
<li>User: {kernelState?.currentUser?.email || 'Not authenticated'}</li>
|
|
253
|
+
</ul>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<Button onClick={handleGreet}>Show Greeting</Button>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/pages/GreetingPage.tsx`
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { Greeting } from '../components/Greeting';
|
|
267
|
+
|
|
268
|
+
export function GreetingPage() {
|
|
269
|
+
return (
|
|
270
|
+
<div className="p-8">
|
|
271
|
+
<h1 className="text-3xl font-bold mb-4">Greeting Page</h1>
|
|
272
|
+
<Greeting name="Developer" />
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 2.7 Create Components
|
|
279
|
+
|
|
280
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/components/Greeting.tsx`
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { useState } from 'react';
|
|
284
|
+
|
|
285
|
+
interface GreetingProps {
|
|
286
|
+
name: string;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function Greeting({ name }: GreetingProps) {
|
|
290
|
+
const [count, setCount] = useState(0);
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<div className="bg-card p-6 rounded-lg border">
|
|
294
|
+
<p className="text-lg">
|
|
295
|
+
Hello, <span className="font-semibold text-primary">{name}</span>!
|
|
296
|
+
</p>
|
|
297
|
+
<p className="text-muted-foreground mt-2">
|
|
298
|
+
You've clicked the button {count} times.
|
|
299
|
+
</p>
|
|
300
|
+
<button
|
|
301
|
+
onClick={() => setCount((c) => c + 1)}
|
|
302
|
+
className="mt-4 px-4 py-2 bg-primary text-primary-foreground rounded-md"
|
|
303
|
+
>
|
|
304
|
+
Click Me
|
|
305
|
+
</button>
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/components/SidebarLink.tsx`
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import { Link } from '@modern-js/runtime/router';
|
|
315
|
+
import { Hand } from 'lucide-react';
|
|
316
|
+
|
|
317
|
+
export function SidebarLink() {
|
|
318
|
+
return (
|
|
319
|
+
<Link
|
|
320
|
+
to="/hello"
|
|
321
|
+
className="flex items-center gap-2 px-3 py-2 text-sm rounded-md hover:bg-accent hover:text-accent-foreground"
|
|
322
|
+
>
|
|
323
|
+
<Hand className="h-4 w-4" />
|
|
324
|
+
<span>Hello Plugin</span>
|
|
325
|
+
</Link>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### 2.8 Create Hooks
|
|
331
|
+
|
|
332
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/hooks/useKernelState.ts`
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { useEffect, useState } from 'react';
|
|
336
|
+
|
|
337
|
+
interface KernelState {
|
|
338
|
+
currentUser: { id: string; email: string; role: string } | null;
|
|
339
|
+
theme: 'light' | 'dark';
|
|
340
|
+
sidebarOpen: boolean;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function useKernelState() {
|
|
344
|
+
const [state, setState] = useState<KernelState | null>(null);
|
|
345
|
+
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
// Access shared state bridge from host
|
|
348
|
+
const kernelBridge = (window as any).__LEGO_KERNEL_STATE__;
|
|
349
|
+
|
|
350
|
+
if (kernelBridge?.useGlobalKernelState) {
|
|
351
|
+
// Subscribe to state changes
|
|
352
|
+
const unsubscribe = kernelBridge.useGlobalKernelState.subscribe(
|
|
353
|
+
(newState: KernelState) => {
|
|
354
|
+
setState({ ...newState });
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
// Initialize with current state
|
|
359
|
+
setState(kernelBridge.useGlobalKernelState.getState());
|
|
360
|
+
|
|
361
|
+
return () => unsubscribe?.();
|
|
362
|
+
}
|
|
363
|
+
}, []);
|
|
364
|
+
|
|
365
|
+
return state;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/hooks/useKernelBus.ts`
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { Garfish } from 'garfish';
|
|
373
|
+
|
|
374
|
+
type BusEvent =
|
|
375
|
+
| { type: 'toast:show'; payload: { message: string; variant: 'success' | 'error' } }
|
|
376
|
+
| { type: 'navigation:goto'; payload: { path: string } };
|
|
377
|
+
|
|
378
|
+
export function useKernelBus() {
|
|
379
|
+
const channel = Garfish.channel;
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
emit: (event: BusEvent) => {
|
|
383
|
+
channel.emit(event.type, event.payload);
|
|
384
|
+
},
|
|
385
|
+
on: (eventType: BusEvent['type'], handler: (payload: any) => void) => {
|
|
386
|
+
channel.on(eventType, handler);
|
|
387
|
+
},
|
|
388
|
+
off: (eventType: BusEvent['type'], handler: (payload: any) => void) => {
|
|
389
|
+
channel.off(eventType, handler);
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### 2.9 Create Slot Registration Helper
|
|
396
|
+
|
|
397
|
+
**File:** `packages/plugins/@lego/plugin-hello/src/lib/slot-registration.ts`
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
interface SlotConfig {
|
|
401
|
+
component: string;
|
|
402
|
+
order: number;
|
|
403
|
+
isVisible?: () => boolean;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
interface SlotsConfig {
|
|
407
|
+
[slotName: string]: SlotConfig;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export async function registerSlots(slots: SlotsConfig) {
|
|
411
|
+
const slotRegistry = (window as any).__LEGO_SLOT_REGISTRY__;
|
|
412
|
+
|
|
413
|
+
if (!slotRegistry) {
|
|
414
|
+
console.warn('Slot registry not found. Host may not be initialized.');
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
for (const [slotName, slotConfig] of Object.entries(slots)) {
|
|
419
|
+
try {
|
|
420
|
+
const module = await import(/* @vite-ignore */ slotConfig.component);
|
|
421
|
+
const Component = module.default;
|
|
422
|
+
|
|
423
|
+
slotRegistry.register(slotName, {
|
|
424
|
+
id: `${slotName}-${Date.now()}`,
|
|
425
|
+
pluginName: '@lego/plugin-hello',
|
|
426
|
+
component: Component,
|
|
427
|
+
order: slotConfig.order,
|
|
428
|
+
isVisible: slotConfig.isVisible || (() => true),
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
console.log(`[Slot] Registered "${slotName}" for @lego/plugin-hello`);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error(`[Slot] Failed to register "${slotName}":`, error);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 3. Register Plugin with Host
|
|
442
|
+
|
|
443
|
+
### 3.1 Update Host Configuration
|
|
444
|
+
|
|
445
|
+
**File:** `host/src/modern.runtime.ts`
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
449
|
+
|
|
450
|
+
export default defineRuntimeConfig({
|
|
451
|
+
masterApp: {
|
|
452
|
+
apps: [
|
|
453
|
+
{
|
|
454
|
+
name: '@lego/plugin-hello',
|
|
455
|
+
entry: 'http://localhost:3001',
|
|
456
|
+
activeWhen: '/hello',
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### 3.2 Create Plugin Route in Host
|
|
464
|
+
|
|
465
|
+
**File:** `host/src/routes/hello/$.tsx`
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';
|
|
469
|
+
|
|
470
|
+
export default function HelloRoute() {
|
|
471
|
+
const { Hello } = useModuleApps();
|
|
472
|
+
|
|
473
|
+
return <Hello />;
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### 3.3 Update Host saas.config.ts
|
|
478
|
+
|
|
479
|
+
**File:** `host/saas.config.ts`
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
import { defineConfig } from '@lego/kernel/config';
|
|
483
|
+
|
|
484
|
+
export default defineConfig({
|
|
485
|
+
plugins: [
|
|
486
|
+
{
|
|
487
|
+
name: '@lego/plugin-hello',
|
|
488
|
+
enabled: true,
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## 4. Development Workflow
|
|
497
|
+
|
|
498
|
+
### 4.1 Start Host and Plugin
|
|
499
|
+
|
|
500
|
+
Open two terminal windows:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
# Terminal 1: Start Host
|
|
504
|
+
cd host
|
|
505
|
+
pnpm run dev
|
|
506
|
+
# Runs on http://localhost:8080
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# Terminal 2: Start Plugin
|
|
511
|
+
cd packages/plugins/@lego/plugin-hello
|
|
512
|
+
pnpm run dev
|
|
513
|
+
# Runs on http://localhost:3001
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### 4.2 Test Plugin
|
|
517
|
+
|
|
518
|
+
Navigate to `http://localhost:8080/hello`
|
|
519
|
+
|
|
520
|
+
**Expected behavior:**
|
|
521
|
+
1. Host loads the Hello plugin at the `/hello` route
|
|
522
|
+
2. Plugin displays "Hello World Plugin" header
|
|
523
|
+
3. Shared kernel state (theme, user, sidebar) is displayed
|
|
524
|
+
4. "Show Greeting" button triggers toast in host app
|
|
525
|
+
5. Sidebar shows "Hello Plugin" link (if slot injection works)
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 5. Plugin Lifecycle
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
533
|
+
│ Plugin Lifecycle │
|
|
534
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
535
|
+
│ │
|
|
536
|
+
│ 1. REGISTRATION │
|
|
537
|
+
│ - Plugin listed in saas.config.ts │
|
|
538
|
+
│ - Host reads plugin config on startup │
|
|
539
|
+
│ │
|
|
540
|
+
│ 2. LOADING │
|
|
541
|
+
│ - User navigates to plugin route (/hello) │
|
|
542
|
+
│ - Garfish.loadApp() fetches plugin entry │
|
|
543
|
+
│ - Plugin JavaScript executes │
|
|
544
|
+
│ │
|
|
545
|
+
│ 3. MOUNTING │
|
|
546
|
+
│ - Plugin App.tsx receives basename prop │
|
|
547
|
+
│ - Plugin registers slots with host │
|
|
548
|
+
│ - Plugin subscribes to kernel bus events │
|
|
549
|
+
│ - Plugin ReactDOM.render into container │
|
|
550
|
+
│ │
|
|
551
|
+
│ 4. ACTIVE STATE │
|
|
552
|
+
│ - Plugin handles its own routing │
|
|
553
|
+
│ - Plugin communicates with host via bus │
|
|
554
|
+
│ - Plugin accesses shared state via window bridge │
|
|
555
|
+
│ │
|
|
556
|
+
│ 5. UNMOUNTING │
|
|
557
|
+
│ - User navigates away from plugin route │
|
|
558
|
+
│ - Plugin cleanup functions run │
|
|
559
|
+
│ - Garfish unloads plugin resources │
|
|
560
|
+
│ │
|
|
561
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## 6. Standalone Development
|
|
567
|
+
|
|
568
|
+
Plugins can run independently for development:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
cd packages/plugins/@lego/plugin-hello
|
|
572
|
+
pnpm run dev
|
|
573
|
+
# Access at http://localhost:3001
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**Standalone App.tsx:**
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
// For standalone development, provide a default basename
|
|
580
|
+
export default function PluginApp(props: PluginProps) {
|
|
581
|
+
const basename = props.basename || '/hello';
|
|
582
|
+
// ... rest of component
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## 7. Plugin Best Practices
|
|
589
|
+
|
|
590
|
+
| Practice | Description |
|
|
591
|
+
|----------|-------------|
|
|
592
|
+
| **Unique Ports** | Each plugin needs a unique dev port |
|
|
593
|
+
| **Isolated State** | Don't rely on host state for core functionality |
|
|
594
|
+
| **Error Boundaries** | Wrap plugin in error boundary |
|
|
595
|
+
| **Type Safety** | Export types from plugin.config.ts |
|
|
596
|
+
| **Testing** | Test plugin standalone and integrated |
|
|
597
|
+
| **Documentation** | Document slots, events, and exports |
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
## 8. Troubleshooting
|
|
602
|
+
|
|
603
|
+
### 8.1 Plugin Not Loading
|
|
604
|
+
|
|
605
|
+
**Symptoms:** Blank page at plugin route
|
|
606
|
+
|
|
607
|
+
**Solutions:**
|
|
608
|
+
1. Check plugin dev server is running
|
|
609
|
+
2. Verify `activeWhen` matches route
|
|
610
|
+
3. Check browser console for errors
|
|
611
|
+
4. Verify `deploy.microFrontend: true` in plugin config
|
|
612
|
+
|
|
613
|
+
### 8.2 Shared State Undefined
|
|
614
|
+
|
|
615
|
+
**Symptoms:** `useKernelState()` returns null
|
|
616
|
+
|
|
617
|
+
**Solutions:**
|
|
618
|
+
1. Verify host has called `registerSharedState()`
|
|
619
|
+
2. Check `window.__LEGO_KERNEL_STATE__` exists
|
|
620
|
+
3. Ensure host loads before plugin
|
|
621
|
+
|
|
622
|
+
### 8.3 Route Mismatch
|
|
623
|
+
|
|
624
|
+
**Symptoms:** 404 when navigating to plugin
|
|
625
|
+
|
|
626
|
+
**Solutions:**
|
|
627
|
+
1. Check `$.tsx` file exists in host routes
|
|
628
|
+
2. Verify `basename` prop is passed correctly
|
|
629
|
+
3. Check for conflicts with other routes
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## 9. Next Steps
|
|
634
|
+
|
|
635
|
+
1. **`05-slot-injection-pattern.md`**: Advanced slot injection patterns
|
|
636
|
+
2. **`06-cli-strategy.md`**: Automate plugin creation with CLI
|
|
637
|
+
3. **`07-deployment.md`**: Deploy plugins to production
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## References
|
|
642
|
+
|
|
643
|
+
- [Modern.js Micro Frontend Development](https://modernjs.dev/guides/topic-detail/micro-frontend/c02-development)
|
|
644
|
+
- [Garfish Documentation](https://www.garfishjs.org/)
|
|
645
|
+
- [Garfish.setExternal API](https://www.garfishjs.org/api/setExternal.html)
|