create-lego-box 0.1.1 → 0.1.2
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/README.md +21 -1
- package/package.json +1 -1
- package/templates/root/README.md +71 -8
- package/templates/root/package.json +1 -1
- package/templates/scripts/update-scaffold.js +136 -0
package/README.md
CHANGED
|
@@ -35,8 +35,10 @@ pnpm create lego-box my-awesome-app
|
|
|
35
35
|
tsconfig.base.json
|
|
36
36
|
.env.example
|
|
37
37
|
scripts/
|
|
38
|
+
dev-all-pilets.js
|
|
38
39
|
dev-multi-pilet.js
|
|
39
40
|
discover-pilets.js
|
|
41
|
+
update-scaffold.js
|
|
40
42
|
run-shell.js
|
|
41
43
|
packages/
|
|
42
44
|
ui-kit/ # Extensible – re-exports @lego-box/ui-kit, add custom components here
|
|
@@ -56,10 +58,28 @@ pnpm create lego-box my-awesome-app
|
|
|
56
58
|
1. `cd my-app`
|
|
57
59
|
2. `pnpm install` (if not run automatically)
|
|
58
60
|
3. Start PocketBase: `pb serve` (or your PocketBase setup)
|
|
59
|
-
4. `pnpm dev` – runs shell + ui-kit + pilets
|
|
61
|
+
4. `pnpm dev` – runs shell + ui-kit + all pilets
|
|
60
62
|
5. Add pilets: `pnpm create:pilet`
|
|
61
63
|
6. Extend ui-kit: edit `packages/ui-kit/src/components/`
|
|
62
64
|
|
|
65
|
+
## Updating your scaffolded app
|
|
66
|
+
|
|
67
|
+
To get the latest shell, ui-kit, and scaffold template files (README, scripts, configs):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm update
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or step by step:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm update @lego-box/shell@latest @lego-box/ui-kit@latest
|
|
77
|
+
pnpm update create-lego-box@latest
|
|
78
|
+
pnpm update:scaffold
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Note:** `update:scaffold` pulls latest template files but **does not modify your pilets** or custom components in `packages/ui-kit/src/components/`.
|
|
82
|
+
|
|
63
83
|
## Publishing
|
|
64
84
|
|
|
65
85
|
To publish to npm for `pnpm create lego-box`:
|
package/package.json
CHANGED
package/templates/root/README.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
This README is written for AI assistants to help developers build microfrontends with Lego Box. Use this as context when implementing features via vibe coding.
|
|
4
4
|
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Then open http://localhost:1234. Start PocketBase (`pb serve`) for full functionality.
|
|
13
|
+
|
|
14
|
+
## Updating Dependencies
|
|
15
|
+
|
|
16
|
+
To get the latest shell, ui-kit, and scaffold files (scripts, README, root config):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm update @lego-box/shell@latest @lego-box/ui-kit@latest
|
|
20
|
+
pnpm update create-lego-box@latest
|
|
21
|
+
pnpm update:scaffold
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or use the combined command:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm update
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Note:** `update:scaffold` pulls the latest template files (README, scripts, packages/create-pilet, packages/ui-kit config) but **does not modify your pilets** — you own those. Your custom components in `packages/ui-kit/src/components/` are preserved.
|
|
31
|
+
|
|
5
32
|
## Architecture
|
|
6
33
|
|
|
7
34
|
- **Shell** (@lego-box/shell): Piral microfrontend shell. Provides auth, PocketBase, RBAC, routing.
|
|
@@ -45,9 +72,10 @@ context.registerProtectedPage('/my-page', MyPage, { action: 'read', subject: 'us
|
|
|
45
72
|
```
|
|
46
73
|
|
|
47
74
|
### registerSidebarMenu(menuItem)
|
|
48
|
-
Register a menu item in the sidebar.
|
|
75
|
+
Register a menu item in the sidebar. Supports single items and multilevel (nested) menus.
|
|
49
76
|
|
|
50
77
|
```ts
|
|
78
|
+
// Single menu item
|
|
51
79
|
context.registerSidebarMenu({
|
|
52
80
|
id: 'my-pilet',
|
|
53
81
|
label: 'My Pilet',
|
|
@@ -56,19 +84,51 @@ context.registerSidebarMenu({
|
|
|
56
84
|
subject: 'demo',
|
|
57
85
|
icon: <MyIcon />
|
|
58
86
|
});
|
|
87
|
+
|
|
88
|
+
// Multilevel menu (parent with children)
|
|
89
|
+
context.registerSidebarMenu({
|
|
90
|
+
id: 'parent-menu',
|
|
91
|
+
label: 'Parent Menu',
|
|
92
|
+
icon: <FolderIcon />,
|
|
93
|
+
children: [
|
|
94
|
+
{ id: 'child-1', label: 'Child 1', href: '/child-1' },
|
|
95
|
+
{ id: 'child-2', label: 'Child 2', href: '/child-2', action: 'read', subject: 'reports' }
|
|
96
|
+
]
|
|
97
|
+
});
|
|
59
98
|
```
|
|
60
99
|
|
|
61
|
-
##
|
|
100
|
+
## Creating Custom UI-Kit Components
|
|
62
101
|
|
|
63
|
-
|
|
102
|
+
Your `packages/ui-kit` extends the base UI-Kit from npm. Add your own components:
|
|
64
103
|
|
|
65
|
-
|
|
104
|
+
1. **Create** `packages/ui-kit/src/components/my-component.tsx`:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import * as React from 'react';
|
|
108
|
+
import { cn } from '@lego-box/ui-kit-base';
|
|
109
|
+
|
|
110
|
+
export function MyCustomCard({ className, children }: { className?: string; children?: React.ReactNode }) {
|
|
111
|
+
return <div className={cn('rounded-lg border p-4', className)}>{children}</div>;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
2. **Export** from `packages/ui-kit/src/components/index.ts`:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
export { MyCustomCard } from './my-component';
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
3. **Use** in pilets:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { MyCustomCard } from '@lego-box/ui-kit';
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use `cn()` from `@lego-box/ui-kit-base` for className merging. Follow existing component patterns.
|
|
66
128
|
|
|
67
|
-
|
|
68
|
-
2. Export from `packages/ui-kit/src/index.ts`
|
|
69
|
-
3. Use in pilets: `import { MyComponent } from '@lego-box/ui-kit';`
|
|
129
|
+
## Permissions
|
|
70
130
|
|
|
71
|
-
|
|
131
|
+
Format: `action:collection` (e.g. `read:users`, `create:tickets`). Permissions are assigned to roles. Use `auth.can('read', 'users')` or `casl.can('read', 'users')` to check.
|
|
72
132
|
|
|
73
133
|
## Migrations
|
|
74
134
|
|
|
@@ -113,10 +173,13 @@ Copy `.env.example` to `.env`:
|
|
|
113
173
|
|
|
114
174
|
## Commands
|
|
115
175
|
|
|
176
|
+
- `pnpm install` – Install dependencies
|
|
116
177
|
- `pnpm dev` – Start UI-Kit, shell, all pilets. Migrations run automatically.
|
|
117
178
|
- `pnpm migrate` – Run migrations manually
|
|
118
179
|
- `pnpm create:pilet` – Scaffold new pilet in `pilets/`
|
|
119
180
|
- `pnpm build` – Build ui-kit and shell
|
|
181
|
+
- `pnpm update` – Update shell, ui-kit, and scaffold template files
|
|
182
|
+
- `pnpm update:scaffold` – Pull latest scaffold files (scripts, README, configs) without touching pilets
|
|
120
183
|
|
|
121
184
|
## Feeds
|
|
122
185
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"{{APP_NAME}}","version":"1.0.0","private":true,"scripts":{"predev":"node scripts/run-migrations.js","dev":"node scripts/dev-
|
|
1
|
+
{"name":"{{APP_NAME}}","version":"1.0.0","private":true,"scripts":{"predev":"node scripts/run-migrations.js","dev":"node scripts/dev-all-pilets.js","predev:all":"node scripts/run-migrations.js","dev:all":"node scripts/dev-all-pilets.js","dev:discover":"node scripts/discover-pilets.js","create:pilet":"node packages/create-pilet/index.js","build":"pnpm --filter @lego-box/ui-kit build","migrate":"node scripts/run-migrations.js","postinstall":"node scripts/copy-tsconfig-base.js","update:scaffold":"node scripts/update-scaffold.js","update":"pnpm update @lego-box/shell@latest @lego-box/ui-kit@latest && pnpm update create-lego-box@latest && pnpm update:scaffold"},"devDependencies":{"@lego-box/shell":"^1.0.0","create-lego-box":"^0.1.1","create-pilet":"workspace:*","concurrently":"^8.2.2","cross-env":"^10.1.0","dotenv":"^17.2.3","ts-node":"^10.9.2"}}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* update-scaffold - Pull latest scaffold template files into your project
|
|
4
|
+
*
|
|
5
|
+
* Copies templates from node_modules/create-lego-box, EXCLUDING pilets
|
|
6
|
+
* (you own your pilets). Preserves packages/ui-kit/src/components/
|
|
7
|
+
* (your custom components).
|
|
8
|
+
*
|
|
9
|
+
* Run after: pnpm update create-lego-box@latest
|
|
10
|
+
* Or as part of: pnpm update
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
17
|
+
const CREATE_LEGO_BOX = path.resolve(ROOT_DIR, 'node_modules/create-lego-box');
|
|
18
|
+
const TEMPLATES = path.join(CREATE_LEGO_BOX, 'templates');
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(CREATE_LEGO_BOX) || !fs.existsSync(TEMPLATES)) {
|
|
21
|
+
console.error('create-lego-box not found. Run: pnpm add -D create-lego-box@latest');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log('Updating scaffold files (preserving pilets and custom components)...\n');
|
|
26
|
+
|
|
27
|
+
// Paths to exclude: pilets, and packages/ui-kit/src/components (user's custom components)
|
|
28
|
+
function shouldExclude(relPath) {
|
|
29
|
+
if (relPath.startsWith('pilets' + path.sep) || relPath === 'pilets') return true;
|
|
30
|
+
if (relPath.includes('packages' + path.sep + 'ui-kit' + path.sep + 'src' + path.sep + 'components')) return true;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function copyRecursive(src, dest, base = '') {
|
|
35
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
const rel = base ? `${base}/${entry.name}` : entry.name;
|
|
38
|
+
if (shouldExclude(rel)) continue;
|
|
39
|
+
|
|
40
|
+
const srcPath = path.join(src, entry.name);
|
|
41
|
+
const destPath = path.join(dest, entry.name);
|
|
42
|
+
|
|
43
|
+
if (entry.isDirectory()) {
|
|
44
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
45
|
+
copyRecursive(srcPath, destPath, rel);
|
|
46
|
+
} else {
|
|
47
|
+
fs.copyFileSync(srcPath, destPath);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Copy root files (preserve app name in package.json)
|
|
53
|
+
const rootFiles = ['package.json', 'pnpm-workspace.yaml', 'tsconfig.base.json', 'env.example', 'README.md'];
|
|
54
|
+
for (const file of rootFiles) {
|
|
55
|
+
const srcPath = path.join(TEMPLATES, 'root', file);
|
|
56
|
+
if (fs.existsSync(srcPath)) {
|
|
57
|
+
const destPath = path.join(ROOT_DIR, file === 'env.example' ? '.env.example' : file);
|
|
58
|
+
if (file === 'package.json') {
|
|
59
|
+
// Preserve user's app name and merge devDependencies (template base + user extras)
|
|
60
|
+
const current = JSON.parse(fs.readFileSync(destPath, 'utf-8'));
|
|
61
|
+
const template = JSON.parse(fs.readFileSync(srcPath, 'utf-8'));
|
|
62
|
+
const merged = {
|
|
63
|
+
...template,
|
|
64
|
+
name: current.name,
|
|
65
|
+
devDependencies: { ...template.devDependencies, ...current.devDependencies },
|
|
66
|
+
};
|
|
67
|
+
fs.writeFileSync(destPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
68
|
+
} else {
|
|
69
|
+
fs.copyFileSync(srcPath, destPath);
|
|
70
|
+
}
|
|
71
|
+
console.log(' Updated:', (destPath.replace(ROOT_DIR, '') || file).replace(/^\/+/, ''));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Copy scripts
|
|
76
|
+
const scriptsSrc = path.join(TEMPLATES, 'scripts');
|
|
77
|
+
const scriptsDest = path.join(ROOT_DIR, 'scripts');
|
|
78
|
+
if (fs.existsSync(scriptsSrc)) {
|
|
79
|
+
fs.mkdirSync(scriptsDest, { recursive: true });
|
|
80
|
+
const files = fs.readdirSync(scriptsSrc);
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
fs.copyFileSync(path.join(scriptsSrc, file), path.join(scriptsDest, file));
|
|
83
|
+
console.log(' Updated: scripts/' + file);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Copy packages/create-pilet
|
|
88
|
+
const createPiletSrc = path.join(TEMPLATES, 'packages', 'create-pilet');
|
|
89
|
+
const createPiletDest = path.join(ROOT_DIR, 'packages', 'create-pilet');
|
|
90
|
+
if (fs.existsSync(createPiletSrc)) {
|
|
91
|
+
fs.rmSync(createPiletDest, { recursive: true, force: true });
|
|
92
|
+
fs.mkdirSync(path.dirname(createPiletDest), { recursive: true });
|
|
93
|
+
copyRecursive(createPiletSrc, createPiletDest, 'packages/create-pilet');
|
|
94
|
+
console.log(' Updated: packages/create-pilet');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Copy packages/ui-kit (excluding src/components)
|
|
98
|
+
const uiKitSrc = path.join(TEMPLATES, 'packages', 'ui-kit');
|
|
99
|
+
const uiKitDest = path.join(ROOT_DIR, 'packages', 'ui-kit');
|
|
100
|
+
if (fs.existsSync(uiKitSrc)) {
|
|
101
|
+
const uiKitComponentsDest = path.join(uiKitDest, 'src', 'components');
|
|
102
|
+
const preserveComponents = fs.existsSync(uiKitComponentsDest);
|
|
103
|
+
if (preserveComponents) {
|
|
104
|
+
const componentsBackup = path.join(ROOT_DIR, '.tmp-ui-kit-components');
|
|
105
|
+
fs.rmSync(componentsBackup, { recursive: true, force: true });
|
|
106
|
+
fs.cpSync(uiKitComponentsDest, componentsBackup, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
fs.rmSync(uiKitDest, { recursive: true, force: true });
|
|
109
|
+
fs.mkdirSync(path.dirname(uiKitDest), { recursive: true });
|
|
110
|
+
copyRecursive(uiKitSrc, uiKitDest, 'packages/ui-kit');
|
|
111
|
+
if (preserveComponents) {
|
|
112
|
+
const componentsBackup = path.join(ROOT_DIR, '.tmp-ui-kit-components');
|
|
113
|
+
fs.rmSync(path.join(uiKitDest, 'src', 'components'), { recursive: true, force: true });
|
|
114
|
+
fs.mkdirSync(path.dirname(path.join(uiKitDest, 'src', 'components')), { recursive: true });
|
|
115
|
+
fs.cpSync(componentsBackup, path.join(uiKitDest, 'src', 'components'), { recursive: true });
|
|
116
|
+
fs.rmSync(componentsBackup, { recursive: true, force: true });
|
|
117
|
+
console.log(' Updated: packages/ui-kit (preserved your custom components)');
|
|
118
|
+
} else {
|
|
119
|
+
console.log(' Updated: packages/ui-kit');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Copy migrations
|
|
124
|
+
const migrationsSrc = path.join(TEMPLATES, 'migrations');
|
|
125
|
+
const migrationsDest = path.join(ROOT_DIR, 'migrations');
|
|
126
|
+
if (fs.existsSync(migrationsSrc)) {
|
|
127
|
+
fs.mkdirSync(migrationsDest, { recursive: true });
|
|
128
|
+
const files = fs.readdirSync(migrationsSrc);
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
fs.copyFileSync(path.join(migrationsSrc, file), path.join(migrationsDest, file));
|
|
131
|
+
}
|
|
132
|
+
console.log(' Updated: migrations/');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log('\nScaffold updated. Run pnpm install if package.json changed.');
|
|
136
|
+
console.log('Your pilets were not modified.');
|