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.
- package/README.md +4 -0
- package/bin/cli.js +673 -0
- package/dist/component.d.ts +1 -1
- package/dist/context.d.ts +4 -3
- package/dist/index.client.d.ts +11 -0
- package/dist/index.d.ts +9 -89
- package/dist/index.js +396 -67
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +392 -0
- package/dist/index.mjs.map +1 -0
- package/dist/jsx-runtime/jsx-runtime.d.ts +0 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/renderer.d.ts +0 -10
- package/dist/server-renderer.d.ts +0 -3
- package/dist/server-types.d.ts +42 -0
- package/package.json +69 -41
- package/templates/basic-app/index.html +6 -6
- package/templates/basic-app/package.json +18 -7
- package/templates/basic-app/postcss.config.js +0 -1
- package/templates/basic-app/src/main.tsx +1 -10
- package/templates/basic-app/tailwind.config.js +2 -23
- package/templates/basic-app/tsconfig.json +4 -17
- package/templates/basic-app/vite.config.ts +3 -54
- package/templates/fullstack-app/api/hello.ts +18 -0
- package/templates/fullstack-app/api/users/[id].ts +73 -0
- package/templates/fullstack-app/api/users/index.ts +32 -0
- package/templates/fullstack-app/package.json +31 -0
- package/templates/fullstack-app/server.ts +46 -0
- package/templates/fullstack-app/src/pages/index.tsx +59 -0
- package/templates/ssr-template/vite.config.ts +1 -11
- package/bin/cli.cjs +0 -16
- package/bin/cli.mjs +0 -237
- package/dist/backend/api-utils.d.ts +0 -38
- package/dist/backend/api-utils.js +0 -135
- package/dist/backend/auth.d.ts +0 -134
- package/dist/backend/auth.js +0 -387
- package/dist/backend/database.d.ts +0 -27
- package/dist/backend/database.js +0 -91
- package/dist/backend/model.d.ts +0 -43
- package/dist/backend/model.js +0 -178
- package/dist/backend/router.d.ts +0 -27
- package/dist/backend/router.js +0 -137
- package/dist/backend/server.d.ts +0 -19
- package/dist/backend/server.js +0 -268
- package/dist/backend/types.d.ts +0 -217
- package/dist/backend/types.js +0 -1
- package/dist/batch.js +0 -22
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -215
- package/dist/component.js +0 -84
- package/dist/components/Counter.js +0 -2
- package/dist/context.js +0 -18
- package/dist/frontend-hamroun.es.js +0 -1378
- package/dist/frontend-hamroun.umd.js +0 -66
- package/dist/hooks.js +0 -164
- package/dist/jsx-runtime/index.d.ts +0 -11
- package/dist/jsx-runtime/index.js +0 -19
- package/dist/jsx-runtime/jsx-dev-runtime.js +0 -1
- package/dist/jsx-runtime/jsx-runtime.js +0 -95
- package/dist/jsx-runtime.js +0 -192
- package/dist/renderer.js +0 -51
- package/dist/server-renderer.js +0 -102
- package/dist/types.js +0 -1
- package/dist/vdom.js +0 -27
- package/scripts/build-cli.js +0 -1199
- package/scripts/generate.js +0 -134
- package/src/backend/api-utils.ts +0 -178
- package/src/backend/auth.ts +0 -544
- package/src/backend/database.ts +0 -104
- package/src/backend/model.ts +0 -198
- package/src/backend/router.ts +0 -176
- package/src/backend/server.ts +0 -330
- package/src/backend/types.ts +0 -257
- package/src/batch.ts +0 -24
- package/src/cli/index.js +0 -554
- package/src/cli/index.ts +0 -257
- package/src/component.ts +0 -98
- package/src/components/Counter.tsx +0 -4
- package/src/context.ts +0 -29
- package/src/hooks.ts +0 -211
- package/src/index.ts +0 -144
- package/src/jsx-runtime/index.ts +0 -27
- package/src/jsx-runtime/jsx-dev-runtime.ts +0 -0
- package/src/jsx-runtime/jsx-runtime.ts +0 -104
- package/src/jsx-runtime.ts +0 -226
- package/src/renderer.ts +0 -55
- package/src/server-renderer.ts +0 -114
- package/src/shims.d.ts +0 -20
- package/src/types/bcrypt.d.ts +0 -30
- package/src/types/jsonwebtoken.d.ts +0 -55
- package/src/types.d.ts +0 -26
- package/src/types.ts +0 -21
- package/src/vdom.ts +0 -34
- package/templates/basic/.eslintignore +0 -5
- package/templates/basic/.eslintrc.json +0 -25
- package/templates/basic/docs/rapport_pfe.aux +0 -27
- package/templates/basic/docs/rapport_pfe.log +0 -399
- package/templates/basic/docs/rapport_pfe.out +0 -10
- package/templates/basic/docs/rapport_pfe.pdf +0 -0
- package/templates/basic/docs/rapport_pfe.tex +0 -68
- package/templates/basic/docs/rapport_pfe.toc +0 -14
- package/templates/basic/index.html +0 -12
- package/templates/basic/jsconfig.json +0 -14
- package/templates/basic/package.json +0 -18
- package/templates/basic/postcss.config.js +0 -7
- package/templates/basic/src/App.js +0 -105
- package/templates/basic/src/App.tsx +0 -65
- package/templates/basic/src/api.ts +0 -58
- package/templates/basic/src/components/Counter.tsx +0 -26
- package/templates/basic/src/components/Header.tsx +0 -9
- package/templates/basic/src/components/TodoList.tsx +0 -90
- package/templates/basic/src/main.css +0 -3
- package/templates/basic/src/main.js +0 -11
- package/templates/basic/src/main.ts +0 -20
- package/templates/basic/src/main.tsx +0 -144
- package/templates/basic/src/server.ts +0 -99
- package/templates/basic/tailwind.config.js +0 -32
- package/templates/basic/tsconfig.json +0 -20
- package/templates/basic/tsconfig.node.json +0 -10
- package/templates/basic/vite.config.js +0 -18
- package/templates/basic/vite.config.ts +0 -86
- package/templates/basic-app/src/App.js +0 -105
- package/templates/basic-app/src/App.tsx +0 -143
- package/templates/basic-app/src/api.ts +0 -58
- package/templates/basic-app/src/components/Counter.tsx +0 -26
- package/templates/basic-app/src/components/Header.tsx +0 -9
- package/templates/basic-app/src/components/TodoList.tsx +0 -90
- package/templates/basic-app/src/main.js +0 -10
- package/templates/basic-app/src/main.ts +0 -21
- package/templates/basic-app/src/react/index.ts +0 -35
- package/templates/basic-app/src/react/jsx-dev-runtime.ts +0 -13
- package/templates/basic-app/src/react/jsx-runtime.ts +0 -12
- package/templates/basic-app/src/server.ts +0 -99
- package/templates/basic-app/src/shims.ts +0 -9
- package/templates/basic-app/tsconfig.node.json +0 -10
- package/templates/basic-app/vite.config.js +0 -22
- package/templates/full-stack/.env.example +0 -11
- package/templates/full-stack/README.md +0 -51
- package/templates/full-stack/index.html +0 -12
- package/templates/full-stack/jsconfig.json +0 -14
- package/templates/full-stack/package.json +0 -21
- package/templates/full-stack/src/App.js +0 -105
- package/templates/full-stack/src/client/App.tsx +0 -50
- package/templates/full-stack/src/client/components/Header.tsx +0 -42
- package/templates/full-stack/src/client/components/UserList.tsx +0 -29
- package/templates/full-stack/src/client/main.tsx +0 -5
- package/templates/full-stack/src/main.css +0 -3
- package/templates/full-stack/src/main.js +0 -11
- package/templates/full-stack/src/main.ts +0 -20
- package/templates/full-stack/src/server/index.ts +0 -99
- package/templates/full-stack/src/server/routes/auth.ts +0 -39
- package/templates/full-stack/src/server/routes/users.ts +0 -48
- package/templates/full-stack/src/shims.ts +0 -9
- package/templates/full-stack/tsconfig.json +0 -20
- package/templates/full-stack/tsconfig.node.json +0 -10
- package/templates/full-stack/tsconfig.server.json +0 -15
- package/templates/full-stack/vite.config.js +0 -18
- package/templates/full-stack/vite.config.ts +0 -85
package/README.md
CHANGED
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);
|
package/dist/component.d.ts
CHANGED
@@ -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<
|
11
|
+
update(): Promise<HTMLElement | Text>;
|
12
12
|
private _updateElement;
|
13
13
|
render(): any;
|
14
14
|
}
|