fexapi 0.1.0 → 0.1.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 (50) hide show
  1. package/README.md +142 -1
  2. package/dist/cli/args.d.ts +24 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +98 -0
  5. package/dist/cli/help.d.ts +2 -0
  6. package/dist/cli/help.d.ts.map +1 -0
  7. package/dist/cli/help.js +51 -0
  8. package/dist/commands/dev.d.ts +7 -0
  9. package/dist/commands/dev.d.ts.map +1 -0
  10. package/dist/commands/dev.js +114 -0
  11. package/dist/commands/generate.d.ts +2 -0
  12. package/dist/commands/generate.d.ts.map +1 -0
  13. package/dist/commands/generate.js +81 -0
  14. package/dist/commands/init.d.ts +4 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +250 -0
  17. package/dist/commands/serve.d.ts +12 -0
  18. package/dist/commands/serve.d.ts.map +1 -0
  19. package/dist/commands/serve.js +68 -0
  20. package/dist/config/generated-spec.d.ts +3 -0
  21. package/dist/config/generated-spec.d.ts.map +1 -0
  22. package/dist/config/generated-spec.js +26 -0
  23. package/dist/config/runtime-config.d.ts +3 -0
  24. package/dist/config/runtime-config.d.ts.map +1 -0
  25. package/dist/config/runtime-config.js +97 -0
  26. package/dist/config/schema-definitions.d.ts +3 -0
  27. package/dist/config/schema-definitions.d.ts.map +1 -0
  28. package/dist/config/schema-definitions.js +90 -0
  29. package/dist/constants.d.ts +2 -0
  30. package/dist/constants.d.ts.map +1 -0
  31. package/dist/constants.js +4 -0
  32. package/dist/index.js +74 -411
  33. package/dist/project/detect.d.ts +4 -0
  34. package/dist/project/detect.d.ts.map +1 -0
  35. package/dist/project/detect.js +113 -0
  36. package/dist/project/paths.d.ts +3 -0
  37. package/dist/project/paths.d.ts.map +1 -0
  38. package/dist/project/paths.js +28 -0
  39. package/dist/schema.d.ts.map +1 -1
  40. package/dist/schema.js +4 -6
  41. package/dist/server.d.ts +5 -1
  42. package/dist/server.d.ts.map +1 -1
  43. package/dist/server.js +123 -43
  44. package/dist/types/config.d.ts +20 -0
  45. package/dist/types/config.d.ts.map +1 -0
  46. package/dist/types/config.js +2 -0
  47. package/dist/types/project.d.ts +7 -0
  48. package/dist/types/project.d.ts.map +1 -0
  49. package/dist/types/project.js +2 -0
  50. package/package.json +56 -51
package/dist/index.js CHANGED
@@ -1,435 +1,98 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const node_fs_1 = require("node:fs");
5
- const node_path_1 = require("node:path");
6
- const schema_1 = require("./schema");
7
- const server_1 = require("./server");
4
+ const args_1 = require("./cli/args");
5
+ const dev_1 = require("./commands/dev");
6
+ const help_1 = require("./cli/help");
7
+ const generate_1 = require("./commands/generate");
8
+ const init_1 = require("./commands/init");
9
+ const serve_1 = require("./commands/serve");
8
10
  const args = process.argv.slice(2);
9
- const GENERATED_SPEC_RELATIVE_PATH = "fexapi/generated.api.json";
10
- const printHelp = () => {
11
- console.log("fexapi-cli");
12
- console.log("");
13
- console.log("Usage:");
14
- console.log(" fexapi init [--force]");
15
- console.log(" fexapi generate");
16
- console.log(" fexapi serve [--host <host>] [--port <number>]");
17
- console.log(" fexapi [--host <host>] [--port <number>]");
18
- console.log(" fexapi --help");
19
- console.log("");
20
- console.log("Examples:");
21
- console.log(" fexapi init");
22
- console.log(" fexapi init --force");
23
- console.log(" fexapi generate");
24
- console.log(" fexapi serve --host 127.0.0.1 --port 5000");
25
- console.log(" fexapi --port 4000");
26
- console.log("");
27
- console.log("Package manager usage:");
28
- console.log(" npx fexapi init");
29
- console.log(" pnpm dlx fexapi init");
30
- console.log(" yarn dlx fexapi init");
31
- console.log("");
32
- console.log("`fexapi init` creates:");
33
- console.log(" fexapi.config.json");
34
- console.log(" fexapi/schema.fexapi");
35
- console.log("");
36
- console.log("Then run:");
37
- console.log(" fexapi generate");
38
- console.log(" fexapi serve");
39
- };
40
- const findClosestPackageJson = (startDirectory) => {
41
- let currentDirectory = startDirectory;
42
- while (true) {
43
- const candidate = (0, node_path_1.join)(currentDirectory, "package.json");
44
- if ((0, node_fs_1.existsSync)(candidate)) {
45
- return candidate;
11
+ const [firstArg, ...restArgs] = args;
12
+ const main = async () => {
13
+ if (firstArg === "init") {
14
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
15
+ console.log("Usage: fexapi init [--force]");
16
+ console.log("Runs an interactive setup wizard and creates fexapi config/schema files.");
17
+ console.log("Use --force to overwrite existing files.");
18
+ process.exit(0);
46
19
  }
47
- const parentDirectory = (0, node_path_1.dirname)(currentDirectory);
48
- if (parentDirectory === currentDirectory) {
49
- return undefined;
20
+ const initOptions = (0, args_1.parseInitOptions)(restArgs);
21
+ if ("error" in initOptions) {
22
+ console.error(initOptions.error);
23
+ console.log("");
24
+ console.log("Usage: fexapi init [--force]");
25
+ process.exit(1);
50
26
  }
51
- currentDirectory = parentDirectory;
27
+ process.exit(await (0, init_1.initializeProject)({ force: initOptions.force }));
52
28
  }
53
- };
54
- const readDependencyNames = (packageJsonPath) => {
55
- const packageJsonText = (0, node_fs_1.readFileSync)(packageJsonPath, "utf-8");
56
- const packageJson = JSON.parse(packageJsonText);
57
- const dependencies = (packageJson.dependencies ?? {});
58
- const devDependencies = (packageJson.devDependencies ?? {});
59
- return new Set([
60
- ...Object.keys(dependencies),
61
- ...Object.keys(devDependencies),
62
- ]);
63
- };
64
- const readWorkspaceDependencyNames = (projectRoot) => {
65
- const result = new Set();
66
- const rootsToScan = [
67
- (0, node_path_1.join)(projectRoot, "apps"),
68
- (0, node_path_1.join)(projectRoot, "packages"),
69
- ];
70
- for (const rootPath of rootsToScan) {
71
- if (!(0, node_fs_1.existsSync)(rootPath)) {
72
- continue;
29
+ else if (firstArg === "generate") {
30
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
31
+ console.log("Usage: fexapi generate");
32
+ console.log("Reads fexapi/schema.fexapi and updates generated API artifacts + migration.");
33
+ process.exit(0);
73
34
  }
74
- for (const entry of (0, node_fs_1.readdirSync)(rootPath)) {
75
- const entryPath = (0, node_path_1.join)(rootPath, entry);
76
- if (!(0, node_fs_1.statSync)(entryPath).isDirectory()) {
77
- continue;
78
- }
79
- const packageJsonPath = (0, node_path_1.join)(entryPath, "package.json");
80
- if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
81
- continue;
82
- }
83
- const dependencyNames = readDependencyNames(packageJsonPath);
84
- for (const dependencyName of dependencyNames) {
85
- result.add(dependencyName);
86
- }
35
+ const generateOptions = (0, args_1.parseGenerateOptions)(restArgs);
36
+ if (generateOptions.error) {
37
+ console.error(generateOptions.error);
38
+ console.log("");
39
+ console.log("Usage: fexapi generate");
40
+ process.exit(1);
87
41
  }
42
+ process.exit((0, generate_1.generateFromSchema)());
88
43
  }
89
- return result;
90
- };
91
- const detectProject = (packageJsonPath, projectRoot) => {
92
- const dependencyNames = readDependencyNames(packageJsonPath);
93
- const frameworks = new Set();
94
- const tooling = new Set();
95
- if (dependencyNames.has("turbo") ||
96
- (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "turbo.json"))) {
97
- tooling.add("turborepo");
98
- }
99
- if (dependencyNames.has("nx") || (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "nx.json"))) {
100
- tooling.add("nx");
101
- }
102
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "pnpm-workspace.yaml"))) {
103
- tooling.add("pnpm-workspace");
104
- }
105
- if (tooling.has("turborepo") || tooling.has("pnpm-workspace")) {
106
- const workspaceDeps = readWorkspaceDependencyNames(projectRoot);
107
- for (const dependencyName of workspaceDeps) {
108
- dependencyNames.add(dependencyName);
109
- }
110
- }
111
- if (dependencyNames.has("next")) {
112
- frameworks.add("nextjs");
113
- }
114
- if (dependencyNames.has("react") || dependencyNames.has("react-dom")) {
115
- frameworks.add("reactjs");
116
- }
117
- if (dependencyNames.has("vue")) {
118
- frameworks.add("vue");
119
- }
120
- if (dependencyNames.has("nuxt")) {
121
- frameworks.add("nuxt");
122
- }
123
- if (dependencyNames.has("svelte")) {
124
- frameworks.add("svelte");
125
- }
126
- if (dependencyNames.has("@sveltejs/kit")) {
127
- frameworks.add("sveltekit");
128
- }
129
- if (dependencyNames.has("@angular/core")) {
130
- frameworks.add("angular");
131
- }
132
- if (dependencyNames.has("solid-js")) {
133
- frameworks.add("solid");
134
- }
135
- if (dependencyNames.has("@remix-run/react") ||
136
- dependencyNames.has("@remix-run/node")) {
137
- frameworks.add("remix");
138
- }
139
- if (dependencyNames.has("astro")) {
140
- frameworks.add("astro");
141
- }
142
- if (dependencyNames.has("vite")) {
143
- tooling.add("vite");
144
- }
145
- const frameworkList = Array.from(frameworks);
146
- const primaryFramework = frameworkList[0] ?? "unknown";
147
- return {
148
- primaryFramework,
149
- frameworks: frameworkList.length > 0 ? frameworkList : ["unknown"],
150
- tooling: Array.from(tooling),
151
- };
152
- };
153
- const getSchemaTemplate = (framework) => {
154
- const frameworkHint = framework === "nextjs"
155
- ? "# Framework: Next.js"
156
- : framework === "reactjs"
157
- ? "# Framework: React"
158
- : "# Framework: unknown";
159
- return [
160
- frameworkHint,
161
- "# Server",
162
- "port: 4000",
163
- "",
164
- "# Routes",
165
- "# Format: METHOD /endpoint: field:type,field:type",
166
- "GET /users: id:uuid,fullName:name,username:string,email:email,avatarUrl:url",
167
- "GET /posts: id:uuid,title:string,body:string,createdAt:date",
168
- ].join("\n");
169
- };
170
- const parseInitOptions = (initArgs) => {
171
- const validFlags = new Set(["--force"]);
172
- const invalidFlags = initArgs.filter((value) => value.startsWith("-") && !validFlags.has(value));
173
- if (invalidFlags.length > 0) {
174
- return { error: `Unknown option(s): ${invalidFlags.join(", ")}` };
175
- }
176
- return { force: initArgs.includes("--force") };
177
- };
178
- const parseGenerateOptions = (generateArgs) => {
179
- const invalidFlags = generateArgs.filter((value) => value.startsWith("-"));
180
- if (invalidFlags.length > 0) {
181
- return { error: `Unknown option(s): ${invalidFlags.join(", ")}` };
182
- }
183
- return {};
184
- };
185
- const resolveProjectRoot = () => {
186
- const packageJsonPath = findClosestPackageJson(process.cwd());
187
- if (!packageJsonPath) {
188
- return undefined;
189
- }
190
- return (0, node_path_1.dirname)(packageJsonPath);
191
- };
192
- const initializeProject = ({ force }) => {
193
- const packageJsonPath = findClosestPackageJson(process.cwd());
194
- if (!packageJsonPath) {
195
- console.error("Could not find package.json in this directory or parent directories.");
196
- return 1;
197
- }
198
- const projectRoot = (0, node_path_1.dirname)(packageJsonPath);
199
- const detectedProject = detectProject(packageJsonPath, projectRoot);
200
- const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
201
- const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
202
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
203
- (0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
204
- const configExists = (0, node_fs_1.existsSync)(configPath);
205
- const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
206
- const config = {
207
- framework: detectedProject.primaryFramework,
208
- frameworks: detectedProject.frameworks,
209
- tooling: detectedProject.tooling,
210
- schemaPath: "fexapi/schema.fexapi",
211
- generatedPath: GENERATED_SPEC_RELATIVE_PATH,
212
- createdAt: new Date().toISOString(),
213
- };
214
- if (!configExists || force) {
215
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
216
- }
217
- if (!schemaExists || force) {
218
- (0, node_fs_1.writeFileSync)(schemaPath, `${getSchemaTemplate(detectedProject.primaryFramework)}\n`, "utf-8");
219
- }
220
- console.log(`Initialized Fexapi in ${projectRoot}`);
221
- console.log(`Detected framework: ${detectedProject.primaryFramework}`);
222
- console.log(`Detected frameworks: ${detectedProject.frameworks.join(", ")}`);
223
- if (detectedProject.tooling.length > 0) {
224
- console.log(`Detected tooling: ${detectedProject.tooling.join(", ")}`);
225
- }
226
- if (configExists && !force) {
227
- console.log(`Exists ${configPath}`);
228
- }
229
- else if (configExists && force) {
230
- console.log(`Overwritten ${configPath}`);
231
- }
232
- else {
233
- console.log(`Created ${configPath}`);
234
- }
235
- if (schemaExists && !force) {
236
- console.log(`Exists ${schemaPath}`);
237
- }
238
- else if (schemaExists && force) {
239
- console.log(`Overwritten ${schemaPath}`);
240
- }
241
- else {
242
- console.log(`Created ${schemaPath}`);
243
- }
244
- if (detectedProject.primaryFramework === "unknown") {
245
- console.log("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
246
- }
247
- return 0;
248
- };
249
- const generateFromSchema = () => {
250
- const projectRoot = resolveProjectRoot();
251
- if (!projectRoot) {
252
- console.error("Could not find package.json in this directory or parent directories.");
253
- return 1;
254
- }
255
- const schemaPath = (0, node_path_1.join)(projectRoot, "fexapi", "schema.fexapi");
256
- const generatedPath = (0, node_path_1.join)(projectRoot, "fexapi", "generated.api.json");
257
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
258
- if (!(0, node_fs_1.existsSync)(schemaPath)) {
259
- console.error(`Schema file not found: ${schemaPath}`);
260
- console.error("Run `fexapi init` first.");
261
- return 1;
262
- }
263
- const schemaText = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
264
- const parsed = (0, schema_1.parseFexapiSchema)(schemaText);
265
- if (parsed.errors.length > 0 || !parsed.schema) {
266
- console.error("Failed to generate API from schema.fexapi");
267
- for (const error of parsed.errors) {
268
- console.error(`- ${error}`);
44
+ else if (firstArg === "dev") {
45
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
46
+ console.log("Usage: fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
47
+ console.log("Starts development server and optionally auto-reloads when config/schema files change.");
48
+ process.exit(0);
269
49
  }
270
- return 1;
271
- }
272
- const generated = {
273
- schemaVersion: 1,
274
- generatedAt: new Date().toISOString(),
275
- port: parsed.schema.port,
276
- routes: parsed.schema.routes,
277
- };
278
- (0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
279
- let existingConfig = {};
280
- if ((0, node_fs_1.existsSync)(configPath)) {
281
- try {
282
- existingConfig = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
50
+ const devOptions = (0, args_1.parseDevOptions)(restArgs);
51
+ if ("error" in devOptions) {
52
+ console.error(devOptions.error);
53
+ console.log("");
54
+ console.log("Usage: fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
55
+ process.exit(1);
283
56
  }
284
- catch {
285
- existingConfig = {};
57
+ const exitCode = (0, dev_1.runDevCommand)(devOptions);
58
+ if (exitCode !== 0) {
59
+ process.exit(exitCode);
286
60
  }
287
61
  }
288
- const updatedConfig = {
289
- ...existingConfig,
290
- schemaPath: "fexapi/schema.fexapi",
291
- generatedPath: GENERATED_SPEC_RELATIVE_PATH,
292
- lastGeneratedAt: new Date().toISOString(),
293
- };
294
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(updatedConfig, null, 2)}\n`, "utf-8");
295
- console.log(`Generated API spec at ${generatedPath}`);
296
- console.log(`Routes generated: ${parsed.schema.routes.length}`);
297
- console.log(`Configured server port: ${parsed.schema.port}`);
298
- return 0;
299
- };
300
- const parseServeOptions = (serveArgs) => {
301
- const getFlagValue = (flagName) => {
302
- const index = serveArgs.indexOf(flagName);
303
- if (index === -1) {
304
- return undefined;
62
+ else if (!firstArg ||
63
+ firstArg === "serve" ||
64
+ firstArg === "run" ||
65
+ firstArg.startsWith("-")) {
66
+ const serveArgs = firstArg === "serve" || firstArg === "run" ? restArgs : args;
67
+ if (serveArgs.includes("--help") || serveArgs.includes("-h")) {
68
+ (0, help_1.printHelp)();
69
+ process.exit(0);
305
70
  }
306
- const value = serveArgs[index + 1];
307
- if (!value || value.startsWith("-")) {
308
- return { error: `Missing value for ${flagName}` };
71
+ const options = (0, args_1.parseServeOptions)(serveArgs);
72
+ if ("error" in options) {
73
+ console.error(options.error);
74
+ console.log("");
75
+ (0, help_1.printHelp)();
76
+ process.exit(1);
309
77
  }
310
- return value;
311
- };
312
- const unknownFlags = serveArgs.filter((value) => value.startsWith("-") && value !== "--host" && value !== "--port");
313
- if (unknownFlags.length > 0) {
314
- return { error: `Unknown option(s): ${unknownFlags.join(", ")}` };
315
- }
316
- const hostValue = getFlagValue("--host");
317
- if (hostValue && typeof hostValue !== "string") {
318
- return hostValue;
319
- }
320
- const portValue = getFlagValue("--port");
321
- if (portValue && typeof portValue !== "string") {
322
- return portValue;
323
- }
324
- const host = hostValue ?? "127.0.0.1";
325
- const port = portValue ? Number(portValue) : undefined;
326
- if (port !== undefined &&
327
- (!Number.isInteger(port) || port < 1 || port > 65535)) {
328
- return { error: `Invalid port: ${portValue ?? ""}`.trim() };
329
- }
330
- return { host, port };
331
- };
332
- const loadGeneratedApiSpec = (projectRoot) => {
333
- const generatedPath = (0, node_path_1.join)(projectRoot, GENERATED_SPEC_RELATIVE_PATH);
334
- if (!(0, node_fs_1.existsSync)(generatedPath)) {
335
- return undefined;
336
- }
337
- try {
338
- const parsed = JSON.parse((0, node_fs_1.readFileSync)(generatedPath, "utf-8"));
339
- if (typeof parsed.port !== "number" || !Array.isArray(parsed.routes)) {
340
- return undefined;
78
+ const exitCode = (0, serve_1.serveProject)(options);
79
+ if (exitCode !== 0) {
80
+ process.exit(exitCode);
341
81
  }
342
- return {
343
- port: parsed.port,
344
- routes: parsed.routes,
345
- };
346
82
  }
347
- catch {
348
- return undefined;
349
- }
350
- };
351
- const [firstArg, ...restArgs] = args;
352
- if (firstArg === "init") {
353
- if (restArgs.includes("--help") || restArgs.includes("-h")) {
354
- console.log("Usage: fexapi init [--force]");
355
- console.log("Detects frameworks/tooling and creates fexapi.config.json + fexapi/schema.fexapi.");
356
- console.log("Use --force to overwrite existing files.");
357
- process.exit(0);
358
- }
359
- const initOptions = parseInitOptions(restArgs);
360
- if ("error" in initOptions) {
361
- console.error(initOptions.error);
362
- console.log("");
363
- console.log("Usage: fexapi init [--force]");
364
- process.exit(1);
365
- }
366
- const exitCode = initializeProject({ force: initOptions.force });
367
- process.exit(exitCode);
368
- }
369
- else if (firstArg === "generate") {
370
- if (restArgs.includes("--help") || restArgs.includes("-h")) {
371
- console.log("Usage: fexapi generate");
372
- console.log("Reads fexapi/schema.fexapi and creates fexapi/generated.api.json.");
83
+ else if (firstArg === "help") {
84
+ (0, help_1.printHelp)();
373
85
  process.exit(0);
374
86
  }
375
- const generateOptions = parseGenerateOptions(restArgs);
376
- if (generateOptions.error) {
377
- console.error(generateOptions.error);
378
- console.log("");
379
- console.log("Usage: fexapi generate");
380
- process.exit(1);
381
- }
382
- const exitCode = generateFromSchema();
383
- process.exit(exitCode);
384
- }
385
- else if (!firstArg || firstArg === "serve" || firstArg.startsWith("-")) {
386
- const serveArgs = firstArg === "serve" ? restArgs : args;
387
- if (serveArgs.includes("--help") || serveArgs.includes("-h")) {
388
- printHelp();
389
- process.exit(0);
390
- }
391
- const options = parseServeOptions(serveArgs);
392
- if ("error" in options) {
393
- console.error(options.error);
87
+ else {
88
+ console.error(`Unknown command: ${firstArg}`);
394
89
  console.log("");
395
- printHelp();
90
+ (0, help_1.printHelp)();
396
91
  process.exit(1);
397
92
  }
398
- const projectRoot = resolveProjectRoot();
399
- const generatedSpec = projectRoot
400
- ? loadGeneratedApiSpec(projectRoot)
401
- : undefined;
402
- const effectivePort = options.port ?? generatedSpec?.port ?? 4000;
403
- if (generatedSpec) {
404
- console.log(`Using generated schema routes (${generatedSpec.routes.length}) from ${GENERATED_SPEC_RELATIVE_PATH}`);
405
- }
406
- else {
407
- console.log("No generated schema found. Run `fexapi generate` to serve schema-defined endpoints.");
408
- }
409
- const server = (0, server_1.startServer)({
410
- host: options.host,
411
- port: effectivePort,
412
- apiSpec: generatedSpec,
413
- });
414
- const shutdown = () => {
415
- server.close((error) => {
416
- if (error) {
417
- console.error("Error while shutting down server", error);
418
- process.exit(1);
419
- }
420
- process.exit(0);
421
- });
422
- };
423
- process.on("SIGINT", shutdown);
424
- process.on("SIGTERM", shutdown);
425
- }
426
- else if (firstArg === "help") {
427
- printHelp();
428
- process.exit(0);
429
- }
430
- else {
431
- console.error(`Unknown command: ${firstArg}`);
432
- console.log("");
433
- printHelp();
93
+ };
94
+ void main().catch((error) => {
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ console.error(`Unexpected error: ${message}`);
434
97
  process.exit(1);
435
- }
98
+ });
@@ -0,0 +1,4 @@
1
+ import type { DetectedProject, SupportedFramework } from "../types/project";
2
+ export declare const detectProject: (packageJsonPath: string, projectRoot: string) => DetectedProject;
3
+ export declare const getSchemaTemplate: (framework: SupportedFramework, port?: number) => string;
4
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/project/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAyD5E,eAAO,MAAM,aAAa,GACxB,iBAAiB,MAAM,EACvB,aAAa,MAAM,KAClB,eAwDF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,kBAAkB,EAC7B,aAAW,KACV,MAkBF,CAAC"}
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSchemaTemplate = exports.detectProject = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const readDependencyNames = (packageJsonPath) => {
7
+ const packageJsonText = (0, node_fs_1.readFileSync)(packageJsonPath, "utf-8");
8
+ const packageJson = JSON.parse(packageJsonText);
9
+ const dependencies = (packageJson.dependencies ?? {});
10
+ const devDependencies = (packageJson.devDependencies ?? {});
11
+ return new Set([
12
+ ...Object.keys(dependencies),
13
+ ...Object.keys(devDependencies),
14
+ ]);
15
+ };
16
+ const readWorkspaceDependencyNames = (projectRoot) => {
17
+ const result = new Set();
18
+ const rootsToScan = [
19
+ (0, node_path_1.join)(projectRoot, "apps"),
20
+ (0, node_path_1.join)(projectRoot, "packages"),
21
+ ];
22
+ for (const rootPath of rootsToScan) {
23
+ if (!(0, node_fs_1.existsSync)(rootPath)) {
24
+ continue;
25
+ }
26
+ for (const entry of (0, node_fs_1.readdirSync)(rootPath)) {
27
+ const entryPath = (0, node_path_1.join)(rootPath, entry);
28
+ if (!(0, node_fs_1.statSync)(entryPath).isDirectory()) {
29
+ continue;
30
+ }
31
+ const packageJsonPath = (0, node_path_1.join)(entryPath, "package.json");
32
+ if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
33
+ continue;
34
+ }
35
+ const dependencyNames = readDependencyNames(packageJsonPath);
36
+ for (const dependencyName of dependencyNames) {
37
+ result.add(dependencyName);
38
+ }
39
+ }
40
+ }
41
+ return result;
42
+ };
43
+ const detectProject = (packageJsonPath, projectRoot) => {
44
+ const dependencyNames = readDependencyNames(packageJsonPath);
45
+ const frameworks = new Set();
46
+ const tooling = new Set();
47
+ if (dependencyNames.has("turbo") ||
48
+ (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "turbo.json"))) {
49
+ tooling.add("turborepo");
50
+ }
51
+ if (dependencyNames.has("nx") || (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "nx.json"))) {
52
+ tooling.add("nx");
53
+ }
54
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "pnpm-workspace.yaml"))) {
55
+ tooling.add("pnpm-workspace");
56
+ }
57
+ if (tooling.has("turborepo") || tooling.has("pnpm-workspace")) {
58
+ const workspaceDeps = readWorkspaceDependencyNames(projectRoot);
59
+ for (const dependencyName of workspaceDeps) {
60
+ dependencyNames.add(dependencyName);
61
+ }
62
+ }
63
+ if (dependencyNames.has("next"))
64
+ frameworks.add("nextjs");
65
+ if (dependencyNames.has("react") || dependencyNames.has("react-dom"))
66
+ frameworks.add("reactjs");
67
+ if (dependencyNames.has("vue"))
68
+ frameworks.add("vue");
69
+ if (dependencyNames.has("nuxt"))
70
+ frameworks.add("nuxt");
71
+ if (dependencyNames.has("svelte"))
72
+ frameworks.add("svelte");
73
+ if (dependencyNames.has("@sveltejs/kit"))
74
+ frameworks.add("sveltekit");
75
+ if (dependencyNames.has("@angular/core"))
76
+ frameworks.add("angular");
77
+ if (dependencyNames.has("solid-js"))
78
+ frameworks.add("solid");
79
+ if (dependencyNames.has("@remix-run/react") ||
80
+ dependencyNames.has("@remix-run/node"))
81
+ frameworks.add("remix");
82
+ if (dependencyNames.has("astro"))
83
+ frameworks.add("astro");
84
+ if (dependencyNames.has("vite")) {
85
+ tooling.add("vite");
86
+ }
87
+ const frameworkList = Array.from(frameworks);
88
+ const primaryFramework = frameworkList[0] ?? "unknown";
89
+ return {
90
+ primaryFramework,
91
+ frameworks: frameworkList.length > 0 ? frameworkList : ["unknown"],
92
+ tooling: Array.from(tooling),
93
+ };
94
+ };
95
+ exports.detectProject = detectProject;
96
+ const getSchemaTemplate = (framework, port = 4000) => {
97
+ const frameworkHint = framework === "nextjs"
98
+ ? "# Framework: Next.js"
99
+ : framework === "reactjs"
100
+ ? "# Framework: React"
101
+ : "# Framework: unknown";
102
+ return [
103
+ frameworkHint,
104
+ "# Server",
105
+ `port: ${port}`,
106
+ "",
107
+ "# Routes",
108
+ "# Format: METHOD /endpoint: field:type,field:type",
109
+ "GET /users: id:uuid,fullName:name,username:string,email:email,avatarUrl:url",
110
+ "GET /posts: id:uuid,title:string,body:string,createdAt:date",
111
+ ].join("\n");
112
+ };
113
+ exports.getSchemaTemplate = getSchemaTemplate;
@@ -0,0 +1,3 @@
1
+ export declare const findClosestPackageJson: (startDirectory: string) => string | undefined;
2
+ export declare const resolveProjectRoot: () => string | undefined;
3
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/project/paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,GACjC,gBAAgB,MAAM,KACrB,MAAM,GAAG,SAgBX,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,SAO9C,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveProjectRoot = exports.findClosestPackageJson = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const findClosestPackageJson = (startDirectory) => {
7
+ let currentDirectory = startDirectory;
8
+ while (true) {
9
+ const candidate = (0, node_path_1.join)(currentDirectory, "package.json");
10
+ if ((0, node_fs_1.existsSync)(candidate)) {
11
+ return candidate;
12
+ }
13
+ const parentDirectory = (0, node_path_1.dirname)(currentDirectory);
14
+ if (parentDirectory === currentDirectory) {
15
+ return undefined;
16
+ }
17
+ currentDirectory = parentDirectory;
18
+ }
19
+ };
20
+ exports.findClosestPackageJson = findClosestPackageJson;
21
+ const resolveProjectRoot = () => {
22
+ const packageJsonPath = (0, exports.findClosestPackageJson)(process.cwd());
23
+ if (!packageJsonPath) {
24
+ return undefined;
25
+ }
26
+ return (0, node_path_1.dirname)(packageJsonPath);
27
+ };
28
+ exports.resolveProjectRoot = resolveProjectRoot;