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,615 @@
|
|
|
1
|
+
# CLI Strategy: Project Scaffolding
|
|
2
|
+
|
|
3
|
+
**Project:** Lego-One (Modern.js SaaS OS)
|
|
4
|
+
**Document:** 06 - CLI Strategy
|
|
5
|
+
**Status:** Research Phase
|
|
6
|
+
|
|
7
|
+
## Executive Summary
|
|
8
|
+
|
|
9
|
+
This document explains how to implement the `pnpm create lego-one` command that allows developers to scaffold a new Lego-One project. Since we're using the **Git Template** approach, the CLI will be a lightweight wrapper around git operations and project configuration.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. CLI Overview
|
|
14
|
+
|
|
15
|
+
### 1.1 Command Structure
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Create new project from template
|
|
19
|
+
pnpm create lego-one my-saas-app
|
|
20
|
+
|
|
21
|
+
# With interactive prompts
|
|
22
|
+
pnpm create lego-one my-saas-app --interactive
|
|
23
|
+
|
|
24
|
+
# Skip git initialization
|
|
25
|
+
pnpm create lego-one my-saas-app --no-git
|
|
26
|
+
|
|
27
|
+
# Use specific branch
|
|
28
|
+
pnpm create lego-one my-saas-app --branch v2.0.0
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 1.2 What the CLI Does
|
|
32
|
+
|
|
33
|
+
1. **Clone Template:** Fetches lego-one from GitHub
|
|
34
|
+
2. **Configure:** Updates package.json, env vars
|
|
35
|
+
3. **Install:** Runs `pnpm install`
|
|
36
|
+
4. **Initialize Git:** Creates initial commit
|
|
37
|
+
5. **Configure Plugins:** Prompts for which plugins to enable
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Implementation Options
|
|
42
|
+
|
|
43
|
+
### 2.1 Option A: npm create (Recommended)
|
|
44
|
+
|
|
45
|
+
Uses npm's built-in `create-*` command pattern.
|
|
46
|
+
|
|
47
|
+
**Entry Point:** `packages/create-lego-one/package.json`
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"name": "create-lego-one",
|
|
52
|
+
"version": "1.0.0",
|
|
53
|
+
"bin": "./dist/index.js",
|
|
54
|
+
"files": ["dist"],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsup src/index.ts --format cjs --clean",
|
|
57
|
+
"dev": "tsup src/index.ts --format cjs --watch"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"prompts": "^2.4.2",
|
|
61
|
+
"commander": "^12.0.0",
|
|
62
|
+
"chalk": "^5.3.0",
|
|
63
|
+
"ora": "^8.0.1",
|
|
64
|
+
"fs-extra": "^11.2.0",
|
|
65
|
+
"execa": "^8.0.1"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2.2 Option B: Global CLI
|
|
71
|
+
|
|
72
|
+
Install globally and run as a command:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pnpm add -g @lego/cli
|
|
76
|
+
lego-one create my-saas-app
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2.3 Option C: npx (Simplest)
|
|
80
|
+
|
|
81
|
+
No package needed - uses a template repo directly:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx create-lego-one@latest my-saas-app
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Our Choice:** Option A (npm create) - standard pattern, good DX.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. CLI Implementation
|
|
92
|
+
|
|
93
|
+
### 3.1 Main Entry Point
|
|
94
|
+
|
|
95
|
+
**File:** `packages/create-lego-one/src/index.ts`
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
#!/usr/bin/env node
|
|
99
|
+
|
|
100
|
+
import { Command } from 'commander';
|
|
101
|
+
import chalk from 'chalk';
|
|
102
|
+
import { createProject } from './commands/create';
|
|
103
|
+
import { addPlugin } from './commands/add-plugin';
|
|
104
|
+
|
|
105
|
+
const program = new Command();
|
|
106
|
+
|
|
107
|
+
program
|
|
108
|
+
.name('create-lego-one')
|
|
109
|
+
.description('Create a new Lego-One SaaS application')
|
|
110
|
+
.version('1.0.0');
|
|
111
|
+
|
|
112
|
+
program
|
|
113
|
+
.argument('[project-name]', 'Name of the project')
|
|
114
|
+
.option('-i, --interactive', 'Use interactive mode')
|
|
115
|
+
.option('--no-git', 'Skip git initialization')
|
|
116
|
+
.option('--branch <branch>', 'Template branch to use', 'main')
|
|
117
|
+
.action(async (projectName, options) => {
|
|
118
|
+
try {
|
|
119
|
+
await createProject(projectName, options);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(chalk.red('Error:'), error.message);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
program
|
|
127
|
+
.command('add-plugin <plugin-name>')
|
|
128
|
+
.description('Add a plugin to the current project')
|
|
129
|
+
.action(addPlugin);
|
|
130
|
+
|
|
131
|
+
program.parse();
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3.2 Create Project Command
|
|
135
|
+
|
|
136
|
+
**File:** `packages/create-lego-one/src/commands/create.ts`
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import path from 'path';
|
|
140
|
+
import fs from 'fs-extra';
|
|
141
|
+
import chalk from 'chalk';
|
|
142
|
+
import ora from 'ora';
|
|
143
|
+
import prompts from 'prompts';
|
|
144
|
+
import { execaCommand } from 'execa';
|
|
145
|
+
|
|
146
|
+
interface CreateOptions {
|
|
147
|
+
interactive?: boolean;
|
|
148
|
+
git?: boolean;
|
|
149
|
+
branch?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function createProject(
|
|
153
|
+
projectName: string | undefined,
|
|
154
|
+
options: CreateOptions
|
|
155
|
+
) {
|
|
156
|
+
const spinner = ora();
|
|
157
|
+
|
|
158
|
+
// Interactive mode or get project name
|
|
159
|
+
let name = projectName;
|
|
160
|
+
let targetDir = process.cwd();
|
|
161
|
+
|
|
162
|
+
if (options.interactive || !name) {
|
|
163
|
+
const response = await prompts([
|
|
164
|
+
{
|
|
165
|
+
type: 'text',
|
|
166
|
+
name: 'name',
|
|
167
|
+
message: 'Project name:',
|
|
168
|
+
initial: 'my-saas-app',
|
|
169
|
+
validate: (n) => /^[a-z0-9-]+$/.test(n) || 'Only lowercase, numbers, and hyphens allowed',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'select',
|
|
173
|
+
name: 'location',
|
|
174
|
+
message: 'Where to create the project?',
|
|
175
|
+
choices: [
|
|
176
|
+
{ title: 'Current directory', value: 'current' },
|
|
177
|
+
{ title: 'New subdirectory', value: 'subdirectory' },
|
|
178
|
+
],
|
|
179
|
+
initial: 0,
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
name = response.name;
|
|
184
|
+
if (response.location === 'subdirectory') {
|
|
185
|
+
targetDir = path.join(targetDir, name);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!name) {
|
|
190
|
+
throw new Error('Project name is required');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const templateRepo = 'https://github.com/lego-one/lego-one.git';
|
|
194
|
+
const tempDir = path.join(targetDir, '.template-temp');
|
|
195
|
+
|
|
196
|
+
// Step 1: Clone template
|
|
197
|
+
spinner.start('Cloning Lego-One template...');
|
|
198
|
+
try {
|
|
199
|
+
await execaCommand(
|
|
200
|
+
`git clone --depth 1 --branch ${options.branch} ${templateRepo} ${tempDir}`
|
|
201
|
+
);
|
|
202
|
+
spinner.succeed('Template cloned');
|
|
203
|
+
} catch (error) {
|
|
204
|
+
spinner.fail('Failed to clone template');
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Step 2: Copy files to target
|
|
209
|
+
spinner.start('Setting up project files...');
|
|
210
|
+
try {
|
|
211
|
+
// Copy all files except .git
|
|
212
|
+
const files = await fs.readdir(tempDir);
|
|
213
|
+
for (const file of files) {
|
|
214
|
+
if (file !== '.git') {
|
|
215
|
+
await fs.copy(
|
|
216
|
+
path.join(tempDir, file),
|
|
217
|
+
path.join(targetDir, file),
|
|
218
|
+
{ overwrite: true }
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Remove temp directory
|
|
224
|
+
await fs.remove(tempDir);
|
|
225
|
+
spinner.succeed('Project files created');
|
|
226
|
+
} catch (error) {
|
|
227
|
+
spinner.fail('Failed to copy files');
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Step 3: Configure package.json
|
|
232
|
+
spinner.start('Configuring project...');
|
|
233
|
+
try {
|
|
234
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
235
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
236
|
+
|
|
237
|
+
// Update project name
|
|
238
|
+
packageJson.name = name;
|
|
239
|
+
|
|
240
|
+
// Remove private flag (user may want to publish)
|
|
241
|
+
delete packageJson.private;
|
|
242
|
+
|
|
243
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
244
|
+
spinner.succeed('Project configured');
|
|
245
|
+
} catch (error) {
|
|
246
|
+
spinner.fail('Failed to configure project');
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Step 4: Interactive plugin selection
|
|
251
|
+
if (options.interactive) {
|
|
252
|
+
const { plugins } = await prompts({
|
|
253
|
+
type: 'multiselect',
|
|
254
|
+
name: 'plugins',
|
|
255
|
+
message: 'Select plugins to enable:',
|
|
256
|
+
choices: [
|
|
257
|
+
{ title: 'Dashboard', value: '@lego/plugin-dashboard', selected: true },
|
|
258
|
+
{ title: 'Auth', value: '@lego/plugin-auth', selected: true },
|
|
259
|
+
{ title: 'Billing (Stripe)', value: '@lego/plugin-billing' },
|
|
260
|
+
{ title: 'Inventory', value: '@lego/plugin-inventory' },
|
|
261
|
+
{ title: 'Loan Calculator', value: '@lego/plugin-loan-calculator' },
|
|
262
|
+
],
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Update saas.config.ts
|
|
266
|
+
await updateSaaSConfig(targetDir, plugins);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Step 5: Install dependencies
|
|
270
|
+
spinner.start('Installing dependencies...');
|
|
271
|
+
try {
|
|
272
|
+
await execaCommand('pnpm install', { cwd: targetDir });
|
|
273
|
+
spinner.succeed('Dependencies installed');
|
|
274
|
+
} catch (error) {
|
|
275
|
+
spinner.warn('Failed to install dependencies. Run `pnpm install` manually.');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Step 6: Git initialization
|
|
279
|
+
if (options.git !== false) {
|
|
280
|
+
spinner.start('Initializing git...');
|
|
281
|
+
try {
|
|
282
|
+
await execaCommand('git init', { cwd: targetDir });
|
|
283
|
+
await execaCommand('git add .', { cwd: targetDir });
|
|
284
|
+
await execaCommand('git commit -m "Initial commit from Lego-One"', {
|
|
285
|
+
cwd: targetDir,
|
|
286
|
+
});
|
|
287
|
+
spinner.succeed('Git initialized');
|
|
288
|
+
} catch (error) {
|
|
289
|
+
spinner.warn('Git initialization failed');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Success message
|
|
294
|
+
console.log('\n' + chalk.green('✓ Project created successfully!\n'));
|
|
295
|
+
console.log(chalk.cyan('Next steps:\n'));
|
|
296
|
+
console.log(` cd ${name}`);
|
|
297
|
+
console.log(' pnpm run dev');
|
|
298
|
+
console.log('\n' + chalk.gray('Happy building!'));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function updateSaaSConfig(targetDir: string, enabledPlugins: string[]) {
|
|
302
|
+
const configPath = path.join(targetDir, 'saas.config.ts');
|
|
303
|
+
|
|
304
|
+
let configContent = await fs.readFile(configPath, 'utf-8');
|
|
305
|
+
|
|
306
|
+
// Update enabled plugins
|
|
307
|
+
const pluginsSection = enabledPlugins
|
|
308
|
+
.map((p) => ` { name: '${p}', enabled: true }`)
|
|
309
|
+
.join(',\n');
|
|
310
|
+
|
|
311
|
+
configContent = configContent.replace(
|
|
312
|
+
/plugins: \[[\s\S]*?\]/,
|
|
313
|
+
`plugins: [\n${pluginsSection}\n ]`
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
await fs.writeFile(configPath, configContent);
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 3.3 Add Plugin Command
|
|
321
|
+
|
|
322
|
+
**File:** `packages/create-lego-one/src/commands/add-plugin.ts`
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import path from 'path';
|
|
326
|
+
import fs from 'fs-extra';
|
|
327
|
+
import chalk from 'chalk';
|
|
328
|
+
import ora from 'ora';
|
|
329
|
+
|
|
330
|
+
export async function addPlugin(pluginName: string) {
|
|
331
|
+
const spinner = ora();
|
|
332
|
+
const projectRoot = process.cwd();
|
|
333
|
+
|
|
334
|
+
// Check if we're in a Lego-One project
|
|
335
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
336
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
337
|
+
throw new Error('Not in a valid project directory');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
341
|
+
if (!packageJson.name?.startsWith('lego-one')) {
|
|
342
|
+
throw new Error('Not a Lego-One project');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
spinner.start(`Adding plugin: ${pluginName}...`);
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
// Update saas.config.ts
|
|
349
|
+
const configPath = path.join(projectRoot, 'saas.config.ts');
|
|
350
|
+
let configContent = await fs.readFile(configPath, 'utf-8');
|
|
351
|
+
|
|
352
|
+
// Add plugin to the list
|
|
353
|
+
const pluginEntry = ` { name: '${pluginName}', enabled: true }`;
|
|
354
|
+
|
|
355
|
+
if (configContent.includes('plugins: [')) {
|
|
356
|
+
configContent = configContent.replace(
|
|
357
|
+
'plugins: [',
|
|
358
|
+
`plugins: [\n${pluginEntry},`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
await fs.writeFile(configPath, configContent);
|
|
363
|
+
|
|
364
|
+
spinner.succeed(`Plugin ${pluginName} added`);
|
|
365
|
+
console.log('\n' + chalk.gray('Update modern.runtime.ts to add the sub-app entry.'));
|
|
366
|
+
} catch (error) {
|
|
367
|
+
spinner.fail(`Failed to add plugin: ${error.message}`);
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## 4. Template Repository Structure
|
|
376
|
+
|
|
377
|
+
### 4.1 GitHub Template Setup
|
|
378
|
+
|
|
379
|
+
**Repository:** `github.com/lego-one/lego-one`
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
lego-one/ # Template repository
|
|
383
|
+
├── .github/
|
|
384
|
+
│ └── workflows/
|
|
385
|
+
│ └── template-sync.yml # Sync upstream changes
|
|
386
|
+
├── host/
|
|
387
|
+
│ ├── src/
|
|
388
|
+
│ ├── modern.config.ts
|
|
389
|
+
│ └── package.json
|
|
390
|
+
├── packages/
|
|
391
|
+
│ └── plugins/
|
|
392
|
+
│ └── @lego/
|
|
393
|
+
│ ├── plugin-dashboard/
|
|
394
|
+
│ ├── plugin-auth/
|
|
395
|
+
│ └── plugin-billing/
|
|
396
|
+
├── saas.config.ts # User-editable config
|
|
397
|
+
├── pnpm-workspace.yaml
|
|
398
|
+
├── package.json # Root package.json
|
|
399
|
+
├── README.md
|
|
400
|
+
└── .gitignore
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 4.2 Template Configuration
|
|
404
|
+
|
|
405
|
+
**File:** `lego-one/.github/template-config.yml`
|
|
406
|
+
|
|
407
|
+
```yaml
|
|
408
|
+
# Template repository configuration
|
|
409
|
+
name: Lego-One SaaS OS
|
|
410
|
+
description: Microkernel SaaS boilerplate with Modern.js
|
|
411
|
+
|
|
412
|
+
# Files to exclude when using as template
|
|
413
|
+
exclude:
|
|
414
|
+
- .git/
|
|
415
|
+
- node_modules/
|
|
416
|
+
- dist/
|
|
417
|
+
- .env.local
|
|
418
|
+
- pnpm-lock.yaml
|
|
419
|
+
|
|
420
|
+
# Prompts for users
|
|
421
|
+
prompts:
|
|
422
|
+
- id: project_name
|
|
423
|
+
name: Project Name
|
|
424
|
+
default: my-saas-app
|
|
425
|
+
- id: pocketbase_url
|
|
426
|
+
name: PocketBase URL
|
|
427
|
+
default: http://localhost:8090
|
|
428
|
+
- id: enable_dashboard
|
|
429
|
+
name: Enable Dashboard Plugin
|
|
430
|
+
type: boolean
|
|
431
|
+
default: true
|
|
432
|
+
- id: enable_auth
|
|
433
|
+
name: Enable Auth Plugin
|
|
434
|
+
type: boolean
|
|
435
|
+
default: true
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## 5. Publishing the CLI
|
|
441
|
+
|
|
442
|
+
### 5.1 Package.json
|
|
443
|
+
|
|
444
|
+
**File:** `packages/create-lego-one/package.json`
|
|
445
|
+
|
|
446
|
+
```json
|
|
447
|
+
{
|
|
448
|
+
"name": "create-lego-one",
|
|
449
|
+
"version": "1.0.0",
|
|
450
|
+
"description": "Create a new Lego-One SaaS application",
|
|
451
|
+
"bin": "./dist/index.js",
|
|
452
|
+
"files": ["dist"],
|
|
453
|
+
"scripts": {
|
|
454
|
+
"build": "tsup src/index.ts --format cjs --clean",
|
|
455
|
+
"dev": "tsup src/index.ts --format cjs --watch",
|
|
456
|
+
"prepublishOnly": "pnpm run build"
|
|
457
|
+
},
|
|
458
|
+
"keywords": [
|
|
459
|
+
"create-app",
|
|
460
|
+
"lego-one",
|
|
461
|
+
"saas",
|
|
462
|
+
"micro-frontend",
|
|
463
|
+
"modernjs"
|
|
464
|
+
],
|
|
465
|
+
"exports": "./dist/index.js",
|
|
466
|
+
"repository": {
|
|
467
|
+
"type": "git",
|
|
468
|
+
"url": "https://github.com/lego-one/create-lego-one.git"
|
|
469
|
+
},
|
|
470
|
+
"dependencies": {
|
|
471
|
+
"commander": "^12.0.0",
|
|
472
|
+
"chalk": "^5.3.0",
|
|
473
|
+
"ora": "^8.0.1",
|
|
474
|
+
"prompts": "^2.4.2",
|
|
475
|
+
"execa": "^8.0.1",
|
|
476
|
+
"fs-extra": "^11.2.0"
|
|
477
|
+
},
|
|
478
|
+
"devDependencies": {
|
|
479
|
+
"@types/node": "^20.11.0",
|
|
480
|
+
"@types/prompts": "^2.4.9",
|
|
481
|
+
"@types/fs-extra": "^11.0.4",
|
|
482
|
+
"tsup": "^8.0.0",
|
|
483
|
+
"typescript": "^5.3.0"
|
|
484
|
+
},
|
|
485
|
+
"engines": {
|
|
486
|
+
"node": ">=18.0.0",
|
|
487
|
+
"pnpm": ">=8.0.0"
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### 5.2 Build Configuration
|
|
493
|
+
|
|
494
|
+
**File:** `packages/create-lego-one/tsup.config.ts`
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { defineConfig } from 'tsup';
|
|
498
|
+
|
|
499
|
+
export default defineConfig({
|
|
500
|
+
entry: ['src/index.ts'],
|
|
501
|
+
format: ['cjs'],
|
|
502
|
+
target: 'node18',
|
|
503
|
+
clean: true,
|
|
504
|
+
shims: true,
|
|
505
|
+
banner: {
|
|
506
|
+
js: '#!/usr/bin/env node',
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### 5.3 Publish to npm
|
|
512
|
+
|
|
513
|
+
```bash
|
|
514
|
+
# Build the CLI
|
|
515
|
+
cd packages/create-lego-one
|
|
516
|
+
pnpm run build
|
|
517
|
+
|
|
518
|
+
# Publish to npm
|
|
519
|
+
pnpm publish --access public
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 6. User Experience
|
|
525
|
+
|
|
526
|
+
### 6.1 Creating a New Project
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
# User runs:
|
|
530
|
+
pnpm create lego-one my-saas-app
|
|
531
|
+
|
|
532
|
+
# Output:
|
|
533
|
+
✓ Cloning Lego-One template...
|
|
534
|
+
✓ Setting up project files...
|
|
535
|
+
✓ Configuring project...
|
|
536
|
+
✓ Installing dependencies...
|
|
537
|
+
✓ Git initialized
|
|
538
|
+
|
|
539
|
+
✓ Project created successfully!
|
|
540
|
+
|
|
541
|
+
Next steps:
|
|
542
|
+
cd my-saas-app
|
|
543
|
+
pnpm run dev
|
|
544
|
+
|
|
545
|
+
Happy building!
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### 6.2 Interactive Mode
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
pnpm create lego-one --interactive
|
|
552
|
+
|
|
553
|
+
# Prompts:
|
|
554
|
+
? Project name: my-saas-app
|
|
555
|
+
? Where to create the project? Current directory
|
|
556
|
+
? Select plugins to enable:
|
|
557
|
+
✓ Dashboard
|
|
558
|
+
✓ Auth
|
|
559
|
+
○ Billing (Stripe)
|
|
560
|
+
○ Inventory
|
|
561
|
+
○ Loan Calculator
|
|
562
|
+
|
|
563
|
+
# ... creates project with selected plugins enabled
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## 7. Update Workflow
|
|
569
|
+
|
|
570
|
+
### 7.1 User Updates from Template
|
|
571
|
+
|
|
572
|
+
Since we're using Git Template approach:
|
|
573
|
+
|
|
574
|
+
```bash
|
|
575
|
+
# In user's project
|
|
576
|
+
git remote add upstream https://github.com/lego-one/lego-one.git
|
|
577
|
+
git fetch upstream
|
|
578
|
+
git merge upstream/main
|
|
579
|
+
|
|
580
|
+
# Resolve conflicts as needed
|
|
581
|
+
# The user keeps their custom plugins in separate branch or subtree
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### 7.2 Plugin Updates
|
|
585
|
+
|
|
586
|
+
Official plugins are managed via git subtree/submodule:
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
# Add official plugin as subtree
|
|
590
|
+
git subtree add \
|
|
591
|
+
--prefix=packages/plugins/@lego/plugin-dashboard \
|
|
592
|
+
https://github.com/lego-one/plugin-dashboard.git \
|
|
593
|
+
main
|
|
594
|
+
|
|
595
|
+
# Update plugin later
|
|
596
|
+
git subtree pull \
|
|
597
|
+
--prefix=packages/plugins/@lego/plugin-dashboard \
|
|
598
|
+
https://github.com/lego-one/plugin-dashboard.git \
|
|
599
|
+
main
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## 8. Next Steps
|
|
605
|
+
|
|
606
|
+
1. **`07-deployment.md`**: Deploy host and plugins to production
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## References
|
|
611
|
+
|
|
612
|
+
- [npm create Documentation](https://docs.npmjs.com/cli/v9/commands/npm-create)
|
|
613
|
+
- [Commander.js](https://commander.js.org/)
|
|
614
|
+
- [GitHub Template Repositories](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository)
|
|
615
|
+
- [pnpm Workspace](https://pnpm.io/workspaces)
|