@aligent/nx-openapi 0.1.0

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,76 @@
1
+ # OpenAPI REST Client Generator Nx Plugin
2
+
3
+ Generate typed REST clients from OpenAPI 3.0+ Specifications.
4
+
5
+ This plugin was generated with [Nx](https://nx.dev).
6
+
7
+ The openapi-plugin can create api clients based on local or remote schema files. The source files must be .yaml or .json and must adhere to the OpenAPI Specification 3.0+
8
+
9
+ ## Usage
10
+
11
+ ### Generating a new client
12
+
13
+ To build a new client in the clients folder:
14
+
15
+ - Run `nx g @aligent/nx-openapi:client`
16
+ - Follow the prompts to name and provide a source file
17
+
18
+ To build with flags (without needing to prompt):
19
+ Run `nx g @aligent/nx-openapi:client` with optional flags:
20
+
21
+ - `--name` Name of the api client.
22
+ - `--schemaPath` Absolute path to the schema. This can be a valid HTTP URL or a local file.
23
+ - `--importPath` The package name used to import the client. Defaults to `@clients/{name}` if not supplied
24
+ - `--configPath` path to the redocly config file responsible for authentication when fetching a schema remotely. For more information: https://openapi-ts.dev/cli#auth.
25
+ - `--skipValidate` If passed, this will skip schema pre-validation. Only do this if you have good reason to - not validating the schema beforehand may produce unpredictable results (either things may not generate at all or they may generate something that is incorrect).
26
+ - `--override` Override the schema (and the generated type) of an existing client.
27
+
28
+ **:rotating_light: Do not edit the files in the `/generated` folder after generating a client. These files are generated using the OpenAPI Schema and editing them may put you at risk of no longer conforming to the specifications of the API you are using! :rotating_light:**
29
+
30
+ ### Regenerating types for an existing client
31
+
32
+ To regenerate an existing client run the same generator command again (as above) and pass in `--override` flag. If the client already exists, its types file will be regenerated from the provided one.
33
+
34
+ ## Development
35
+
36
+ ### Building
37
+
38
+ Run `nx run nx-openapi:build` to build the library.
39
+
40
+ ### Running unit tests
41
+
42
+ Run `nx run nx-openapi:test` to execute the unit tests.
43
+
44
+ ### Details
45
+
46
+ The plugin was created following the standards for Nx custom plugins. It contains a single `generator.ts` file which is responsible for the main logic behind generating the client.
47
+ For more information on client generation check [Nx plugin documentation](https://nx.dev/features/generate-code)
48
+
49
+ Normally, plugin generators take pre-defined files specified in the `/files` directory and automatically use that a basis for writing to a `Tree` object (the data structure Nx uses to generate files).
50
+
51
+ Since we are using `openapi-typescript` to generate files in real time, we can't create a pre-defined template. Instead perform write operations to the `Tree` ourselves. We do this by taking the output of the Typescript generation and calling a built in `write` process, to write the generated code into a file as part of the generation.
52
+
53
+ The flow for the generator is as follows:
54
+
55
+ ```mermaid
56
+ graph LR;
57
+ R[Receive path arguments from Nx cli]-->GL[Get local schema from filesystem];
58
+ R-->GR[Get remote schema from url];
59
+ GL-->G[Generate TS];
60
+ GR-->G;
61
+ G-->W[Write generated .ts file to tree];
62
+ W-->NX[Nx generator uses tree to build directory];
63
+ NX-->C[Clean up];
64
+ ```
65
+
66
+ ## What gets generated?
67
+
68
+ The generator will generate two main things:
69
+
70
+ A `types` file that will contain several typescript interfaces depending on the OpenAPI schema specification it was passed:
71
+
72
+ - `paths`: Defines the routes that the API has as well as what parameters you can pass into them and what you should expect in return. [More information](https://swagger.io/docs/specification/v3_0/paths-and-operations/#paths)
73
+ - `operations`: The defined operations that can be performed on a given path. [More information.](https://swagger.io/docs/specification/v3_0/paths-and-operations/#operations)
74
+ - `components`: A way of avoid duplication when paths or operations contain the same structure in their responses. Components define a structure that is used multiple time throughout the API. [More information.](https://swagger.io/docs/specification/v3_0/components/)
75
+
76
+ A `client` file that will contain some commented, boilerplate code to help you get started.
@@ -0,0 +1,9 @@
1
+ {
2
+ "generators": {
3
+ "client": {
4
+ "factory": "./src/generators/client/generator",
5
+ "schema": "./src/generators/client/schema.json",
6
+ "description": "Generate a new openapi REST client"
7
+ }
8
+ }
9
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@aligent/nx-openapi",
3
+ "version": "0.1.0",
4
+ "type": "commonjs",
5
+ "main": "./src/index.js",
6
+ "typings": "./src/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/aligent/microservice-development-utilities.git",
10
+ "directory": "packages/nx-openapi"
11
+ },
12
+ "dependencies": {
13
+ "@nx/devkit": "20.8.1",
14
+ "@redocly/openapi-core": "^1.34.2",
15
+ "openapi-typescript": "^7.6.1",
16
+ "typescript": "^5.8.3"
17
+ },
18
+ "generators": "./generators.json",
19
+ "author": "Aligent",
20
+ "license": "MIT",
21
+ "types": "./src/index.d.ts"
22
+ }
@@ -0,0 +1 @@
1
+ The <%= name %> REST API Client was generated using Nx Generators and openapi-typescript codegen
@@ -0,0 +1,22 @@
1
+ // The following is an example file generated to demonstrate how to use your newly generated types
2
+ import { paths } from '../generated';
3
+ import createClient from 'openapi-fetch';
4
+
5
+ // This type is an example of what is initialised using the types generated in the paths interface which is created when generation occurs.
6
+ // Its worth looking into that paths interface, to see the the types that were generated for your client.
7
+ type ExampleResponse =
8
+ paths['/customers']['get']['responses']['200']['content']['application/json'];
9
+
10
+ // Using openapi-fetch we can create a fully typed REST client by passing in paths as a generic.
11
+ // If you wish however, you can use any api client you want (axios, basic fetch etc.) and use the paths separately to maintain type safety in your client.
12
+ const client = createClient<paths>({
13
+ baseUrl: '',
14
+ signal: AbortSignal.timeout(10000),
15
+ });
16
+
17
+ // Client getters are then fully typed. Try deleting '/customers' and seeing what routes you can use!
18
+ const response = client.GET('/customers', {
19
+ params: {
20
+ query: {},
21
+ },
22
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "esModuleInterop": true
5
+ },
6
+ "include": [
7
+ "src/**/*.ts",
8
+ "**/*.test.ts"
9
+ ]
10
+ }
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { ClientGeneratorSchema } from './schema';
3
+ export declare function clientGenerator(tree: Tree, options: ClientGeneratorSchema): Promise<void>;
4
+ export default clientGenerator;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clientGenerator = clientGenerator;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const generate_openapi_types_1 = require("../../helpers/generate-openapi-types");
6
+ const utilities_1 = require("../../helpers/utilities");
7
+ const VALID_EXTENSIONS = ['yaml', 'yml', 'json'];
8
+ async function clientGenerator(tree, options) {
9
+ const { name, schemaPath, importPath = `@clients/${name}`, skipValidate, override } = options;
10
+ const ext = schemaPath.split('.').pop() || '';
11
+ if (!VALID_EXTENSIONS.includes(ext)) {
12
+ throw new Error(`Invalid schema file extension: ${ext}`);
13
+ }
14
+ if (!skipValidate) {
15
+ const hasError = await (0, generate_openapi_types_1.validateSchema)(schemaPath);
16
+ if (hasError) {
17
+ throw new Error('Schema validation failed!');
18
+ }
19
+ }
20
+ const projectRoot = `clients/${name}`;
21
+ const schemaDest = `${projectRoot}/schema.${ext}`;
22
+ const typesDest = `${projectRoot}/generated/index.ts`;
23
+ const isNewProject = (0, utilities_1.attemptToAddProjectConfiguration)(tree, name, projectRoot);
24
+ if (!isNewProject && !override) {
25
+ devkit_1.logger.info(`Project ${name} already exists. Use --override to override the existing schema.`);
26
+ return;
27
+ }
28
+ await (0, generate_openapi_types_1.copySchema)(tree, schemaDest, schemaPath);
29
+ await (0, generate_openapi_types_1.generateOpenApiTypes)(tree, schemaDest, typesDest);
30
+ if (isNewProject) {
31
+ devkit_1.logger.info(`Creating new project at ${projectRoot}`);
32
+ // Generate other files
33
+ (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, './files'), projectRoot, options);
34
+ // Add the project to the tsconfig paths so it can be imported by namespace
35
+ (0, utilities_1.addTsConfigPath)(tree, importPath, [(0, devkit_1.joinPathFragments)(projectRoot, './src', 'index.ts')]);
36
+ }
37
+ await (0, devkit_1.formatFiles)(tree);
38
+ }
39
+ exports.default = clientGenerator;
@@ -0,0 +1,8 @@
1
+ export interface ClientGeneratorSchema {
2
+ name: string;
3
+ schemaPath: string;
4
+ configPath?: string;
5
+ importPath?: string;
6
+ skipValidate: boolean;
7
+ override: boolean;
8
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "Client",
4
+ "type": "object",
5
+ "description": "Aligent OpenAPI Client plugin generator. This generator is used for generating type-safe REST API clients using OpenAPI 3.0 Schema files. ",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "Name of the api client.",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "Name of your api client: "
15
+ },
16
+ "schemaPath": {
17
+ "type": "string",
18
+ "description": "Absolute path to the schema. Specify a valid HTTP url or a local file.",
19
+ "$default": {
20
+ "$source": "argv",
21
+ "index": 1
22
+ },
23
+ "x-prompt": "Absolute schema path (a valid HTTP url or a local file): "
24
+ },
25
+ "configPath": {
26
+ "type": "string",
27
+ "description": "Path to the redocly config file responsible for auth. For more information: https://openapi-ts.dev/cli#auth."
28
+ },
29
+ "importPath": {
30
+ "type": "string",
31
+ "description": "The package name used to import the client, like @myorg/my-awesome-client. Defaults to @clients/{name} if not supplied"
32
+ },
33
+ "skipValidate": {
34
+ "type": "boolean",
35
+ "description": "Skip validation in the generation process",
36
+ "default": false
37
+ },
38
+ "override": {
39
+ "type": "boolean",
40
+ "description": "Override existing project schema",
41
+ "default": false
42
+ }
43
+ },
44
+ "required": ["name", "schemaPath"]
45
+ }
@@ -0,0 +1,26 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: Sample API
4
+ description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
5
+ version: 0.1.9
6
+
7
+ servers:
8
+ - url: http://api.example.com/v1
9
+ description: Optional server description, e.g. Main (production) server
10
+ - url: http://staging-api.example.com
11
+ description: Optional server description, e.g. Internal staging server for testing
12
+
13
+ paths:
14
+ /users:
15
+ get:
16
+ summary: Returns a list of users.
17
+ description: Optional extended description in CommonMark or HTML.
18
+ responses:
19
+ '200': # status code
20
+ description: A JSON array of user names
21
+ content:
22
+ application/json:
23
+ schema:
24
+ type: array
25
+ items:
26
+ type: string
@@ -0,0 +1,26 @@
1
+ # openapi: 3.0.0
2
+ info:
3
+ title: Sample API
4
+ description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
5
+ version: 0.1.9
6
+
7
+ servers:
8
+ - url: http://api.example.com/v1
9
+ description: Optional server description, e.g. Main (production) server
10
+ - url: http://staging-api.example.com
11
+ description: Optional server description, e.g. Internal staging server for testing
12
+
13
+ paths:
14
+ /users:
15
+ get:
16
+ summary: Returns a list of users.
17
+ description: Optional extended description in CommonMark or HTML.
18
+ responses:
19
+ '200': # status code
20
+ description: A JSON array of user names
21
+ content:
22
+ application/json:
23
+ schema:
24
+ type: array
25
+ items:
26
+ type: string
@@ -0,0 +1,33 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: Sample API
4
+ description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
5
+ version: 0.1.9
6
+ security:
7
+ - sampleScheme: []
8
+
9
+ servers:
10
+ - url: http://api.example.com/v1
11
+ description: Optional server description, e.g. Main (production) server
12
+ - url: http://staging-api.example.com
13
+ description: Optional server description, e.g. Internal staging server for testing
14
+
15
+ paths:
16
+ /users:
17
+ get:
18
+ summary: Returns a list of users.
19
+ description: Optional extended description in CommonMark or HTML.
20
+ responses:
21
+ '200': # status code
22
+ description: A JSON array of user names
23
+ content:
24
+ application/json:
25
+ schema:
26
+ type: array
27
+ items:
28
+ type: string
29
+ components:
30
+ securitySchemes:
31
+ sampleScheme:
32
+ type: http
33
+ scheme: basic
@@ -0,0 +1,35 @@
1
+ import { Tree } from '@nx/devkit';
2
+ /**
3
+ * Generates TypeScript types from an OpenAPI schema
4
+ * and writes the generated types to a specified destination.
5
+ *
6
+ * @param {Tree} tree - The file system tree representing the current project.
7
+ * @param {string} schemaPath - The path to the OpenAPI schema file.
8
+ * @param {string} typeDest - The destination path for the generated TypeScript types.
9
+ * @returns {Promise<void>} A promise that resolves when the types are successfully generated.
10
+ * @throws {Error} If the schema cannot be read or the types cannot be generated.
11
+ */
12
+ export declare function generateOpenApiTypes(tree: Tree, schemaPath: string, typeDest: string): Promise<void>;
13
+ /**
14
+ * Copies the OpenAPI schema from the source to a specified destination.
15
+ *
16
+ * This function supports both local and remote schemas. If the schema is remote (HTTP/HTTPS),
17
+ * it fetches the schema from the URL. If the schema is local, it reads the schema from the file system.
18
+ *
19
+ * @param {Tree} tree - The file system tree representing the current project.
20
+ * @param {string} destination - The destination path where the schema will be copied.
21
+ * @param {string} schemaPath - The path to the schema file (local or remote).
22
+ * @returns {Promise<void>} A promise that resolves when the schema is successfully copied.
23
+ * @throws {Error} If the schema cannot be read or is empty.
24
+ */
25
+ export declare function copySchema(tree: Tree, destination: string, schemaPath: string): Promise<void>;
26
+ /**
27
+ * Validates an OpenAPI schema using the Redocly OpenAPI linter.
28
+ *
29
+ * This function uses the `@redocly/openapi-core` library to lint the schema at the specified path.
30
+ * It logs warnings and errors based on the severity of the issues found in the schema.
31
+ *
32
+ * @param {string} path - The path to the OpenAPI schema file.
33
+ * @returns {Promise<boolean>} A promise that resolves to `true` if validation errors are found, otherwise `false`.
34
+ */
35
+ export declare function validateSchema(path: string): Promise<boolean>;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateOpenApiTypes = generateOpenApiTypes;
37
+ exports.copySchema = copySchema;
38
+ exports.validateSchema = validateSchema;
39
+ const devkit_1 = require("@nx/devkit");
40
+ const openapi_core_1 = require("@redocly/openapi-core");
41
+ const fs_1 = require("fs");
42
+ const openapi_typescript_1 = __importStar(require("openapi-typescript"));
43
+ /**
44
+ * Generates TypeScript types from an OpenAPI schema
45
+ * and writes the generated types to a specified destination.
46
+ *
47
+ * @param {Tree} tree - The file system tree representing the current project.
48
+ * @param {string} schemaPath - The path to the OpenAPI schema file.
49
+ * @param {string} typeDest - The destination path for the generated TypeScript types.
50
+ * @returns {Promise<void>} A promise that resolves when the types are successfully generated.
51
+ * @throws {Error} If the schema cannot be read or the types cannot be generated.
52
+ */
53
+ async function generateOpenApiTypes(tree, schemaPath, typeDest) {
54
+ devkit_1.logger.info(`Generating types from ${schemaPath}`);
55
+ const schema = tree.read(schemaPath);
56
+ if (!schema) {
57
+ throw new Error(`Failed to read schema at ${schemaPath}`);
58
+ }
59
+ const ast = await (0, openapi_typescript_1.default)(schema);
60
+ tree.write(typeDest, (0, openapi_typescript_1.astToString)(ast));
61
+ }
62
+ // We do not want to test this function.
63
+ // It only downloads/copies the schema to a specified destination.
64
+ /* v8 ignore start */
65
+ /**
66
+ * Copies the OpenAPI schema from the source to a specified destination.
67
+ *
68
+ * This function supports both local and remote schemas. If the schema is remote (HTTP/HTTPS),
69
+ * it fetches the schema from the URL. If the schema is local, it reads the schema from the file system.
70
+ *
71
+ * @param {Tree} tree - The file system tree representing the current project.
72
+ * @param {string} destination - The destination path where the schema will be copied.
73
+ * @param {string} schemaPath - The path to the schema file (local or remote).
74
+ * @returns {Promise<void>} A promise that resolves when the schema is successfully copied.
75
+ * @throws {Error} If the schema cannot be read or is empty.
76
+ */
77
+ async function copySchema(tree, destination, schemaPath) {
78
+ const isRemoteSchema = schemaPath.startsWith('http://') || schemaPath.startsWith('https://');
79
+ let schema;
80
+ // TODO: MI-203 - Support private schema endpoint
81
+ if (isRemoteSchema) {
82
+ const response = await fetch(schemaPath);
83
+ schema = Buffer.from(await response.arrayBuffer());
84
+ }
85
+ else {
86
+ schema = (0, fs_1.readFileSync)(schemaPath);
87
+ }
88
+ if (!schema || !schema.length) {
89
+ throw new Error(`Failed to read schema at ${schemaPath}`);
90
+ }
91
+ tree.write(destination, schema);
92
+ }
93
+ /* v8 ignore end */
94
+ /**
95
+ * Validates an OpenAPI schema using the Redocly OpenAPI linter.
96
+ *
97
+ * This function uses the `@redocly/openapi-core` library to lint the schema at the specified path.
98
+ * It logs warnings and errors based on the severity of the issues found in the schema.
99
+ *
100
+ * @param {string} path - The path to the OpenAPI schema file.
101
+ * @returns {Promise<boolean>} A promise that resolves to `true` if validation errors are found, otherwise `false`.
102
+ */
103
+ async function validateSchema(path) {
104
+ let hasError = false;
105
+ try {
106
+ // TODO: MI-203 - Support private schema endpoint
107
+ const config = await (0, openapi_core_1.loadConfig)();
108
+ const results = await (0, openapi_core_1.lint)({ ref: path, config });
109
+ results.forEach(result => {
110
+ const location = result.location.map(({ pointer, reportOnKey }) => ({
111
+ pointer,
112
+ reportOnKey,
113
+ }));
114
+ if (result.severity === 'warn') {
115
+ devkit_1.logger.warn(JSON.stringify({ location, message: result.message, severity: result.severity }));
116
+ return;
117
+ }
118
+ devkit_1.logger.error(JSON.stringify({ location, message: result.message, severity: result.severity }));
119
+ hasError = true;
120
+ });
121
+ }
122
+ catch (e) {
123
+ const message = e instanceof Error ? e.message : 'Unknown error';
124
+ devkit_1.logger.error(`Failed to validate schema: ${message}`);
125
+ hasError = true;
126
+ }
127
+ return hasError;
128
+ }
@@ -0,0 +1,26 @@
1
+ import { Tree } from '@nx/devkit';
2
+ /**
3
+ * Attempts to add a new project configuration to the workspace.
4
+ *
5
+ * This function adds a new project configuration to the workspace. If a project with the same name
6
+ * already exists, it returns `false`. If an error occurs for any other reason, the error is re-thrown.
7
+ *
8
+ * @param {Tree} tree - The file system tree representing the current project.
9
+ * @param {string} name - The name of the project to add.
10
+ * @param {string} projectRoot - The root directory of the project.
11
+ * @returns {boolean} `true` if the project configuration was added successfully, `false` if the project already exists.
12
+ * @throws {Error} If an error occurs that is not related to the project already existing.
13
+ */
14
+ export declare function attemptToAddProjectConfiguration(tree: Tree, name: string, projectRoot: string): boolean;
15
+ /**
16
+ * Adds a new path mapping to the `paths` property in the root TypeScript configuration file.
17
+ *
18
+ * This function updates the `tsconfig.base.json` or `tsconfig.json` file to include a new path mapping
19
+ * for the specified import path. If the import path already exists, an error is thrown.
20
+ *
21
+ * @param {Tree} tree - The file system tree representing the current project.
22
+ * @param {string} importPath - The import path to add to the `paths` property.
23
+ * @param {string[]} lookupPaths - The array of paths to associate with the import path.
24
+ * @throws {Error} If the import path already exists in the `paths` property.
25
+ */
26
+ export declare function addTsConfigPath(tree: Tree, importPath: string, lookupPaths: string[]): void;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attemptToAddProjectConfiguration = attemptToAddProjectConfiguration;
4
+ exports.addTsConfigPath = addTsConfigPath;
5
+ const devkit_1 = require("@nx/devkit");
6
+ /**
7
+ * Attempts to add a new project configuration to the workspace.
8
+ *
9
+ * This function adds a new project configuration to the workspace. If a project with the same name
10
+ * already exists, it returns `false`. If an error occurs for any other reason, the error is re-thrown.
11
+ *
12
+ * @param {Tree} tree - The file system tree representing the current project.
13
+ * @param {string} name - The name of the project to add.
14
+ * @param {string} projectRoot - The root directory of the project.
15
+ * @returns {boolean} `true` if the project configuration was added successfully, `false` if the project already exists.
16
+ * @throws {Error} If an error occurs that is not related to the project already existing.
17
+ */
18
+ function attemptToAddProjectConfiguration(tree, name, projectRoot) {
19
+ try {
20
+ (0, devkit_1.addProjectConfiguration)(tree, name, {
21
+ root: projectRoot,
22
+ projectType: 'library',
23
+ sourceRoot: `${projectRoot}/src`,
24
+ targets: {},
25
+ tags: ['client', name],
26
+ });
27
+ return true;
28
+ }
29
+ catch (err) {
30
+ if (err instanceof Error && err.message.includes('already exists')) {
31
+ return false;
32
+ }
33
+ throw err;
34
+ }
35
+ }
36
+ /**
37
+ * The utility functions below are only exported by '@nx/js', not '@nx/devkit'
38
+ * They're simple so we recreate them here instead of adding '@nx/js' as a dependency
39
+ * Source: {@link https://github.com/nrwl/nx/blob/master/packages/js/src/utils/typescript/ts-config.ts}
40
+ */
41
+ function getRootTsConfigPathInTree(tree) {
42
+ for (const path of ['tsconfig.base.json', 'tsconfig.json']) {
43
+ if (tree.exists(path)) {
44
+ return path;
45
+ }
46
+ }
47
+ return 'tsconfig.base.json';
48
+ }
49
+ /**
50
+ * Adds a new path mapping to the `paths` property in the root TypeScript configuration file.
51
+ *
52
+ * This function updates the `tsconfig.base.json` or `tsconfig.json` file to include a new path mapping
53
+ * for the specified import path. If the import path already exists, an error is thrown.
54
+ *
55
+ * @param {Tree} tree - The file system tree representing the current project.
56
+ * @param {string} importPath - The import path to add to the `paths` property.
57
+ * @param {string[]} lookupPaths - The array of paths to associate with the import path.
58
+ * @throws {Error} If the import path already exists in the `paths` property.
59
+ */
60
+ function addTsConfigPath(tree, importPath, lookupPaths) {
61
+ (0, devkit_1.updateJson)(tree, getRootTsConfigPathInTree(tree), json => {
62
+ json.compilerOptions ??= {};
63
+ const c = json.compilerOptions;
64
+ c.paths ??= {};
65
+ if (c.paths[importPath]) {
66
+ throw new Error(`You already have a library using the import path "${importPath}". Make sure to specify a unique one.`);
67
+ }
68
+ c.paths[importPath] = lookupPaths;
69
+ return json;
70
+ });
71
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './generators/client/generator';
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /* v8 ignore next 1 */
18
+ __exportStar(require("./generators/client/generator"), exports);
@@ -0,0 +1,12 @@
1
+ declare module 'openapi-typescript' {
2
+ import type { Readable } from 'node:stream';
3
+ import { astToString, OpenAPI3, OpenAPITSOptions } from 'openapi-typescript/dist/index.js';
4
+ import ts from 'typescript';
5
+
6
+ export default function openapiTS(
7
+ source: string | URL | OpenAPI3 | Buffer | Readable,
8
+ options?: OpenAPITSOptions
9
+ ): Promise<ts.Node[]>;
10
+
11
+ export { astToString };
12
+ }