@flexireact/core 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/cli/index.js +1514 -0
  2. package/dist/cli/index.js.map +1 -0
  3. package/dist/core/client/index.js +373 -0
  4. package/dist/core/client/index.js.map +1 -0
  5. package/dist/core/index.js +6415 -0
  6. package/dist/core/index.js.map +1 -0
  7. package/dist/core/server/index.js +3094 -0
  8. package/dist/core/server/index.js.map +1 -0
  9. package/package.json +80 -80
  10. package/bin/flexireact.js +0 -23
  11. package/cli/generators.ts +0 -616
  12. package/cli/index.ts +0 -1182
  13. package/core/actions/index.ts +0 -364
  14. package/core/api.ts +0 -143
  15. package/core/build/index.ts +0 -425
  16. package/core/cli/logger.ts +0 -353
  17. package/core/client/Link.tsx +0 -345
  18. package/core/client/hydration.ts +0 -147
  19. package/core/client/index.ts +0 -12
  20. package/core/client/islands.ts +0 -143
  21. package/core/client/navigation.ts +0 -212
  22. package/core/client/runtime.ts +0 -52
  23. package/core/config.ts +0 -116
  24. package/core/context.ts +0 -83
  25. package/core/dev.ts +0 -47
  26. package/core/devtools/index.ts +0 -644
  27. package/core/edge/cache.ts +0 -344
  28. package/core/edge/fetch-polyfill.ts +0 -247
  29. package/core/edge/handler.ts +0 -248
  30. package/core/edge/index.ts +0 -81
  31. package/core/edge/ppr.ts +0 -264
  32. package/core/edge/runtime.ts +0 -161
  33. package/core/font/index.ts +0 -306
  34. package/core/helpers.ts +0 -494
  35. package/core/image/index.ts +0 -413
  36. package/core/index.ts +0 -218
  37. package/core/islands/index.ts +0 -293
  38. package/core/loader.ts +0 -111
  39. package/core/logger.ts +0 -242
  40. package/core/metadata/index.ts +0 -622
  41. package/core/middleware/index.ts +0 -416
  42. package/core/plugins/index.ts +0 -373
  43. package/core/render/index.ts +0 -1243
  44. package/core/render.ts +0 -136
  45. package/core/router/index.ts +0 -551
  46. package/core/router.ts +0 -141
  47. package/core/rsc/index.ts +0 -199
  48. package/core/server/index.ts +0 -779
  49. package/core/server.ts +0 -203
  50. package/core/ssg/index.ts +0 -346
  51. package/core/start-dev.ts +0 -6
  52. package/core/start-prod.ts +0 -6
  53. package/core/tsconfig.json +0 -30
  54. package/core/types.ts +0 -239
  55. package/core/utils.ts +0 -176
package/cli/generators.ts DELETED
@@ -1,616 +0,0 @@
1
- /**
2
- * FlexiReact CLI Generators
3
- * Scaffolding commands for rapid development
4
- */
5
-
6
- import fs from 'fs';
7
- import path from 'path';
8
- import pc from 'picocolors';
9
- import prompts from 'prompts';
10
-
11
- const log = {
12
- info: (msg: string) => console.log(`${pc.cyan('ℹ')} ${msg}`),
13
- success: (msg: string) => console.log(`${pc.green('✓')} ${msg}`),
14
- warn: (msg: string) => console.log(`${pc.yellow('⚠')} ${pc.yellow(msg)}`),
15
- error: (msg: string) => console.log(`${pc.red('✗')} ${pc.red(msg)}`),
16
- blank: () => console.log(''),
17
- };
18
-
19
- // ============================================================================
20
- // Templates
21
- // ============================================================================
22
-
23
- const templates = {
24
- // Page template
25
- page: (name: string, options: { client?: boolean }) => `${options.client ? "'use client';\n\n" : ''}import React from 'react';
26
-
27
- export default function ${toPascalCase(name)}Page() {
28
- return (
29
- <div className="min-h-screen p-8">
30
- <h1 className="text-4xl font-bold">${toPascalCase(name)}</h1>
31
- <p className="text-gray-400 mt-4">Welcome to ${name} page</p>
32
- </div>
33
- );
34
- }
35
- `,
36
-
37
- // Layout template
38
- layout: (name: string) => `import React from 'react';
39
-
40
- interface ${toPascalCase(name)}LayoutProps {
41
- children: React.ReactNode;
42
- }
43
-
44
- export default function ${toPascalCase(name)}Layout({ children }: ${toPascalCase(name)}LayoutProps) {
45
- return (
46
- <div className="${name}-layout">
47
- {/* Add your layout wrapper here */}
48
- {children}
49
- </div>
50
- );
51
- }
52
- `,
53
-
54
- // Component template
55
- component: (name: string, options: { client?: boolean; props?: boolean }) => {
56
- const propsInterface = options.props ? `
57
- interface ${toPascalCase(name)}Props {
58
- className?: string;
59
- children?: React.ReactNode;
60
- }
61
- ` : '';
62
- const propsType = options.props ? `{ className, children }: ${toPascalCase(name)}Props` : '{}';
63
-
64
- return `${options.client ? "'use client';\n\n" : ''}import React from 'react';
65
- import { cn } from '@/lib/utils';
66
- ${propsInterface}
67
- export function ${toPascalCase(name)}(${propsType}) {
68
- return (
69
- <div className={cn('${toKebabCase(name)}', ${options.props ? 'className' : "''"})}>
70
- ${options.props ? '{children}' : `{/* ${toPascalCase(name)} content */}`}
71
- </div>
72
- );
73
- }
74
-
75
- export default ${toPascalCase(name)};
76
- `;
77
- },
78
-
79
- // Hook template
80
- hook: (name: string) => `import { useState, useEffect, useCallback } from 'react';
81
-
82
- export function use${toPascalCase(name)}() {
83
- const [state, setState] = useState(null);
84
- const [loading, setLoading] = useState(false);
85
- const [error, setError] = useState<Error | null>(null);
86
-
87
- const execute = useCallback(async () => {
88
- setLoading(true);
89
- setError(null);
90
- try {
91
- // Add your logic here
92
- setState(null);
93
- } catch (err) {
94
- setError(err instanceof Error ? err : new Error('Unknown error'));
95
- } finally {
96
- setLoading(false);
97
- }
98
- }, []);
99
-
100
- return { state, loading, error, execute };
101
- }
102
-
103
- export default use${toPascalCase(name)};
104
- `,
105
-
106
- // API route template
107
- api: (name: string) => `import type { IncomingMessage, ServerResponse } from 'http';
108
-
109
- export default async function handler(req: IncomingMessage, res: ServerResponse) {
110
- const method = req.method;
111
-
112
- switch (method) {
113
- case 'GET':
114
- return handleGet(req, res);
115
- case 'POST':
116
- return handlePost(req, res);
117
- default:
118
- res.statusCode = 405;
119
- res.setHeader('Content-Type', 'application/json');
120
- res.end(JSON.stringify({ error: 'Method not allowed' }));
121
- }
122
- }
123
-
124
- async function handleGet(req: IncomingMessage, res: ServerResponse) {
125
- res.statusCode = 200;
126
- res.setHeader('Content-Type', 'application/json');
127
- res.end(JSON.stringify({ message: '${name} API - GET' }));
128
- }
129
-
130
- async function handlePost(req: IncomingMessage, res: ServerResponse) {
131
- // Parse body
132
- let body = '';
133
- for await (const chunk of req) {
134
- body += chunk;
135
- }
136
- const data = body ? JSON.parse(body) : {};
137
-
138
- res.statusCode = 200;
139
- res.setHeader('Content-Type', 'application/json');
140
- res.end(JSON.stringify({ message: '${name} API - POST', data }));
141
- }
142
- `,
143
-
144
- // Server Action template
145
- action: (name: string) => `'use server';
146
-
147
- import { revalidatePath } from '@flexireact/core';
148
-
149
- export async function ${toCamelCase(name)}Action(formData: FormData) {
150
- // Validate input
151
- const data = Object.fromEntries(formData);
152
-
153
- try {
154
- // Add your server logic here
155
- console.log('${toPascalCase(name)} action executed:', data);
156
-
157
- // Revalidate cache if needed
158
- // revalidatePath('/');
159
-
160
- return { success: true, data };
161
- } catch (error) {
162
- return {
163
- success: false,
164
- error: error instanceof Error ? error.message : 'Unknown error'
165
- };
166
- }
167
- }
168
- `,
169
-
170
- // Middleware template
171
- middleware: (name: string) => `import type { IncomingMessage, ServerResponse } from 'http';
172
-
173
- export interface ${toPascalCase(name)}MiddlewareOptions {
174
- // Add your options here
175
- }
176
-
177
- export function ${toCamelCase(name)}Middleware(options: ${toPascalCase(name)}MiddlewareOptions = {}) {
178
- return async (
179
- req: IncomingMessage,
180
- res: ServerResponse,
181
- next: () => Promise<void>
182
- ) => {
183
- // Before request handling
184
- console.log('[${toPascalCase(name)}] Request:', req.url);
185
-
186
- // Continue to next middleware/handler
187
- await next();
188
-
189
- // After request handling (optional)
190
- };
191
- }
192
-
193
- export default ${toCamelCase(name)}Middleware;
194
- `,
195
-
196
- // Context template
197
- context: (name: string) => `'use client';
198
-
199
- import React, { createContext, useContext, useState, useCallback } from 'react';
200
-
201
- interface ${toPascalCase(name)}State {
202
- // Add your state properties here
203
- value: string | null;
204
- }
205
-
206
- interface ${toPascalCase(name)}ContextValue extends ${toPascalCase(name)}State {
207
- setValue: (value: string) => void;
208
- reset: () => void;
209
- }
210
-
211
- const ${toPascalCase(name)}Context = createContext<${toPascalCase(name)}ContextValue | null>(null);
212
-
213
- export function ${toPascalCase(name)}Provider({ children }: { children: React.ReactNode }) {
214
- const [state, setState] = useState<${toPascalCase(name)}State>({
215
- value: null,
216
- });
217
-
218
- const setValue = useCallback((value: string) => {
219
- setState(prev => ({ ...prev, value }));
220
- }, []);
221
-
222
- const reset = useCallback(() => {
223
- setState({ value: null });
224
- }, []);
225
-
226
- return (
227
- <${toPascalCase(name)}Context.Provider value={{ ...state, setValue, reset }}>
228
- {children}
229
- </${toPascalCase(name)}Context.Provider>
230
- );
231
- }
232
-
233
- export function use${toPascalCase(name)}() {
234
- const context = useContext(${toPascalCase(name)}Context);
235
- if (!context) {
236
- throw new Error('use${toPascalCase(name)} must be used within a ${toPascalCase(name)}Provider');
237
- }
238
- return context;
239
- }
240
- `,
241
-
242
- // Loading template
243
- loading: () => `import React from 'react';
244
- import { Spinner } from '@flexireact/flexi-ui';
245
-
246
- export default function Loading() {
247
- return (
248
- <div className="min-h-screen flex items-center justify-center">
249
- <Spinner size="lg" />
250
- </div>
251
- );
252
- }
253
- `,
254
-
255
- // Error template
256
- error: () => `'use client';
257
-
258
- import React from 'react';
259
- import { Button, Alert } from '@flexireact/flexi-ui';
260
-
261
- interface ErrorProps {
262
- error: Error;
263
- reset: () => void;
264
- }
265
-
266
- export default function Error({ error, reset }: ErrorProps) {
267
- return (
268
- <div className="min-h-screen flex flex-col items-center justify-center p-8">
269
- <Alert variant="error" className="max-w-md mb-8">
270
- <h2 className="font-bold text-lg mb-2">Something went wrong</h2>
271
- <p className="text-sm">{error.message}</p>
272
- </Alert>
273
- <Button onClick={reset}>Try again</Button>
274
- </div>
275
- );
276
- }
277
- `,
278
-
279
- // Not found template
280
- notFound: () => `import React from 'react';
281
- import { Button } from '@flexireact/flexi-ui';
282
-
283
- export default function NotFound() {
284
- return (
285
- <div className="min-h-screen flex flex-col items-center justify-center p-8">
286
- <h1 className="text-8xl font-bold text-primary mb-4">404</h1>
287
- <p className="text-gray-400 text-xl mb-8">Page not found</p>
288
- <Button asChild>
289
- <a href="/">← Back Home</a>
290
- </Button>
291
- </div>
292
- );
293
- }
294
- `,
295
- };
296
-
297
- // ============================================================================
298
- // Utility Functions
299
- // ============================================================================
300
-
301
- function toPascalCase(str: string): string {
302
- return str
303
- .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
304
- .replace(/^(.)/, (_, c) => c.toUpperCase());
305
- }
306
-
307
- function toCamelCase(str: string): string {
308
- return str
309
- .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
310
- .replace(/^(.)/, (_, c) => c.toLowerCase());
311
- }
312
-
313
- function toKebabCase(str: string): string {
314
- return str
315
- .replace(/([a-z])([A-Z])/g, '$1-$2')
316
- .replace(/[_\s]+/g, '-')
317
- .toLowerCase();
318
- }
319
-
320
- function ensureDir(filePath: string): void {
321
- const dir = path.dirname(filePath);
322
- if (!fs.existsSync(dir)) {
323
- fs.mkdirSync(dir, { recursive: true });
324
- }
325
- }
326
-
327
- function writeFile(filePath: string, content: string): void {
328
- ensureDir(filePath);
329
- fs.writeFileSync(filePath, content);
330
- }
331
-
332
- // ============================================================================
333
- // Generator Commands
334
- // ============================================================================
335
-
336
- export type GeneratorType =
337
- | 'page'
338
- | 'layout'
339
- | 'component'
340
- | 'hook'
341
- | 'api'
342
- | 'action'
343
- | 'middleware'
344
- | 'context'
345
- | 'loading'
346
- | 'error'
347
- | 'not-found';
348
-
349
- export async function runGenerate(type?: string, name?: string): Promise<void> {
350
- const cwd = process.cwd();
351
-
352
- // Check if we're in a FlexiReact project
353
- if (!fs.existsSync(path.join(cwd, 'package.json'))) {
354
- log.error('Not in a FlexiReact project. Run this command in your project root.');
355
- process.exit(1);
356
- }
357
-
358
- // Interactive mode if no type provided
359
- if (!type) {
360
- const response = await prompts([
361
- {
362
- type: 'select',
363
- name: 'type',
364
- message: 'What do you want to generate?',
365
- choices: [
366
- { title: '📄 Page', value: 'page', description: 'A new page in app/ or pages/' },
367
- { title: '📐 Layout', value: 'layout', description: 'A layout wrapper component' },
368
- { title: '🧩 Component', value: 'component', description: 'A reusable React component' },
369
- { title: '🪝 Hook', value: 'hook', description: 'A custom React hook' },
370
- { title: '🔌 API Route', value: 'api', description: 'An API endpoint' },
371
- { title: '⚡ Server Action', value: 'action', description: 'A server action function' },
372
- { title: '🛡️ Middleware', value: 'middleware', description: 'Request middleware' },
373
- { title: '🌐 Context', value: 'context', description: 'React context provider' },
374
- { title: '⏳ Loading', value: 'loading', description: 'Loading state component' },
375
- { title: '❌ Error', value: 'error', description: 'Error boundary component' },
376
- { title: '🔍 Not Found', value: 'not-found', description: '404 page component' },
377
- ],
378
- },
379
- {
380
- type: (prev) => ['loading', 'error', 'not-found'].includes(prev) ? null : 'text',
381
- name: 'name',
382
- message: 'Name:',
383
- validate: (v: string) => v.length > 0 || 'Name is required',
384
- },
385
- ]);
386
-
387
- if (!response.type) process.exit(0);
388
- type = response.type;
389
- name = response.name;
390
- }
391
-
392
- // Validate type
393
- const validTypes: GeneratorType[] = [
394
- 'page', 'layout', 'component', 'hook', 'api',
395
- 'action', 'middleware', 'context', 'loading', 'error', 'not-found'
396
- ];
397
-
398
- if (!validTypes.includes(type as GeneratorType)) {
399
- log.error(`Invalid type: ${type}`);
400
- log.info(`Valid types: ${validTypes.join(', ')}`);
401
- process.exit(1);
402
- }
403
-
404
- // Special types that don't need a name
405
- if (type && ['loading', 'error', 'not-found'].includes(type)) {
406
- await generateSpecialFile(type as 'loading' | 'error' | 'not-found', cwd);
407
- return;
408
- }
409
-
410
- // Get name if not provided
411
- let finalName = name;
412
- if (!finalName) {
413
- const response = await prompts({
414
- type: 'text',
415
- name: 'name',
416
- message: `${toPascalCase(type || 'item')} name:`,
417
- validate: (v: string) => v.length > 0 || 'Name is required',
418
- });
419
- finalName = response.name;
420
- if (!finalName) process.exit(0);
421
- }
422
-
423
- // Generate based on type
424
- switch (type) {
425
- case 'page':
426
- await generatePage(finalName, cwd);
427
- break;
428
- case 'layout':
429
- await generateLayout(finalName, cwd);
430
- break;
431
- case 'component':
432
- await generateComponent(finalName, cwd);
433
- break;
434
- case 'hook':
435
- await generateHook(finalName, cwd);
436
- break;
437
- case 'api':
438
- await generateApi(finalName, cwd);
439
- break;
440
- case 'action':
441
- await generateAction(finalName, cwd);
442
- break;
443
- case 'middleware':
444
- await generateMiddleware(finalName, cwd);
445
- break;
446
- case 'context':
447
- await generateContext(finalName, cwd);
448
- break;
449
- }
450
- }
451
-
452
- async function generatePage(name: string, cwd: string): Promise<void> {
453
- const response = await prompts([
454
- {
455
- type: 'select',
456
- name: 'directory',
457
- message: 'Where to create the page?',
458
- choices: [
459
- { title: 'app/ (App Router)', value: 'app' },
460
- { title: 'pages/ (Pages Router)', value: 'pages' },
461
- ],
462
- },
463
- {
464
- type: 'toggle',
465
- name: 'client',
466
- message: 'Client component? (use client)',
467
- initial: false,
468
- active: 'Yes',
469
- inactive: 'No',
470
- },
471
- ]);
472
-
473
- const fileName = response.directory === 'app' ? 'page.tsx' : `${toKebabCase(name)}.tsx`;
474
- const filePath = response.directory === 'app'
475
- ? path.join(cwd, 'app', toKebabCase(name), fileName)
476
- : path.join(cwd, 'pages', fileName);
477
-
478
- writeFile(filePath, templates.page(name, { client: response.client }));
479
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
480
- }
481
-
482
- async function generateLayout(name: string, cwd: string): Promise<void> {
483
- const filePath = path.join(cwd, 'app', toKebabCase(name), 'layout.tsx');
484
- writeFile(filePath, templates.layout(name));
485
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
486
- }
487
-
488
- async function generateComponent(name: string, cwd: string): Promise<void> {
489
- const response = await prompts([
490
- {
491
- type: 'select',
492
- name: 'directory',
493
- message: 'Where to create the component?',
494
- choices: [
495
- { title: 'components/', value: 'components' },
496
- { title: 'app/components/', value: 'app/components' },
497
- ],
498
- },
499
- {
500
- type: 'toggle',
501
- name: 'client',
502
- message: 'Client component?',
503
- initial: true,
504
- active: 'Yes',
505
- inactive: 'No',
506
- },
507
- {
508
- type: 'toggle',
509
- name: 'props',
510
- message: 'Include props interface?',
511
- initial: true,
512
- active: 'Yes',
513
- inactive: 'No',
514
- },
515
- ]);
516
-
517
- const filePath = path.join(cwd, response.directory, `${toPascalCase(name)}.tsx`);
518
- writeFile(filePath, templates.component(name, { client: response.client, props: response.props }));
519
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
520
- }
521
-
522
- async function generateHook(name: string, cwd: string): Promise<void> {
523
- const hookName = name.startsWith('use') ? name : `use-${name}`;
524
- const filePath = path.join(cwd, 'hooks', `${toKebabCase(hookName)}.ts`);
525
- writeFile(filePath, templates.hook(hookName.replace(/^use-?/, '')));
526
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
527
- }
528
-
529
- async function generateApi(name: string, cwd: string): Promise<void> {
530
- const filePath = path.join(cwd, 'pages', 'api', `${toKebabCase(name)}.ts`);
531
- writeFile(filePath, templates.api(name));
532
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
533
- }
534
-
535
- async function generateAction(name: string, cwd: string): Promise<void> {
536
- const filePath = path.join(cwd, 'actions', `${toKebabCase(name)}.ts`);
537
- writeFile(filePath, templates.action(name));
538
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
539
- }
540
-
541
- async function generateMiddleware(name: string, cwd: string): Promise<void> {
542
- const filePath = path.join(cwd, 'middleware', `${toKebabCase(name)}.ts`);
543
- writeFile(filePath, templates.middleware(name));
544
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
545
- }
546
-
547
- async function generateContext(name: string, cwd: string): Promise<void> {
548
- const filePath = path.join(cwd, 'contexts', `${toPascalCase(name)}Context.tsx`);
549
- writeFile(filePath, templates.context(name));
550
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
551
- }
552
-
553
- async function generateSpecialFile(type: 'loading' | 'error' | 'not-found', cwd: string): Promise<void> {
554
- const response = await prompts({
555
- type: 'text',
556
- name: 'path',
557
- message: 'Path (relative to app/):',
558
- initial: '',
559
- });
560
-
561
- const basePath = response.path ? path.join(cwd, 'app', response.path) : path.join(cwd, 'app');
562
-
563
- let fileName: string;
564
- let content: string;
565
-
566
- switch (type) {
567
- case 'loading':
568
- fileName = 'loading.tsx';
569
- content = templates.loading();
570
- break;
571
- case 'error':
572
- fileName = 'error.tsx';
573
- content = templates.error();
574
- break;
575
- case 'not-found':
576
- fileName = 'not-found.tsx';
577
- content = templates.notFound();
578
- break;
579
- }
580
-
581
- const filePath = path.join(basePath, fileName);
582
- writeFile(filePath, content);
583
- log.success(`Created ${pc.cyan(path.relative(cwd, filePath))}`);
584
- }
585
-
586
- // ============================================================================
587
- // List Generators
588
- // ============================================================================
589
-
590
- export function listGenerators(): void {
591
- console.log(`
592
- ${pc.bold('Available Generators:')}
593
-
594
- ${pc.cyan('page')} Create a new page (app/ or pages/)
595
- ${pc.cyan('layout')} Create a layout wrapper
596
- ${pc.cyan('component')} Create a React component
597
- ${pc.cyan('hook')} Create a custom hook
598
- ${pc.cyan('api')} Create an API route
599
- ${pc.cyan('action')} Create a server action
600
- ${pc.cyan('middleware')} Create request middleware
601
- ${pc.cyan('context')} Create a React context
602
- ${pc.cyan('loading')} Create a loading component
603
- ${pc.cyan('error')} Create an error boundary
604
- ${pc.cyan('not-found')} Create a 404 page
605
-
606
- ${pc.bold('Usage:')}
607
- ${pc.dim('$')} flexi generate ${pc.cyan('<type>')} ${pc.dim('[name]')}
608
- ${pc.dim('$')} flexi g ${pc.cyan('<type>')} ${pc.dim('[name]')}
609
-
610
- ${pc.bold('Examples:')}
611
- ${pc.dim('$')} flexi g page dashboard
612
- ${pc.dim('$')} flexi g component Button
613
- ${pc.dim('$')} flexi g hook auth
614
- ${pc.dim('$')} flexi g api users
615
- `);
616
- }