frontend-hamroun 1.2.16 → 1.2.17

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.
Files changed (158) hide show
  1. package/README.md +4 -0
  2. package/bin/cli.js +673 -0
  3. package/dist/component.d.ts +1 -1
  4. package/dist/context.d.ts +4 -3
  5. package/dist/index.client.d.ts +11 -0
  6. package/dist/index.d.ts +9 -89
  7. package/dist/index.js +396 -67
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +392 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/jsx-runtime/jsx-runtime.d.ts +0 -1
  12. package/dist/jsx-runtime.d.ts +1 -1
  13. package/dist/renderer.d.ts +0 -10
  14. package/dist/server-renderer.d.ts +0 -3
  15. package/dist/server-types.d.ts +42 -0
  16. package/package.json +69 -41
  17. package/templates/basic-app/index.html +6 -6
  18. package/templates/basic-app/package.json +18 -7
  19. package/templates/basic-app/postcss.config.js +0 -1
  20. package/templates/basic-app/src/main.tsx +1 -10
  21. package/templates/basic-app/tailwind.config.js +2 -23
  22. package/templates/basic-app/tsconfig.json +4 -17
  23. package/templates/basic-app/vite.config.ts +3 -54
  24. package/templates/fullstack-app/api/hello.ts +18 -0
  25. package/templates/fullstack-app/api/users/[id].ts +73 -0
  26. package/templates/fullstack-app/api/users/index.ts +32 -0
  27. package/templates/fullstack-app/package.json +31 -0
  28. package/templates/fullstack-app/server.ts +46 -0
  29. package/templates/fullstack-app/src/pages/index.tsx +59 -0
  30. package/templates/ssr-template/vite.config.ts +1 -11
  31. package/bin/cli.cjs +0 -16
  32. package/bin/cli.mjs +0 -237
  33. package/dist/backend/api-utils.d.ts +0 -38
  34. package/dist/backend/api-utils.js +0 -135
  35. package/dist/backend/auth.d.ts +0 -134
  36. package/dist/backend/auth.js +0 -387
  37. package/dist/backend/database.d.ts +0 -27
  38. package/dist/backend/database.js +0 -91
  39. package/dist/backend/model.d.ts +0 -43
  40. package/dist/backend/model.js +0 -178
  41. package/dist/backend/router.d.ts +0 -27
  42. package/dist/backend/router.js +0 -137
  43. package/dist/backend/server.d.ts +0 -19
  44. package/dist/backend/server.js +0 -268
  45. package/dist/backend/types.d.ts +0 -217
  46. package/dist/backend/types.js +0 -1
  47. package/dist/batch.js +0 -22
  48. package/dist/cli/index.d.ts +0 -2
  49. package/dist/cli/index.js +0 -215
  50. package/dist/component.js +0 -84
  51. package/dist/components/Counter.js +0 -2
  52. package/dist/context.js +0 -18
  53. package/dist/frontend-hamroun.es.js +0 -1378
  54. package/dist/frontend-hamroun.umd.js +0 -66
  55. package/dist/hooks.js +0 -164
  56. package/dist/jsx-runtime/index.d.ts +0 -11
  57. package/dist/jsx-runtime/index.js +0 -19
  58. package/dist/jsx-runtime/jsx-dev-runtime.js +0 -1
  59. package/dist/jsx-runtime/jsx-runtime.js +0 -95
  60. package/dist/jsx-runtime.js +0 -192
  61. package/dist/renderer.js +0 -51
  62. package/dist/server-renderer.js +0 -102
  63. package/dist/types.js +0 -1
  64. package/dist/vdom.js +0 -27
  65. package/scripts/build-cli.js +0 -1199
  66. package/scripts/generate.js +0 -134
  67. package/src/backend/api-utils.ts +0 -178
  68. package/src/backend/auth.ts +0 -544
  69. package/src/backend/database.ts +0 -104
  70. package/src/backend/model.ts +0 -198
  71. package/src/backend/router.ts +0 -176
  72. package/src/backend/server.ts +0 -330
  73. package/src/backend/types.ts +0 -257
  74. package/src/batch.ts +0 -24
  75. package/src/cli/index.js +0 -554
  76. package/src/cli/index.ts +0 -257
  77. package/src/component.ts +0 -98
  78. package/src/components/Counter.tsx +0 -4
  79. package/src/context.ts +0 -29
  80. package/src/hooks.ts +0 -211
  81. package/src/index.ts +0 -144
  82. package/src/jsx-runtime/index.ts +0 -27
  83. package/src/jsx-runtime/jsx-dev-runtime.ts +0 -0
  84. package/src/jsx-runtime/jsx-runtime.ts +0 -104
  85. package/src/jsx-runtime.ts +0 -226
  86. package/src/renderer.ts +0 -55
  87. package/src/server-renderer.ts +0 -114
  88. package/src/shims.d.ts +0 -20
  89. package/src/types/bcrypt.d.ts +0 -30
  90. package/src/types/jsonwebtoken.d.ts +0 -55
  91. package/src/types.d.ts +0 -26
  92. package/src/types.ts +0 -21
  93. package/src/vdom.ts +0 -34
  94. package/templates/basic/.eslintignore +0 -5
  95. package/templates/basic/.eslintrc.json +0 -25
  96. package/templates/basic/docs/rapport_pfe.aux +0 -27
  97. package/templates/basic/docs/rapport_pfe.log +0 -399
  98. package/templates/basic/docs/rapport_pfe.out +0 -10
  99. package/templates/basic/docs/rapport_pfe.pdf +0 -0
  100. package/templates/basic/docs/rapport_pfe.tex +0 -68
  101. package/templates/basic/docs/rapport_pfe.toc +0 -14
  102. package/templates/basic/index.html +0 -12
  103. package/templates/basic/jsconfig.json +0 -14
  104. package/templates/basic/package.json +0 -18
  105. package/templates/basic/postcss.config.js +0 -7
  106. package/templates/basic/src/App.js +0 -105
  107. package/templates/basic/src/App.tsx +0 -65
  108. package/templates/basic/src/api.ts +0 -58
  109. package/templates/basic/src/components/Counter.tsx +0 -26
  110. package/templates/basic/src/components/Header.tsx +0 -9
  111. package/templates/basic/src/components/TodoList.tsx +0 -90
  112. package/templates/basic/src/main.css +0 -3
  113. package/templates/basic/src/main.js +0 -11
  114. package/templates/basic/src/main.ts +0 -20
  115. package/templates/basic/src/main.tsx +0 -144
  116. package/templates/basic/src/server.ts +0 -99
  117. package/templates/basic/tailwind.config.js +0 -32
  118. package/templates/basic/tsconfig.json +0 -20
  119. package/templates/basic/tsconfig.node.json +0 -10
  120. package/templates/basic/vite.config.js +0 -18
  121. package/templates/basic/vite.config.ts +0 -86
  122. package/templates/basic-app/src/App.js +0 -105
  123. package/templates/basic-app/src/App.tsx +0 -143
  124. package/templates/basic-app/src/api.ts +0 -58
  125. package/templates/basic-app/src/components/Counter.tsx +0 -26
  126. package/templates/basic-app/src/components/Header.tsx +0 -9
  127. package/templates/basic-app/src/components/TodoList.tsx +0 -90
  128. package/templates/basic-app/src/main.js +0 -10
  129. package/templates/basic-app/src/main.ts +0 -21
  130. package/templates/basic-app/src/react/index.ts +0 -35
  131. package/templates/basic-app/src/react/jsx-dev-runtime.ts +0 -13
  132. package/templates/basic-app/src/react/jsx-runtime.ts +0 -12
  133. package/templates/basic-app/src/server.ts +0 -99
  134. package/templates/basic-app/src/shims.ts +0 -9
  135. package/templates/basic-app/tsconfig.node.json +0 -10
  136. package/templates/basic-app/vite.config.js +0 -22
  137. package/templates/full-stack/.env.example +0 -11
  138. package/templates/full-stack/README.md +0 -51
  139. package/templates/full-stack/index.html +0 -12
  140. package/templates/full-stack/jsconfig.json +0 -14
  141. package/templates/full-stack/package.json +0 -21
  142. package/templates/full-stack/src/App.js +0 -105
  143. package/templates/full-stack/src/client/App.tsx +0 -50
  144. package/templates/full-stack/src/client/components/Header.tsx +0 -42
  145. package/templates/full-stack/src/client/components/UserList.tsx +0 -29
  146. package/templates/full-stack/src/client/main.tsx +0 -5
  147. package/templates/full-stack/src/main.css +0 -3
  148. package/templates/full-stack/src/main.js +0 -11
  149. package/templates/full-stack/src/main.ts +0 -20
  150. package/templates/full-stack/src/server/index.ts +0 -99
  151. package/templates/full-stack/src/server/routes/auth.ts +0 -39
  152. package/templates/full-stack/src/server/routes/users.ts +0 -48
  153. package/templates/full-stack/src/shims.ts +0 -9
  154. package/templates/full-stack/tsconfig.json +0 -20
  155. package/templates/full-stack/tsconfig.node.json +0 -10
  156. package/templates/full-stack/tsconfig.server.json +0 -15
  157. package/templates/full-stack/vite.config.js +0 -18
  158. package/templates/full-stack/vite.config.ts +0 -85
package/README.md CHANGED
@@ -13,6 +13,10 @@ A lightweight JavaScript framework with Virtual DOM and hooks implementation ins
13
13
 
14
14
 
15
15
 
16
+
17
+
18
+
19
+
16
20
  ```bash
17
21
  npm install frontend-hamroun
18
22
  ```
package/bin/cli.js ADDED
@@ -0,0 +1,673 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import inquirer from 'inquirer';
5
+ import fs from 'fs-extra';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import chalk from 'chalk';
9
+ import { createSpinner } from 'nanospinner';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // Component templates
15
+ const FUNCTION_COMPONENT_TEMPLATE = (name) => `import { useState, useEffect } from 'frontend-hamroun';
16
+
17
+ export function ${name}(props) {
18
+ // State hooks
19
+ const [state, setState] = useState(null);
20
+
21
+ // Effect hooks
22
+ useEffect(() => {
23
+ // Component mounted
24
+ return () => {
25
+ // Component will unmount
26
+ };
27
+ }, []);
28
+
29
+ return (
30
+ <div className="${name.toLowerCase()}">
31
+ <h2>${name} Component</h2>
32
+ {/* Your JSX here */}
33
+ </div>
34
+ );
35
+ }
36
+ `;
37
+
38
+ const CSS_TEMPLATE = (name) => `.${name.toLowerCase()} {
39
+ display: flex;
40
+ flex-direction: column;
41
+ padding: 1rem;
42
+ margin: 0.5rem;
43
+ border-radius: 4px;
44
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
45
+ }
46
+ `;
47
+
48
+ const TEST_TEMPLATE = (name) => `import { render, screen } from '@testing-library/frontend-hamroun';
49
+ import { ${name} } from './${name}';
50
+
51
+ describe('${name} Component', () => {
52
+ test('renders correctly', () => {
53
+ render(<${name} />);
54
+ const element = screen.getByText('${name} Component');
55
+ expect(element).toBeInTheDocument();
56
+ });
57
+ });
58
+ `;
59
+
60
+ // Dockerfile templates
61
+ const DOCKERFILE_TEMPLATE = `# Stage 1: Build the application
62
+ FROM node:18-alpine as build
63
+
64
+ # Set working directory
65
+ WORKDIR /app
66
+
67
+ # Copy package files
68
+ COPY package.json package-lock.json ./
69
+
70
+ # Install dependencies
71
+ RUN npm ci
72
+
73
+ # Copy source files
74
+ COPY . .
75
+
76
+ # Build the application
77
+ RUN npm run build
78
+
79
+ # Stage 2: Serve the application
80
+ FROM nginx:alpine
81
+
82
+ # Copy the build output from the previous stage
83
+ COPY --from=build /app/dist /usr/share/nginx/html
84
+
85
+ # Expose port 80
86
+ EXPOSE 80
87
+
88
+ # Start nginx
89
+ CMD ["nginx", "-g", "daemon off;"]
90
+ `;
91
+
92
+ const SSR_DOCKERFILE_TEMPLATE = `# Stage 1: Build the application
93
+ FROM node:18-alpine as build
94
+
95
+ # Set working directory
96
+ WORKDIR /app
97
+
98
+ # Copy package files
99
+ COPY package.json package-lock.json ./
100
+
101
+ # Install dependencies
102
+ RUN npm ci
103
+
104
+ # Copy source files
105
+ COPY . .
106
+
107
+ # Build the application
108
+ RUN npm run build
109
+
110
+ # Stage 2: Run the server
111
+ FROM node:18-alpine
112
+
113
+ WORKDIR /app
114
+
115
+ # Copy package files and install production dependencies only
116
+ COPY package.json package-lock.json ./
117
+ RUN npm ci --production
118
+
119
+ # Copy build artifacts
120
+ COPY --from=build /app/dist ./dist
121
+ COPY --from=build /app/server ./server
122
+
123
+ # Expose port 3000
124
+ EXPOSE 3000
125
+
126
+ # Start the server
127
+ CMD ["node", "server/index.js"]
128
+ `;
129
+
130
+ async function init() {
131
+ const program = new Command();
132
+
133
+ program
134
+ .name('frontend-hamroun')
135
+ .description('CLI for Frontend Hamroun framework')
136
+ .version('1.0.0');
137
+
138
+ // Create new project
139
+ program
140
+ .command('create')
141
+ .description('Create a new Frontend Hamroun application')
142
+ .argument('[name]', 'Project name')
143
+ .action(async (name) => {
144
+ const projectName = name || await askProjectName();
145
+ await createProject(projectName);
146
+ });
147
+
148
+ // Generate component
149
+ program
150
+ .command('generate')
151
+ .alias('g')
152
+ .description('Generate a new component')
153
+ .argument('<name>', 'Component name')
154
+ .option('-d, --directory <directory>', 'Target directory', './src/components')
155
+ .action(async (name, options) => {
156
+ await generateComponent(name, options.directory);
157
+ });
158
+
159
+ // Add Dockerfile
160
+ program
161
+ .command('docker')
162
+ .description('Add Dockerfile to project')
163
+ .option('-s, --ssr', 'Use SSR-compatible Dockerfile')
164
+ .action(async (options) => {
165
+ await addDockerfile(options.ssr);
166
+ });
167
+
168
+ // Add generate API route command
169
+ program
170
+ .command('api')
171
+ .description('Generate a new API route')
172
+ .argument('<name>', 'API route name (e.g., users or users/[id])')
173
+ .option('-d, --directory <directory>', 'Target directory', './api')
174
+ .action(async (name, options) => {
175
+ await generateApiRoute(name, options.directory);
176
+ });
177
+
178
+ // Interactive mode if no command provided
179
+ if (process.argv.length <= 2) {
180
+ await interactiveMode();
181
+ } else {
182
+ program.parse();
183
+ }
184
+ }
185
+
186
+ async function interactiveMode() {
187
+ const { action } = await inquirer.prompt([{
188
+ type: 'list',
189
+ name: 'action',
190
+ message: 'What would you like to do?',
191
+ choices: [
192
+ { name: 'Create a new project', value: 'create' },
193
+ { name: 'Generate a component', value: 'generate' },
194
+ { name: 'Generate an API route', value: 'api' },
195
+ { name: 'Add Dockerfile to project', value: 'docker' }
196
+ ]
197
+ }]);
198
+
199
+ if (action === 'create') {
200
+ const projectName = await askProjectName();
201
+ await createProject(projectName);
202
+ } else if (action === 'generate') {
203
+ const { name } = await inquirer.prompt([{
204
+ type: 'input',
205
+ name: 'name',
206
+ message: 'Component name:',
207
+ validate: (input) => input ? true : 'Component name is required'
208
+ }]);
209
+
210
+ const { directory } = await inquirer.prompt([{
211
+ type: 'input',
212
+ name: 'directory',
213
+ message: 'Target directory:',
214
+ default: './src/components'
215
+ }]);
216
+
217
+ await generateComponent(name, directory);
218
+ } else if (action === 'api') {
219
+ const { name } = await inquirer.prompt([{
220
+ type: 'input',
221
+ name: 'name',
222
+ message: 'API route name:',
223
+ validate: (input) => input ? true : 'API route name is required'
224
+ }]);
225
+
226
+ const { directory } = await inquirer.prompt([{
227
+ type: 'input',
228
+ name: 'directory',
229
+ message: 'Target directory:',
230
+ default: './api'
231
+ }]);
232
+
233
+ await generateApiRoute(name, directory);
234
+ } else if (action === 'docker') {
235
+ const { isSSR } = await inquirer.prompt([{
236
+ type: 'confirm',
237
+ name: 'isSSR',
238
+ message: 'Is this a server-side rendered app?',
239
+ default: false
240
+ }]);
241
+
242
+ await addDockerfile(isSSR);
243
+ }
244
+ }
245
+
246
+ async function askProjectName() {
247
+ const { projectName } = await inquirer.prompt([{
248
+ type: 'input',
249
+ name: 'projectName',
250
+ message: 'What is your project named?',
251
+ default: 'my-frontend-app'
252
+ }]);
253
+ return projectName;
254
+ }
255
+
256
+ async function askProjectType() {
257
+ const { template } = await inquirer.prompt([{
258
+ type: 'list',
259
+ name: 'template',
260
+ message: 'Select project type:',
261
+ choices: [
262
+ { name: 'Client Side App', value: 'basic-app' },
263
+ { name: 'Server Side Rendered App', value: 'ssr-template' },
264
+ { name: 'Full Stack App', value: 'fullstack-app' }
265
+ ]
266
+ }]);
267
+ return template;
268
+ }
269
+
270
+ async function createProject(projectName) {
271
+ const spinner = createSpinner('Creating project...').start();
272
+
273
+ try {
274
+ const template = await askProjectType();
275
+ const templateDir = path.join(__dirname, '..', 'templates', template);
276
+ const targetDir = path.join(process.cwd(), projectName);
277
+
278
+ // Create project directory
279
+ await fs.ensureDir(targetDir);
280
+
281
+ // Copy template files
282
+ await fs.copy(templateDir, targetDir);
283
+
284
+ // Update package.json
285
+ const pkgPath = path.join(targetDir, 'package.json');
286
+ const pkg = await fs.readJson(pkgPath);
287
+ pkg.name = projectName;
288
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
289
+
290
+ // Automatically add Dockerfile
291
+ const isSSR = template === 'ssr-template';
292
+ const dockerContent = isSSR ? SSR_DOCKERFILE_TEMPLATE : DOCKERFILE_TEMPLATE;
293
+ await fs.writeFile(path.join(targetDir, 'Dockerfile'), dockerContent);
294
+
295
+ spinner.success({ text: `Project ${chalk.green(projectName)} created successfully with Dockerfile!` });
296
+
297
+ // Show next steps
298
+ console.log('\nNext steps:');
299
+ console.log(chalk.cyan(` cd ${projectName}`));
300
+ console.log(chalk.cyan(' npm install'));
301
+ console.log(chalk.cyan(' npm run dev'));
302
+ console.log(chalk.yellow('\nTo build Docker image:'));
303
+ console.log(chalk.cyan(' docker build -t my-app .'));
304
+ console.log(chalk.cyan(' docker run -p 3000:' + (isSSR ? '3000' : '80') + ' my-app'));
305
+
306
+ } catch (error) {
307
+ spinner.error({ text: 'Failed to create project' });
308
+ console.error(chalk.red(error));
309
+ process.exit(1);
310
+ }
311
+ }
312
+
313
+ async function generateComponent(name, directory) {
314
+ const spinner = createSpinner(`Generating ${name} component...`).start();
315
+
316
+ try {
317
+ const targetDir = path.join(process.cwd(), directory, name);
318
+
319
+ // Create component directory
320
+ await fs.ensureDir(targetDir);
321
+
322
+ // Create component files
323
+ await fs.writeFile(
324
+ path.join(targetDir, `${name}.jsx`),
325
+ FUNCTION_COMPONENT_TEMPLATE(name)
326
+ );
327
+
328
+ await fs.writeFile(
329
+ path.join(targetDir, `${name}.css`),
330
+ CSS_TEMPLATE(name)
331
+ );
332
+
333
+ await fs.writeFile(
334
+ path.join(targetDir, `${name}.test.jsx`),
335
+ TEST_TEMPLATE(name)
336
+ );
337
+
338
+ await fs.writeFile(
339
+ path.join(targetDir, 'index.js'),
340
+ `export { ${name} } from './${name}';\n`
341
+ );
342
+
343
+ spinner.success({ text: `Component ${chalk.green(name)} generated successfully!` });
344
+
345
+ console.log('\nFiles created:');
346
+ console.log(chalk.cyan(` ${path.join(directory, name, `${name}.jsx`)}`));
347
+ console.log(chalk.cyan(` ${path.join(directory, name, `${name}.css`)}`));
348
+ console.log(chalk.cyan(` ${path.join(directory, name, `${name}.test.jsx`)}`));
349
+ console.log(chalk.cyan(` ${path.join(directory, name, 'index.js')}`));
350
+
351
+ } catch (error) {
352
+ spinner.error({ text: 'Failed to generate component' });
353
+ console.error(chalk.red(error));
354
+ process.exit(1);
355
+ }
356
+ }
357
+
358
+ async function generateApiRoute(name, directory) {
359
+ const spinner = createSpinner(`Generating ${name} API route...`).start();
360
+
361
+ try {
362
+ const routePath = name.includes('/') ? name : name;
363
+ const routeDir = path.dirname(path.join(process.cwd(), directory, routePath));
364
+ const routeFileName = path.basename(routePath);
365
+ const targetPath = path.join(routeDir, `${routeFileName}.ts`);
366
+
367
+ // Create directory structure
368
+ await fs.ensureDir(routeDir);
369
+
370
+ // Create API route file
371
+ const isDynamic = routeFileName.startsWith('[') && routeFileName.endsWith(']');
372
+ const template = isDynamic ? DYNAMIC_API_ROUTE_TEMPLATE : API_ROUTE_TEMPLATE;
373
+
374
+ await fs.writeFile(targetPath, template);
375
+
376
+ // Check if server.ts file exists, if not create it
377
+ const serverFilePath = path.join(process.cwd(), 'server.ts');
378
+ if (!await fs.pathExists(serverFilePath)) {
379
+ await fs.writeFile(serverFilePath, SERVER_TEMPLATE);
380
+ console.log(chalk.green('\nCreated server.ts file with Express setup'));
381
+
382
+ // Create tsconfig.server.json if it doesn't exist
383
+ const tsconfigPath = path.join(process.cwd(), 'tsconfig.server.json');
384
+ if (!await fs.pathExists(tsconfigPath)) {
385
+ await fs.writeFile(tsconfigPath, TSCONFIG_SERVER_TEMPLATE);
386
+ console.log(chalk.green('Created tsconfig.server.json for server-side TypeScript'));
387
+ }
388
+
389
+ // Check and update package.json to add server dependencies
390
+ try {
391
+ const pkgPath = path.join(process.cwd(), 'package.json');
392
+ if (await fs.pathExists(pkgPath)) {
393
+ const pkg = await fs.readJson(pkgPath);
394
+
395
+ // Check if we need to add server dependencies
396
+ let needsUpdate = false;
397
+ const serverDeps = {
398
+ "express": "^4.18.2",
399
+ "cors": "^2.8.5",
400
+ };"mongodb": "^5.7.0", // Add MongoDB support
401
+ "jsonwebtoken": "^9.0.2", // Add JWT support for auth
402
+ const devDeps = {.4.3" // Add password hashing support
403
+ "@types/express": "^4.17.17",
404
+ "@types/cors": "^2.8.13"
405
+ };nst devDeps = {
406
+ "@types/express": "^4.17.17",
407
+ // Add dependencies if needed
408
+ pkg.dependencies = pkg.dependencies || {};
409
+ for (const [dep, version] of Object.entries(serverDeps)) {
410
+ if (!pkg.dependencies[dep]) {
411
+ pkg.dependencies[dep] = version;
412
+ needsUpdate = true;
413
+ }Add dependencies if needed
414
+ }kg.dependencies = pkg.dependencies || {};
415
+ for (const [dep, version] of Object.entries(serverDeps)) {
416
+ // Add dev dependencies if needed
417
+ pkg.devDependencies = pkg.devDependencies || {};
418
+ for (const [dep, version] of Object.entries(devDeps)) {
419
+ if (!pkg.devDependencies[dep]) {
420
+ pkg.devDependencies[dep] = version;
421
+ needsUpdate = true;
422
+ }Add dev dependencies if needed
423
+ }kg.devDependencies = pkg.devDependencies || {};
424
+ for (const [dep, version] of Object.entries(devDeps)) {
425
+ // Add start script if it doesn't exist
426
+ pkg.scripts = pkg.scripts || {};ersion;
427
+ if (!pkg.scripts.start) {
428
+ pkg.scripts.start = "node server.js";
429
+ needsUpdate = true;
430
+ }
431
+ // Add start script if it doesn't exist
432
+ // Save changes if needed || {};
433
+ if (needsUpdate) {tart) {
434
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
435
+ console.log(chalk.green('Updated package.json with server dependencies'));
436
+ }
437
+ }
438
+ } catch (error) {es if needed
439
+ console.warn(chalk.yellow('Could not update package.json:', error.message));
440
+ } await fs.writeJson(pkgPath, pkg, { spaces: 2 });
441
+ } console.log(chalk.green('Updated package.json with server dependencies'));
442
+ }
443
+ spinner.success({ text: `API route ${chalk.green(name)} generated successfully!` });
444
+ } catch (error) {
445
+ console.log('\nFile created:');Could not update package.json:', error.message));
446
+ console.log(chalk.cyan(` ${path.join(directory, routePath)}.ts`));
447
+ }
448
+ } catch (error) {
449
+ spinner.error({ text: 'Failed to generate API route' });generated successfully!` });
450
+ console.error(chalk.red(error));
451
+ process.exit(1);ile created:');
452
+ } console.log(chalk.cyan(` ${path.join(directory, routePath)}.ts`));
453
+ }
454
+ } catch (error) {
455
+ // Add a server template for Express generate API route' });
456
+ const SERVER_TEMPLATE = `import { server } from 'frontend-hamroun';
457
+ process.exit(1);
458
+ async function startServer() {
459
+ try {
460
+ // Dynamically import server module
461
+ const { Server } = await server.getServer();
462
+ t SERVER_TEMPLATE = `import { server } from 'frontend-hamroun';
463
+ // Create and configure the server
464
+ const app = new Server({ {
465
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
466
+ apiDir: './api',ort server module
467
+ staticDir: './public', server.getServer();
468
+
469
+ // Enable CORSr
470
+ enableCors: true,t app = new Server({
471
+ corsOptions: { process.env.PORT ? parseInt(process.env.PORT) : 3000,
472
+ origin: process.env.CORS_ORIGIN || '*', // Set to specific domain in production
473
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],lic',
474
+ allowedHeaders: ['Content-Type', 'Authorization']
475
+ }, Uncomment to enable database
476
+ /*
477
+ // Uncomment to enable database (choose one)
478
+ /*url: process.env.DATABASE_URL || 'mongodb://localhost:27017/my_app',
479
+ db: { 'mongodb'
480
+ // MongoDB
481
+ url: process.env.MONGODB_URL || 'mongodb://localhost:27017/my_app',
482
+ type: 'mongodb'
483
+ Uncomment to enable authentication
484
+ // MySQL*
485
+ // url: process.env.MYSQL_URL || 'mysql://user:password@localhost:3306/my_db', auth: {
486
+ // type: 'mysql'.env.JWT_SECRET || 'your-secret-key',
487
+ '
488
+ // PostgreSQL }
489
+ // url: process.env.POSTGRES_URL || 'postgres://user:password@localhost:5432/my_db',
490
+ // type: 'postgres'
491
+ },
492
+ */
493
+
494
+ // Uncomment to enable authentication
495
+ /* running at http://localhost:' +
496
+ auth: {T || 3000));
497
+ secret: process.env.JWT_SECRET || 'your-secret-key',
498
+ expiresIn: '7d'ful shutdown
499
+ }
500
+ */hutting down server...');
501
+ }); await app.stop();
502
+ process.exit(0);
503
+ // Connect to database if configured });
504
+ if (app.getDatabase()) {or) {
505
+ await app.getDatabase().connect(); console.error('Failed to start server:', error);
506
+ console.log('Connected to database'); process.exit(1);
507
+ }
508
+
509
+ // Start the server
510
+ await app.start();rver();
511
+
512
+ console.log('Server running at http://localhost:' +
513
+ (process.env.PORT || 3000));c function addDockerfile(isSSR) {
514
+ ockerfile...').start();
515
+ // Handle graceful shutdown
516
+ process.on('SIGINT', async () => {
517
+ console.log('Shutting down server...');PLATE : DOCKERFILE_TEMPLATE;
518
+ if (app.getDatabase()) {ath.join(process.cwd(), 'Dockerfile');
519
+ await app.getDatabase().disconnect();
520
+ console.log('Database connection closed');
521
+ }xists(targetPath)) {
522
+ await app.stop();ner.stop();
523
+ console.log('Server stopped');const { overwrite } = await inquirer.prompt([{
524
+ process.exit(0);,
525
+ });
526
+ } catch (error) {: 'Dockerfile already exists. Overwrite?',
527
+ console.error('Failed to start server:', error); default: false
528
+ process.exit(1);}]);
529
+ }
530
+ } if (!overwrite) {
531
+ console.log(chalk.yellow('Operation cancelled.'));
532
+ startServer();
533
+ `;
534
+
535
+ async function addDockerfile(isSSR) {
536
+ const spinner = createSpinner('Adding Dockerfile...').start();}
537
+
538
+ try {
539
+ const dockerContent = isSSR ? SSR_DOCKERFILE_TEMPLATE : DOCKERFILE_TEMPLATE;
540
+ const targetPath = path.join(process.cwd(), 'Dockerfile');
541
+ ({ text: 'Dockerfile added successfully!' });
542
+ // Check if Dockerfile already exists
543
+ if (await fs.pathExists(targetPath)) {Docker image:');
544
+ spinner.stop();k.cyan(' docker build -t my-app .'));
545
+ const { overwrite } = await inquirer.prompt([{ console.log(chalk.cyan(' docker run -p 3000:' + (isSSR ? '3000' : '80') + ' my-app'));
546
+ type: 'confirm',
547
+ name: 'overwrite', } catch (error) {
548
+ message: 'Dockerfile already exists. Overwrite?','Failed to add Dockerfile' });
549
+ default: false
550
+ }]); process.exit(1);
551
+
552
+ if (!overwrite) {
553
+ console.log(chalk.yellow('Operation cancelled.'));
554
+ return;lates
555
+ }quest, Response } from 'express';
556
+
557
+ spinner.start();port const get = (req: Request, res: Response) => {
558
+ } res.json({
559
+
560
+ // Write Dockerfileeq.query,
561
+ await fs.writeFile(targetPath, dockerContent);
562
+
563
+ spinner.success({ text: 'Dockerfile added successfully!' });
564
+
565
+ console.log('\nTo build and run Docker image:');port const post = (req: Request, res: Response) => {
566
+ console.log(chalk.cyan(' docker build -t my-app .')); res.json({
567
+ console.log(chalk.cyan(' docker run -p 3000:' + (isSSR ? '3000' : '80') + ' my-app'));
568
+ q.body,
569
+ } catch (error) {)
570
+ spinner.error({ text: 'Failed to add Dockerfile' });
571
+ console.error(chalk.red(error));
572
+ process.exit(1);
573
+ }port const put = (req: Request, res: Response) => {
574
+ } res.json({
575
+
576
+ // Add API route templatesq.body,
577
+ const API_ROUTE_TEMPLATE = `import { Request, Response } from 'express';
578
+
579
+ export const get = (req: Request, res: Response) => {
580
+ res.json({
581
+ message: 'This is a GET endpoint',export const delete_ = (req: Request, res: Response) => {
582
+ query: req.query,
583
+ timestamp: new Date().toISOString()ETE endpoint',
584
+ });().toISOString()
585
+ };
586
+
587
+ export const post = (req: Request, res: Response) => {
588
+ res.json({You can add middleware that will be applied to all methods
589
+ message: 'This is a POST endpoint',port const middleware = [
590
+ body: req.body,// Example middleware
591
+ timestamp: new Date().toISOString() (req: Request, res: Response, next: Function) => {
592
+ });
593
+ }; next();
594
+
595
+ export const put = (req: Request, res: Response) => {
596
+ res.json({
597
+ message: 'This is a PUT endpoint',
598
+ body: req.body,TE_TEMPLATE = `import { Request, Response } from 'express';
599
+ timestamp: new Date().toISOString()
600
+ });t const get = (req: Request, res: Response) => {
601
+ };res.json({
602
+ message: 'This is a dynamic route GET endpoint',
603
+ export const delete_ = (req: Request, res: Response) => {
604
+ res.json({eq.query,
605
+ message: 'This is a DELETE endpoint',
606
+ timestamp: new Date().toISOString()
607
+ });
608
+ };
609
+ t const put = (req: Request, res: Response) => {
610
+ // You can add middleware that will be applied to all methodsres.json({
611
+ export const middleware = [ message: 'This is a dynamic route PUT endpoint',
612
+ // Example middleware
613
+ (req: Request, res: Response, next: Function) => {q.body,
614
+ console.log(\`\${req.method} \${req.url} - \${new Date().toISOString()}\`);
615
+ next();
616
+ }
617
+ ];
618
+ `;port const delete_ = (req: Request, res: Response) => {
619
+ res.json({
620
+ const DYNAMIC_API_ROUTE_TEMPLATE = `import { Request, Response } from 'express'; message: 'This is a dynamic route DELETE endpoint',
621
+
622
+ export const get = (req: Request, res: Response) => {ng()
623
+ res.json({
624
+ message: 'This is a dynamic route GET endpoint',
625
+ params: req.params,
626
+ query: req.query,
627
+ timestamp: new Date().toISOString()template
628
+ });EMPLATE = `{
629
+ };
630
+ ,
631
+ export const put = (req: Request, res: Response) => {ext",
632
+ res.json({on": "NodeNext",
633
+ message: 'This is a dynamic route PUT endpoint',rue,
634
+ params: req.params,"outDir": "./dist",
635
+ body: req.body,
636
+ timestamp: new Date().toISOString()
637
+ }); "noEmit": false,
638
+ }; "strict": true,
639
+
640
+
641
+
642
+
643
+
644
+
645
+
646
+
647
+
648
+
649
+
650
+
651
+
652
+
653
+
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+
663
+
664
+
665
+
666
+
667
+
668
+ init().catch(console.error);}`; "exclude": ["node_modules", "dist", "**/*.test.ts"] "include": ["server.ts", "api/**/*.ts"], }, "skipLibCheck": true "strict": true, "noEmit": false, "sourceMap": true, "declaration": true, "outDir": "./dist", "esModuleInterop": true, "moduleResolution": "NodeNext", "module": "NodeNext", "target": "ES2020", "compilerOptions": {const TSCONFIG_SERVER_TEMPLATE = `{// Add tsconfig.server.json template`;}; }); timestamp: new Date().toISOString() params: req.params, message: 'This is a dynamic route DELETE endpoint', res.json({export const delete_ = (req: Request, res: Response) => { },
669
+ "include": ["server.ts", "api/**/*.ts"],
670
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
671
+ }`;
672
+
673
+ init().catch(console.error);
@@ -8,7 +8,7 @@ export declare class Component {
8
8
  setState(newState: any): Promise<void>;
9
9
  private _replayEvents;
10
10
  private _deepCloneWithEvents;
11
- update(): Promise<Text | HTMLElement>;
11
+ update(): Promise<HTMLElement | Text>;
12
12
  private _updateElement;
13
13
  render(): any;
14
14
  }