@flight-framework/cli 0.0.10 → 0.0.12
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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/bin.js +311 -2
- package/dist/bin.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.js +311 -2
- package/dist/index.js.map +1 -1
- package/package.json +53 -53
- package/templates/base/README.md.template +26 -26
- package/templates/base/_gitignore +25 -25
- package/templates/base/flight.config.ts.template +15 -10
- package/templates/base/styles/global.css +58 -58
- package/templates/htmx/index.html +18 -18
- package/templates/htmx/package.json.template +18 -14
- package/templates/htmx/vite.config.ts +6 -6
- package/templates/lit/index.html +14 -14
- package/templates/lit/package.json.template +21 -19
- package/templates/lit/src/app-root.ts +18 -18
- package/templates/lit/src/entry-client.ts +5 -5
- package/templates/lit/src/entry-server.ts +9 -9
- package/templates/lit/tsconfig.json +18 -18
- package/templates/lit/vite.config.ts +6 -6
- package/templates/preact/index.html +14 -14
- package/templates/preact/package.json.template +22 -20
- package/templates/preact/src/App.tsx +8 -8
- package/templates/preact/src/entry-client.tsx +11 -11
- package/templates/preact/src/entry-server.tsx +6 -6
- package/templates/preact/tsconfig.json +18 -18
- package/templates/preact/vite.config.ts +8 -8
- package/templates/qwik/index.html +14 -14
- package/templates/qwik/package.json.template +20 -18
- package/templates/qwik/src/App.tsx +10 -10
- package/templates/qwik/src/entry-client.tsx +4 -4
- package/templates/qwik/src/entry-server.tsx +9 -9
- package/templates/qwik/tsconfig.json +18 -18
- package/templates/qwik/vite.config.ts +8 -8
- package/templates/react/index.html +13 -13
- package/templates/react/package.json.template +24 -22
- package/templates/react/src/App.tsx +13 -13
- package/templates/react/src/context/RouterContext.tsx +63 -63
- package/templates/react/src/entry-client.tsx +19 -19
- package/templates/react/src/entry-server.tsx +17 -17
- package/templates/react/tsconfig.json +19 -19
- package/templates/react/vite.config.ts +12 -12
- package/templates/solid/index.html +14 -14
- package/templates/solid/package.json.template +21 -19
- package/templates/solid/src/App.tsx +8 -8
- package/templates/solid/src/entry-client.tsx +11 -11
- package/templates/solid/src/entry-server.tsx +6 -6
- package/templates/solid/tsconfig.json +18 -18
- package/templates/solid/vite.config.ts +8 -8
- package/templates/svelte/index.html +14 -14
- package/templates/svelte/package.json.template +21 -19
- package/templates/svelte/src/App.svelte +4 -4
- package/templates/svelte/src/entry-client.ts +7 -7
- package/templates/svelte/src/entry-server.ts +7 -7
- package/templates/svelte/tsconfig.json +17 -17
- package/templates/svelte/vite.config.ts +8 -8
- package/templates/vanilla/index.html +14 -14
- package/templates/vanilla/package.json.template +19 -15
- package/templates/vanilla/src/main.ts +10 -10
- package/templates/vanilla/tsconfig.json +16 -16
- package/templates/vanilla/vite.config.ts +6 -6
- package/templates/vue/index.html +14 -14
- package/templates/vue/package.json.template +21 -19
- package/templates/vue/src/App.vue +6 -6
- package/templates/vue/src/entry-client.ts +12 -12
- package/templates/vue/src/entry-server.ts +8 -8
- package/templates/vue/tsconfig.json +17 -17
- package/templates/vue/vite.config.ts +8 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Flight Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @flight-framework/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for Flight Framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @flight-framework/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use via npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @flight-framework/cli <command>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
### create
|
|
20
|
+
|
|
21
|
+
Create a new Flight project.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
flight create [project-name]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
|
|
29
|
+
| Option | Description |
|
|
30
|
+
|--------|-------------|
|
|
31
|
+
| `--ui <framework>` | UI framework: react, vue, svelte, solid, preact, qwik, lit, htmx |
|
|
32
|
+
| `--typescript` | Use TypeScript (default) |
|
|
33
|
+
| `--no-typescript` | Use JavaScript |
|
|
34
|
+
|
|
35
|
+
### dev
|
|
36
|
+
|
|
37
|
+
Start the development server.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
flight dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
|
|
45
|
+
| Option | Description | Default |
|
|
46
|
+
|--------|-------------|---------|
|
|
47
|
+
| `-p, --port` | Port number | `5173` |
|
|
48
|
+
| `-h, --host` | Host to bind | `localhost` |
|
|
49
|
+
| `--open` | Open browser | `false` |
|
|
50
|
+
| `--ssr` | Enable SSR | From config |
|
|
51
|
+
|
|
52
|
+
### build
|
|
53
|
+
|
|
54
|
+
Build for production.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
flight build
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### start
|
|
61
|
+
|
|
62
|
+
Start the production server.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
flight start
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Options:
|
|
69
|
+
|
|
70
|
+
| Option | Description | Default |
|
|
71
|
+
|--------|-------------|---------|
|
|
72
|
+
| `-p, --port` | Port number | `3000` |
|
|
73
|
+
| `-h, --host` | Host to bind | `0.0.0.0` |
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
package/dist/bin.js
CHANGED
|
@@ -33,6 +33,11 @@ var UI_FRAMEWORKS = [
|
|
|
33
33
|
{ title: "Htmx", value: "htmx", description: "HTML over the wire, no JavaScript" },
|
|
34
34
|
{ title: "Vanilla", value: "vanilla", description: "No framework, just TypeScript" }
|
|
35
35
|
];
|
|
36
|
+
var BUNDLERS = [
|
|
37
|
+
{ title: "Vite", value: "vite", description: "Next Gen Frontend Tooling (recommended)" },
|
|
38
|
+
{ title: "esbuild", value: "esbuild", description: "Ultra-fast builds for performance-focused projects" },
|
|
39
|
+
{ title: "Rolldown", value: "rolldown", description: "Rust-based Rollup replacement (experimental)" }
|
|
40
|
+
];
|
|
36
41
|
async function createCommand(name, options) {
|
|
37
42
|
printLogo();
|
|
38
43
|
console.log(pc.cyan("\n[*] Creating a new Flight project...\n"));
|
|
@@ -70,6 +75,21 @@ async function createCommand(name, options) {
|
|
|
70
75
|
console.log(pc.red("Project creation cancelled."));
|
|
71
76
|
return;
|
|
72
77
|
}
|
|
78
|
+
let bundler = options.bundler;
|
|
79
|
+
if (!bundler) {
|
|
80
|
+
const response = await prompts({
|
|
81
|
+
type: "select",
|
|
82
|
+
name: "bundler",
|
|
83
|
+
message: "Choose your bundler:",
|
|
84
|
+
choices: BUNDLERS,
|
|
85
|
+
initial: 0
|
|
86
|
+
});
|
|
87
|
+
bundler = response.bundler;
|
|
88
|
+
}
|
|
89
|
+
if (!bundler) {
|
|
90
|
+
console.log(pc.red("Project creation cancelled."));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
73
93
|
const projectPath = resolve(process.cwd(), projectName);
|
|
74
94
|
if (existsSync(projectPath)) {
|
|
75
95
|
const files = readdirSync(projectPath);
|
|
@@ -90,7 +110,7 @@ async function createCommand(name, options) {
|
|
|
90
110
|
Creating project in ${projectPath}...
|
|
91
111
|
`));
|
|
92
112
|
try {
|
|
93
|
-
copyTemplate(projectPath, uiFramework, projectName);
|
|
113
|
+
copyTemplate(projectPath, uiFramework, bundler, projectName);
|
|
94
114
|
console.log(pc.green("\u2713") + " Project structure created");
|
|
95
115
|
if (options.git) {
|
|
96
116
|
try {
|
|
@@ -136,7 +156,7 @@ function detectPackageManager() {
|
|
|
136
156
|
if (process.env.npm_config_user_agent?.includes("bun")) return "bun";
|
|
137
157
|
return "npm";
|
|
138
158
|
}
|
|
139
|
-
function copyTemplate(projectPath, ui, projectName) {
|
|
159
|
+
function copyTemplate(projectPath, ui, bundler, projectName) {
|
|
140
160
|
const baseDir = join(TEMPLATES_DIR, "base");
|
|
141
161
|
const uiDir = join(TEMPLATES_DIR, ui);
|
|
142
162
|
mkdirSync(projectPath, { recursive: true });
|
|
@@ -146,6 +166,8 @@ function copyTemplate(projectPath, ui, projectName) {
|
|
|
146
166
|
const vars = {
|
|
147
167
|
"{{PROJECT_NAME}}": projectName,
|
|
148
168
|
"{{UI_FRAMEWORK}}": ui,
|
|
169
|
+
"{{BUNDLER}}": bundler,
|
|
170
|
+
"{{BUNDLER_PACKAGE}}": `@flight-framework/bundler-${bundler}`,
|
|
149
171
|
"{{LANGUAGE}}": "TypeScript"
|
|
150
172
|
};
|
|
151
173
|
copyDirWithTemplates(baseDir, projectPath, vars);
|
|
@@ -571,6 +593,292 @@ async function previewCommand(options) {
|
|
|
571
593
|
}
|
|
572
594
|
}
|
|
573
595
|
|
|
596
|
+
// src/commands/routes-generate.ts
|
|
597
|
+
import { resolve as resolve5 } from "path";
|
|
598
|
+
|
|
599
|
+
// src/generators/routes.ts
|
|
600
|
+
import { readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
601
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
602
|
+
function filePathToUrlPath(filePath) {
|
|
603
|
+
let urlPath = filePath.replace(/\.(page|route)\.(tsx?|jsx?)$/, "").replace(/\.(get|post|put|patch|delete|options|head)$/, "").replace(/\/index$/, "").replace(/\/?\([^)]+\)/g, "").replace(/\[\.\.\.(\w+)\]/g, "*$1").replace(/\[(\w+)\]/g, ":$1");
|
|
604
|
+
if (!urlPath.startsWith("/")) {
|
|
605
|
+
urlPath = "/" + urlPath;
|
|
606
|
+
}
|
|
607
|
+
if (urlPath === "" || urlPath === "/") {
|
|
608
|
+
urlPath = "/";
|
|
609
|
+
}
|
|
610
|
+
urlPath = urlPath.replace(/\/+/g, "/");
|
|
611
|
+
return urlPath;
|
|
612
|
+
}
|
|
613
|
+
function extractHttpMethod(filename) {
|
|
614
|
+
const match = filename.match(/\.(get|post|put|patch|delete|options|head)\.(tsx?|jsx?)$/i);
|
|
615
|
+
return match ? match[1].toUpperCase() : void 0;
|
|
616
|
+
}
|
|
617
|
+
function isLayoutFile(filename) {
|
|
618
|
+
return filename.startsWith("_layout.");
|
|
619
|
+
}
|
|
620
|
+
function isLoadingFile(filename) {
|
|
621
|
+
return filename.startsWith("_loading.");
|
|
622
|
+
}
|
|
623
|
+
function isErrorFile(filename) {
|
|
624
|
+
return filename.startsWith("_error.");
|
|
625
|
+
}
|
|
626
|
+
function isNotFoundFile(filename) {
|
|
627
|
+
return filename.startsWith("_not-found.");
|
|
628
|
+
}
|
|
629
|
+
function isRouteFile(filename) {
|
|
630
|
+
return /\.(page|route)\.(tsx?|jsx?)$/.test(filename);
|
|
631
|
+
}
|
|
632
|
+
function isApiRouteFile(filename) {
|
|
633
|
+
return /\.(get|post|put|patch|delete|options|head)\.(tsx?|jsx?)$/.test(filename);
|
|
634
|
+
}
|
|
635
|
+
function hasDynamicSegments(path) {
|
|
636
|
+
return path.includes("[") && path.includes("]");
|
|
637
|
+
}
|
|
638
|
+
function scanDirectory(dir, basePath = "", results = []) {
|
|
639
|
+
if (!existsSync4(dir)) {
|
|
640
|
+
return results;
|
|
641
|
+
}
|
|
642
|
+
const entries = readdirSync2(dir);
|
|
643
|
+
for (const entry of entries) {
|
|
644
|
+
const fullPath = join3(dir, entry);
|
|
645
|
+
const relativePath = join3(basePath, entry);
|
|
646
|
+
const stat = statSync2(fullPath);
|
|
647
|
+
if (stat.isDirectory()) {
|
|
648
|
+
if (entry.startsWith(".") || entry === "node_modules") {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
scanDirectory(fullPath, relativePath, results);
|
|
652
|
+
} else if (stat.isFile()) {
|
|
653
|
+
const routePath = filePathToUrlPath(dirname2(relativePath));
|
|
654
|
+
const normalizedFilePath = relativePath.replace(/\\/g, "/");
|
|
655
|
+
const isDynamic = hasDynamicSegments(relativePath);
|
|
656
|
+
if (isLayoutFile(entry)) {
|
|
657
|
+
results.push({
|
|
658
|
+
path: routePath,
|
|
659
|
+
filePath: normalizedFilePath,
|
|
660
|
+
isLayout: true,
|
|
661
|
+
isLoading: false,
|
|
662
|
+
isError: false,
|
|
663
|
+
isNotFound: false,
|
|
664
|
+
isDynamic,
|
|
665
|
+
isApiRoute: false
|
|
666
|
+
});
|
|
667
|
+
} else if (isLoadingFile(entry)) {
|
|
668
|
+
results.push({
|
|
669
|
+
path: routePath,
|
|
670
|
+
filePath: normalizedFilePath,
|
|
671
|
+
isLayout: false,
|
|
672
|
+
isLoading: true,
|
|
673
|
+
isError: false,
|
|
674
|
+
isNotFound: false,
|
|
675
|
+
isDynamic,
|
|
676
|
+
isApiRoute: false
|
|
677
|
+
});
|
|
678
|
+
} else if (isErrorFile(entry)) {
|
|
679
|
+
results.push({
|
|
680
|
+
path: routePath,
|
|
681
|
+
filePath: normalizedFilePath,
|
|
682
|
+
isLayout: false,
|
|
683
|
+
isLoading: false,
|
|
684
|
+
isError: true,
|
|
685
|
+
isNotFound: false,
|
|
686
|
+
isDynamic,
|
|
687
|
+
isApiRoute: false
|
|
688
|
+
});
|
|
689
|
+
} else if (isNotFoundFile(entry)) {
|
|
690
|
+
results.push({
|
|
691
|
+
path: routePath,
|
|
692
|
+
filePath: normalizedFilePath,
|
|
693
|
+
isLayout: false,
|
|
694
|
+
isLoading: false,
|
|
695
|
+
isError: false,
|
|
696
|
+
isNotFound: true,
|
|
697
|
+
isDynamic,
|
|
698
|
+
isApiRoute: false
|
|
699
|
+
});
|
|
700
|
+
} else if (isApiRouteFile(entry)) {
|
|
701
|
+
const method = extractHttpMethod(entry);
|
|
702
|
+
results.push({
|
|
703
|
+
path: filePathToUrlPath(relativePath),
|
|
704
|
+
filePath: normalizedFilePath,
|
|
705
|
+
isLayout: false,
|
|
706
|
+
isLoading: false,
|
|
707
|
+
isError: false,
|
|
708
|
+
isNotFound: false,
|
|
709
|
+
isDynamic,
|
|
710
|
+
isApiRoute: true,
|
|
711
|
+
httpMethod: method
|
|
712
|
+
});
|
|
713
|
+
} else if (isRouteFile(entry)) {
|
|
714
|
+
results.push({
|
|
715
|
+
path: filePathToUrlPath(relativePath),
|
|
716
|
+
filePath: normalizedFilePath,
|
|
717
|
+
isLayout: false,
|
|
718
|
+
isLoading: false,
|
|
719
|
+
isError: false,
|
|
720
|
+
isNotFound: false,
|
|
721
|
+
isDynamic,
|
|
722
|
+
isApiRoute: false
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return results;
|
|
728
|
+
}
|
|
729
|
+
function sortRoutes(routes) {
|
|
730
|
+
return routes.sort((a, b) => {
|
|
731
|
+
if (!a.isDynamic && b.isDynamic) return -1;
|
|
732
|
+
if (a.isDynamic && !b.isDynamic) return 1;
|
|
733
|
+
const aSegments = a.path.split("/").length;
|
|
734
|
+
const bSegments = b.path.split("/").length;
|
|
735
|
+
if (aSegments !== bSegments) return aSegments - bSegments;
|
|
736
|
+
return a.path.localeCompare(b.path);
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function generateRouteManifest(routesDir) {
|
|
740
|
+
const allRoutes = scanDirectory(routesDir);
|
|
741
|
+
const isPageRoute = (r) => !r.isLayout && !r.isLoading && !r.isError && !r.isNotFound && !r.isApiRoute;
|
|
742
|
+
const routes = sortRoutes(allRoutes.filter(isPageRoute));
|
|
743
|
+
const layouts = allRoutes.filter((r) => r.isLayout);
|
|
744
|
+
const loadingStates = allRoutes.filter((r) => r.isLoading);
|
|
745
|
+
const errorBoundaries = allRoutes.filter((r) => r.isError);
|
|
746
|
+
const notFoundPages = allRoutes.filter((r) => r.isNotFound);
|
|
747
|
+
const apiRoutes = sortRoutes(allRoutes.filter((r) => r.isApiRoute));
|
|
748
|
+
return {
|
|
749
|
+
routes,
|
|
750
|
+
layouts,
|
|
751
|
+
loadingStates,
|
|
752
|
+
errorBoundaries,
|
|
753
|
+
notFoundPages,
|
|
754
|
+
apiRoutes,
|
|
755
|
+
generated: (/* @__PURE__ */ new Date()).toISOString()
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function generateRoutesFile(manifest, outputDir) {
|
|
759
|
+
if (!existsSync4(outputDir)) {
|
|
760
|
+
mkdirSync2(outputDir, { recursive: true });
|
|
761
|
+
}
|
|
762
|
+
const routesContent = `/**
|
|
763
|
+
* Auto-generated by Flight CLI
|
|
764
|
+
* Do not edit manually
|
|
765
|
+
* Generated: ${manifest.generated}
|
|
766
|
+
*/
|
|
767
|
+
|
|
768
|
+
import type { RouteDefinition } from '@flight-framework/router';
|
|
769
|
+
|
|
770
|
+
// Page Routes
|
|
771
|
+
export const routes: RouteDefinition[] = [
|
|
772
|
+
${manifest.routes.map((r) => ` {
|
|
773
|
+
path: '${r.path}',
|
|
774
|
+
component: () => import('../routes/${r.filePath.replace(/\.(tsx?|jsx?)$/, "")}'),
|
|
775
|
+
},`).join("\n")}
|
|
776
|
+
];
|
|
777
|
+
|
|
778
|
+
// Layout Components
|
|
779
|
+
export const layouts = [
|
|
780
|
+
${manifest.layouts.map((r) => ` {
|
|
781
|
+
path: '${r.path}',
|
|
782
|
+
component: () => import('../routes/${r.filePath.replace(/\.(tsx?|jsx?)$/, "")}'),
|
|
783
|
+
},`).join("\n")}
|
|
784
|
+
];
|
|
785
|
+
|
|
786
|
+
// Loading State Components
|
|
787
|
+
export const loadingStates = [
|
|
788
|
+
${manifest.loadingStates.map((r) => ` {
|
|
789
|
+
path: '${r.path}',
|
|
790
|
+
component: () => import('../routes/${r.filePath.replace(/\.(tsx?|jsx?)$/, "")}'),
|
|
791
|
+
},`).join("\n")}
|
|
792
|
+
];
|
|
793
|
+
|
|
794
|
+
// Error Boundary Components
|
|
795
|
+
export const errorBoundaries = [
|
|
796
|
+
${manifest.errorBoundaries.map((r) => ` {
|
|
797
|
+
path: '${r.path}',
|
|
798
|
+
component: () => import('../routes/${r.filePath.replace(/\.(tsx?|jsx?)$/, "")}'),
|
|
799
|
+
},`).join("\n")}
|
|
800
|
+
];
|
|
801
|
+
|
|
802
|
+
// Not Found Page Components
|
|
803
|
+
export const notFoundPages = [
|
|
804
|
+
${manifest.notFoundPages.map((r) => ` {
|
|
805
|
+
path: '${r.path}',
|
|
806
|
+
component: () => import('../routes/${r.filePath.replace(/\.(tsx?|jsx?)$/, "")}'),
|
|
807
|
+
},`).join("\n")}
|
|
808
|
+
];
|
|
809
|
+
|
|
810
|
+
// API Routes
|
|
811
|
+
export const apiRoutes = [
|
|
812
|
+
${manifest.apiRoutes.map((r) => ` {
|
|
813
|
+
path: '${r.path}',
|
|
814
|
+
method: '${r.httpMethod}',
|
|
815
|
+
handler: () => import('../routes/${r.filePath.replace(/\.(tsx?|jsx?)$/, "")}'),
|
|
816
|
+
},`).join("\n")}
|
|
817
|
+
];
|
|
818
|
+
|
|
819
|
+
// Type-safe route paths
|
|
820
|
+
export type AppRoutes = ${manifest.routes.length > 0 ? manifest.routes.map((r) => `'${r.path}'`).join(" | ") : "never"};
|
|
821
|
+
|
|
822
|
+
// Type-safe API route paths
|
|
823
|
+
export type ApiRoutes = ${manifest.apiRoutes.length > 0 ? manifest.apiRoutes.map((r) => `'${r.path}'`).join(" | ") : "never"};
|
|
824
|
+
`;
|
|
825
|
+
writeFileSync2(join3(outputDir, "routes.ts"), routesContent, "utf-8");
|
|
826
|
+
const typesContent = `/**
|
|
827
|
+
* Auto-generated route types
|
|
828
|
+
* Generated: ${manifest.generated}
|
|
829
|
+
*/
|
|
830
|
+
|
|
831
|
+
import type { RouteParams } from '@flight-framework/router';
|
|
832
|
+
|
|
833
|
+
// Extract params from route patterns
|
|
834
|
+
${manifest.routes.filter((r) => r.isDynamic).map((r) => {
|
|
835
|
+
const paramMatches = r.path.match(/:(\w+)/g) || [];
|
|
836
|
+
const params = paramMatches.map((p) => p.slice(1));
|
|
837
|
+
const typeName = r.path.replace(/[/:]/g, "_").replace(/^_/, "").replace(/_$/, "") || "Root";
|
|
838
|
+
return `export type ${typeName}Params = { ${params.map((p) => `${p}: string`).join("; ")} };`;
|
|
839
|
+
}).join("\n")}
|
|
840
|
+
`;
|
|
841
|
+
writeFileSync2(join3(outputDir, "types.ts"), typesContent, "utf-8");
|
|
842
|
+
}
|
|
843
|
+
async function generateRoutes(options) {
|
|
844
|
+
const { routesDir, outputDir } = options;
|
|
845
|
+
console.log(`Scanning routes in: ${routesDir}`);
|
|
846
|
+
const manifest = generateRouteManifest(routesDir);
|
|
847
|
+
const stats = [
|
|
848
|
+
`${manifest.routes.length} pages`,
|
|
849
|
+
`${manifest.layouts.length} layouts`,
|
|
850
|
+
`${manifest.loadingStates.length} loading states`,
|
|
851
|
+
`${manifest.errorBoundaries.length} error boundaries`,
|
|
852
|
+
`${manifest.notFoundPages.length} not-found pages`,
|
|
853
|
+
`${manifest.apiRoutes.length} API routes`
|
|
854
|
+
].join(", ");
|
|
855
|
+
console.log(`Found: ${stats}`);
|
|
856
|
+
generateRoutesFile(manifest, outputDir);
|
|
857
|
+
console.log(`Generated route manifest in: ${outputDir}`);
|
|
858
|
+
return manifest;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// src/commands/routes-generate.ts
|
|
862
|
+
async function routesGenerateCommand(options = {}) {
|
|
863
|
+
const cwd = process.cwd();
|
|
864
|
+
const routesDir = options.routesDir ? resolve5(cwd, options.routesDir) : resolve5(cwd, "src/routes");
|
|
865
|
+
const outputDir = options.outputDir ? resolve5(cwd, options.outputDir) : resolve5(cwd, "src/.flight");
|
|
866
|
+
try {
|
|
867
|
+
const manifest = await generateRoutes({
|
|
868
|
+
routesDir,
|
|
869
|
+
outputDir,
|
|
870
|
+
watch: options.watch
|
|
871
|
+
});
|
|
872
|
+
console.log("\nRoute manifest generated successfully!");
|
|
873
|
+
console.log(` Pages: ${manifest.routes.length}`);
|
|
874
|
+
console.log(` API Routes: ${manifest.apiRoutes.length}`);
|
|
875
|
+
console.log(` Layouts: ${manifest.layouts.length}`);
|
|
876
|
+
} catch (error) {
|
|
877
|
+
console.error("Failed to generate routes:", error);
|
|
878
|
+
process.exit(1);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
574
882
|
// src/index.ts
|
|
575
883
|
var cli = cac("flight");
|
|
576
884
|
var LOGO = `
|
|
@@ -593,6 +901,7 @@ cli.command("create [name]", "Create a new Flight project").option("-t, --templa
|
|
|
593
901
|
cli.command("dev", "Start development server").option("-p, --port <port>", "Port to listen on").option("-h, --host <host>", "Host to bind to").option("--open", "Open browser on start").option("--https", "Enable HTTPS").option("--ssr", "Enable Server-Side Rendering").action(devCommand);
|
|
594
902
|
cli.command("build", "Build for production").option("--outDir <dir>", "Output directory").option("--sourcemap", "Generate source maps").option("--minify", "Minify output", { default: true }).action(buildCommand);
|
|
595
903
|
cli.command("preview", "Preview production build").option("-p, --port <port>", "Port to listen on").option("-h, --host <host>", "Host to bind to").option("--open", "Open browser on start").action(previewCommand);
|
|
904
|
+
cli.command("routes:generate", "Generate route manifest from routes directory").option("--routesDir <dir>", "Routes directory", { default: "src/routes" }).option("--outputDir <dir>", "Output directory", { default: "src/.flight" }).action(routesGenerateCommand);
|
|
596
905
|
function run() {
|
|
597
906
|
try {
|
|
598
907
|
cli.parse(process.argv, { run: false });
|