@elsapiens/cli 0.1.0 → 0.1.3

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
@@ -14,7 +14,10 @@ npm install -g @elsapiens/cli
14
14
 
15
15
  ## Features
16
16
 
17
- - Create new projects with elSapiens SDK
17
+ - Create new projects with complete elSapiens SDK setup
18
+ - Full AppLayout with sidebar and topbar
19
+ - All providers pre-configured (Theme, Auth, Locale, Help)
20
+ - Services ready to use (Logger, ApiClient, ShortcutManager)
18
21
  - Add pages with correct structure and patterns
19
22
  - Add components with TypeScript templates
20
23
  - Interactive prompts for configuration
@@ -28,10 +31,6 @@ npm install -g @elsapiens/cli
28
31
  # Interactive mode
29
32
  el init my-app
30
33
 
31
- # With template selection
32
- el init my-app --template minimal
33
- el init my-app --template full
34
-
35
34
  # Skip prompts
36
35
  el init my-app --yes
37
36
 
@@ -39,30 +38,45 @@ el init my-app --yes
39
38
  el init my-app --no-git
40
39
  ```
41
40
 
42
- **Templates:**
43
-
44
- | Template | Description |
45
- |----------|-------------|
46
- | `minimal` | Basic setup with single Dashboard page |
47
- | `full` | Complete setup with Dashboard, Settings, and examples |
48
-
49
- **Generated Structure (minimal):**
41
+ **Generated Structure:**
50
42
 
51
43
  ```
52
44
  my-app/
53
45
  ├── package.json
54
46
  ├── vite.config.ts
55
47
  ├── tsconfig.json
48
+ ├── tsconfig.node.json
49
+ ├── tailwind.config.js
50
+ ├── postcss.config.js
56
51
  ├── index.html
52
+ ├── .env.example
57
53
  ├── .gitignore
58
54
  └── src/
59
- ├── main.tsx
60
- ├── App.tsx
61
- ├── index.css
55
+ ├── main.tsx # All providers configured
56
+ ├── App.tsx # Full AppLayout with sidebar/topbar
57
+ ├── index.css # Design system styles
58
+ ├── help-topics.ts # Help system topics
59
+ ├── services/
60
+ │ └── index.ts # Logger, ApiClient, ShortcutManager
61
+ ├── translations/
62
+ │ └── common.ts # i18n translations
62
63
  └── pages/
63
- └── Dashboard.tsx
64
+ ├── Dashboard.tsx
65
+ └── Settings.tsx
64
66
  ```
65
67
 
68
+ **Included Features:**
69
+
70
+ | Feature | Description |
71
+ |---------|-------------|
72
+ | ThemeProvider | Dark/light mode with system detection |
73
+ | AuthProvider | Authentication context |
74
+ | LocaleProvider | Internationalization support |
75
+ | HelpProvider | Help system with keyboard shortcuts |
76
+ | AppLayout | Sidebar navigation + topbar |
77
+ | AuthGuard | Route protection |
78
+ | Services | Logger, ApiClient, ShortcutManager |
79
+
66
80
  ### Add Page
67
81
 
68
82
  ```bash
@@ -89,33 +103,6 @@ el add page Users --path src/features/users/pages
89
103
  | `list` | Data listing with search, filters, and table |
90
104
  | `settings` | Configuration page with form sections and toggles |
91
105
 
92
- **Page Structure:**
93
-
94
- All pages follow the established alignment pattern:
95
-
96
- ```tsx
97
- <div className="p-6">
98
- {/* Page Header - pl-6 to align with table spacer */}
99
- <div className="flex items-center justify-between mb-8 pl-6">
100
- <div>
101
- <h1 className="text-2xl font-bold text-foreground">Title</h1>
102
- <p className="text-muted-foreground">Description</p>
103
- </div>
104
- {/* Actions */}
105
- </div>
106
-
107
- {/* Content with Cards */}
108
- <Card padding="none">
109
- <CardHeader className="p-6">
110
- <CardTitle>Section</CardTitle>
111
- </CardHeader>
112
- <CardContent className="p-6 pt-0">
113
- {/* Content */}
114
- </CardContent>
115
- </Card>
116
- </div>
117
- ```
118
-
119
106
  ### Add Component
120
107
 
121
108
  ```bash
@@ -150,7 +137,6 @@ $ el init my-dashboard
150
137
  elSapiens CLI v0.1.0
151
138
 
152
139
  ? Project name: my-dashboard
153
- ? Select template: Minimal
154
140
  ? Initialize git repository? Yes
155
141
 
156
142
  Creating project...
@@ -160,6 +146,10 @@ Creating project...
160
146
  ✓ src/main.tsx
161
147
  ✓ src/App.tsx
162
148
  ✓ src/pages/Dashboard.tsx
149
+ ✓ src/pages/Settings.tsx
150
+ ✓ src/services/index.ts
151
+ ✓ src/translations/common.ts
152
+ ✓ src/help-topics.ts
163
153
 
164
154
  Done! Run:
165
155
  cd my-dashboard
@@ -206,7 +196,6 @@ Create a new elSapiens project.
206
196
 
207
197
  | Option | Description |
208
198
  |--------|-------------|
209
- | `-t, --template <type>` | Project template (minimal, full) |
210
199
  | `--no-git` | Skip git initialization |
211
200
  | `-y, --yes` | Skip prompts, use defaults |
212
201
 
@@ -240,7 +229,6 @@ elsapiens init my-app
240
229
  # Add command aliases
241
230
  el add page Users
242
231
  el generate page Users
243
- el g page Users
244
232
  ```
245
233
 
246
234
  ## Requirements
@@ -250,26 +238,99 @@ el g page Users
250
238
 
251
239
  ## Development
252
240
 
241
+ ### Building from Source
242
+
253
243
  ```bash
254
244
  # Clone the repository
255
245
  git clone https://github.com/elsapiens/sdk.git
256
- cd sdk/packages/cli
246
+ cd sdk
257
247
 
258
- # Install dependencies
248
+ # Install all dependencies
259
249
  pnpm install
260
250
 
261
- # Build
251
+ # Build all packages (required for CLI to work)
262
252
  pnpm build
263
253
 
264
- # Test locally
265
- node dist/index.js --help
266
- node dist/index.js init test-app
254
+ # Or build just the CLI
255
+ cd packages/cli && pnpm build
256
+ ```
257
+
258
+ ### Using CLI from Git Checkout (Without npm)
259
+
260
+ You can use the CLI directly from a git checkout without publishing to npm:
261
+
262
+ ```bash
263
+ # Create a project using the local CLI build
264
+ node /path/to/sdk/packages/cli/dist/index.js init my-project
265
+
266
+ # Or set up an alias for convenience
267
+ alias el-dev="node /path/to/sdk/packages/cli/dist/index.js"
268
+ el-dev init my-project
269
+ ```
270
+
271
+ ### Testing Template Changes
272
+
273
+ ```bash
274
+ # 1. Make changes to templates
275
+ code packages/cli/src/templates/app/page-dashboard.ejs
276
+
277
+ # 2. Rebuild the CLI
278
+ cd packages/cli && pnpm build
279
+
280
+ # 3. Create a fresh test project
281
+ rm -rf /tmp/test-project
282
+ node dist/index.js init /tmp/test-project
283
+
284
+ # 4. Test the changes
285
+ cd /tmp/test-project && pnpm install && pnpm dev
286
+ ```
287
+
288
+ ### Install Globally (One-Time Setup)
267
289
 
268
- # Link globally for testing
290
+ The easiest way to install the CLI globally from your local SDK:
291
+
292
+ ```bash
293
+ cd packages/cli
294
+
295
+ # Run the setup script (builds and links globally)
296
+ pnpm setup:global
297
+ ```
298
+
299
+ After setup, use the `el` command from anywhere:
300
+
301
+ ```bash
302
+ el init my-project
303
+ el add page Users
304
+ el add component UserCard
305
+ ```
306
+
307
+ ### Manual Linking
308
+
309
+ ```bash
310
+ cd packages/cli
311
+ pnpm build
269
312
  pnpm link --global
313
+
314
+ # Verify
270
315
  el --version
271
316
  ```
272
317
 
318
+ ### Unlink
319
+
320
+ ```bash
321
+ cd packages/cli
322
+ pnpm unlink:global
323
+ ```
324
+
325
+ ### Shell Alias (Alternative)
326
+
327
+ Add to `~/.zshrc` or `~/.bashrc`:
328
+
329
+ ```bash
330
+ export ELSDK_PATH="/path/to/elSDK"
331
+ alias el="node $ELSDK_PATH/packages/cli/dist/index.js"
332
+ ```
333
+
273
334
  ## Publishing
274
335
 
275
336
  ```bash
package/dist/index.js CHANGED
@@ -220,6 +220,44 @@ async function initCommand(projectName, options) {
220
220
  if (!name) {
221
221
  name = "my-elsapiens-app";
222
222
  }
223
+ let httpsPort = "443";
224
+ let grpcPort = "9090";
225
+ let restPort = "8080";
226
+ if (!options.yes) {
227
+ httpsPort = await text2({
228
+ message: "HTTPS port:",
229
+ placeholder: "443",
230
+ initialValue: "443",
231
+ validate: (value) => {
232
+ const port = parseInt(value, 10);
233
+ if (isNaN(port) || port < 1 || port > 65535) {
234
+ return "Please enter a valid port number (1-65535)";
235
+ }
236
+ }
237
+ }) || "443";
238
+ grpcPort = await text2({
239
+ message: "gRPC port:",
240
+ placeholder: "9090",
241
+ initialValue: "9090",
242
+ validate: (value) => {
243
+ const port = parseInt(value, 10);
244
+ if (isNaN(port) || port < 1 || port > 65535) {
245
+ return "Please enter a valid port number (1-65535)";
246
+ }
247
+ }
248
+ }) || "9090";
249
+ restPort = await text2({
250
+ message: "REST API port:",
251
+ placeholder: "8080",
252
+ initialValue: "8080",
253
+ validate: (value) => {
254
+ const port = parseInt(value, 10);
255
+ if (isNaN(port) || port < 1 || port > 65535) {
256
+ return "Please enter a valid port number (1-65535)";
257
+ }
258
+ }
259
+ }) || "8080";
260
+ }
223
261
  const projectDir = resolvePath(name);
224
262
  if (await directoryExists(projectDir)) {
225
263
  const shouldOverwrite = options.yes ? false : await confirm2({
@@ -230,25 +268,6 @@ async function initCommand(projectName, options) {
230
268
  cancel2("Project creation cancelled.");
231
269
  }
232
270
  }
233
- let template = options.template;
234
- if (!options.yes) {
235
- template = await select2({
236
- message: "Select a template:",
237
- options: [
238
- {
239
- value: "minimal",
240
- label: "Minimal",
241
- hint: "Basic setup with a single Dashboard page"
242
- },
243
- {
244
- value: "full",
245
- label: "Full",
246
- hint: "Complete setup with Dashboard, Settings, and example components"
247
- }
248
- ],
249
- initialValue: template
250
- });
251
- }
252
271
  const s = spinner2();
253
272
  s.start("Creating project...");
254
273
  try {
@@ -256,8 +275,7 @@ async function initCommand(projectName, options) {
256
275
  const pascalName = toPascalCase(name);
257
276
  const packageJson = await renderApp("package", {
258
277
  projectName: name,
259
- pascalName,
260
- template
278
+ pascalName
261
279
  });
262
280
  await writeFile(path3.join(projectDir, "package.json"), packageJson);
263
281
  const viteConfig = await renderApp("vite-config", {
@@ -265,10 +283,24 @@ async function initCommand(projectName, options) {
265
283
  pascalName
266
284
  });
267
285
  await writeFile(path3.join(projectDir, "vite.config.ts"), viteConfig);
286
+ const tailwindConfig = await renderApp("tailwind-config", {
287
+ projectName: name,
288
+ pascalName
289
+ });
290
+ await writeFile(path3.join(projectDir, "tailwind.config.js"), tailwindConfig);
291
+ const postcssConfig = await renderApp("postcss-config", {
292
+ projectName: name,
293
+ pascalName
294
+ });
295
+ await writeFile(path3.join(projectDir, "postcss.config.js"), postcssConfig);
268
296
  const tsconfig = await renderApp("tsconfig", {
269
297
  projectName: name
270
298
  });
271
299
  await writeFile(path3.join(projectDir, "tsconfig.json"), tsconfig);
300
+ const tsconfigNode = await renderApp("tsconfig-node", {
301
+ projectName: name
302
+ });
303
+ await writeFile(path3.join(projectDir, "tsconfig.node.json"), tsconfigNode);
272
304
  const indexHtml = await renderApp("index-html", {
273
305
  projectName: name,
274
306
  pascalName
@@ -281,14 +313,17 @@ async function initCommand(projectName, options) {
281
313
  await writeFile(path3.join(projectDir, "src", "main.tsx"), mainTsx);
282
314
  const appTsx = await renderApp("app", {
283
315
  projectName: name,
284
- pascalName,
285
- template
316
+ pascalName
286
317
  });
287
318
  await writeFile(path3.join(projectDir, "src", "App.tsx"), appTsx);
288
319
  const indexCss = await renderApp("index-css", {
289
320
  projectName: name
290
321
  });
291
322
  await writeFile(path3.join(projectDir, "src", "index.css"), indexCss);
323
+ const viteEnv = await renderApp("vite-env", {
324
+ projectName: name
325
+ });
326
+ await writeFile(path3.join(projectDir, "src", "vite-env.d.ts"), viteEnv);
292
327
  const dashboardPage = await renderApp("page-dashboard", {
293
328
  projectName: name,
294
329
  pascalName
@@ -297,16 +332,91 @@ async function initCommand(projectName, options) {
297
332
  path3.join(projectDir, "src", "pages", "Dashboard.tsx"),
298
333
  dashboardPage
299
334
  );
300
- if (template === "full") {
301
- const settingsPage = await renderApp("page-settings", {
302
- projectName: name,
303
- pascalName
304
- });
305
- await writeFile(
306
- path3.join(projectDir, "src", "pages", "Settings.tsx"),
307
- settingsPage
308
- );
309
- }
335
+ const settingsPage = await renderApp("page-settings", {
336
+ projectName: name,
337
+ pascalName
338
+ });
339
+ await writeFile(
340
+ path3.join(projectDir, "src", "pages", "Settings.tsx"),
341
+ settingsPage
342
+ );
343
+ const translationsCommonEn = await renderApp("translations-common-en", {
344
+ projectName: name,
345
+ pascalName
346
+ });
347
+ await writeFile(
348
+ path3.join(projectDir, "src", "translations", "common", "en.ts"),
349
+ translationsCommonEn
350
+ );
351
+ const translationsCommonIndex = await renderApp("translations-common-index", {
352
+ projectName: name,
353
+ pascalName
354
+ });
355
+ await writeFile(
356
+ path3.join(projectDir, "src", "translations", "common", "index.ts"),
357
+ translationsCommonIndex
358
+ );
359
+ const translationsSettingsEn = await renderApp("translations-settings-en", {
360
+ projectName: name,
361
+ pascalName
362
+ });
363
+ await writeFile(
364
+ path3.join(projectDir, "src", "translations", "settings", "en.ts"),
365
+ translationsSettingsEn
366
+ );
367
+ const translationsSettingsIndex = await renderApp("translations-settings-index", {
368
+ projectName: name,
369
+ pascalName
370
+ });
371
+ await writeFile(
372
+ path3.join(projectDir, "src", "translations", "settings", "index.ts"),
373
+ translationsSettingsIndex
374
+ );
375
+ const helpTopics = await renderApp("help-topics", {
376
+ projectName: name,
377
+ pascalName
378
+ });
379
+ await writeFile(
380
+ path3.join(projectDir, "src", "help-topics.ts"),
381
+ helpTopics
382
+ );
383
+ const servicesSetup = await renderApp("services-setup", {
384
+ projectName: name,
385
+ pascalName
386
+ });
387
+ await writeFile(
388
+ path3.join(projectDir, "src", "services", "index.ts"),
389
+ servicesSetup
390
+ );
391
+ const envExample = await renderApp("env-example", {
392
+ projectName: name,
393
+ pascalName,
394
+ httpsPort,
395
+ grpcPort,
396
+ restPort
397
+ });
398
+ await writeFile(path3.join(projectDir, ".env.example"), envExample);
399
+ await writeFile(path3.join(projectDir, ".env"), envExample);
400
+ const eslintConfig = await renderApp("eslint-config", {
401
+ projectName: name,
402
+ pascalName
403
+ });
404
+ await writeFile(path3.join(projectDir, "eslint.config.js"), eslintConfig);
405
+ const vitestConfig = await renderApp("vitest-config", {
406
+ projectName: name,
407
+ pascalName
408
+ });
409
+ await writeFile(path3.join(projectDir, "vitest.config.ts"), vitestConfig);
410
+ const testSetup = await renderApp("test-setup", {
411
+ projectName: name,
412
+ pascalName
413
+ });
414
+ await writeFile(path3.join(projectDir, "src", "test", "setup.ts"), testSetup);
415
+ const huskyPreCommit = await renderApp("husky-pre-commit", {
416
+ projectName: name,
417
+ pascalName
418
+ });
419
+ await writeFile(path3.join(projectDir, ".husky", "pre-commit"), huskyPreCommit);
310
420
  const gitignore = `# Dependencies
311
421
  node_modules/
312
422
 
@@ -331,6 +441,9 @@ Thumbs.db
331
441
  # Logs
332
442
  *.log
333
443
  npm-debug.log*
444
+
445
+ # Coverage
446
+ coverage/
334
447
  `;
335
448
  await writeFile(path3.join(projectDir, ".gitignore"), gitignore);
336
449
  s.stop("Project created!");
@@ -341,8 +454,10 @@ npm-debug.log*
341
454
  });
342
455
  if (initGit) {
343
456
  try {
344
- execSync("git init", { cwd: projectDir, stdio: "ignore" });
345
- logger.success("Git repository initialized");
457
+ execSync("git init -b main", { cwd: projectDir, stdio: "ignore" });
458
+ execSync("git add .", { cwd: projectDir, stdio: "ignore" });
459
+ execSync('git commit -m "Initial commit"', { cwd: projectDir, stdio: "ignore" });
460
+ logger.success("Git repository initialized with initial commit");
346
461
  } catch {
347
462
  logger.warn("Failed to initialize git repository");
348
463
  }
@@ -550,7 +665,7 @@ var VERSION = "0.1.0";
550
665
  function createCli() {
551
666
  const program = new Command();
552
667
  program.name("elsapiens").description("CLI scaffolding tool for elSapiens SDK projects").version(VERSION, "-v, --version", "Display version number");
553
- program.command("init [project-name]").alias("create").description("Create a new elSapiens project").option("-t, --template <type>", "Project template (minimal or full)", "minimal").option("--no-git", "Skip git initialization").option("-y, --yes", "Skip prompts and use defaults").action(initCommand);
668
+ program.command("init [project-name]").alias("create").description("Create a new elSapiens project with full setup").option("--no-git", "Skip git initialization").option("-y, --yes", "Skip prompts and use defaults").action(initCommand);
554
669
  program.command("add").alias("generate").alias("g").description("Add a page or component to the project").addCommand(
555
670
  new Command("page").argument("[name]", "Page name").description("Add a new page").option("-t, --template <type>", "Page template (dashboard, list, settings)", "dashboard").option("-p, --path <dir>", "Custom pages directory").action(addCommand.page)
556
671
  ).addCommand(