orval 8.0.0-rc.1 → 8.0.0-rc.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.
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ import { c as name, i as startWatcher, l as version, n as loadConfigFile, o as normalizeOptions, r as generateSpec, s as description, t as findConfigFile } from "../config-BH1mpZT1.mjs";
3
+ import path from "node:path";
4
+ import { Option, program } from "@commander-js/extra-typings";
5
+ import { ErrorWithTag, OutputClient, OutputMode, isString, log, logError, startMessage } from "@orval/core";
6
+
7
+ //#region src/bin/orval.ts
8
+ const orvalMessage = startMessage({
9
+ name,
10
+ version,
11
+ description
12
+ });
13
+ const cli = program.name("orval").description(orvalMessage).version(version);
14
+ cli.addOption(new Option("-o, --output <path>", "output file destination").conflicts(["config", "project"])).addOption(new Option("-i, --input <path>", "input file (yaml or json openapi specs)").conflicts(["config", "project"])).addOption(new Option("-c, --config <path>", "override flags by a config file").conflicts(["input", "output"])).addOption(new Option("-p, --project <name>", "focus a project of the config").conflicts(["input", "output"])).addOption(new Option("-m, --mode <name>", "default mode that will be used").choices(Object.values(OutputMode))).option("-w, --watch [paths...]", "Watch mode, if path is not specified, it watches the input target").addOption(new Option("--client <name>", "default client that will be used").choices(Object.values(OutputClient))).option("--mock", "activate the mock").option("--clean [paths...]", "Clean output directory").option("--prettier", "Prettier generated files").option("--biome", "biome generated files").option("--tsconfig <path>", "path to your tsconfig file").action(async (options) => {
15
+ log(orvalMessage);
16
+ if (isString(options.input) && isString(options.output)) {
17
+ const normalizedOptions = await normalizeOptions({
18
+ input: options.input,
19
+ output: {
20
+ target: options.output,
21
+ clean: options.clean,
22
+ prettier: options.prettier,
23
+ biome: options.biome,
24
+ mock: options.mock,
25
+ client: options.client,
26
+ mode: options.mode,
27
+ tsconfig: options.tsconfig
28
+ }
29
+ });
30
+ if (options.watch) await startWatcher(options.watch, async () => {
31
+ try {
32
+ await generateSpec(process.cwd(), normalizedOptions);
33
+ } catch (error) {
34
+ logError(error);
35
+ process.exit(1);
36
+ }
37
+ }, normalizedOptions.input.target);
38
+ else try {
39
+ await generateSpec(process.cwd(), normalizedOptions);
40
+ } catch (error) {
41
+ if (error instanceof ErrorWithTag) logError(error.cause, error.tag);
42
+ else logError(error);
43
+ process.exit(1);
44
+ }
45
+ } else {
46
+ const configFilePath = findConfigFile(options.config);
47
+ const workspace = path.dirname(configFilePath);
48
+ const configFile = await loadConfigFile(configFilePath);
49
+ if (isString(options.project) && !Object.hasOwn(configFile, options.project)) {
50
+ logError("Project not found in config", options.project);
51
+ process.exit(1);
52
+ }
53
+ const configs = Object.entries(configFile).filter(([projectName]) => !isString(options.project) || projectName === options.project);
54
+ let hasErrors = false;
55
+ for (const [projectName, config] of configs) {
56
+ const normalizedOptions = await normalizeOptions(config, workspace, options);
57
+ if (options.watch === void 0) try {
58
+ await generateSpec(workspace, normalizedOptions, projectName);
59
+ } catch (error) {
60
+ hasErrors = true;
61
+ logError(error, projectName);
62
+ }
63
+ else {
64
+ const fileToWatch = isString(normalizedOptions.input.target) ? normalizedOptions.input.target : void 0;
65
+ await startWatcher(options.watch, async () => {
66
+ try {
67
+ await generateSpec(workspace, normalizedOptions, projectName);
68
+ } catch (error) {
69
+ logError(error, projectName);
70
+ }
71
+ }, fileToWatch);
72
+ }
73
+ }
74
+ if (hasErrors) logError("One or more project failed, see above for details");
75
+ }
76
+ });
77
+ await cli.parseAsync(process.argv);
78
+
79
+ //#endregion
80
+ export { };
81
+ //# sourceMappingURL=orval.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orval.mjs","names":["pkg.name","pkg.version","pkg.description"],"sources":["../../src/bin/orval.ts"],"sourcesContent":["#!/usr/bin/env node\nimport path from 'node:path';\n\nimport { Option, program } from '@commander-js/extra-typings';\nimport {\n ErrorWithTag,\n isString,\n log,\n logError,\n OutputClient,\n OutputMode,\n startMessage,\n} from '@orval/core';\n\nimport pkg from '../../package.json';\nimport { generateSpec } from '../generate-spec';\nimport { findConfigFile, loadConfigFile } from '../utils/config';\nimport { normalizeOptions } from '../utils/options';\nimport { startWatcher } from '../utils/watcher';\n\nconst orvalMessage = startMessage({\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n});\nconst cli = program\n .name('orval')\n .description(orvalMessage)\n .version(pkg.version);\n\ncli\n .addOption(\n new Option('-o, --output <path>', 'output file destination').conflicts([\n 'config',\n 'project',\n ]),\n )\n .addOption(\n new Option(\n '-i, --input <path>',\n 'input file (yaml or json openapi specs)',\n ).conflicts(['config', 'project']),\n )\n .addOption(\n new Option(\n '-c, --config <path>',\n 'override flags by a config file',\n ).conflicts(['input', 'output']),\n )\n .addOption(\n new Option(\n '-p, --project <name>',\n 'focus a project of the config',\n ).conflicts(['input', 'output']),\n )\n .addOption(\n new Option('-m, --mode <name>', 'default mode that will be used').choices(\n Object.values(OutputMode),\n ),\n )\n .option(\n '-w, --watch [paths...]',\n 'Watch mode, if path is not specified, it watches the input target',\n )\n .addOption(\n new Option('--client <name>', 'default client that will be used').choices(\n Object.values(OutputClient),\n ),\n )\n .option('--mock', 'activate the mock')\n .option('--clean [paths...]', 'Clean output directory')\n .option('--prettier', 'Prettier generated files')\n .option('--biome', 'biome generated files')\n .option('--tsconfig <path>', 'path to your tsconfig file')\n .action(async (options) => {\n log(orvalMessage);\n\n if (isString(options.input) && isString(options.output)) {\n const normalizedOptions = await normalizeOptions({\n input: options.input,\n output: {\n target: options.output,\n clean: options.clean,\n prettier: options.prettier,\n biome: options.biome,\n mock: options.mock,\n client: options.client,\n mode: options.mode,\n tsconfig: options.tsconfig,\n },\n });\n\n if (options.watch) {\n await startWatcher(\n options.watch,\n async () => {\n try {\n await generateSpec(process.cwd(), normalizedOptions);\n } catch (error) {\n logError(error);\n process.exit(1);\n }\n },\n normalizedOptions.input.target as string,\n );\n } else {\n try {\n await generateSpec(process.cwd(), normalizedOptions);\n } catch (error) {\n if (error instanceof ErrorWithTag) {\n logError(error.cause, error.tag);\n } else {\n logError(error);\n }\n process.exit(1);\n }\n }\n } else {\n const configFilePath = findConfigFile(options.config);\n const workspace = path.dirname(configFilePath);\n const configFile = await loadConfigFile(configFilePath);\n\n if (\n isString(options.project) &&\n !Object.hasOwn(configFile, options.project)\n ) {\n logError('Project not found in config', options.project);\n process.exit(1);\n }\n\n const configs = Object.entries(configFile).filter(\n ([projectName]) =>\n // only filter by project if specified\n !isString(options.project) || projectName === options.project,\n );\n\n let hasErrors = false;\n for (const [projectName, config] of configs) {\n const normalizedOptions = await normalizeOptions(\n config,\n workspace,\n options,\n );\n\n if (options.watch === undefined) {\n try {\n await generateSpec(workspace, normalizedOptions, projectName);\n } catch (error) {\n hasErrors = true;\n logError(error, projectName);\n }\n } else {\n const fileToWatch = isString(normalizedOptions.input.target)\n ? normalizedOptions.input.target\n : undefined;\n\n await startWatcher(\n options.watch,\n async () => {\n try {\n await generateSpec(workspace, normalizedOptions, projectName);\n } catch (error) {\n logError(error, projectName);\n }\n },\n fileToWatch,\n );\n }\n }\n\n if (hasErrors)\n logError('One or more project failed, see above for details');\n }\n });\n\nawait cli.parseAsync(process.argv);\n"],"mappings":"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"}
@@ -1,11 +1,8 @@
1
- import { ErrorWithTag, FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMode, PropertySortOrder, RefComponentSuffix, asyncReduce, createLogger, createSuccessMessage, dynamicImport, generateComponentDefinition, generateDependencyImports, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getFileInfo, getFullRoute, getMockFileExtensionByTypeName, getRoute, ibmOpenapiValidator, isBoolean, isFunction, isObject, isReference, isRootKey, isSchema, isString, isUndefined, isUrl, jsDoc, log, logError, mergeDeep, openApiConverter, pascal, removeFilesAndEmptyFolders, resolveRef, upath, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
2
- import fs from "node:fs";
3
1
  import path from "node:path";
4
- import process$1 from "node:process";
5
- import { createJiti } from "jiti";
2
+ import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMode, PropertySortOrder, RefComponentSuffix, asyncReduce, createLogger, createSuccessMessage, dynamicImport, generateComponentDefinition, generateDependencyImports, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getFileInfo, getFullRoute, getMockFileExtensionByTypeName, getRoute, ibmOpenapiValidator, isBoolean, isFunction, isObject, isReference, isRootKey, isSchema, isString, isUndefined, isUrl, jsDoc, log, logError, mergeDeep, openApiConverter, pascal, removeFilesAndEmptyFolders, resolveRef, upath, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
6
3
  import SwaggerParser from "@apidevtools/swagger-parser";
7
4
  import chalk from "chalk";
8
- import fs$1 from "fs-extra";
5
+ import fs from "fs-extra";
9
6
  import yaml from "js-yaml";
10
7
  import * as mock from "@orval/mock";
11
8
  import { DEFAULT_MOCK_OPTIONS, generateMockImports } from "@orval/mock";
@@ -17,14 +14,22 @@ import mcp from "@orval/mcp";
17
14
  import query from "@orval/query";
18
15
  import swr from "@orval/swr";
19
16
  import zod from "@orval/zod";
17
+ import { ExecaError, execa } from "execa";
18
+ import { unique } from "remeda";
19
+ import { parseArgsStringToArgv } from "string-argv";
20
20
  import https from "node:https";
21
21
  import enquirer from "enquirer";
22
22
  import { findUp } from "find-up";
23
23
  import { parse } from "tsconfck";
24
- import { ExecaError, execa } from "execa";
25
- import { unique } from "remeda";
26
- import { parseArgsStringToArgv } from "string-argv";
24
+ import fs$1 from "node:fs";
25
+ import { createJiti } from "jiti";
26
+
27
+ //#region package.json
28
+ var name = "orval";
29
+ var description = "A swagger client generator for typescript";
30
+ var version = "8.0.0-rc.2";
27
31
 
32
+ //#endregion
28
33
  //#region src/client.ts
29
34
  const DEFAULT_CLIENT = OutputClient.AXIOS;
30
35
  const getGeneratorClient = (outputClient, output) => {
@@ -347,7 +352,7 @@ const getAllSchemas = (spec, specKey) => {
347
352
 
348
353
  //#endregion
349
354
  //#region src/import-specs.ts
350
- const resolveSpecs = async (path$1, { validate,...options }, _isUrl, isOnlySchema) => {
355
+ const resolveSpecs = async (path$1, { validate, ...options }, _isUrl, isOnlySchema) => {
351
356
  try {
352
357
  if (validate) try {
353
358
  await SwaggerParser.validate(path$1, options);
@@ -359,7 +364,7 @@ const resolveSpecs = async (path$1, { validate,...options }, _isUrl, isOnlySchem
359
364
  if (_isUrl) return data;
360
365
  return Object.fromEntries(Object.entries(data).sort().map(([key, value]) => [isUrl(key) ? key : upath.resolve(key), value]));
361
366
  } catch {
362
- const file = await fs$1.readFile(path$1, "utf8");
367
+ const file = await fs.readFile(path$1, "utf8");
363
368
  return { [path$1]: yaml.load(file) };
364
369
  }
365
370
  };
@@ -383,10 +388,26 @@ const importSpecs = async (workspace, options) => {
383
388
  };
384
389
 
385
390
  //#endregion
386
- //#region package.json
387
- var name = "orval";
388
- var description = "A swagger client generator for typescript";
389
- var version = "8.0.0-rc.1";
391
+ //#region src/utils/execute-hook.ts
392
+ const executeHook = async (name$1, commands = [], args = []) => {
393
+ log(chalk.white(`Running ${name$1} hook...`));
394
+ for (const command of commands) try {
395
+ if (isString(command)) await executeCommand(command, args);
396
+ else if (isFunction(command)) await command(args);
397
+ else if (isObject(command)) await executeObjectCommand(command, args);
398
+ } catch (error) {
399
+ logError(error, `Failed to run ${name$1} hook`);
400
+ }
401
+ };
402
+ async function executeCommand(command, args) {
403
+ const [cmd, ..._args] = [...parseArgsStringToArgv(command), ...args];
404
+ await execa(cmd, _args);
405
+ }
406
+ async function executeObjectCommand(command, args) {
407
+ if (command.injectGeneratedDirsAndFiles === false) args = [];
408
+ if (isString(command.command)) await executeCommand(command.command, args);
409
+ else if (isFunction(command.command)) await command.command();
410
+ }
390
411
 
391
412
  //#endregion
392
413
  //#region src/utils/request.ts
@@ -439,7 +460,7 @@ const getGithubSpecReq = ({ accessToken, repo, owner, branch, path: path$1 }) =>
439
460
  let githubToken = null;
440
461
  const getGithubAcessToken = async (githubTokenPath) => {
441
462
  if (githubToken) return githubToken;
442
- if (await fs$1.pathExists(githubTokenPath)) return fs$1.readFile(githubTokenPath, "utf8");
463
+ if (await fs.pathExists(githubTokenPath)) return fs.readFile(githubTokenPath, "utf8");
443
464
  else {
444
465
  const answers = await enquirer.prompt([{
445
466
  type: "input",
@@ -451,7 +472,7 @@ const getGithubAcessToken = async (githubTokenPath) => {
451
472
  message: "Would you like to store your token for the next time? (stored in your node_modules)"
452
473
  }]);
453
474
  githubToken = answers.githubToken;
454
- if (answers.saveToken) await fs$1.outputFile(githubTokenPath, answers.githubToken);
475
+ if (answers.saveToken) await fs.outputFile(githubTokenPath, answers.githubToken);
455
476
  return answers.githubToken;
456
477
  }
457
478
  };
@@ -475,7 +496,7 @@ const getGithubOpenApi = async (url) => {
475
496
  type: "confirm",
476
497
  name: "removeToken",
477
498
  message: "Your token doesn't have the correct permissions, should we remove it?"
478
- }])).removeToken) await fs$1.unlink(githubTokenPath);
499
+ }])).removeToken) await fs.unlink(githubTokenPath);
479
500
  }
480
501
  }
481
502
  return body.data?.repository?.object.text;
@@ -486,7 +507,7 @@ const getGithubOpenApi = async (url) => {
486
507
  type: "confirm",
487
508
  name: "removeToken",
488
509
  message: "Your token doesn't have the correct permissions, should we remove it?"
489
- }])).removeToken) await fs$1.unlink(githubTokenPath);
510
+ }])).removeToken) await fs.unlink(githubTokenPath);
490
511
  }
491
512
  throw new Error(error.body.message || `Oups... 🍻. ${error}`);
492
513
  }
@@ -518,7 +539,7 @@ const loadPackageJson = async (packageJson, workspace = process.cwd()) => {
518
539
  return;
519
540
  }
520
541
  const normalizedPath = normalizePath(packageJson, workspace);
521
- if (fs$1.existsSync(normalizedPath)) {
542
+ if (fs.existsSync(normalizedPath)) {
522
543
  const pkg = await dynamicImport(normalizedPath);
523
544
  if (isPackageJson(pkg)) return await maybeReplaceCatalog(pkg, workspace);
524
545
  else throw new Error(`Invalid package.json file: ${normalizedPath}`);
@@ -536,7 +557,7 @@ const maybeReplaceCatalog = async (pkg, workspace) => {
536
557
  log(`⚠️ ${chalk.yellow("package.json contains pnpm catalog: in dependencies, but no pnpm-workspace.yaml was found.")}`);
537
558
  return pkg;
538
559
  }
539
- const file = await fs$1.readFile(filePath, "utf8");
560
+ const file = await fs.readFile(filePath, "utf8");
540
561
  const pnpmWorkspaceFile = yaml.load(file);
541
562
  performSubstitution(pkg.dependencies, pnpmWorkspaceFile);
542
563
  performSubstitution(pkg.devDependencies, pnpmWorkspaceFile);
@@ -582,7 +603,7 @@ const loadTsconfig = async (tsconfig, workspace = process.cwd()) => {
582
603
  }
583
604
  if (isString(tsconfig)) {
584
605
  const normalizedPath = normalizePath(tsconfig, workspace);
585
- if (fs$1.existsSync(normalizedPath)) {
606
+ if (fs.existsSync(normalizedPath)) {
586
607
  const config = await parse(normalizedPath);
587
608
  return config.referenced?.find(({ tsconfigFile }) => tsconfigFile === normalizedPath)?.tsconfig || config.tsconfig;
588
609
  }
@@ -813,7 +834,7 @@ const normalizePath = (path$1, workspace) => {
813
834
  return upath.resolve(workspace, path$1);
814
835
  };
815
836
  const normalizeOperationsAndTags = (operationsOrTags, workspace, global) => {
816
- return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, query: query$1, zod: zod$1,...rest }]) => {
837
+ return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, query: query$1, zod: zod$1, ...rest }]) => {
817
838
  return [key, {
818
839
  ...rest,
819
840
  ...query$1 ? { query: normalizeQueryOptions(query$1, workspace, global.query) } : {},
@@ -943,45 +964,37 @@ const getDefaultFilesHeader = ({ title, description: description$1, version: ver
943
964
 
944
965
  //#endregion
945
966
  //#region src/utils/watcher.ts
946
- const startWatcher = async (watchOptions, watchFn, defaultTarget = ".") => {
967
+ /**
968
+ * Start a file watcher and invoke an async callback on file changes.
969
+ *
970
+ * If `watchOptions` is falsy the watcher is not started. Supported shapes:
971
+ * - boolean: when true the `defaultTarget` is watched
972
+ * - string: a single path to watch
973
+ * - string[]: an array of paths to watch
974
+ *
975
+ * @param watchOptions - false to disable watching, or a path/paths to watch
976
+ * @param watchFn - async callback executed on change events
977
+ * @param defaultTarget - path(s) to watch when `watchOptions` is `true` (default: '.')
978
+ * @returns Resolves once the watcher has been started (or immediately if disabled)
979
+ *
980
+ * @example
981
+ * await startWatcher(true, async () => { await buildProject(); }, 'src');
982
+ */
983
+ async function startWatcher(watchOptions, watchFn, defaultTarget = ".") {
947
984
  if (!watchOptions) return;
948
985
  const { watch } = await import("chokidar");
949
986
  const ignored = ["**/{.git,node_modules}/**"];
950
- const watchPaths = typeof watchOptions === "boolean" ? defaultTarget : Array.isArray(watchOptions) ? watchOptions.filter((path$1) => typeof path$1 === "string") : watchOptions;
987
+ const watchPaths = typeof watchOptions === "boolean" ? defaultTarget : watchOptions;
951
988
  log(`Watching for changes in ${Array.isArray(watchPaths) ? watchPaths.map((v) => "\"" + v + "\"").join(" | ") : "\"" + watchPaths + "\""}`);
952
989
  watch(watchPaths, {
953
990
  ignorePermissionErrors: true,
954
991
  ignored
955
- }).on("all", async (type, file) => {
992
+ }).on("all", (type, file) => {
956
993
  log(`Change detected: ${type} ${file}`);
957
- try {
958
- await watchFn();
959
- } catch (error) {
994
+ watchFn().catch((error) => {
960
995
  logError(error);
961
- }
996
+ });
962
997
  });
963
- };
964
-
965
- //#endregion
966
- //#region src/utils/execute-hook.ts
967
- const executeHook = async (name$1, commands = [], args = []) => {
968
- log(chalk.white(`Running ${name$1} hook...`));
969
- for (const command of commands) try {
970
- if (isString(command)) await executeCommand(command, args);
971
- else if (isFunction(command)) await command(args);
972
- else if (isObject(command)) await executeObjectCommand(command, args);
973
- } catch (error) {
974
- logError(error, `Failed to run ${name$1} hook`);
975
- }
976
- };
977
- async function executeCommand(command, args) {
978
- const [cmd, ..._args] = [...parseArgsStringToArgv(command), ...args];
979
- await execa(cmd, _args);
980
- }
981
- async function executeObjectCommand(command, args) {
982
- if (command.injectGeneratedDirsAndFiles === false) args = [];
983
- if (isString(command.command)) await executeCommand(command.command, args);
984
- else if (isFunction(command.command)) await command.command();
985
998
  }
986
999
 
987
1000
  //#endregion
@@ -1040,16 +1053,16 @@ const writeSpecs = async (builder, workspace, options, projectName) => {
1040
1053
  if (output.schemas) imports.push(upath.relativeSafe(workspacePath, getFileInfo(output.schemas).dirname));
1041
1054
  if (output.indexFiles) {
1042
1055
  const indexFile = upath.join(workspacePath, "/index.ts");
1043
- if (await fs$1.pathExists(indexFile)) {
1044
- const data = await fs$1.readFile(indexFile, "utf8");
1056
+ if (await fs.pathExists(indexFile)) {
1057
+ const data = await fs.readFile(indexFile, "utf8");
1045
1058
  const importsNotDeclared = imports.filter((imp) => !data.includes(imp));
1046
- await fs$1.appendFile(indexFile, unique(importsNotDeclared).map((imp) => `export * from '${imp}';\n`).join(""));
1047
- } else await fs$1.outputFile(indexFile, unique(imports).map((imp) => `export * from '${imp}';`).join("\n") + "\n");
1059
+ await fs.appendFile(indexFile, unique(importsNotDeclared).map((imp) => `export * from '${imp}';\n`).join(""));
1060
+ } else await fs.outputFile(indexFile, unique(imports).map((imp) => `export * from '${imp}';`).join("\n") + "\n");
1048
1061
  implementationPaths = [indexFile, ...implementationPaths];
1049
1062
  }
1050
1063
  }
1051
1064
  if (builder.extraFiles.length > 0) {
1052
- await Promise.all(builder.extraFiles.map(async (file) => fs$1.outputFile(file.path, file.content)));
1065
+ await Promise.all(builder.extraFiles.map(async (file) => fs.outputFile(file.path, file.content)));
1053
1066
  implementationPaths = [...implementationPaths, ...builder.extraFiles.map((file) => file.path)];
1054
1067
  }
1055
1068
  const paths = [...output.schemas ? [getFileInfo(output.schemas).dirname] : [], ...implementationPaths];
@@ -1109,8 +1122,19 @@ const getWriteMode = (mode) => {
1109
1122
  };
1110
1123
 
1111
1124
  //#endregion
1112
- //#region src/generate.ts
1113
- const generateSpec = async (workspace, options, projectName) => {
1125
+ //#region src/generate-spec.ts
1126
+ /**
1127
+ * Generate client/spec files for a single Orval project.
1128
+ *
1129
+ * @param workspace - Absolute or relative workspace path used to resolve imports.
1130
+ * @param options - Normalized generation options for this project.
1131
+ * @param projectName - Optional project name used in logging output.
1132
+ * @returns A promise that resolves once generation (and optional cleaning) completes.
1133
+ *
1134
+ * @example
1135
+ * await generateSpec(process.cwd(), normalizedOptions, 'my-project');
1136
+ */
1137
+ async function generateSpec(workspace, options, projectName) {
1114
1138
  if (options.output.clean) {
1115
1139
  const extraPatterns = Array.isArray(options.output.clean) ? options.output.clean : [];
1116
1140
  if (options.output.target) await removeFilesAndEmptyFolders([
@@ -1123,44 +1147,35 @@ const generateSpec = async (workspace, options, projectName) => {
1123
1147
  "!**/*.d.ts",
1124
1148
  ...extraPatterns
1125
1149
  ], getFileInfo(options.output.schemas).dirname);
1126
- log(`${projectName ? `${projectName}: ` : ""}Cleaning output folder`);
1150
+ log(`${projectName} Cleaning output folder`);
1127
1151
  }
1128
1152
  await writeSpecs(await importSpecs(workspace, options), workspace, options, projectName);
1129
- };
1130
- const generateSpecs = async (config, workspace, projectName) => {
1131
- if (projectName) {
1132
- const options = config[projectName];
1133
- if (options) try {
1134
- await generateSpec(workspace, options, projectName);
1135
- } catch (error) {
1136
- throw new ErrorWithTag(error instanceof Error ? error.message : "unknown error", projectName, { cause: error });
1137
- }
1138
- else throw new Error("Project not found");
1139
- return;
1140
- }
1141
- let hasErrors;
1142
- for (const [projectName$1, options] of Object.entries(config)) {
1143
- if (!options) {
1144
- hasErrors = true;
1145
- logError("No options found", projectName$1);
1146
- continue;
1147
- }
1148
- try {
1149
- await generateSpec(workspace, options, projectName$1);
1150
- } catch (error) {
1151
- hasErrors = true;
1152
- logError(error, projectName$1);
1153
- }
1154
- }
1155
- if (hasErrors) throw new Error("One or more project failed, see above for details");
1156
- };
1153
+ }
1154
+
1155
+ //#endregion
1156
+ //#region src/utils/config.ts
1157
+ /**
1158
+ * Resolve the Orval config file path.
1159
+ *
1160
+ * @param configFilePath - Optional path to the config file (absolute or relative).
1161
+ * @returns The absolute path to the resolved config file.
1162
+ * @throws If a provided path does not exist or if no config file is found.
1163
+ *
1164
+ * @example
1165
+ * // explicit path
1166
+ * const p = findConfigFile('./orval.config.ts');
1167
+ *
1168
+ * @example
1169
+ * // automatic discovery (searches process.cwd())
1170
+ * const p = findConfigFile();
1171
+ */
1157
1172
  function findConfigFile(configFilePath) {
1158
1173
  if (configFilePath) {
1159
- const absolutePath = path.isAbsolute(configFilePath) ? configFilePath : path.resolve(process$1.cwd(), configFilePath);
1160
- if (!fs.existsSync(absolutePath)) throw new Error(`Config file ${configFilePath} does not exist`);
1174
+ const absolutePath = path.isAbsolute(configFilePath) ? configFilePath : path.resolve(process.cwd(), configFilePath);
1175
+ if (!fs$1.existsSync(absolutePath)) throw new Error(`Config file ${configFilePath} does not exist`);
1161
1176
  return absolutePath;
1162
1177
  }
1163
- const root = process$1.cwd();
1178
+ const root = process.cwd();
1164
1179
  for (const ext of [
1165
1180
  ".ts",
1166
1181
  ".js",
@@ -1168,34 +1183,26 @@ function findConfigFile(configFilePath) {
1168
1183
  ".mts"
1169
1184
  ]) {
1170
1185
  const fullPath = path.resolve(root, `orval.config${ext}`);
1171
- if (fs.existsSync(fullPath)) return fullPath;
1186
+ if (fs$1.existsSync(fullPath)) return fullPath;
1172
1187
  }
1173
1188
  throw new Error(`No config file found in ${root}`);
1174
1189
  }
1190
+ /**
1191
+ * Load an Orval config file
1192
+ * @param configFilePath - Path to the config file (absolute or relative).
1193
+ * @returns The resolved Orval `Config` object.
1194
+ * @throws If the module does not provide a default export or the default export resolves to `undefined`.
1195
+ *
1196
+ * @example
1197
+ * // load a config object
1198
+ * const cfg = await loadConfigFile('./orval.config.ts');
1199
+ */
1175
1200
  async function loadConfigFile(configFilePath) {
1176
- const module = await createJiti(import.meta.url, { interopDefault: true }).import(configFilePath, { default: true });
1177
- if (module === void 0) throw new Error(`${configFilePath} doesn't have a default export`);
1178
- return await Promise.resolve(module);
1201
+ const configExternal = await createJiti(process.cwd(), { interopDefault: true }).import(configFilePath, { default: true });
1202
+ if (configExternal === void 0) throw new Error(`${configFilePath} doesn't have a default export`);
1203
+ return await (isFunction(configExternal) ? configExternal() : configExternal);
1179
1204
  }
1180
- const generateConfig = async (configFile, options) => {
1181
- const configFilePath = findConfigFile(configFile);
1182
- let configExternal;
1183
- try {
1184
- configExternal = await loadConfigFile(configFilePath);
1185
- } catch (error) {
1186
- const errorMsg = error instanceof Error ? error.message : "unknown error";
1187
- throw new Error(`failed to load from ${configFilePath} => ${errorMsg}`);
1188
- }
1189
- const workspace = path.dirname(configFilePath);
1190
- const config = await (isFunction(configExternal) ? configExternal() : configExternal);
1191
- const normalizedConfig = await asyncReduce(Object.entries(config), async (acc, [key, value]) => {
1192
- acc[key] = await normalizeOptions(value, workspace, options);
1193
- return acc;
1194
- }, {});
1195
- const fileToWatch = Object.entries(normalizedConfig).filter(([project]) => options?.projectName === void 0 || project === options.projectName).map(([, options$1]) => options$1?.input.target).filter((target) => isString(target));
1196
- await (options?.watch && fileToWatch.length > 0 ? startWatcher(options.watch, () => generateSpecs(normalizedConfig, workspace, options.projectName), fileToWatch) : generateSpecs(normalizedConfig, workspace, options?.projectName));
1197
- };
1198
1205
 
1199
1206
  //#endregion
1200
- export { normalizeOptions as a, version as c, defineConfig as i, generateSpec as n, description as o, startWatcher as r, name as s, generateConfig as t };
1201
- //# sourceMappingURL=generate-6oT76j_W.js.map
1207
+ export { defineConfig as a, name as c, startWatcher as i, version as l, loadConfigFile as n, normalizeOptions as o, generateSpec as r, description as s, findConfigFile as t };
1208
+ //# sourceMappingURL=config-BH1mpZT1.mjs.map