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,855 @@
|
|
|
1
|
+
# Infrastructure Setup
|
|
2
|
+
|
|
3
|
+
> **For AI:** This is the first implementation document. Complete all tasks in order before proceeding to `02-pocketbase-setup.md`.
|
|
4
|
+
|
|
5
|
+
**Goal:** Set up the monorepo structure, configure tooling (pnpm, TypeScript, ESLint, Prettier), and create base package configurations.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Monorepo with pnpm workspaces. Root manages shared dev dependencies. Host app and plugins are separate workspaces.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** pnpm, TypeScript, ESLint, Prettier, Turbo (optional), Husky not needed (keeping it simple per decision)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Task 1: Initialize Root Package
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Create: `package.json`
|
|
17
|
+
- Create: `pnpm-workspace.yaml`
|
|
18
|
+
- Create: `pnpm-lock.yaml` (auto-generated)
|
|
19
|
+
- Create: `.gitignore`
|
|
20
|
+
- Create: `.npmrc`
|
|
21
|
+
|
|
22
|
+
**Step 1: Create package.json**
|
|
23
|
+
|
|
24
|
+
Create `package.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"name": "lego-one",
|
|
29
|
+
"version": "1.0.0",
|
|
30
|
+
"private": true,
|
|
31
|
+
"description": "Microkernel SaaS OS with Modern.js + Garfish + PocketBase",
|
|
32
|
+
"type": "module",
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "pnpm --filter './host' dev",
|
|
35
|
+
"dev:host": "pnpm --filter './host' dev",
|
|
36
|
+
"dev:plugins": "pnpm -r --filter './packages/plugins/@lego/*' dev",
|
|
37
|
+
"dev:all": "pnpm --filter './host' dev & pnpm -r --filter './packages/plugins/@lego/*' dev",
|
|
38
|
+
"build": "pnpm --filter './host' build && pnpm -r --filter './packages/plugins/@lego/*' build",
|
|
39
|
+
"lint": "pnpm -r lint",
|
|
40
|
+
"lint:fix": "pnpm -r lint:fix",
|
|
41
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
42
|
+
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
43
|
+
"typecheck": "pnpm -r typecheck",
|
|
44
|
+
"test": "pnpm -r test",
|
|
45
|
+
"test:e2e": "pnpm --filter './host' test:e2e",
|
|
46
|
+
"clean": "pnpm -r exec rm -rf dist node_modules",
|
|
47
|
+
"create-plugin": "node scripts/create-plugin.js"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.11.0",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
52
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
53
|
+
"eslint": "^8.57.0",
|
|
54
|
+
"eslint-config-prettier": "^9.1.0",
|
|
55
|
+
"prettier": "^3.2.0",
|
|
56
|
+
"prettier-plugin-tailwindcss": "^0.5.0",
|
|
57
|
+
"typescript": "^5.3.0"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0",
|
|
61
|
+
"pnpm": ">=8.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Step 2: Create pnpm-workspace.yaml**
|
|
67
|
+
|
|
68
|
+
Create `pnpm-workspace.yaml`:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
packages:
|
|
72
|
+
- 'host'
|
|
73
|
+
- 'packages/plugins/@lego/*'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Step 3: Create .npmrc**
|
|
77
|
+
|
|
78
|
+
Create `.npmrc`:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
shamefully-hoist=false
|
|
82
|
+
strict-peer-dependencies=false
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Step 4: Create .gitignore**
|
|
86
|
+
|
|
87
|
+
Create `.gitignore`:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
# Dependencies
|
|
91
|
+
node_modules/
|
|
92
|
+
.pnp*
|
|
93
|
+
|
|
94
|
+
# Build outputs
|
|
95
|
+
dist/
|
|
96
|
+
build/
|
|
97
|
+
*.tsbuildinfo
|
|
98
|
+
|
|
99
|
+
# Environment
|
|
100
|
+
.env
|
|
101
|
+
.env.local
|
|
102
|
+
.env.production
|
|
103
|
+
.env.development
|
|
104
|
+
*.log
|
|
105
|
+
|
|
106
|
+
# IDE
|
|
107
|
+
.vscode/
|
|
108
|
+
.idea/
|
|
109
|
+
*.swp
|
|
110
|
+
*.swo
|
|
111
|
+
*~
|
|
112
|
+
.DS_Store
|
|
113
|
+
|
|
114
|
+
# Testing
|
|
115
|
+
coverage/
|
|
116
|
+
.nyc_output/
|
|
117
|
+
playwright-report/
|
|
118
|
+
test-results/
|
|
119
|
+
|
|
120
|
+
# PocketBase
|
|
121
|
+
pocketbase/pb_data/
|
|
122
|
+
pocketbase/pb_public/
|
|
123
|
+
pocketbase/pb_hooks/
|
|
124
|
+
*.pb
|
|
125
|
+
|
|
126
|
+
# Temporary
|
|
127
|
+
*.tmp
|
|
128
|
+
.cache/
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Step 5: Verify setup**
|
|
132
|
+
|
|
133
|
+
Run:
|
|
134
|
+
```bash
|
|
135
|
+
pnpm install
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Expected: No errors, `node_modules` created, `pnpm-lock.yaml` generated.
|
|
139
|
+
|
|
140
|
+
**Step 6: Commit**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
git add .
|
|
144
|
+
git commit -m "chore: initialize monorepo with pnpm workspaces"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Task 2: Configure TypeScript
|
|
150
|
+
|
|
151
|
+
**Files:**
|
|
152
|
+
- Create: `tsconfig.json`
|
|
153
|
+
- Create: `tsconfig.base.json`
|
|
154
|
+
|
|
155
|
+
**Step 1: Create base TypeScript config**
|
|
156
|
+
|
|
157
|
+
Create `tsconfig.base.json`:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"compilerOptions": {
|
|
162
|
+
"target": "ES2020",
|
|
163
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
164
|
+
"module": "ESNext",
|
|
165
|
+
"moduleResolution": "bundler",
|
|
166
|
+
"resolveJsonModule": true,
|
|
167
|
+
"allowJs": true,
|
|
168
|
+
"strict": true,
|
|
169
|
+
"noEmit": true,
|
|
170
|
+
"esModuleInterop": true,
|
|
171
|
+
"skipLibCheck": true,
|
|
172
|
+
"forceConsistentCasingInFileNames": true,
|
|
173
|
+
"isolatedModules": true,
|
|
174
|
+
"jsx": "react-jsx",
|
|
175
|
+
"jsxImportSource": "react",
|
|
176
|
+
"allowImportingTsExtensions": true,
|
|
177
|
+
"noUnusedLocals": true,
|
|
178
|
+
"noUnusedParameters": true,
|
|
179
|
+
"noFallthroughCasesInSwitch": true,
|
|
180
|
+
"baseUrl": ".",
|
|
181
|
+
"paths": {
|
|
182
|
+
"@/*": ["./src/*"]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Step 2: Create root TypeScript config**
|
|
189
|
+
|
|
190
|
+
Create `tsconfig.json`:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"files": [],
|
|
195
|
+
"references": [
|
|
196
|
+
{ "path": "./host" },
|
|
197
|
+
{ "path": "./packages/plugins/@lego/plugin-dashboard" },
|
|
198
|
+
{ "path": "./packages/plugins/@lego/plugin-todo" }
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Step 3: Commit**
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git add tsconfig.*
|
|
207
|
+
git commit -m "chore: add TypeScript configuration"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Task 3: Configure ESLint
|
|
213
|
+
|
|
214
|
+
**Files:**
|
|
215
|
+
- Create: `.eslintrc.js`
|
|
216
|
+
- Create: `.eslintignore`
|
|
217
|
+
|
|
218
|
+
**Step 1: Create ESLint config**
|
|
219
|
+
|
|
220
|
+
Create `.eslintrc.js`:
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
module.exports = {
|
|
224
|
+
root: true,
|
|
225
|
+
env: {
|
|
226
|
+
browser: true,
|
|
227
|
+
es2022: true,
|
|
228
|
+
node: true,
|
|
229
|
+
},
|
|
230
|
+
extends: [
|
|
231
|
+
'eslint:recommended',
|
|
232
|
+
'@typescript-eslint/recommended',
|
|
233
|
+
'prettier',
|
|
234
|
+
],
|
|
235
|
+
parser: '@typescript-eslint/parser',
|
|
236
|
+
parserOptions: {
|
|
237
|
+
ecmaVersion: 'latest',
|
|
238
|
+
sourceType: 'module',
|
|
239
|
+
ecmaFeatures: {
|
|
240
|
+
jsx: true,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
plugins: ['@typescript-eslint'],
|
|
244
|
+
rules: {
|
|
245
|
+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
246
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
247
|
+
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
|
248
|
+
},
|
|
249
|
+
ignorePatterns: ['dist', 'node_modules', '*.config.js'],
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Step 2: Create .eslintignore**
|
|
254
|
+
|
|
255
|
+
Create `.eslintignore`:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
dist
|
|
259
|
+
node_modules
|
|
260
|
+
*.config.js
|
|
261
|
+
coverage
|
|
262
|
+
playwright-report
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Step 3: Add lint script to each workspace** (will be done when creating host/plugins)
|
|
266
|
+
|
|
267
|
+
**Step 4: Commit**
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
git add .eslintrc* .eslintignore
|
|
271
|
+
git commit -m "chore: add ESLint configuration"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Task 4: Configure Prettier
|
|
277
|
+
|
|
278
|
+
**Files:**
|
|
279
|
+
- Create: `.prettierrc`
|
|
280
|
+
- Create: `.prettierignore`
|
|
281
|
+
|
|
282
|
+
**Step 1: Create Prettier config**
|
|
283
|
+
|
|
284
|
+
Create `.prettierrc`:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"semi": true,
|
|
289
|
+
"trailingComma": "es5",
|
|
290
|
+
"singleQuote": true,
|
|
291
|
+
"printWidth": 100,
|
|
292
|
+
"tabWidth": 2,
|
|
293
|
+
"useTabs": false,
|
|
294
|
+
"arrowParens": "always",
|
|
295
|
+
"endOfLine": "lf",
|
|
296
|
+
"plugins": ["prettier-plugin-tailwindcss"]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Step 2: Create .prettierignore**
|
|
301
|
+
|
|
302
|
+
Create `.prettierignore`:
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
dist
|
|
306
|
+
node_modules
|
|
307
|
+
pnpm-lock.yaml
|
|
308
|
+
package-lock.json
|
|
309
|
+
coverage
|
|
310
|
+
playwright-report
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Step 3: Commit**
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
git add .prettierrc*
|
|
317
|
+
git commit -m "chore: add Prettier configuration"
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Task 5: Create Root Scripts Directory
|
|
323
|
+
|
|
324
|
+
**Files:**
|
|
325
|
+
- Create: `scripts/create-plugin.js`
|
|
326
|
+
|
|
327
|
+
**Step 1: Create plugin scaffolding script**
|
|
328
|
+
|
|
329
|
+
Create `scripts/create-plugin.js`:
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
#!/usr/bin/env node
|
|
333
|
+
|
|
334
|
+
const fs = require('fs');
|
|
335
|
+
const path = require('path');
|
|
336
|
+
|
|
337
|
+
const args = process.argv.slice(2);
|
|
338
|
+
|
|
339
|
+
if (args.length === 0) {
|
|
340
|
+
console.error('Usage: node scripts/create-plugin.js <plugin-name>');
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const pluginName = args[0].startsWith('plugin-')
|
|
345
|
+
? args[0]
|
|
346
|
+
: `plugin-${args[0]}`;
|
|
347
|
+
const pluginPascal = pluginName
|
|
348
|
+
.split('-')
|
|
349
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
350
|
+
.join('');
|
|
351
|
+
|
|
352
|
+
const pluginDir = path.join(process.cwd(), 'packages/plugins/@lego', pluginName);
|
|
353
|
+
|
|
354
|
+
// Create plugin directory
|
|
355
|
+
fs.mkdirSync(pluginDir, { recursive: true });
|
|
356
|
+
fs.mkdirSync(path.join(pluginDir, 'src'));
|
|
357
|
+
fs.mkdirSync(path.join(pluginDir, 'src/pages'));
|
|
358
|
+
fs.mkdirSync(path.join(pluginDir, 'src/components'));
|
|
359
|
+
fs.mkdirSync(path.join(pluginDir, 'src/hooks'));
|
|
360
|
+
fs.mkdirSync(path.join(pluginDir, 'src/lib'));
|
|
361
|
+
|
|
362
|
+
// Create package.json
|
|
363
|
+
const packageJson = {
|
|
364
|
+
name: `@lego/${pluginName}`,
|
|
365
|
+
version: '1.0.0',
|
|
366
|
+
private: true,
|
|
367
|
+
type: 'module',
|
|
368
|
+
exports: {
|
|
369
|
+
'.': './src/main.tsx',
|
|
370
|
+
'./plugin.config': './plugin.config.ts',
|
|
371
|
+
},
|
|
372
|
+
scripts: {
|
|
373
|
+
dev: 'modern dev',
|
|
374
|
+
build: 'modern build',
|
|
375
|
+
preview: 'modern preview',
|
|
376
|
+
lint: 'eslint src',
|
|
377
|
+
typecheck: 'tsc --noEmit',
|
|
378
|
+
},
|
|
379
|
+
dependencies: {
|
|
380
|
+
'@modern-js/runtime': '^2.0.0',
|
|
381
|
+
'react': '^18.3.0',
|
|
382
|
+
'react-dom': '^18.3.0',
|
|
383
|
+
},
|
|
384
|
+
devDependencies: {
|
|
385
|
+
'@modern-js/app-tools': '^2.0.0',
|
|
386
|
+
'@modern-js/plugin-garfish': '^2.0.0',
|
|
387
|
+
'@types/react': '^18.3.0',
|
|
388
|
+
'@types/react-dom': '^18.3.0',
|
|
389
|
+
typescript: '^5.3.0',
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
fs.writeFileSync(
|
|
394
|
+
path.join(pluginDir, 'package.json'),
|
|
395
|
+
JSON.stringify(packageJson, null, 2)
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
// Create plugin.config.ts
|
|
399
|
+
const pluginConfig = `import { definePluginConfig } from '@lego/kernel/plugin-config';
|
|
400
|
+
|
|
401
|
+
export default definePluginConfig({
|
|
402
|
+
name: '@lego/${pluginName}',
|
|
403
|
+
version: '1.0.0',
|
|
404
|
+
displayName: '${pluginPascal}',
|
|
405
|
+
description: 'A plugin for ${pluginName}',
|
|
406
|
+
|
|
407
|
+
slots: {
|
|
408
|
+
'sidebar:nav': {
|
|
409
|
+
component: './src/components/SidebarLink',
|
|
410
|
+
order: 100,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
`;
|
|
415
|
+
|
|
416
|
+
fs.writeFileSync(path.join(pluginDir, 'plugin.config.ts'), pluginConfig);
|
|
417
|
+
|
|
418
|
+
// Create modern.config.ts
|
|
419
|
+
const modernConfig = `import { appTools, defineConfig } from '@modern-js/app-tools';
|
|
420
|
+
import { garfishPlugin } from '@modern-js/plugin-garfish';
|
|
421
|
+
|
|
422
|
+
// Get next available port starting from 3001
|
|
423
|
+
const basePort = 3001 + parseInt(process.env.PLUGIN_INDEX || '0');
|
|
424
|
+
|
|
425
|
+
export default defineConfig({
|
|
426
|
+
dev: {
|
|
427
|
+
port: basePort,
|
|
428
|
+
},
|
|
429
|
+
runtime: {
|
|
430
|
+
router: true,
|
|
431
|
+
},
|
|
432
|
+
deploy: {
|
|
433
|
+
microFrontend: true,
|
|
434
|
+
},
|
|
435
|
+
plugins: [appTools(), garfishPlugin()],
|
|
436
|
+
source: {
|
|
437
|
+
alias: {
|
|
438
|
+
'@': './src',
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
`;
|
|
443
|
+
|
|
444
|
+
fs.writeFileSync(path.join(pluginDir, 'modern.config.ts'), modernConfig);
|
|
445
|
+
|
|
446
|
+
// Create tsconfig.json
|
|
447
|
+
const tsconfig = {
|
|
448
|
+
extends: '../../tsconfig.base.json',
|
|
449
|
+
compilerOptions: {
|
|
450
|
+
composite: true,
|
|
451
|
+
outDir: './dist',
|
|
452
|
+
rootDir: './src',
|
|
453
|
+
},
|
|
454
|
+
include: ['src'],
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
fs.writeFileSync(path.join(pluginDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
458
|
+
|
|
459
|
+
// Create main App.tsx
|
|
460
|
+
const appTsx = `import { BrowserRouter, Routes, Route } from '@modern-js/runtime/router';
|
|
461
|
+
|
|
462
|
+
interface PluginAppProps {
|
|
463
|
+
basename?: string;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export default function PluginApp({ basename }: PluginAppProps) {
|
|
467
|
+
const routerBasename = basename || '/';
|
|
468
|
+
|
|
469
|
+
return (
|
|
470
|
+
<BrowserRouter basename={routerBasename}>
|
|
471
|
+
<Routes>
|
|
472
|
+
<Route index element={<div>TODO: Implement ${pluginPascal}</div>} />
|
|
473
|
+
</Routes>
|
|
474
|
+
</BrowserRouter>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
`;
|
|
478
|
+
|
|
479
|
+
fs.writeFileSync(path.join(pluginDir, 'src/App.tsx'), appTsx);
|
|
480
|
+
|
|
481
|
+
// Create main.tsx entry point
|
|
482
|
+
const mainTsx = `import React from 'react';
|
|
483
|
+
import ReactDOM from 'react-dom/client';
|
|
484
|
+
import App from './App';
|
|
485
|
+
|
|
486
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
487
|
+
<React.StrictMode>
|
|
488
|
+
<App />
|
|
489
|
+
</React.StrictMode>
|
|
490
|
+
);
|
|
491
|
+
`;
|
|
492
|
+
|
|
493
|
+
fs.writeFileSync(path.join(pluginDir, 'src/main.tsx'), mainTsx);
|
|
494
|
+
|
|
495
|
+
// Create basic SidebarLink component
|
|
496
|
+
const sidebarLink = `import { Link } from '@modern-js/runtime/router';
|
|
497
|
+
|
|
498
|
+
export function SidebarLink() {
|
|
499
|
+
return (
|
|
500
|
+
<Link
|
|
501
|
+
to="/${pluginName.replace('plugin-', '')}"
|
|
502
|
+
className="flex items-center gap-2 px-3 py-2 text-sm rounded-md hover:bg-accent hover:text-accent-foreground"
|
|
503
|
+
>
|
|
504
|
+
<span>${pluginPascal}</span>
|
|
505
|
+
</Link>
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
`;
|
|
509
|
+
|
|
510
|
+
fs.writeFileSync(path.join(pluginDir, 'src/components/SidebarLink.tsx'), sidebarLink);
|
|
511
|
+
|
|
512
|
+
// Create HTML template
|
|
513
|
+
const html = `<!DOCTYPE html>
|
|
514
|
+
<html lang="en">
|
|
515
|
+
<head>
|
|
516
|
+
<meta charset="UTF-8" />
|
|
517
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
518
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
519
|
+
<title>${pluginPascal}</title>
|
|
520
|
+
</head>
|
|
521
|
+
<body>
|
|
522
|
+
<div id="root"></div>
|
|
523
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
524
|
+
</body>
|
|
525
|
+
</html>
|
|
526
|
+
`;
|
|
527
|
+
|
|
528
|
+
fs.writeFileSync(path.join(pluginDir, 'src/index.html'), html);
|
|
529
|
+
|
|
530
|
+
console.log(`โ
Plugin created: packages/plugins/@lego/${pluginName}`);
|
|
531
|
+
console.log(``);
|
|
532
|
+
console.log(`Next steps:`);
|
|
533
|
+
console.log(` 1. cd packages/plugins/@lego/${pluginName}`);
|
|
534
|
+
console.log(` 2. pnpm install`);
|
|
535
|
+
console.log(` 3. pnpm run dev`);
|
|
536
|
+
`;
|
|
537
|
+
|
|
538
|
+
// Update root tsconfig.json to include new plugin
|
|
539
|
+
const rootTsconfig = JSON.parse(fs.readFileSync('tsconfig.json', 'utf8'));
|
|
540
|
+
rootTsconfig.references.push({
|
|
541
|
+
path: `./packages/plugins/@lego/${pluginName}`,
|
|
542
|
+
});
|
|
543
|
+
fs.writeFileSync('tsconfig.json', JSON.stringify(rootTsconfig, null, 2));
|
|
544
|
+
|
|
545
|
+
// Update pnpm-workspace.yaml
|
|
546
|
+
const workspaceContent = fs.readFileSync('pnpm-workspace.yaml', 'utf8');
|
|
547
|
+
if (!workspaceContent.includes(pluginName)) {
|
|
548
|
+
const newWorkspace = workspaceContent.replace(
|
|
549
|
+
/(packages:\/\s*@lego\/\*)/,
|
|
550
|
+
`$1\n - '${pluginName}'`
|
|
551
|
+
);
|
|
552
|
+
fs.writeFileSync('pnpm-workspace.yaml', newWorkspace);
|
|
553
|
+
}
|
|
554
|
+
`;
|
|
555
|
+
|
|
556
|
+
**Step 2: Make script executable**
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
chmod +x scripts/create-plugin.js
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
**Step 3: Commit**
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
git add scripts/
|
|
566
|
+
git commit -m "chore: add plugin scaffolding CLI script"
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Task 6: Create Environment Configuration Structure
|
|
572
|
+
|
|
573
|
+
**Files:**
|
|
574
|
+
- Create: `.env.development`
|
|
575
|
+
- Create: `.env.production`
|
|
576
|
+
- Create: `.env.example`
|
|
577
|
+
- Create: `src/env/env.schema.ts`
|
|
578
|
+
|
|
579
|
+
**Step 1: Create development environment**
|
|
580
|
+
|
|
581
|
+
Create `.env.development`:
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
# App
|
|
585
|
+
VITE_APP_NAME=Lego-One
|
|
586
|
+
VITE_APP_URL=http://localhost:8080
|
|
587
|
+
|
|
588
|
+
# PocketBase
|
|
589
|
+
VITE_POCKETBASE_URL=http://localhost:8090
|
|
590
|
+
VITE_POCKETBASE_ADMIN_EMAIL=admin@lego-one.dev
|
|
591
|
+
VITE_POCKETBASE_ADMIN_PASSWORD=admin123456
|
|
592
|
+
|
|
593
|
+
# Seed Admin (for development)
|
|
594
|
+
VITE_SEED_ADMIN_EMAIL=admin@lego-one.dev
|
|
595
|
+
VITE_SEED_ADMIN_PASSWORD=admin123456
|
|
596
|
+
VITE_SEED_ADMIN_NAME=Super Admin
|
|
597
|
+
VITE_SEED_ORG_NAME=Demo Organization
|
|
598
|
+
|
|
599
|
+
# Plugin Ports
|
|
600
|
+
VITE_DEV_HOST_PORT=8080
|
|
601
|
+
VITE_DEV_DASHBOARD_PORT=3001
|
|
602
|
+
VITE_DEV_TODO_PORT=3002
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**Step 2: Create production environment**
|
|
606
|
+
|
|
607
|
+
Create `.env.production`:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
# App
|
|
611
|
+
VITE_APP_NAME=Lego-One
|
|
612
|
+
VITE_APP_URL=https://your-domain.com
|
|
613
|
+
|
|
614
|
+
# PocketBase (configure for production)
|
|
615
|
+
VITE_POCKETBASE_URL=https://your-pocketbase-url.com
|
|
616
|
+
VITE_POCKETBASE_ADMIN_EMAIL=
|
|
617
|
+
VITE_POCKETBASE_ADMIN_PASSWORD=
|
|
618
|
+
|
|
619
|
+
# No seed admin in production
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Step 3: Create environment example**
|
|
623
|
+
|
|
624
|
+
Create `.env.example`:
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
# Copy this file to .env.development or .env.production
|
|
628
|
+
|
|
629
|
+
# App Configuration
|
|
630
|
+
VITE_APP_NAME=Lego-One
|
|
631
|
+
VITE_APP_URL=http://localhost:8080
|
|
632
|
+
|
|
633
|
+
# PocketBase Configuration
|
|
634
|
+
VITE_POCKETBASE_URL=http://localhost:8090
|
|
635
|
+
VITE_POCKETBASE_ADMIN_EMAIL=admin@example.com
|
|
636
|
+
VITE_POCKETBASE_ADMIN_PASSWORD=change-me
|
|
637
|
+
|
|
638
|
+
# Seed Admin (development only)
|
|
639
|
+
VITE_SEED_ADMIN_EMAIL=admin@lego-one.dev
|
|
640
|
+
VITE_SEED_ADMIN_PASSWORD=admin123456
|
|
641
|
+
VITE_SEED_ADMIN_NAME=Super Admin
|
|
642
|
+
VITE_SEED_ORG_NAME=Demo Organization
|
|
643
|
+
|
|
644
|
+
# Plugin Development Ports
|
|
645
|
+
VITE_DEV_HOST_PORT=8080
|
|
646
|
+
VITE_DEV_DASHBOARD_PORT=3001
|
|
647
|
+
VITE_DEV_TODO_PORT=3002
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**Step 4: Create zod validation schema**
|
|
651
|
+
|
|
652
|
+
Create `src/env/env.schema.ts`:
|
|
653
|
+
|
|
654
|
+
```typescript
|
|
655
|
+
import { z } from 'zod';
|
|
656
|
+
|
|
657
|
+
const envSchema = z.object({
|
|
658
|
+
// App
|
|
659
|
+
VITE_APP_NAME: z.string().default('Lego-One'),
|
|
660
|
+
VITE_APP_URL: z.string().url(),
|
|
661
|
+
|
|
662
|
+
// PocketBase
|
|
663
|
+
VITE_POCKETBASE_URL: z.string().url(),
|
|
664
|
+
VITE_POCKETBASE_ADMIN_EMAIL: z.string().email(),
|
|
665
|
+
VITE_POCKETBASE_ADMIN_PASSWORD: z.string().min(8),
|
|
666
|
+
|
|
667
|
+
// Seed Admin (development only)
|
|
668
|
+
VITE_SEED_ADMIN_EMAIL: z.string().email().optional(),
|
|
669
|
+
VITE_SEED_ADMIN_PASSWORD: z.string().min(8).optional(),
|
|
670
|
+
VITE_SEED_ADMIN_NAME: z.string().optional(),
|
|
671
|
+
VITE_SEED_ORG_NAME: z.string().optional(),
|
|
672
|
+
|
|
673
|
+
// Plugin Ports
|
|
674
|
+
VITE_DEV_HOST_PORT: z.string().transform(Number).default(8080),
|
|
675
|
+
VITE_DEV_DASHBOARD_PORT: z.string().transform(Number).default(3001),
|
|
676
|
+
VITE_DEV_TODO_PORT: z.string().transform(Number).default(3002),
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
export type Env = z.infer<typeof envSchema>;
|
|
680
|
+
|
|
681
|
+
export function validateEnv(): Env {
|
|
682
|
+
const env = {
|
|
683
|
+
VITE_APP_NAME: import.meta.env.VITE_APP_NAME,
|
|
684
|
+
VITE_APP_URL: import.meta.env.VITE_APP_URL,
|
|
685
|
+
VITE_POCKETBASE_URL: import.meta.env.VITE_POCKETBASE_URL,
|
|
686
|
+
VITE_POCKETBASE_ADMIN_EMAIL: import.meta.env.VITE_POCKETBASE_ADMIN_EMAIL,
|
|
687
|
+
VITE_POCKETBASE_ADMIN_PASSWORD: import.meta.env.VITE_POCKETBASE_ADMIN_PASSWORD,
|
|
688
|
+
VITE_SEED_ADMIN_EMAIL: import.meta.env.VITE_SEED_ADMIN_EMAIL,
|
|
689
|
+
VITE_SEED_ADMIN_PASSWORD: import.meta.env.VITE_SEED_ADMIN_PASSWORD,
|
|
690
|
+
VITE_SEED_ADMIN_NAME: import.meta.env.VITE_SEED_ADMIN_NAME,
|
|
691
|
+
VITE_SEED_ORG_NAME: import.meta.env.VITE_SEED_ORG_NAME,
|
|
692
|
+
VITE_DEV_HOST_PORT: import.meta.env.VITE_DEV_HOST_PORT,
|
|
693
|
+
VITE_DEV_DASHBOARD_PORT: import.meta.env.VITE_DEV_DASHBOARD_PORT,
|
|
694
|
+
VITE_DEV_TODO_PORT: import.meta.env.VITE_DEV_TODO_PORT,
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
const result = envSchema.safeParse(env);
|
|
698
|
+
|
|
699
|
+
if (!result.success) {
|
|
700
|
+
const errors = result.error.flatten().fieldErrors;
|
|
701
|
+
console.error('โ Invalid environment variables:');
|
|
702
|
+
for (const [field, error] of Object.entries(errors)) {
|
|
703
|
+
console.error(` ${field}: ${error?.join(', ')}`);
|
|
704
|
+
}
|
|
705
|
+
throw new Error('Environment validation failed');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return result.data;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export const env = validateEnv();
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
**Step 5: Commit**
|
|
715
|
+
|
|
716
|
+
```bash
|
|
717
|
+
git add .env* src/env/
|
|
718
|
+
git commit -m "chore: add environment configuration with zod validation"
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## Task 7: Create README
|
|
724
|
+
|
|
725
|
+
**Files:**
|
|
726
|
+
- Create: `README.md`
|
|
727
|
+
|
|
728
|
+
**Step 1: Create README**
|
|
729
|
+
|
|
730
|
+
Create `README.md`:
|
|
731
|
+
|
|
732
|
+
```markdown
|
|
733
|
+
# Lego-One
|
|
734
|
+
|
|
735
|
+
> **Microkernel SaaS OS** - A modular boilerplate for building SaaS applications with Modern.js, Garfish, and PocketBase.
|
|
736
|
+
|
|
737
|
+
## Features
|
|
738
|
+
|
|
739
|
+
- ๐๏ธ **Microkernel Architecture** - Host (Kernel) + Plugins (Features)
|
|
740
|
+
- ๐ **Multi-Tenancy** - Multiple organizations with RBAC
|
|
741
|
+
- ๐ **Plugin System** - Enable/disable plugins via config or UI
|
|
742
|
+
- ๐จ **Modern UI** - Tailwind CSS + Shadcn components
|
|
743
|
+
- โก **Performance** - Rspack bundler, code-splitting, lazy loading
|
|
744
|
+
- ๐งช **Tested** - Vitest + Playwright
|
|
745
|
+
- ๐ **Type-Safe** - TypeScript strict mode
|
|
746
|
+
|
|
747
|
+
## Quick Start
|
|
748
|
+
|
|
749
|
+
### Prerequisites
|
|
750
|
+
|
|
751
|
+
- Node.js >= 18.0.0
|
|
752
|
+
- pnpm >= 8.0.0
|
|
753
|
+
|
|
754
|
+
### Installation
|
|
755
|
+
|
|
756
|
+
```bash
|
|
757
|
+
# Install dependencies
|
|
758
|
+
pnpm install
|
|
759
|
+
|
|
760
|
+
# Start development (host + all plugins)
|
|
761
|
+
pnpm run dev:all
|
|
762
|
+
|
|
763
|
+
# Or start individually
|
|
764
|
+
pnpm run dev:host # :8080
|
|
765
|
+
pnpm run dev:plugins # All plugins
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### First Run
|
|
769
|
+
|
|
770
|
+
1. PocketBase will auto-initialize with seed admin
|
|
771
|
+
2. Login with: `admin@lego-one.dev` / `admin123456`
|
|
772
|
+
3. Explore the Dashboard and Todo plugins
|
|
773
|
+
|
|
774
|
+
## Project Structure
|
|
775
|
+
|
|
776
|
+
```
|
|
777
|
+
lego-one/
|
|
778
|
+
โโโ host/ # Kernel application
|
|
779
|
+
โโโ packages/
|
|
780
|
+
โ โโโ plugins/
|
|
781
|
+
โ โโโ @lego/
|
|
782
|
+
โ โโโ plugin-dashboard
|
|
783
|
+
โ โโโ plugin-todo
|
|
784
|
+
โโโ docs/
|
|
785
|
+
โ โโโ research/ # Architecture research
|
|
786
|
+
โ โโโ plans/ # Implementation plans
|
|
787
|
+
โ โโโ setup/ # User guides
|
|
788
|
+
โโโ pocketbase/ # Backend data
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
## Documentation
|
|
792
|
+
|
|
793
|
+
- [Implementation Plans](./docs/plans/00-index.md) - Complete implementation guide
|
|
794
|
+
- [Setup Guides](./docs/setup/) - How to create plugins, configure, etc.
|
|
795
|
+
- [Research](./docs/research/) - Architecture research documents
|
|
796
|
+
|
|
797
|
+
## License
|
|
798
|
+
|
|
799
|
+
MIT
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
**Step 2: Commit**
|
|
803
|
+
|
|
804
|
+
```bash
|
|
805
|
+
git add README.md
|
|
806
|
+
git commit -m "docs: add project README"
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
## Verification
|
|
812
|
+
|
|
813
|
+
After completing all tasks:
|
|
814
|
+
|
|
815
|
+
**Step 1: Verify file structure**
|
|
816
|
+
|
|
817
|
+
Run:
|
|
818
|
+
```bash
|
|
819
|
+
ls -la
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
Expected: `package.json`, `pnpm-workspace.yaml`, `.npmrc`, `.gitignore`, `tsconfig*`, `.eslintrc*`, `.prettierrc*`, `.env*`, `scripts/`, `src/env/`, `README.md`
|
|
823
|
+
|
|
824
|
+
**Step 2: Verify dependencies install**
|
|
825
|
+
|
|
826
|
+
Run:
|
|
827
|
+
```bash
|
|
828
|
+
pnpm install
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
Expected: No errors, `node_modules` created.
|
|
832
|
+
|
|
833
|
+
**Step 3: Verify TypeScript**
|
|
834
|
+
|
|
835
|
+
Run:
|
|
836
|
+
```bash
|
|
837
|
+
pnpm typecheck
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
Expected: No errors (some "cannot find module" is OK since we haven't created workspaces yet).
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
## Summary
|
|
845
|
+
|
|
846
|
+
After completing this document, you have:
|
|
847
|
+
|
|
848
|
+
โ
Monorepo structure with pnpm workspaces
|
|
849
|
+
โ
TypeScript configuration (root + base)
|
|
850
|
+
โ
ESLint + Prettier configured
|
|
851
|
+
โ
Environment variables with zod validation
|
|
852
|
+
โ
Plugin scaffolding CLI script
|
|
853
|
+
โ
README documentation
|
|
854
|
+
|
|
855
|
+
**Next:** โ [`02-pocketbase-setup.md`](./02-pocketbase-setup.md)
|