@asyncapi/generator 1.16.0 → 1.17.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/docs/api.md +12 -0
- package/docs/index.md +3 -0
- package/docs/installation-guide.md +1 -1
- package/docs/model-generation.md +84 -0
- package/docs/template-development.md +3 -1
- package/docs/template.md +2 -2
- package/docs/usage.md +3 -3
- package/docs/using-private-template.md +29 -0
- package/lib/filtersRegistry.js +20 -5
- package/lib/generator.js +124 -77
- package/lib/logMessages.js +6 -2
- package/lib/utils.js +0 -11
- package/package.json +3 -3
package/docs/api.md
CHANGED
|
@@ -13,6 +13,7 @@ Reference API documentation for AsyncAPI Generator library.
|
|
|
13
13
|
* [Generator](#Generator)
|
|
14
14
|
* [new Generator(templateName, targetDir, options)](#new_Generator_new)
|
|
15
15
|
* _instance_
|
|
16
|
+
* [.registry](#Generator+registry) : `Object`
|
|
16
17
|
* [.templateName](#Generator+templateName) : `String`
|
|
17
18
|
* [.targetDir](#Generator+targetDir) : `String`
|
|
18
19
|
* [.entrypoint](#Generator+entrypoint) : `String`
|
|
@@ -64,6 +65,10 @@ Instantiates a new Generator object.
|
|
|
64
65
|
- [.install] `Boolean` ` = false` - Install the template and its dependencies, even when the template has already been installed.
|
|
65
66
|
- [.debug] `Boolean` ` = false` - Enable more specific errors in the console. At the moment it only shows specific errors about filters. Keep in mind that as a result errors about template are less descriptive.
|
|
66
67
|
- [.mapBaseUrlToFolder] `Object.<String, String>` - Optional parameter to map schema references from a base url to a local base folder e.g. url=https://schema.example.com/crm/ folder=./test/docs/ .
|
|
68
|
+
- [.registry] `Object` - Optional parameter with private registry configuration
|
|
69
|
+
- [.url] `String` - Parameter to pass npm registry url
|
|
70
|
+
- [.auth] `String` - Optional parameter to pass npm registry username and password encoded with base64, formatted like username:password value should be encoded
|
|
71
|
+
- [.token] `String` - Optional parameter to pass npm registry auth token that you can grab from .npmrc file
|
|
67
72
|
|
|
68
73
|
**Example**
|
|
69
74
|
```js
|
|
@@ -80,6 +85,13 @@ const generator = new Generator('@asyncapi/html-template', path.resolve(__dirnam
|
|
|
80
85
|
});
|
|
81
86
|
```
|
|
82
87
|
|
|
88
|
+
<a name="Generator+registry"></a>
|
|
89
|
+
|
|
90
|
+
* generator.registry : `Object`** :
|
|
91
|
+
Npm registry information.
|
|
92
|
+
|
|
93
|
+
**Kind**: instance property of [`Generator`](#Generator)
|
|
94
|
+
|
|
83
95
|
<a name="Generator+templateName"></a>
|
|
84
96
|
|
|
85
97
|
* generator.templateName : `String`** :
|
package/docs/index.md
CHANGED
|
@@ -5,6 +5,9 @@ weight: 10
|
|
|
5
5
|
|
|
6
6
|
The AsyncAPI generator is a tool that generates anything you want using the **[AsyncAPI Document](generator/asyncapi-document)** and **[Template](generator/template)** that are supplied as inputs to the AsyncAPI CLI. The generator was built with extensibility in mind; you can use the generator to generate anything you want, provided that it can be defined in a template, such as code, diagrams, markdown files, microservices, and applications. A number of [community-maintained templates](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate) are now available for immediate usage.
|
|
7
7
|
|
|
8
|
+
> **Note:**
|
|
9
|
+
> If your primary objective is to generate models/classes for your event-driven architecture apps, use [AsyncAPI Modelina](/docs/tools/generator/model-generation), which is supported in the AsyncAPI CLI, instead of using the AsyncAPI Generator. Modelina is specifically designed for model generation and provides utilities for working with the AsyncAPI document.
|
|
10
|
+
|
|
8
11
|
### Generator use cases
|
|
9
12
|
- Generation of interactive and understandable API documentation
|
|
10
13
|
- Generation of APIs' client libraries
|
|
@@ -53,7 +53,7 @@ You can install in Linux by using `dpkg`, a package manager for debian:
|
|
|
53
53
|
For further installation instructions for different operating systems, read the [AsyncAPI CLI documentation](https://github.com/asyncapi/cli#installation).
|
|
54
54
|
|
|
55
55
|
> **Remember:**
|
|
56
|
-
> Each [community-developed template](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate) is dependent on a certain version of the generator for it to work correctly. Before you install the
|
|
56
|
+
> Each [community-developed template](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate) is dependent on a certain version of the generator for it to work correctly. Before you install the AsyncAPI CLI, check the template's `package.json` for the version of the AsyncAPI CLI your template is compatible with. Read the [versioning docs](versioning) to learn why it's important to use certain generator versions with your templates.
|
|
57
57
|
|
|
58
58
|
### Update AsyncAPI CLI
|
|
59
59
|
There are several reasons why you might want to update your generator version:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Adding models generation in template"
|
|
3
|
+
weight: 200
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
This guide will walk you through the process of enabling models/types generation in a template by using [Modelina](https://www.asyncapi.com/tools/modelina).
|
|
7
|
+
|
|
8
|
+
Modelina is an AsyncAPI library designed for generating data models using inputs such as [AsyncAPI](generator/asyncapi-document), OpenAPI, or JSON schema inputs. Its functionality revolves around creating data models from the provided AsyncAPI document and the model template, which defines message payloads. It is better to use Modelina in your template to handle model generation rather than providing custom templates.
|
|
9
|
+
|
|
10
|
+
You can integrate the work shown in this guide into a template by following the [tutorial about creating a template](https://www.asyncapi.com/docs/tools/generator/generator-template).
|
|
11
|
+
|
|
12
|
+
In this guide, you'll learn how to use Modelina in a template code to enable support for Python data model generation.
|
|
13
|
+
|
|
14
|
+
## Add Modelina dependency
|
|
15
|
+
|
|
16
|
+
Install Modelina in your project using npm: `npm install --save @asyncapi/modelina`.
|
|
17
|
+
|
|
18
|
+
Ensure your template's `package.json` file now contains Modelina pointing to its latest version:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
"dependencies": {
|
|
22
|
+
// ...
|
|
23
|
+
"@asyncapi/modelina": "^2.0.5"
|
|
24
|
+
// ...
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Create a models.js file
|
|
29
|
+
|
|
30
|
+
Create a new directory in the **template** directory named **src/models** and create a **models.js** file within it. In the **models.js** file, add the following code:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
// 1
|
|
34
|
+
import { File } from '@asyncapi/generator-react-sdk';
|
|
35
|
+
// 2
|
|
36
|
+
import { PythonGenerator, FormatHelpers } from '@asyncapi/modelina';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef RenderArgument
|
|
40
|
+
* @type {object}
|
|
41
|
+
* @property {AsyncAPIDocument} asyncapi document object received from the generator.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Render all schema models
|
|
46
|
+
* @param {RenderArgument} param0
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
// 3
|
|
50
|
+
export default async function schemaRender({ asyncapi }) {
|
|
51
|
+
// 4
|
|
52
|
+
const pythonGenerator = new PythonGenerator();
|
|
53
|
+
// 5
|
|
54
|
+
const models = await pythonGenerator.generate(asyncapi);
|
|
55
|
+
// 6
|
|
56
|
+
const files = [];
|
|
57
|
+
// 7
|
|
58
|
+
for (const model of models) {
|
|
59
|
+
// 8
|
|
60
|
+
const modelFileName = `${FormatHelpers.toPascalCase(model.modelName)}.py`;
|
|
61
|
+
// 9
|
|
62
|
+
files.push(<File name={modelFileName}>{model.result}</File>);
|
|
63
|
+
}
|
|
64
|
+
return files;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Let's break it down. The code snippet above does the following:
|
|
69
|
+
|
|
70
|
+
1. The `File` component from the [generator react SDK](https://github.com/asyncapi/generator-react-sdk) is needed to handle the further rendering of generated models into files.
|
|
71
|
+
2. The `PythonGenerator` generator is the core needed for model generation. Additionally, you can import [FormatHelpers](https://github.com/asyncapi/modelina/blob/master/src/helpers/FormatHelpers.ts) that provides a set of helpers making it easier to modify model names to match your required case.
|
|
72
|
+
3. You can change the name `schemaRender` to anything else like `modelRenderer`. More importantly, this must be an `async` function and a default export. This function is invoked during generation process and should contain the logic behind models generation.
|
|
73
|
+
4. First, create an instance of the `PythonGenerator` model generator. If you decide to use present functionality from Modelina, you need to pass your presets here during instance creation.
|
|
74
|
+
5. The actual model generation is one line of code, and as a result you get an array of models that later you need to turn into files.
|
|
75
|
+
6. You need to define an array that must be returned from `schemaRender` function. The array must contain React components, and in this case, the `<File>` component.
|
|
76
|
+
7. Iterate over generated models and use their content to create proper definitions of `<File>` components.
|
|
77
|
+
8. Notice how using Modelina helpers, in this case the `toPascalCase` function, let's you make sure that the filename of your model follows specific case pattern.
|
|
78
|
+
9. Each component must be added into the `files` array that you later return from the default function. Notice the definition of the `<File>` component that enables you to provide the name of resulting file and the content of the model. Notice also `model.result` that shows that initially generated array with models did not contain raw models content but a set of output objects that contain not only `result` but also other info, like for example `modelName`.
|
|
79
|
+
|
|
80
|
+
With such a model template that uses Modelina, as a result of generation process you would receive a set of model files in `$OUTPUT_DIR/src/models` directory.
|
|
81
|
+
|
|
82
|
+
## Conclusion
|
|
83
|
+
|
|
84
|
+
Modelina provides a flexible and powerful way to generate data models from AsyncAPI, OpenAPI, or JSON Schema documents. By integrating Modelina you can much faster enable models generation in your template.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
title: "Template development"
|
|
3
3
|
weight: 80
|
|
4
4
|
---
|
|
5
|
+
> **Note**
|
|
6
|
+
> It is advised against attempting to manually template types and models from scratch using the AsyncAPI templating engines such as Nunjucks and React render engines. Instead, it is recommended to use [AsyncAPI Modelina](/docs/tools/generator/model-generation) a dedicated library for model generation.
|
|
5
7
|
|
|
6
8
|
## Minimum template requirements
|
|
7
9
|
|
|
@@ -130,7 +132,7 @@ Newer:
|
|
|
130
132
|
<Text>Version is: **{params.version || asyncapi.info.version()}**</Text>
|
|
131
133
|
```
|
|
132
134
|
|
|
133
|
-
Now that you have added all the configuration options, you can start the generation process using the
|
|
135
|
+
Now that you have added all the configuration options, you can start the generation process using the AsyncAPI CLI. You can pass these parameters via the CLI: `--param name=value or -p name=value`.
|
|
134
136
|
The above configuration helps template users override the existing version with a new version on the command line. (Example: `-p version=2.0.0`)
|
|
135
137
|
|
|
136
138
|
## Hooks
|
package/docs/template.md
CHANGED
|
@@ -16,7 +16,7 @@ Examples outputs:
|
|
|
16
16
|
|
|
17
17
|
A template is an independent Node.js project unrelated to the `generator` repository. AsyncAPI templates are managed, released, and published separately. You can also create templates and manage templates on your own.
|
|
18
18
|
|
|
19
|
-
The generator uses the official [Arborist](https://www.npmjs.com/package/@npmcli/arborist) NPM library. (This means templates do not have to be published to package managers to use them.) Arborist helps the generator fetch the template's source code and use it for the generation process.
|
|
19
|
+
The generator uses the official [Arborist](https://www.npmjs.com/package/@npmcli/arborist) NPM library. (This means templates do not have to be published to package managers to use them.) Arborist helps the generator fetch the template's source code and use it for the generation process. By default, this library pulls data from the default NPM registry, which is https://registry.npmjs.org. You can also configure the generator to fetch templates that are private or hosted in different NPM registry
|
|
20
20
|
|
|
21
21
|
You can store template projects on a local drive or as a `git` repository during the development process.
|
|
22
22
|
|
|
@@ -24,7 +24,7 @@ You can store template projects on a local drive or as a `git` repository during
|
|
|
24
24
|
|
|
25
25
|
1. Template is provided as input to the **Generator**.
|
|
26
26
|
2. **asyncapi** is the original AsyncAPI document injected into your template file by default.
|
|
27
|
-
3. **params** are the parameters you pass to the
|
|
27
|
+
3. **params** are the parameters you pass to the AsyncAPI CLI. Later, you can also pass these **params** further to other components.
|
|
28
28
|
4. The generator passes both the original **asyncapi**, the original AsyncAPI document, and the **params** to the **Template Context**.
|
|
29
29
|
5. Concurrently, the generator passes **Template files** to the **Render engine** as well. AsyncAPI uses two render engines — _react_ and _nunjucks_.
|
|
30
30
|
6. Once the Render Engine receives both the Template Files and the Template Context, it injects all the dynamic values into your react or nunjucks engine, based on the Template Files using the Template Context.
|
package/docs/usage.md
CHANGED
|
@@ -4,10 +4,10 @@ weight: 30
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
There are two ways to use the generator:
|
|
7
|
-
- [
|
|
7
|
+
- [AsyncAPI CLI](#generator-cli)
|
|
8
8
|
- [Generator library](#using-as-a-modulepackage)
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## AsyncAPI CLI
|
|
11
11
|
```bash
|
|
12
12
|
Usage: asyncapi generate fromTemplate <asyncapi> <template> [<options>]
|
|
13
13
|
|
|
@@ -43,7 +43,7 @@ npm install <folder>
|
|
|
43
43
|
|
|
44
44
|
### Global templates installed with `yarn` or `npm`
|
|
45
45
|
|
|
46
|
-
You can preinstall templates globally before installing the
|
|
46
|
+
You can preinstall templates globally before installing the [AsyncAPI CLI](https://www.asyncapi.com/docs/tools/cli). The generator first tries to locate the template in local dependencies; if absent it checks where the global generator packages are installed.
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
49
|
npm install -g @asyncapi/html-template@0.16.0
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Using private templates"
|
|
3
|
+
weight: 180
|
|
4
|
+
---
|
|
5
|
+
Generator allows fetching the template from private repositories like Verdaccio, Nexus, npm, etc.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Private registry options:
|
|
9
|
+
|
|
10
|
+
* **registry.url**: The URL of the registry where the private template is located. Defaults to `registry.npmjs.org`.
|
|
11
|
+
* **registry.auth**: An optional parameter to pass the npm registry username and password encoded with base64, formatted as `username:password`. For example, if the username and password are `admin` and `nimda`, you need to encode them with the base64 value like `admin:nimda` which results in `YWRtaW46bmltZGE=`.
|
|
12
|
+
**registry.token**: An optional parameter to pass to the npm registry authentication token. To get the token, you can first authenticate with the registry using `npm login` and then grab the generated token from the `.npmrc` file.
|
|
13
|
+
|
|
14
|
+
## Pulling private template using library:
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
const generator = new Generator('@asyncapi/html-template', 'output',
|
|
18
|
+
{
|
|
19
|
+
debug: true,
|
|
20
|
+
registry: {
|
|
21
|
+
url: 'http://verdaccio:4873',
|
|
22
|
+
auth: 'YWRtaW46bmltZGE='
|
|
23
|
+
// base64 encoded username and password
|
|
24
|
+
// represented as admin:nimda
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
Assuming you host `@asyncapi/html-template` in a private package registry like Verdaccio. To pull this template, you need to provide `registry.url` option that points to the registry URL and `registry.auth` as a base64 encoded value that represents the username and password. Instead of username and password, you can also pass `registry.token`.
|
package/lib/filtersRegistry.js
CHANGED
|
@@ -12,7 +12,7 @@ const { isAsyncFunction } = require('./utils');
|
|
|
12
12
|
*/
|
|
13
13
|
module.exports.registerFilters = async (nunjucks, templateConfig, templateDir, filtersDir) => {
|
|
14
14
|
await registerLocalFilters(nunjucks, templateDir, filtersDir);
|
|
15
|
-
|
|
15
|
+
registerConfigFilters(nunjucks, templateDir, templateConfig);
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -71,16 +71,31 @@ async function registerConfigFilters(nunjucks, templateDir, templateConfig) {
|
|
|
71
71
|
|
|
72
72
|
const promises = confFilters.map(async filtersModule => {
|
|
73
73
|
let mod;
|
|
74
|
+
let filterName = filtersModule;
|
|
74
75
|
try {
|
|
75
76
|
//first we try to grab module with filters by the module name
|
|
76
77
|
//this is when generation is used on production using remote templates
|
|
77
|
-
mod = require(
|
|
78
|
+
mod = require(filterName);
|
|
78
79
|
} catch (error) {
|
|
79
80
|
//in case template is local but was not installed in node_modules of the generator then we need to explicitly provide modules location
|
|
80
|
-
|
|
81
|
+
try {
|
|
82
|
+
filterName = path.resolve(templateDir, DEFAULT_MODULES_DIR, filtersModule);
|
|
83
|
+
mod = require(filterName);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
//sometimes it may happen that template is located in node_modules with other templates and its filter package is on the same level as template, as it is shared with other templates
|
|
86
|
+
try {
|
|
87
|
+
filterName = path.resolve(templateDir, '../..', filtersModule);
|
|
88
|
+
mod = require(filterName);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
//in rare cases, especially in isolated tests, it may happen that installation
|
|
91
|
+
//ends but is not yet fully completed, so initial require of the same path do not work
|
|
92
|
+
//but in next attempt it works
|
|
93
|
+
//we need to keep this workaround until we find a solution
|
|
94
|
+
mod = require(filterName);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
81
97
|
}
|
|
82
|
-
|
|
83
|
-
addFilters(nunjucks, mod);
|
|
98
|
+
return addFilters(nunjucks, mod);
|
|
84
99
|
});
|
|
85
100
|
|
|
86
101
|
await Promise.all(promises);
|
package/lib/generator.js
CHANGED
|
@@ -21,7 +21,6 @@ const {
|
|
|
21
21
|
copyFile,
|
|
22
22
|
exists,
|
|
23
23
|
fetchSpec,
|
|
24
|
-
getInvalidOptions,
|
|
25
24
|
isReactTemplate,
|
|
26
25
|
isJsFile,
|
|
27
26
|
registerSourceMap,
|
|
@@ -44,8 +43,7 @@ const DEFAULT_TEMPLATES_DIR = path.resolve(ROOT_DIR, 'node_modules');
|
|
|
44
43
|
|
|
45
44
|
const TRANSPILED_TEMPLATE_LOCATION = '__transpiled';
|
|
46
45
|
const TEMPLATE_CONTENT_DIRNAME = 'template';
|
|
47
|
-
const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder'];
|
|
48
|
-
|
|
46
|
+
const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder', 'url', 'auth', 'token', 'registry'];
|
|
49
47
|
const logMessage = require('./logMessages');
|
|
50
48
|
|
|
51
49
|
const shouldIgnoreFile = filePath =>
|
|
@@ -86,14 +84,21 @@ class Generator {
|
|
|
86
84
|
* @param {Boolean} [options.install=false] Install the template and its dependencies, even when the template has already been installed.
|
|
87
85
|
* @param {Boolean} [options.debug=false] Enable more specific errors in the console. At the moment it only shows specific errors about filters. Keep in mind that as a result errors about template are less descriptive.
|
|
88
86
|
* @param {Object<String, String>} [options.mapBaseUrlToFolder] Optional parameter to map schema references from a base url to a local base folder e.g. url=https://schema.example.com/crm/ folder=./test/docs/ .
|
|
87
|
+
* @param {Object} [options.registry] Optional parameter with private registry configuration
|
|
88
|
+
* @param {String} [options.registry.url] Parameter to pass npm registry url
|
|
89
|
+
* @param {String} [options.registry.auth] Optional parameter to pass npm registry username and password encoded with base64, formatted like username:password value should be encoded
|
|
90
|
+
* @param {String} [options.registry.token] Optional parameter to pass npm registry auth token that you can grab from .npmrc file
|
|
89
91
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
|
|
93
|
+
constructor(templateName, targetDir, { templateParams = {}, entrypoint, noOverwriteGlobs, disabledHooks, output = 'fs', forceWrite = false, install = false, debug = false, mapBaseUrlToFolder = {}, registry = {}} = {}) {
|
|
94
|
+
const options = arguments[arguments.length - 1];
|
|
95
|
+
this.verifyoptions(options);
|
|
93
96
|
if (!templateName) throw new Error('No template name has been specified.');
|
|
94
97
|
if (!entrypoint && !targetDir) throw new Error('No target directory has been specified.');
|
|
95
98
|
if (!['fs', 'string'].includes(output)) throw new Error(`Invalid output type ${output}. Valid values are 'fs' and 'string'.`);
|
|
96
99
|
|
|
100
|
+
/** @type {Object} Npm registry information. */
|
|
101
|
+
this.registry = registry;
|
|
97
102
|
/** @type {String} Name of the template to generate. */
|
|
98
103
|
this.templateName = templateName;
|
|
99
104
|
/** @type {String} Path to the directory where the files will be generated. */
|
|
@@ -136,6 +141,23 @@ class Generator {
|
|
|
136
141
|
});
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Check if the Registry Options are valid or not.
|
|
146
|
+
*
|
|
147
|
+
* @private
|
|
148
|
+
* @param {Object} invalidRegOptions Invalid Registry Options.
|
|
149
|
+
*
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
verifyoptions(Options) {
|
|
153
|
+
if (typeof Options !== 'object') return [];
|
|
154
|
+
const invalidOptions = Object.keys(Options).filter(param => !GENERATOR_OPTIONS.includes(param));
|
|
155
|
+
|
|
156
|
+
if (invalidOptions.length > 0) {
|
|
157
|
+
throw new Error(`These options are not supported by the generator: ${invalidOptions.join(', ')}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
139
161
|
/**
|
|
140
162
|
* Generates files from a given template and an AsyncAPIDocument object.
|
|
141
163
|
*
|
|
@@ -143,7 +165,7 @@ class Generator {
|
|
|
143
165
|
* @example
|
|
144
166
|
* await generator.generate(myAsyncAPIdocument);
|
|
145
167
|
* console.log('Done!');
|
|
146
|
-
*
|
|
168
|
+
*
|
|
147
169
|
* @example
|
|
148
170
|
* generator
|
|
149
171
|
* .generate(myAsyncAPIdocument)
|
|
@@ -201,9 +223,9 @@ class Generator {
|
|
|
201
223
|
* @example
|
|
202
224
|
* const generator = new Generator();
|
|
203
225
|
* await generator.setupOutput();
|
|
204
|
-
*
|
|
226
|
+
*
|
|
205
227
|
* @async
|
|
206
|
-
*
|
|
228
|
+
*
|
|
207
229
|
* @throws {Error} If 'output' is set to 'string' without providing 'entrypoint'.
|
|
208
230
|
*/
|
|
209
231
|
async setupOutput() {
|
|
@@ -259,7 +281,6 @@ class Generator {
|
|
|
259
281
|
*/
|
|
260
282
|
async installAndSetupTemplate() {
|
|
261
283
|
const { name: templatePkgName, path: templatePkgPath } = await this.installTemplate(this.install);
|
|
262
|
-
|
|
263
284
|
this.templateDir = templatePkgPath;
|
|
264
285
|
this.templateName = templatePkgName;
|
|
265
286
|
this.templateContentDir = path.resolve(this.templateDir, TEMPLATE_CONTENT_DIRNAME);
|
|
@@ -287,11 +308,9 @@ class Generator {
|
|
|
287
308
|
await this.parseInput(this.asyncapi, parseOptions);
|
|
288
309
|
validateTemplateConfig(this.templateConfig, this.templateParams, this.asyncapi);
|
|
289
310
|
await this.configureTemplate();
|
|
290
|
-
|
|
291
311
|
if (!isReactTemplate(this.templateConfig)) {
|
|
292
312
|
await registerFilters(this.nunjucks, this.templateConfig, this.templateDir, FILTERS_DIRNAME);
|
|
293
313
|
}
|
|
294
|
-
|
|
295
314
|
await registerHooks(this.hooks, this.templateConfig, this.templateDir, HOOKS_DIRNAME);
|
|
296
315
|
await this.launchHook('generate:before');
|
|
297
316
|
}
|
|
@@ -313,16 +332,14 @@ class Generator {
|
|
|
313
332
|
async handleEntrypoint() {
|
|
314
333
|
if (this.entrypoint) {
|
|
315
334
|
const entrypointPath = path.resolve(this.templateContentDir, this.entrypoint);
|
|
316
|
-
|
|
317
335
|
if (!(await exists(entrypointPath))) {
|
|
318
336
|
throw new Error(`Template entrypoint "${entrypointPath}" couldn't be found.`);
|
|
319
337
|
}
|
|
320
|
-
|
|
321
338
|
if (this.output === 'fs') {
|
|
322
339
|
await this.generateFile(this.asyncapi, path.basename(entrypointPath), path.dirname(entrypointPath));
|
|
323
340
|
await this.launchHook('generate:after');
|
|
324
341
|
} else if (this.output === 'string') {
|
|
325
|
-
return this.renderFile(this.asyncapi, entrypointPath);
|
|
342
|
+
return await this.renderFile(this.asyncapi, entrypointPath);
|
|
326
343
|
}
|
|
327
344
|
} else {
|
|
328
345
|
await this.generateDirectoryStructure(this.asyncapi);
|
|
@@ -424,7 +441,7 @@ class Generator {
|
|
|
424
441
|
if (!isParsableCompatible) {
|
|
425
442
|
throw new Error('Parameter "asyncapiString" must be a non-empty string.');
|
|
426
443
|
}
|
|
427
|
-
return this.generate(asyncapiString, parseOptions);
|
|
444
|
+
return await this.generate(asyncapiString, parseOptions);
|
|
428
445
|
}
|
|
429
446
|
|
|
430
447
|
/**
|
|
@@ -451,7 +468,7 @@ class Generator {
|
|
|
451
468
|
*/
|
|
452
469
|
async generateFromURL(asyncapiURL) {
|
|
453
470
|
const doc = await fetchSpec(asyncapiURL);
|
|
454
|
-
return this.generate(doc, { path: asyncapiURL });
|
|
471
|
+
return await this.generate(doc, { path: asyncapiURL });
|
|
455
472
|
}
|
|
456
473
|
|
|
457
474
|
/**
|
|
@@ -478,7 +495,7 @@ class Generator {
|
|
|
478
495
|
*/
|
|
479
496
|
async generateFromFile(asyncapiFile) {
|
|
480
497
|
const doc = await readFile(asyncapiFile, { encoding: 'utf8' });
|
|
481
|
-
return this.generate(doc, { path: asyncapiFile });
|
|
498
|
+
return await this.generate(doc, { path: asyncapiFile });
|
|
482
499
|
}
|
|
483
500
|
|
|
484
501
|
/**
|
|
@@ -502,69 +519,99 @@ class Generator {
|
|
|
502
519
|
return await readFile(path.resolve(templatesDir, templateName, filePath), 'utf8');
|
|
503
520
|
}
|
|
504
521
|
|
|
522
|
+
/**
|
|
523
|
+
* @private
|
|
524
|
+
* @param {Object} arbOptions ArbOptions to intialise the Registry details.
|
|
525
|
+
*/
|
|
526
|
+
initialiseArbOptions(arbOptions) {
|
|
527
|
+
let registryUrl = 'registry.npmjs.org';
|
|
528
|
+
let authorizationName = 'anonymous';
|
|
529
|
+
const providedRegistry = this.registry.url;
|
|
530
|
+
|
|
531
|
+
if (providedRegistry) {
|
|
532
|
+
arbOptions.registry = providedRegistry;
|
|
533
|
+
registryUrl = providedRegistry;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const domainName = registryUrl.replace(/^https?:\/\//, '');
|
|
537
|
+
//doing basic if/else so basically only one auth type is used and token as more secure is primary
|
|
538
|
+
if (this.registry.token) {
|
|
539
|
+
authorizationName = `//${domainName}:_authToken`;
|
|
540
|
+
arbOptions[authorizationName] = this.registry.token;
|
|
541
|
+
} else if (this.registry.auth) {
|
|
542
|
+
authorizationName = `//${domainName}:_auth`;
|
|
543
|
+
arbOptions[authorizationName] = this.registry.auth;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
//not sharing in logs neither token nor auth for security reasons
|
|
547
|
+
log.debug(`Using npm registry ${registryUrl} and authorization type ${authorizationName} to handle template installation.`);
|
|
548
|
+
}
|
|
505
549
|
/**
|
|
506
550
|
* Downloads and installs a template and its dependencies
|
|
507
551
|
*
|
|
508
552
|
* @param {Boolean} [force=false] Whether to force installation (and skip cache) or not.
|
|
509
553
|
*/
|
|
510
|
-
installTemplate(force = false) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
try {
|
|
518
|
-
installedPkg = getTemplateDetails(this.templateName, PACKAGE_JSON_FILENAME);
|
|
519
|
-
pkgPath = installedPkg && installedPkg.pkgPath;
|
|
520
|
-
packageVersion = installedPkg && installedPkg.version;
|
|
521
|
-
log.debug(logMessage.templateSource(pkgPath));
|
|
522
|
-
if (packageVersion) log.debug(logMessage.templateVersion(packageVersion));
|
|
523
|
-
|
|
524
|
-
return resolve({
|
|
525
|
-
name: installedPkg.name,
|
|
526
|
-
path: pkgPath
|
|
527
|
-
});
|
|
528
|
-
} catch (e) {
|
|
529
|
-
log.debug(logMessage.packageNotAvailable(pkgPath), e);
|
|
530
|
-
// We did our best. Proceed with installation...
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
const debugMessage = force ? logMessage.TEMPLATE_INSTALL_FLAG_MSG : logMessage.TEMPLATE_INSTALL_DISK_MSG;
|
|
535
|
-
log.debug(logMessage.installationDebugMessage(debugMessage));
|
|
536
|
-
|
|
537
|
-
if (isFileSystemPath(this.templateName)) log.debug(logMessage.NPM_INSTALL_TRIGGER);
|
|
538
|
-
|
|
539
|
-
const arb = new Arborist({
|
|
540
|
-
path: ROOT_DIR
|
|
541
|
-
});
|
|
542
|
-
|
|
554
|
+
async installTemplate(force = false) {
|
|
555
|
+
if (!force) {
|
|
556
|
+
let pkgPath;
|
|
557
|
+
let installedPkg;
|
|
558
|
+
let packageVersion;
|
|
559
|
+
|
|
543
560
|
try {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
const addResult = arb[Symbol.for('resolvedAdd')];
|
|
551
|
-
if (!addResult) return reject('Unable to resolve the name of the added package. It was most probably not added to node_modules successfully');
|
|
552
|
-
|
|
553
|
-
const packageName = addResult[0].name;
|
|
554
|
-
const packageVersion = installResult.children.get(packageName).version;
|
|
555
|
-
const packagePath = installResult.children.get(packageName).path;
|
|
556
|
-
|
|
557
|
-
if (!isFileSystemPath(this.templateName)) log.debug(logMessage.templateSuccessfullyInstalled(packageName, packagePath));
|
|
561
|
+
installedPkg = getTemplateDetails(this.templateName, PACKAGE_JSON_FILENAME);
|
|
562
|
+
pkgPath = installedPkg && installedPkg.pkgPath;
|
|
563
|
+
packageVersion = installedPkg && installedPkg.version;
|
|
564
|
+
log.debug(logMessage.templateSource(pkgPath));
|
|
558
565
|
if (packageVersion) log.debug(logMessage.templateVersion(packageVersion));
|
|
559
|
-
|
|
560
|
-
return
|
|
561
|
-
name:
|
|
562
|
-
path:
|
|
563
|
-
}
|
|
564
|
-
} catch (
|
|
565
|
-
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
name: installedPkg.name,
|
|
569
|
+
path: pkgPath
|
|
570
|
+
};
|
|
571
|
+
} catch (e) {
|
|
572
|
+
log.debug(logMessage.packageNotAvailable(installedPkg), e);
|
|
573
|
+
// We did our best. Proceed with installation...
|
|
566
574
|
}
|
|
567
|
-
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const debugMessage = force ? logMessage.TEMPLATE_INSTALL_FLAG_MSG : logMessage.TEMPLATE_INSTALL_DISK_MSG;
|
|
578
|
+
log.debug(logMessage.installationDebugMessage(debugMessage));
|
|
579
|
+
|
|
580
|
+
if (isFileSystemPath(this.templateName)) log.debug(logMessage.NPM_INSTALL_TRIGGER);
|
|
581
|
+
|
|
582
|
+
const arbOptions = {
|
|
583
|
+
path: ROOT_DIR,
|
|
584
|
+
};
|
|
585
|
+
if (this.registry) {
|
|
586
|
+
this.initialiseArbOptions(arbOptions);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const arb = new Arborist(arbOptions);
|
|
590
|
+
|
|
591
|
+
try {
|
|
592
|
+
const installResult = await arb.reify({
|
|
593
|
+
add: [this.templateName],
|
|
594
|
+
saveType: 'prod',
|
|
595
|
+
save: false
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
const addResult = arb[Symbol.for('resolvedAdd')];
|
|
599
|
+
if (!addResult) throw new Error('Unable to resolve the name of the added package. It was most probably not added to node_modules successfully');
|
|
600
|
+
|
|
601
|
+
const packageName = addResult[0].name;
|
|
602
|
+
const packageVersion = installResult.children.get(packageName).version;
|
|
603
|
+
const packagePath = installResult.children.get(packageName).path;
|
|
604
|
+
|
|
605
|
+
if (!isFileSystemPath(this.templateName)) log.debug(logMessage.templateSuccessfullyInstalled(packageName, packagePath));
|
|
606
|
+
if (packageVersion) log.debug(logMessage.templateVersion(packageVersion));
|
|
607
|
+
|
|
608
|
+
return {
|
|
609
|
+
name: packageName,
|
|
610
|
+
path: packagePath,
|
|
611
|
+
};
|
|
612
|
+
} catch (err) {
|
|
613
|
+
throw new Error('Installation failed', err);
|
|
614
|
+
}
|
|
568
615
|
}
|
|
569
616
|
|
|
570
617
|
/**
|
|
@@ -729,7 +776,7 @@ class Generator {
|
|
|
729
776
|
* @param {String} baseDir Base directory of the given file name.
|
|
730
777
|
* @returns {Promise}
|
|
731
778
|
*/
|
|
732
|
-
generateSeparateFiles(asyncapiDocument, array, template, fileName, baseDir) {
|
|
779
|
+
async generateSeparateFiles(asyncapiDocument, array, template, fileName, baseDir) {
|
|
733
780
|
const promises = [];
|
|
734
781
|
|
|
735
782
|
Object.keys(array).forEach((name) => {
|
|
@@ -768,7 +815,7 @@ class Generator {
|
|
|
768
815
|
const newFileName = fileName.replace(`\$\$${template}\$\$`, filename);
|
|
769
816
|
const targetFile = path.resolve(this.targetDir, relativeBaseDir, newFileName);
|
|
770
817
|
const relativeTargetFile = path.relative(this.targetDir, targetFile);
|
|
771
|
-
const shouldOverwriteFile = this.shouldOverwriteFile(relativeTargetFile);
|
|
818
|
+
const shouldOverwriteFile = await this.shouldOverwriteFile(relativeTargetFile);
|
|
772
819
|
if (!shouldOverwriteFile) return;
|
|
773
820
|
//Ensure the same object are parsed to the renderFile method as before.
|
|
774
821
|
const temp = {};
|
|
@@ -815,7 +862,7 @@ class Generator {
|
|
|
815
862
|
|
|
816
863
|
if (shouldIgnoreFile(relativeSourceFile)) return;
|
|
817
864
|
|
|
818
|
-
const shouldOverwriteFile = this.shouldOverwriteFile(relativeTargetFile);
|
|
865
|
+
const shouldOverwriteFile = await this.shouldOverwriteFile(relativeTargetFile);
|
|
819
866
|
if (!shouldOverwriteFile) return;
|
|
820
867
|
|
|
821
868
|
if (this.templateConfig.conditionalFiles && this.templateConfig.conditionalFiles[relativeSourceFile]) {
|
|
@@ -896,7 +943,7 @@ class Generator {
|
|
|
896
943
|
*
|
|
897
944
|
* @private
|
|
898
945
|
* @param {string} filePath Path to the file to check against a list of glob patterns.
|
|
899
|
-
* @return {boolean}
|
|
946
|
+
* @return {Promise<boolean>}
|
|
900
947
|
*/
|
|
901
948
|
async shouldOverwriteFile(filePath) {
|
|
902
949
|
if (!Array.isArray(this.noOverwriteGlobs)) return true;
|
package/lib/logMessages.js
CHANGED
|
@@ -18,8 +18,12 @@ function templateNotFound(templateName) {
|
|
|
18
18
|
return `${templateName} not found in local dependencies but found it installed as a global package.`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function packageNotAvailable(
|
|
22
|
-
|
|
21
|
+
function packageNotAvailable(packageDetails) {
|
|
22
|
+
if (packageDetails && packageDetails.pkgPath) {
|
|
23
|
+
return `Unable to resolve template location at ${packageDetails.pkgPath}. Package is not available locally.`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return `Template is not available locally and expected location is undefined. Known details are: ${JSON.stringify(packageDetails, null, 2)}`;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
function installationDebugMessage(debugMessage) {
|
package/lib/utils.js
CHANGED
|
@@ -124,17 +124,6 @@ utils.getGeneratorVersion = () => {
|
|
|
124
124
|
return packageJson.version;
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
-
/**
|
|
128
|
-
* Filters out the Generator invalid options given
|
|
129
|
-
*
|
|
130
|
-
* @param {Array}
|
|
131
|
-
* @returns {Array}
|
|
132
|
-
*/
|
|
133
|
-
utils.getInvalidOptions = (generatorOptions, options) => {
|
|
134
|
-
if (typeof options !== 'object') return [];
|
|
135
|
-
return Object.keys(options).filter(param => !generatorOptions.includes(param));
|
|
136
|
-
};
|
|
137
|
-
|
|
138
127
|
/**
|
|
139
128
|
* Determine whether the given function is asynchronous.
|
|
140
129
|
* @private
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asyncapi/generator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.1",
|
|
4
4
|
"description": "The AsyncAPI generator. It can generate documentation, code, anything!",
|
|
5
5
|
"main": "./lib/generator.js",
|
|
6
6
|
"bin": {
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"homepage": "https://github.com/asyncapi/generator",
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@asyncapi/generator-react-sdk": "^1.0.6",
|
|
52
|
-
"@asyncapi/parser": "^3.0.
|
|
53
|
-
"@npmcli/arborist": "
|
|
52
|
+
"@asyncapi/parser": "^3.0.3",
|
|
53
|
+
"@npmcli/arborist": "5.6.3",
|
|
54
54
|
"@smoya/multi-parser": "^5.0.0",
|
|
55
55
|
"ajv": "^8.12.0",
|
|
56
56
|
"chokidar": "^3.4.0",
|