@fluixi/cli 0.1.0-alpha.74 → 0.1.0-alpha.76

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/dist/cli.cjs ADDED
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli.ts
5
+ var import_config = require("@fluixi/start/config");
6
+ var import_commands = require("@fluixi/start/commands");
7
+ var import_ui2 = require("@fluixi/start/ui");
8
+
9
+ // src/generate.ts
10
+ var import_node_fs = require("node:fs");
11
+ var import_node_path = require("node:path");
12
+ var import_ui = require("@fluixi/start/ui");
13
+ var GEN_USAGE = `Usage: fluixi generate <kind> <name>
14
+
15
+ Kinds:
16
+ component <Name> a reusable component → src/components/<Name>.{tsx,ts}
17
+ route <path> a file-based route page → src/routes/<path>.{tsx,ts}
18
+ middleware the request middleware file → src/middleware.ts
19
+ auth @fluixi/auth wiring → auth.config + auth.client + middleware + /api/auth route
20
+
21
+ Component/route syntax follows the project (JSX → .tsx, html\`\` → .ts); override with --jsx / --html.
22
+ `;
23
+ function detectFormat() {
24
+ const args = process.argv.slice(3);
25
+ if (args.includes("--html")) return "html";
26
+ if (args.includes("--jsx")) return "jsx";
27
+ try {
28
+ const ts = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.resolve)("tsconfig.json"), "utf8"));
29
+ if (ts?.compilerOptions && !ts.compilerOptions.jsx) return "html";
30
+ } catch {
31
+ }
32
+ return "jsx";
33
+ }
34
+ function pascal(s) {
35
+ return s.replace(/\[(.+?)\]/g, "$1").split(/[^a-zA-Z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("") || "Component";
36
+ }
37
+ function resolveHint(Name, ext) {
38
+ const configs = ["vite.config.ts", "vite.config.js", "fluixi.config.ts", "fluixi.config.js"];
39
+ const found = configs.map((f) => (0, import_node_path.resolve)(f)).find((f) => (0, import_node_fs.existsSync)(f));
40
+ if (!found) return;
41
+ try {
42
+ if (/\bresolve\s*:\s*\[/.test((0, import_node_fs.readFileSync)(found, "utf8"))) return;
43
+ } catch {
44
+ return;
45
+ }
46
+ console.log(
47
+ ` ${import_ui.c.gray("to use")} ${import_ui.c.cyan(`<${Name} />`)} ${import_ui.c.gray("without importing it, add to")} ${import_ui.c.cyan((0, import_node_path.relative)(process.cwd(), found))}:`
48
+ );
49
+ console.log(` ${import_ui.c.gray(`resolve: [{ match: /^([A-Z]\\w+)$/, module: '/src/components/$1.${ext}' }]`)}`);
50
+ console.log(` ${import_ui.c.gray("one pattern covers the whole folder. Importing it works too — including with")} ${import_ui.c.cyan("load:")}`);
51
+ }
52
+ function write(file, content) {
53
+ const rel = (0, import_node_path.relative)(process.cwd(), file);
54
+ if ((0, import_node_fs.existsSync)(file)) {
55
+ console.error(` ${import_ui.c.red("✗")} ${rel} ${import_ui.c.gray("already exists")}`);
56
+ process.exit(1);
57
+ }
58
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(file), { recursive: true });
59
+ (0, import_node_fs.writeFileSync)(file, content);
60
+ console.log(` ${import_ui.c.green("✓")} ${import_ui.c.gray("created")} ${import_ui.c.cyan(rel)}`);
61
+ }
62
+ var component = (Name) => `import type { Component, ComponentChild } from '@fluixi/core';
63
+
64
+ export type ${Name}Props = {
65
+ children?: ComponentChild;
66
+ };
67
+
68
+ // Typed component: \`props\` is inferred from ${Name}Props. Use FxParent/FxVoid/FxFlow
69
+ // from '@fluixi/core' for children-required/forbidden variants.
70
+ export const ${Name}: Component<${Name}Props> = (props) => {
71
+ return <div class="${Name.toLowerCase()}">{props.children ?? '${Name}'}</div>;
72
+ };
73
+ `;
74
+ var route = (Name) => `export default function ${Name}() {
75
+ return (
76
+ <section>
77
+ <h1>${Name}</h1>
78
+ </section>
79
+ );
80
+ }
81
+ `;
82
+ var componentHtml = (Name) => `import type { Component, ComponentChild } from '@fluixi/core';
83
+ import { html } from '@fluixi/core';
84
+
85
+ export type ${Name}Props = {
86
+ children?: ComponentChild;
87
+ };
88
+
89
+ // Typed component: \`props\` is inferred from ${Name}Props. Use FxParent/FxVoid/FxFlow
90
+ // from '@fluixi/core' for children-required/forbidden variants.
91
+ export const ${Name}: Component<${Name}Props> = (props) => {
92
+ return html\`<div class="${Name.toLowerCase()}">\${props.children ?? '${Name}'}</div>\`;
93
+ };
94
+ `;
95
+ var routeHtml = (Name) => `import { html } from '@fluixi/start';
96
+
97
+ export default function ${Name}() {
98
+ return html\`
99
+ <section>
100
+ <h1>${Name}</h1>
101
+ </section>
102
+ \`;
103
+ }
104
+ `;
105
+ var middleware = () => `import { defineMiddleware } from '@fluixi/start';
106
+
107
+ export default defineMiddleware([
108
+ (request, next) => {
109
+ // return a Response to short-circuit (auth/redirect), or next() to continue.
110
+ return next();
111
+ },
112
+ ]);
113
+ `;
114
+ var authConfig = () => `import { betterAuth } from 'better-auth';
115
+ import { defineAuth } from '@fluixi/auth/server';
116
+
117
+ export type AppUser = { id: string; email: string; name?: string; role?: string };
118
+
119
+ // Better Auth owns the audited primitives; defineAuth registers it with @fluixi/auth.
120
+ export const auth = defineAuth<AppUser>(
121
+ betterAuth({
122
+ // database: <your adapter>,
123
+ emailAndPassword: { enabled: true },
124
+ // socialProviders: { google: { … }, github: { … } },
125
+ } as Parameters<typeof betterAuth>[0]),
126
+ );
127
+ `;
128
+ var authClient = () => `import { createAuthClient } from 'better-auth/client';
129
+ import { createAuthHooks } from '@fluixi/auth/client';
130
+ import type { AppUser } from './auth.config.js';
131
+
132
+ const client = createAuthClient({ baseURL: '/api/auth' });
133
+
134
+ // Or, to reuse an existing reactive session: createAuthHooks({ session: yourSessionStore }).
135
+ export const { useUser, useSession, useAuth, useSignIn, useSignOut, protectedRoute } =
136
+ createAuthHooks<AppUser>({ client, loginPath: '/login' });
137
+ `;
138
+ var authMiddleware = () => `import { defineMiddleware } from '@fluixi/start';
139
+ import { authMiddleware } from '@fluixi/auth/server';
140
+ import './auth.config.js';
141
+
142
+ export default defineMiddleware([authMiddleware()]);
143
+ `;
144
+ var authRoute = () => `import { mountAuthRoutes } from '@fluixi/auth/server';
145
+ import '../../../auth.config.js';
146
+
147
+ const handler = mountAuthRoutes();
148
+ export const GET = handler;
149
+ export const POST = handler;
150
+ export default handler;
151
+ `;
152
+ function generate(kind, name) {
153
+ switch (kind) {
154
+ case "component":
155
+ case "c": {
156
+ if (!name) return fail("component name required, e.g. `fluixi g component Button`");
157
+ const Name = pascal(name);
158
+ const html = detectFormat() === "html";
159
+ const ext = html ? "ts" : "tsx";
160
+ write((0, import_node_path.resolve)("src/components", `${Name}.${ext}`), (html ? componentHtml : component)(Name));
161
+ resolveHint(Name, ext);
162
+ break;
163
+ }
164
+ case "route":
165
+ case "r": {
166
+ if (!name) return fail("route path required, e.g. `fluixi g route about`");
167
+ const html = detectFormat() === "html";
168
+ const Name = pascal((0, import_node_path.basename)(name));
169
+ write((0, import_node_path.resolve)("src/routes", `${name}.${html ? "ts" : "tsx"}`), (html ? routeHtml : route)(Name));
170
+ break;
171
+ }
172
+ case "middleware":
173
+ case "m":
174
+ write((0, import_node_path.resolve)("src/middleware.ts"), middleware());
175
+ break;
176
+ case "auth":
177
+ write((0, import_node_path.resolve)("src/auth.config.ts"), authConfig());
178
+ write((0, import_node_path.resolve)("src/auth.client.ts"), authClient());
179
+ write((0, import_node_path.resolve)("src/middleware.ts"), authMiddleware());
180
+ write((0, import_node_path.resolve)("src/routes/api/auth/[...all].ts"), authRoute());
181
+ console.log(` ${import_ui.c.gray("next: install")} ${import_ui.c.cyan("better-auth")}${import_ui.c.gray(", set your DB adapter in auth.config.ts, and forward `request` in entry-server")}`);
182
+ break;
183
+ default:
184
+ console.error(GEN_USAGE);
185
+ process.exit(1);
186
+ }
187
+ }
188
+ function fail(msg) {
189
+ console.error(` ${import_ui.c.red("✗")} ${msg}`);
190
+ process.exit(1);
191
+ }
192
+
193
+ // src/cli.ts
194
+ function usage() {
195
+ const cmd = (name, desc) => ` ${import_ui2.c.cyan(name.padEnd(25))}${import_ui2.c.gray(desc)}`;
196
+ return [
197
+ "",
198
+ ` ${(0, import_ui2.wordmark)()} ${import_ui2.c.gray("the Fluixi command-line tool")}`,
199
+ "",
200
+ ` ${import_ui2.c.bold("Usage")} ${import_ui2.c.gray("fluixi <command>")}`,
201
+ "",
202
+ cmd("dev", "start the SSR dev server (Vite middleware + HMR)"),
203
+ cmd("build", "build the client + server bundles for production"),
204
+ cmd("start", "run the production SSR server (after build)"),
205
+ cmd("generate <kind> <name>", "scaffold a component / route / middleware / auth " + import_ui2.c.dim("(alias: g)")),
206
+ "",
207
+ ` ${import_ui2.c.gray("New project:")} ${import_ui2.c.cyan("npm create fluixi")} ${import_ui2.c.gray("<dir>")}`,
208
+ ""
209
+ ].join("\n");
210
+ }
211
+ async function main() {
212
+ const cmd = process.argv[2];
213
+ if (!cmd || cmd === "-h" || cmd === "--help") {
214
+ console.log(usage());
215
+ process.exit(cmd ? 0 : 1);
216
+ }
217
+ if (cmd === "generate" || cmd === "g") {
218
+ generate(process.argv[3], process.argv[4]);
219
+ return;
220
+ }
221
+ const config = await (0, import_config.loadConfig)();
222
+ switch (cmd) {
223
+ case "dev":
224
+ await (0, import_commands.dev)(config);
225
+ break;
226
+ case "build":
227
+ await (0, import_commands.build)(config);
228
+ break;
229
+ case "start":
230
+ await (0, import_commands.start)(config);
231
+ break;
232
+ default:
233
+ console.error(`
234
+ ${import_ui2.c.red("✗")} Unknown command: ${import_ui2.c.bold(cmd)}`);
235
+ console.log(usage());
236
+ process.exit(1);
237
+ }
238
+ }
239
+ main().catch((e) => {
240
+ console.error(`
241
+ ${import_ui2.c.red("✗")} ${e?.message ?? e}
242
+ `);
243
+ process.exit(1);
244
+ });
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { loadConfig } from '@fluixi/start';
2
+ import { loadConfig } from '@fluixi/start/config';
3
3
  import { dev, build, start } from '@fluixi/start/commands';
4
4
  import { wordmark, c } from '@fluixi/start/ui';
5
5
  import { generate } from './generate.js';
package/dist/cli.mjs ADDED
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { loadConfig } from "@fluixi/start/config";
5
+ import { dev, build, start } from "@fluixi/start/commands";
6
+ import { wordmark, c as c2 } from "@fluixi/start/ui";
7
+
8
+ // src/generate.ts
9
+ import { writeFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
10
+ import { resolve, dirname, relative, basename } from "node:path";
11
+ import { c } from "@fluixi/start/ui";
12
+ var GEN_USAGE = `Usage: fluixi generate <kind> <name>
13
+
14
+ Kinds:
15
+ component <Name> a reusable component → src/components/<Name>.{tsx,ts}
16
+ route <path> a file-based route page → src/routes/<path>.{tsx,ts}
17
+ middleware the request middleware file → src/middleware.ts
18
+ auth @fluixi/auth wiring → auth.config + auth.client + middleware + /api/auth route
19
+
20
+ Component/route syntax follows the project (JSX → .tsx, html\`\` → .ts); override with --jsx / --html.
21
+ `;
22
+ function detectFormat() {
23
+ const args = process.argv.slice(3);
24
+ if (args.includes("--html")) return "html";
25
+ if (args.includes("--jsx")) return "jsx";
26
+ try {
27
+ const ts = JSON.parse(readFileSync(resolve("tsconfig.json"), "utf8"));
28
+ if (ts?.compilerOptions && !ts.compilerOptions.jsx) return "html";
29
+ } catch {
30
+ }
31
+ return "jsx";
32
+ }
33
+ function pascal(s) {
34
+ return s.replace(/\[(.+?)\]/g, "$1").split(/[^a-zA-Z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("") || "Component";
35
+ }
36
+ function resolveHint(Name, ext) {
37
+ const configs = ["vite.config.ts", "vite.config.js", "fluixi.config.ts", "fluixi.config.js"];
38
+ const found = configs.map((f) => resolve(f)).find((f) => existsSync(f));
39
+ if (!found) return;
40
+ try {
41
+ if (/\bresolve\s*:\s*\[/.test(readFileSync(found, "utf8"))) return;
42
+ } catch {
43
+ return;
44
+ }
45
+ console.log(
46
+ ` ${c.gray("to use")} ${c.cyan(`<${Name} />`)} ${c.gray("without importing it, add to")} ${c.cyan(relative(process.cwd(), found))}:`
47
+ );
48
+ console.log(` ${c.gray(`resolve: [{ match: /^([A-Z]\\w+)$/, module: '/src/components/$1.${ext}' }]`)}`);
49
+ console.log(` ${c.gray("one pattern covers the whole folder. Importing it works too — including with")} ${c.cyan("load:")}`);
50
+ }
51
+ function write(file, content) {
52
+ const rel = relative(process.cwd(), file);
53
+ if (existsSync(file)) {
54
+ console.error(` ${c.red("✗")} ${rel} ${c.gray("already exists")}`);
55
+ process.exit(1);
56
+ }
57
+ mkdirSync(dirname(file), { recursive: true });
58
+ writeFileSync(file, content);
59
+ console.log(` ${c.green("✓")} ${c.gray("created")} ${c.cyan(rel)}`);
60
+ }
61
+ var component = (Name) => `import type { Component, ComponentChild } from '@fluixi/core';
62
+
63
+ export type ${Name}Props = {
64
+ children?: ComponentChild;
65
+ };
66
+
67
+ // Typed component: \`props\` is inferred from ${Name}Props. Use FxParent/FxVoid/FxFlow
68
+ // from '@fluixi/core' for children-required/forbidden variants.
69
+ export const ${Name}: Component<${Name}Props> = (props) => {
70
+ return <div class="${Name.toLowerCase()}">{props.children ?? '${Name}'}</div>;
71
+ };
72
+ `;
73
+ var route = (Name) => `export default function ${Name}() {
74
+ return (
75
+ <section>
76
+ <h1>${Name}</h1>
77
+ </section>
78
+ );
79
+ }
80
+ `;
81
+ var componentHtml = (Name) => `import type { Component, ComponentChild } from '@fluixi/core';
82
+ import { html } from '@fluixi/core';
83
+
84
+ export type ${Name}Props = {
85
+ children?: ComponentChild;
86
+ };
87
+
88
+ // Typed component: \`props\` is inferred from ${Name}Props. Use FxParent/FxVoid/FxFlow
89
+ // from '@fluixi/core' for children-required/forbidden variants.
90
+ export const ${Name}: Component<${Name}Props> = (props) => {
91
+ return html\`<div class="${Name.toLowerCase()}">\${props.children ?? '${Name}'}</div>\`;
92
+ };
93
+ `;
94
+ var routeHtml = (Name) => `import { html } from '@fluixi/start';
95
+
96
+ export default function ${Name}() {
97
+ return html\`
98
+ <section>
99
+ <h1>${Name}</h1>
100
+ </section>
101
+ \`;
102
+ }
103
+ `;
104
+ var middleware = () => `import { defineMiddleware } from '@fluixi/start';
105
+
106
+ export default defineMiddleware([
107
+ (request, next) => {
108
+ // return a Response to short-circuit (auth/redirect), or next() to continue.
109
+ return next();
110
+ },
111
+ ]);
112
+ `;
113
+ var authConfig = () => `import { betterAuth } from 'better-auth';
114
+ import { defineAuth } from '@fluixi/auth/server';
115
+
116
+ export type AppUser = { id: string; email: string; name?: string; role?: string };
117
+
118
+ // Better Auth owns the audited primitives; defineAuth registers it with @fluixi/auth.
119
+ export const auth = defineAuth<AppUser>(
120
+ betterAuth({
121
+ // database: <your adapter>,
122
+ emailAndPassword: { enabled: true },
123
+ // socialProviders: { google: { … }, github: { … } },
124
+ } as Parameters<typeof betterAuth>[0]),
125
+ );
126
+ `;
127
+ var authClient = () => `import { createAuthClient } from 'better-auth/client';
128
+ import { createAuthHooks } from '@fluixi/auth/client';
129
+ import type { AppUser } from './auth.config.js';
130
+
131
+ const client = createAuthClient({ baseURL: '/api/auth' });
132
+
133
+ // Or, to reuse an existing reactive session: createAuthHooks({ session: yourSessionStore }).
134
+ export const { useUser, useSession, useAuth, useSignIn, useSignOut, protectedRoute } =
135
+ createAuthHooks<AppUser>({ client, loginPath: '/login' });
136
+ `;
137
+ var authMiddleware = () => `import { defineMiddleware } from '@fluixi/start';
138
+ import { authMiddleware } from '@fluixi/auth/server';
139
+ import './auth.config.js';
140
+
141
+ export default defineMiddleware([authMiddleware()]);
142
+ `;
143
+ var authRoute = () => `import { mountAuthRoutes } from '@fluixi/auth/server';
144
+ import '../../../auth.config.js';
145
+
146
+ const handler = mountAuthRoutes();
147
+ export const GET = handler;
148
+ export const POST = handler;
149
+ export default handler;
150
+ `;
151
+ function generate(kind, name) {
152
+ switch (kind) {
153
+ case "component":
154
+ case "c": {
155
+ if (!name) return fail("component name required, e.g. `fluixi g component Button`");
156
+ const Name = pascal(name);
157
+ const html = detectFormat() === "html";
158
+ const ext = html ? "ts" : "tsx";
159
+ write(resolve("src/components", `${Name}.${ext}`), (html ? componentHtml : component)(Name));
160
+ resolveHint(Name, ext);
161
+ break;
162
+ }
163
+ case "route":
164
+ case "r": {
165
+ if (!name) return fail("route path required, e.g. `fluixi g route about`");
166
+ const html = detectFormat() === "html";
167
+ const Name = pascal(basename(name));
168
+ write(resolve("src/routes", `${name}.${html ? "ts" : "tsx"}`), (html ? routeHtml : route)(Name));
169
+ break;
170
+ }
171
+ case "middleware":
172
+ case "m":
173
+ write(resolve("src/middleware.ts"), middleware());
174
+ break;
175
+ case "auth":
176
+ write(resolve("src/auth.config.ts"), authConfig());
177
+ write(resolve("src/auth.client.ts"), authClient());
178
+ write(resolve("src/middleware.ts"), authMiddleware());
179
+ write(resolve("src/routes/api/auth/[...all].ts"), authRoute());
180
+ console.log(` ${c.gray("next: install")} ${c.cyan("better-auth")}${c.gray(", set your DB adapter in auth.config.ts, and forward `request` in entry-server")}`);
181
+ break;
182
+ default:
183
+ console.error(GEN_USAGE);
184
+ process.exit(1);
185
+ }
186
+ }
187
+ function fail(msg) {
188
+ console.error(` ${c.red("✗")} ${msg}`);
189
+ process.exit(1);
190
+ }
191
+
192
+ // src/cli.ts
193
+ function usage() {
194
+ const cmd = (name, desc) => ` ${c2.cyan(name.padEnd(25))}${c2.gray(desc)}`;
195
+ return [
196
+ "",
197
+ ` ${wordmark()} ${c2.gray("the Fluixi command-line tool")}`,
198
+ "",
199
+ ` ${c2.bold("Usage")} ${c2.gray("fluixi <command>")}`,
200
+ "",
201
+ cmd("dev", "start the SSR dev server (Vite middleware + HMR)"),
202
+ cmd("build", "build the client + server bundles for production"),
203
+ cmd("start", "run the production SSR server (after build)"),
204
+ cmd("generate <kind> <name>", "scaffold a component / route / middleware / auth " + c2.dim("(alias: g)")),
205
+ "",
206
+ ` ${c2.gray("New project:")} ${c2.cyan("npm create fluixi")} ${c2.gray("<dir>")}`,
207
+ ""
208
+ ].join("\n");
209
+ }
210
+ async function main() {
211
+ const cmd = process.argv[2];
212
+ if (!cmd || cmd === "-h" || cmd === "--help") {
213
+ console.log(usage());
214
+ process.exit(cmd ? 0 : 1);
215
+ }
216
+ if (cmd === "generate" || cmd === "g") {
217
+ generate(process.argv[3], process.argv[4]);
218
+ return;
219
+ }
220
+ const config = await loadConfig();
221
+ switch (cmd) {
222
+ case "dev":
223
+ await dev(config);
224
+ break;
225
+ case "build":
226
+ await build(config);
227
+ break;
228
+ case "start":
229
+ await start(config);
230
+ break;
231
+ default:
232
+ console.error(`
233
+ ${c2.red("✗")} Unknown command: ${c2.bold(cmd)}`);
234
+ console.log(usage());
235
+ process.exit(1);
236
+ }
237
+ }
238
+ main().catch((e) => {
239
+ console.error(`
240
+ ${c2.red("✗")} ${e?.message ?? e}
241
+ `);
242
+ process.exit(1);
243
+ });