dorval 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # dorval
2
+
3
+ CLI tool for generating Dart/Flutter API clients from OpenAPI specifications.
4
+
5
+ ## Features
6
+
7
+ - šŸŽÆ Generate type-safe Dart API clients from OpenAPI/Swagger specs
8
+ - ā„ļø Freezed models for immutable data classes
9
+ - 🌐 Dio HTTP client integration
10
+ - šŸ“ Full OpenAPI 3.0 support
11
+ - šŸ”§ Customizable code generation
12
+ - šŸ‘€ Watch mode for development
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install -g dorval
18
+ # or
19
+ yarn global add dorval
20
+ # or
21
+ npx dorval
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Command Line
27
+
28
+ ```bash
29
+ # Generate from OpenAPI spec
30
+ dorval generate -i ./openapi.yaml -o ./lib/api
31
+
32
+ # Watch mode
33
+ dorval watch -i ./openapi.yaml -o ./lib/api
34
+
35
+ # With configuration file
36
+ dorval generate
37
+ ```
38
+
39
+ ### Configuration File
40
+
41
+ Create an `orval.config.js` file:
42
+
43
+ ```javascript
44
+ module.exports = {
45
+ petstore: {
46
+ input: './petstore.yaml',
47
+ output: {
48
+ target: './lib/api',
49
+ mode: 'split',
50
+ client: 'dio',
51
+ clean: true
52
+ }
53
+ }
54
+ };
55
+ ```
56
+
57
+ ## CLI Options
58
+
59
+ ```
60
+ dorval generate [options]
61
+
62
+ Options:
63
+ -i, --input <path> Path to OpenAPI specification
64
+ -o, --output <path> Output directory
65
+ -c, --config <path> Path to config file
66
+ --mode <mode> File organization (single|split)
67
+ --client <client> HTTP client (dio)
68
+ --clean Clean output directory
69
+ -h, --help Display help
70
+ ```
71
+
72
+ ## Generated Output
73
+
74
+ After running the generator, add these dependencies to your Flutter project:
75
+
76
+ ```yaml
77
+ dependencies:
78
+ dio: ^5.0.0
79
+ freezed_annotation: ^2.4.1
80
+ json_annotation: ^4.8.1
81
+
82
+ dev_dependencies:
83
+ build_runner: ^2.4.6
84
+ freezed: ^2.4.5
85
+ json_serializable: ^6.7.1
86
+ ```
87
+
88
+ Then run:
89
+ ```bash
90
+ flutter pub run build_runner build
91
+ ```
92
+
93
+ ## Example
94
+
95
+ ```dart
96
+ import 'package:your_app/api/api_client.dart';
97
+ import 'package:your_app/api/services/index.dart';
98
+
99
+ final apiClient = ApiClient(baseUrl: 'https://api.example.com');
100
+ final userService = UserService(apiClient);
101
+
102
+ // Make API calls
103
+ final users = await userService.getUsers();
104
+ final user = await userService.getUserById('123');
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/bin/dorval.ts
27
+ var import_commander = require("commander");
28
+ var import_chalk3 = __toESM(require("chalk"));
29
+
30
+ // src/commands/generate.ts
31
+ var import_chalk = __toESM(require("chalk"));
32
+ var import_ora = __toESM(require("ora"));
33
+ var import_core = require("@dorval/core");
34
+
35
+ // src/config.ts
36
+ var import_cosmiconfig = require("cosmiconfig");
37
+ var explorer = (0, import_cosmiconfig.cosmiconfigSync)("orval", {
38
+ searchPlaces: [
39
+ "orval.config.ts",
40
+ "orval.config.js",
41
+ "orval.config.mjs",
42
+ "orval.config.cjs",
43
+ ".orvalrc",
44
+ ".orvalrc.json",
45
+ ".orvalrc.yaml",
46
+ ".orvalrc.yml",
47
+ "package.json"
48
+ ]
49
+ });
50
+ async function loadConfig(configPath) {
51
+ const result = configPath ? explorer.load(configPath) : explorer.search();
52
+ if (!result) {
53
+ throw new Error("No configuration file found");
54
+ }
55
+ const config = result.config.default || result.config;
56
+ if (typeof config === "object" && !config.input) {
57
+ const firstKey = Object.keys(config)[0];
58
+ return config[firstKey];
59
+ }
60
+ return config;
61
+ }
62
+
63
+ // src/commands/generate.ts
64
+ async function generateCommand(options) {
65
+ const spinner = (0, import_ora.default)("Loading configuration...").start();
66
+ try {
67
+ let config;
68
+ if (options.config) {
69
+ config = await loadConfig(options.config);
70
+ } else if (options.input && options.output) {
71
+ config = {
72
+ input: options.input,
73
+ output: {
74
+ target: options.output,
75
+ client: options.client || "dio",
76
+ mode: "split",
77
+ override: {
78
+ generator: {
79
+ freezed: true,
80
+ jsonSerializable: true,
81
+ nullSafety: true
82
+ }
83
+ }
84
+ }
85
+ };
86
+ } else {
87
+ throw new Error("Either provide a config file or input/output options");
88
+ }
89
+ spinner.text = "Parsing OpenAPI specification...";
90
+ const files = await (0, import_core.generateDartCode)(config);
91
+ spinner.succeed(import_chalk.default.green(`\u2705 Generated ${files.length} files`));
92
+ console.log(import_chalk.default.cyan("\nGenerated files:"));
93
+ files.forEach((file) => {
94
+ console.log(import_chalk.default.gray(` - ${file.path}`));
95
+ });
96
+ if (config.hooks?.afterAllFilesWrite) {
97
+ spinner.start("Running post-generation hooks...");
98
+ spinner.succeed("Hooks completed");
99
+ }
100
+ console.log(import_chalk.default.green("\n\u2728 Generation completed successfully!"));
101
+ } catch (error) {
102
+ spinner.fail(import_chalk.default.red("Generation failed"));
103
+ console.error(import_chalk.default.red(`
104
+ Error: ${error instanceof Error ? error.message : String(error)}`));
105
+ process.exit(1);
106
+ }
107
+ }
108
+
109
+ // src/commands/watch.ts
110
+ var import_chalk2 = __toESM(require("chalk"));
111
+ var import_ora2 = __toESM(require("ora"));
112
+ var fs = __toESM(require("fs"));
113
+ var path = __toESM(require("path"));
114
+ var import_core2 = require("@dorval/core");
115
+ async function watchCommand(options) {
116
+ const spinner = (0, import_ora2.default)("Starting watch mode...").start();
117
+ try {
118
+ const config = await loadConfig(options.config);
119
+ if (typeof config.input !== "string") {
120
+ throw new Error("Watch mode requires a file path input");
121
+ }
122
+ const inputPath = path.resolve(config.input);
123
+ spinner.succeed(import_chalk2.default.green(`Watching ${inputPath} for changes...`));
124
+ await (0, import_core2.generateDartCode)(config);
125
+ console.log(import_chalk2.default.cyan("\u2705 Initial generation completed"));
126
+ fs.watchFile(inputPath, async () => {
127
+ console.log(import_chalk2.default.yellow("\n\u{1F4DD} File changed, regenerating..."));
128
+ try {
129
+ await (0, import_core2.generateDartCode)(config);
130
+ console.log(import_chalk2.default.green("\u2705 Regeneration completed"));
131
+ } catch (error) {
132
+ console.error(import_chalk2.default.red(`\u274C Regeneration failed: ${error instanceof Error ? error.message : String(error)}`));
133
+ }
134
+ });
135
+ process.stdin.resume();
136
+ process.on("SIGINT", () => {
137
+ console.log(import_chalk2.default.yellow("\n\u{1F44B} Stopping watch mode..."));
138
+ fs.unwatchFile(inputPath);
139
+ process.exit(0);
140
+ });
141
+ } catch (error) {
142
+ spinner.fail(import_chalk2.default.red("Watch mode failed"));
143
+ console.error(import_chalk2.default.red(`
144
+ Error: ${error instanceof Error ? error.message : String(error)}`));
145
+ process.exit(1);
146
+ }
147
+ }
148
+
149
+ // package.json
150
+ var version = "0.0.1";
151
+
152
+ // src/bin/dorval.ts
153
+ import_commander.program.name("dorval").description("Generate Dart API clients from OpenAPI specifications").version(version);
154
+ import_commander.program.command("generate").alias("gen").description("Generate Dart code from OpenAPI spec").option("-c, --config <path>", "Path to config file", "./orval.config.ts").option("-i, --input <path>", "OpenAPI spec file or URL").option("-o, --output <path>", "Output directory").option("--client <type>", "Client type (dio, http, chopper)", "dio").option("--watch", "Watch for changes").action(generateCommand);
155
+ import_commander.program.command("watch").description("Watch OpenAPI spec for changes and regenerate").option("-c, --config <path>", "Path to config file", "./orval.config.ts").action(watchCommand);
156
+ import_commander.program.action(() => {
157
+ console.log(import_chalk3.default.cyan(`
158
+ \u{1F3AF} Dorval v${version}
159
+ Generate type-safe Dart API clients from OpenAPI specifications.
160
+
161
+ Usage:
162
+ dorval generate [options]
163
+ dorval watch [options]
164
+
165
+ Run 'dorval --help' for more information.
166
+ `));
167
+ });
168
+ import_commander.program.parse(process.argv);
169
+ if (!process.argv.slice(2).length) {
170
+ import_commander.program.outputHelp();
171
+ }
172
+ //# sourceMappingURL=dorval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bin/dorval.ts","../../src/commands/generate.ts","../../src/config.ts","../../src/commands/watch.ts","../../package.json"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Dorval CLI entry point\n */\n\nimport { program } from 'commander';\nimport chalk from 'chalk';\nimport { generateCommand } from '../commands/generate';\nimport { watchCommand } from '../commands/watch';\nimport { version } from '../../package.json';\n\nprogram\n .name('dorval')\n .description('Generate Dart API clients from OpenAPI specifications')\n .version(version);\n\n// Generate command\nprogram\n .command('generate')\n .alias('gen')\n .description('Generate Dart code from OpenAPI spec')\n .option('-c, --config <path>', 'Path to config file', './orval.config.ts')\n .option('-i, --input <path>', 'OpenAPI spec file or URL')\n .option('-o, --output <path>', 'Output directory')\n .option('--client <type>', 'Client type (dio, http, chopper)', 'dio')\n .option('--watch', 'Watch for changes')\n .action(generateCommand);\n\n// Watch command\nprogram\n .command('watch')\n .description('Watch OpenAPI spec for changes and regenerate')\n .option('-c, --config <path>', 'Path to config file', './orval.config.ts')\n .action(watchCommand);\n\n// Default action\nprogram\n .action(() => {\n console.log(chalk.cyan(`\nšŸŽÆ Dorval v${version}\nGenerate type-safe Dart API clients from OpenAPI specifications.\n\nUsage:\n dorval generate [options]\n dorval watch [options]\n\nRun 'dorval --help' for more information.\n `));\n });\n\n// Parse command line arguments\nprogram.parse(process.argv);\n\n// Show help if no arguments\nif (!process.argv.slice(2).length) {\n program.outputHelp();\n}","/**\n * Generate command implementation\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { generateDartCode, DartGeneratorOptions } from '@dorval/core';\nimport { loadConfig } from '../config';\n\ninterface GenerateOptions {\n config?: string;\n input?: string;\n output?: string;\n client?: 'dio' | 'http' | 'chopper';\n watch?: boolean;\n}\n\nexport async function generateCommand(options: GenerateOptions) {\n const spinner = ora('Loading configuration...').start();\n \n try {\n // Load configuration\n let config: DartGeneratorOptions;\n \n if (options.config) {\n config = await loadConfig(options.config);\n } else if (options.input && options.output) {\n // Use command line options\n config = {\n input: options.input,\n output: {\n target: options.output,\n client: options.client || 'dio',\n mode: 'split',\n override: {\n generator: {\n freezed: true,\n jsonSerializable: true,\n nullSafety: true\n }\n }\n }\n };\n } else {\n throw new Error('Either provide a config file or input/output options');\n }\n \n spinner.text = 'Parsing OpenAPI specification...';\n \n // Generate code\n const files = await generateDartCode(config);\n \n spinner.succeed(chalk.green(`āœ… Generated ${files.length} files`));\n \n // List generated files\n console.log(chalk.cyan('\\nGenerated files:'));\n files.forEach(file => {\n console.log(chalk.gray(` - ${file.path}`));\n });\n \n // Run post-generation hooks\n if (config.hooks?.afterAllFilesWrite) {\n spinner.start('Running post-generation hooks...');\n // TODO: Implement hook execution\n spinner.succeed('Hooks completed');\n }\n \n console.log(chalk.green('\\n✨ Generation completed successfully!'));\n \n } catch (error) {\n spinner.fail(chalk.red('Generation failed'));\n console.error(chalk.red(`\\nError: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n}","/**\n * Configuration loader\n */\n\nimport { cosmiconfigSync } from 'cosmiconfig';\nimport { DartGeneratorOptions } from '@dorval/core';\n\nconst explorer = cosmiconfigSync('orval', {\n searchPlaces: [\n 'orval.config.ts',\n 'orval.config.js',\n 'orval.config.mjs',\n 'orval.config.cjs',\n '.orvalrc',\n '.orvalrc.json',\n '.orvalrc.yaml',\n '.orvalrc.yml',\n 'package.json'\n ]\n});\n\nexport async function loadConfig(configPath?: string): Promise<DartGeneratorOptions> {\n const result = configPath\n ? explorer.load(configPath)\n : explorer.search();\n \n if (!result) {\n throw new Error('No configuration file found');\n }\n \n // Handle default export or direct config\n const config = result.config.default || result.config;\n \n // If config has multiple specs, use the first one\n if (typeof config === 'object' && !config.input) {\n const firstKey = Object.keys(config)[0];\n return config[firstKey];\n }\n \n return config;\n}","/**\n * Watch command implementation\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { generateDartCode } from '@dorval/core';\nimport { loadConfig } from '../config';\n\ninterface WatchOptions {\n config?: string;\n}\n\nexport async function watchCommand(options: WatchOptions) {\n const spinner = ora('Starting watch mode...').start();\n \n try {\n // Load configuration\n const config = await loadConfig(options.config);\n \n if (typeof config.input !== 'string') {\n throw new Error('Watch mode requires a file path input');\n }\n \n const inputPath = path.resolve(config.input);\n \n spinner.succeed(chalk.green(`Watching ${inputPath} for changes...`));\n \n // Initial generation\n await generateDartCode(config);\n console.log(chalk.cyan('āœ… Initial generation completed'));\n \n // Watch for changes\n fs.watchFile(inputPath, async () => {\n console.log(chalk.yellow('\\nšŸ“ File changed, regenerating...'));\n \n try {\n await generateDartCode(config);\n console.log(chalk.green('āœ… Regeneration completed'));\n } catch (error) {\n console.error(chalk.red(`āŒ Regeneration failed: ${error instanceof Error ? error.message : String(error)}`));\n }\n });\n \n // Keep process alive\n process.stdin.resume();\n \n // Handle exit\n process.on('SIGINT', () => {\n console.log(chalk.yellow('\\nšŸ‘‹ Stopping watch mode...'));\n fs.unwatchFile(inputPath);\n process.exit(0);\n });\n \n } catch (error) {\n spinner.fail(chalk.red('Watch mode failed'));\n console.error(chalk.red(`\\nError: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n}","{\n \"name\": \"dorval\",\n \"version\": \"0.0.1\",\n \"description\": \"CLI tool for generating Dart/Flutter API clients from OpenAPI specifications\",\n \"license\": \"MIT\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"bin\": {\n \"dorval\": \"./dist/bin/dorval.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup ./src/index.ts ./src/bin/dorval.ts --clean --sourcemap\",\n \"dev\": \"tsup ./src/index.ts ./src/bin/dorval.ts --clean --sourcemap --watch src --dts\",\n \"lint\": \"eslint src/**/*.ts\",\n \"test\": \"vitest run\"\n },\n \"dependencies\": {\n \"@dorval/core\": \"0.0.1\",\n \"commander\": \"^11.0.0\",\n \"chalk\": \"^4.1.2\",\n \"ora\": \"^5.4.1\",\n \"cosmiconfig\": \"^8.2.0\",\n \"fs-extra\": \"^11.3.0\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^20.13.0\",\n \"typescript\": \"^5.2.2\",\n \"tsup\": \"^8.5.0\",\n \"vitest\": \"^0.6.3\"\n },\n \"engines\": {\n \"node\": \">=18\"\n }\n}"],"mappings":"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","names":["import_chalk","ora","chalk","import_chalk","import_ora","import_core","ora","chalk","chalk"]}
package/dist/index.js ADDED
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ generateCommand: () => generateCommand,
34
+ loadConfig: () => loadConfig,
35
+ runCLI: () => runCLI,
36
+ watchCommand: () => watchCommand
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/cli.ts
41
+ var import_commander = require("commander");
42
+
43
+ // src/commands/generate.ts
44
+ var import_chalk = __toESM(require("chalk"));
45
+ var import_ora = __toESM(require("ora"));
46
+ var import_core = require("@dorval/core");
47
+
48
+ // src/config.ts
49
+ var import_cosmiconfig = require("cosmiconfig");
50
+ var explorer = (0, import_cosmiconfig.cosmiconfigSync)("orval", {
51
+ searchPlaces: [
52
+ "orval.config.ts",
53
+ "orval.config.js",
54
+ "orval.config.mjs",
55
+ "orval.config.cjs",
56
+ ".orvalrc",
57
+ ".orvalrc.json",
58
+ ".orvalrc.yaml",
59
+ ".orvalrc.yml",
60
+ "package.json"
61
+ ]
62
+ });
63
+ async function loadConfig(configPath) {
64
+ const result = configPath ? explorer.load(configPath) : explorer.search();
65
+ if (!result) {
66
+ throw new Error("No configuration file found");
67
+ }
68
+ const config = result.config.default || result.config;
69
+ if (typeof config === "object" && !config.input) {
70
+ const firstKey = Object.keys(config)[0];
71
+ return config[firstKey];
72
+ }
73
+ return config;
74
+ }
75
+
76
+ // src/commands/generate.ts
77
+ async function generateCommand(options) {
78
+ const spinner = (0, import_ora.default)("Loading configuration...").start();
79
+ try {
80
+ let config;
81
+ if (options.config) {
82
+ config = await loadConfig(options.config);
83
+ } else if (options.input && options.output) {
84
+ config = {
85
+ input: options.input,
86
+ output: {
87
+ target: options.output,
88
+ client: options.client || "dio",
89
+ mode: "split",
90
+ override: {
91
+ generator: {
92
+ freezed: true,
93
+ jsonSerializable: true,
94
+ nullSafety: true
95
+ }
96
+ }
97
+ }
98
+ };
99
+ } else {
100
+ throw new Error("Either provide a config file or input/output options");
101
+ }
102
+ spinner.text = "Parsing OpenAPI specification...";
103
+ const files = await (0, import_core.generateDartCode)(config);
104
+ spinner.succeed(import_chalk.default.green(`\u2705 Generated ${files.length} files`));
105
+ console.log(import_chalk.default.cyan("\nGenerated files:"));
106
+ files.forEach((file) => {
107
+ console.log(import_chalk.default.gray(` - ${file.path}`));
108
+ });
109
+ if (config.hooks?.afterAllFilesWrite) {
110
+ spinner.start("Running post-generation hooks...");
111
+ spinner.succeed("Hooks completed");
112
+ }
113
+ console.log(import_chalk.default.green("\n\u2728 Generation completed successfully!"));
114
+ } catch (error) {
115
+ spinner.fail(import_chalk.default.red("Generation failed"));
116
+ console.error(import_chalk.default.red(`
117
+ Error: ${error instanceof Error ? error.message : String(error)}`));
118
+ process.exit(1);
119
+ }
120
+ }
121
+
122
+ // src/commands/watch.ts
123
+ var import_chalk2 = __toESM(require("chalk"));
124
+ var import_ora2 = __toESM(require("ora"));
125
+ var fs = __toESM(require("fs"));
126
+ var path = __toESM(require("path"));
127
+ var import_core2 = require("@dorval/core");
128
+ async function watchCommand(options) {
129
+ const spinner = (0, import_ora2.default)("Starting watch mode...").start();
130
+ try {
131
+ const config = await loadConfig(options.config);
132
+ if (typeof config.input !== "string") {
133
+ throw new Error("Watch mode requires a file path input");
134
+ }
135
+ const inputPath = path.resolve(config.input);
136
+ spinner.succeed(import_chalk2.default.green(`Watching ${inputPath} for changes...`));
137
+ await (0, import_core2.generateDartCode)(config);
138
+ console.log(import_chalk2.default.cyan("\u2705 Initial generation completed"));
139
+ fs.watchFile(inputPath, async () => {
140
+ console.log(import_chalk2.default.yellow("\n\u{1F4DD} File changed, regenerating..."));
141
+ try {
142
+ await (0, import_core2.generateDartCode)(config);
143
+ console.log(import_chalk2.default.green("\u2705 Regeneration completed"));
144
+ } catch (error) {
145
+ console.error(import_chalk2.default.red(`\u274C Regeneration failed: ${error instanceof Error ? error.message : String(error)}`));
146
+ }
147
+ });
148
+ process.stdin.resume();
149
+ process.on("SIGINT", () => {
150
+ console.log(import_chalk2.default.yellow("\n\u{1F44B} Stopping watch mode..."));
151
+ fs.unwatchFile(inputPath);
152
+ process.exit(0);
153
+ });
154
+ } catch (error) {
155
+ spinner.fail(import_chalk2.default.red("Watch mode failed"));
156
+ console.error(import_chalk2.default.red(`
157
+ Error: ${error instanceof Error ? error.message : String(error)}`));
158
+ process.exit(1);
159
+ }
160
+ }
161
+
162
+ // src/cli.ts
163
+ function runCLI(args) {
164
+ const program = new import_commander.Command();
165
+ program.name("dorval").description("Generate Dart API clients from OpenAPI specifications");
166
+ program.command("generate").description("Generate Dart code").option("-c, --config <path>", "Config file path").option("-i, --input <path>", "Input spec").option("-o, --output <path>", "Output directory").action(generateCommand);
167
+ program.command("watch").description("Watch for changes").option("-c, --config <path>", "Config file path").action(watchCommand);
168
+ program.parse(args);
169
+ }
170
+ // Annotate the CommonJS export names for ESM import in node:
171
+ 0 && (module.exports = {
172
+ generateCommand,
173
+ loadConfig,
174
+ runCLI,
175
+ watchCommand
176
+ });
177
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cli.ts","../src/commands/generate.ts","../src/config.ts","../src/commands/watch.ts"],"sourcesContent":["/**\n * @dorval/cli\n * CLI exports for programmatic usage\n */\n\nexport { runCLI } from './cli';\nexport { loadConfig } from './config';\nexport { generateCommand } from './commands/generate';\nexport { watchCommand } from './commands/watch';","/**\n * CLI runner for programmatic usage\n */\n\nimport { Command } from 'commander';\nimport { generateCommand } from './commands/generate';\nimport { watchCommand } from './commands/watch';\n\nexport function runCLI(args: string[]): void {\n const program = new Command();\n \n program\n .name('dorval')\n .description('Generate Dart API clients from OpenAPI specifications');\n \n program\n .command('generate')\n .description('Generate Dart code')\n .option('-c, --config <path>', 'Config file path')\n .option('-i, --input <path>', 'Input spec')\n .option('-o, --output <path>', 'Output directory')\n .action(generateCommand);\n \n program\n .command('watch')\n .description('Watch for changes')\n .option('-c, --config <path>', 'Config file path')\n .action(watchCommand);\n \n program.parse(args);\n}","/**\n * Generate command implementation\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { generateDartCode, DartGeneratorOptions } from '@dorval/core';\nimport { loadConfig } from '../config';\n\ninterface GenerateOptions {\n config?: string;\n input?: string;\n output?: string;\n client?: 'dio' | 'http' | 'chopper';\n watch?: boolean;\n}\n\nexport async function generateCommand(options: GenerateOptions) {\n const spinner = ora('Loading configuration...').start();\n \n try {\n // Load configuration\n let config: DartGeneratorOptions;\n \n if (options.config) {\n config = await loadConfig(options.config);\n } else if (options.input && options.output) {\n // Use command line options\n config = {\n input: options.input,\n output: {\n target: options.output,\n client: options.client || 'dio',\n mode: 'split',\n override: {\n generator: {\n freezed: true,\n jsonSerializable: true,\n nullSafety: true\n }\n }\n }\n };\n } else {\n throw new Error('Either provide a config file or input/output options');\n }\n \n spinner.text = 'Parsing OpenAPI specification...';\n \n // Generate code\n const files = await generateDartCode(config);\n \n spinner.succeed(chalk.green(`āœ… Generated ${files.length} files`));\n \n // List generated files\n console.log(chalk.cyan('\\nGenerated files:'));\n files.forEach(file => {\n console.log(chalk.gray(` - ${file.path}`));\n });\n \n // Run post-generation hooks\n if (config.hooks?.afterAllFilesWrite) {\n spinner.start('Running post-generation hooks...');\n // TODO: Implement hook execution\n spinner.succeed('Hooks completed');\n }\n \n console.log(chalk.green('\\n✨ Generation completed successfully!'));\n \n } catch (error) {\n spinner.fail(chalk.red('Generation failed'));\n console.error(chalk.red(`\\nError: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n}","/**\n * Configuration loader\n */\n\nimport { cosmiconfigSync } from 'cosmiconfig';\nimport { DartGeneratorOptions } from '@dorval/core';\n\nconst explorer = cosmiconfigSync('orval', {\n searchPlaces: [\n 'orval.config.ts',\n 'orval.config.js',\n 'orval.config.mjs',\n 'orval.config.cjs',\n '.orvalrc',\n '.orvalrc.json',\n '.orvalrc.yaml',\n '.orvalrc.yml',\n 'package.json'\n ]\n});\n\nexport async function loadConfig(configPath?: string): Promise<DartGeneratorOptions> {\n const result = configPath\n ? explorer.load(configPath)\n : explorer.search();\n \n if (!result) {\n throw new Error('No configuration file found');\n }\n \n // Handle default export or direct config\n const config = result.config.default || result.config;\n \n // If config has multiple specs, use the first one\n if (typeof config === 'object' && !config.input) {\n const firstKey = Object.keys(config)[0];\n return config[firstKey];\n }\n \n return config;\n}","/**\n * Watch command implementation\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { generateDartCode } from '@dorval/core';\nimport { loadConfig } from '../config';\n\ninterface WatchOptions {\n config?: string;\n}\n\nexport async function watchCommand(options: WatchOptions) {\n const spinner = ora('Starting watch mode...').start();\n \n try {\n // Load configuration\n const config = await loadConfig(options.config);\n \n if (typeof config.input !== 'string') {\n throw new Error('Watch mode requires a file path input');\n }\n \n const inputPath = path.resolve(config.input);\n \n spinner.succeed(chalk.green(`Watching ${inputPath} for changes...`));\n \n // Initial generation\n await generateDartCode(config);\n console.log(chalk.cyan('āœ… Initial generation completed'));\n \n // Watch for changes\n fs.watchFile(inputPath, async () => {\n console.log(chalk.yellow('\\nšŸ“ File changed, regenerating...'));\n \n try {\n await generateDartCode(config);\n console.log(chalk.green('āœ… Regeneration completed'));\n } catch (error) {\n console.error(chalk.red(`āŒ Regeneration failed: ${error instanceof Error ? error.message : String(error)}`));\n }\n });\n \n // Keep process alive\n process.stdin.resume();\n \n // Handle exit\n process.on('SIGINT', () => {\n console.log(chalk.yellow('\\nšŸ‘‹ Stopping watch mode...'));\n fs.unwatchFile(inputPath);\n process.exit(0);\n });\n \n } catch (error) {\n spinner.fail(chalk.red('Watch mode failed'));\n console.error(chalk.red(`\\nError: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n}"],"mappings":"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","names":["ora","chalk","import_chalk","import_ora","import_core","ora","chalk"]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "dorval",
3
+ "version": "0.0.1",
4
+ "description": "CLI tool for generating Dart/Flutter API clients from OpenAPI specifications",
5
+ "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "dorval": "./dist/bin/dorval.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup ./src/index.ts ./src/bin/dorval.ts --clean --sourcemap",
16
+ "dev": "tsup ./src/index.ts ./src/bin/dorval.ts --clean --sourcemap --watch src --dts",
17
+ "lint": "eslint src/**/*.ts",
18
+ "test": "vitest run"
19
+ },
20
+ "dependencies": {
21
+ "@dorval/core": "0.0.1",
22
+ "commander": "^11.0.0",
23
+ "chalk": "^4.1.2",
24
+ "ora": "^5.4.1",
25
+ "cosmiconfig": "^8.2.0",
26
+ "fs-extra": "^11.3.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/fs-extra": "^11.0.4",
30
+ "@types/node": "^20.13.0",
31
+ "typescript": "^5.2.2",
32
+ "tsup": "^8.5.0",
33
+ "vitest": "^0.6.3"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ }
38
+ }