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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lego-box",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Scaffold a new Lego Box microfrontend app with ui-kit and pilets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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. Menu label = pilet name when scaffolded with create:pilet.
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
- ## Permissions
100
+ ## Creating Custom UI-Kit Components
62
101
 
63
- 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.
102
+ Your `packages/ui-kit` extends the base UI-Kit from npm. Add your own components:
64
103
 
65
- ## Creating Custom UI-Kit Components
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
- 1. Create `packages/ui-kit/src/components/my-component.tsx`
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
- Use `cn()` from `../lib/utils` for className merging. Follow existing component patterns.
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-multi-pilet.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"},"devDependencies":{"@lego-box/shell":"^1.0.0","create-pilet":"workspace:*","concurrently":"^8.2.2","cross-env":"^10.1.0","dotenv":"^17.2.3","ts-node":"^10.9.2"}}
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.');