diff --git a/.changeset/hooks-in.md b/.changeset/hooks-in.md new file mode 100644 index 000000000..d3e5da350 --- /dev/null +++ b/.changeset/hooks-in.md @@ -0,0 +1,7 @@ +--- +"@asyncapi/generator": minor +--- + +- Package `@asyncapi/generator-hooks` is now part of `generator` repo and won't be released separately. Theource code is stored under `apps/hooks` but the `package/library` name stays as it was originally for backward compatibility, +- By default, the `@asyncapi/generator-hooks` package, known as **package** contains many different hooks used in templates and is available in the generator. You no longer have to configure it in your `package.json` in `dependencies`. The package, `@asyncapi/generator-hooks` will no longer be published to NPM separately and is deprecated. You can still have your own hooks, store them in a separate package, and configure them with your template. +- Remember that the fact that the hooks package is now included by default, doesn't mean all hooks from it are enabled by default. You still have to enable a given hook in the configuration file explicitly because some hooks can execute automatically without passing a specific parameter. Also, a hook's supported parameters need to be defined in your template's config. diff --git a/.gitignore b/.gitignore index a53c05bd1..68abb85a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ **/node_modules/ - +.DS_Store # Local env files .env .env.local diff --git a/README.md b/README.md index 5fe3c6018..3d09b55be 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ This is a Monorepo managed using [Turborepo](https://turbo.build/) and contains 1. [Generator](apps/generator): This is a tool that you can use to generate whatever you want basing on the AsyncAPI specification file as an input. -2. [Nunjucks-filters](apps/nunjucks-filters): This library contains generator filters that can be reused across multiple templates, helping to avoid redundant work. These filters are designed specifically for Nunjucks templates and are included by default with the generator, so there's no need to add them to dependencies seprately. +1. [Hooks](apps/hooks): This library contains generator filters that can be reused across multiple templates, helping to avoid redundant work. Hooks are designed to let template developers hook into the template generation process. For example, one can create a hook code that will be automatically invoked right after the template generation process has ended. +1. [Nunjucks-filters](apps/nunjucks-filters): This library contains generator filters that can be reused across multiple templates, helping to avoid redundant work. These filters are designed specifically for Nunjucks templates and are included by default with the generator, so there's no need to add them to dependencies separately. -![npm](https://img.shields.io/npm/v/@asyncapi/generator?style=for-the-badge) ![npm](https://img.shields.io/npm/dt/@asyncapi/generator?style=for-the-badge) -> warning: This package doesn't support AsyncAPI 1.x anymore. We recommend to upgrade to the latest AsyncAPI version using the [AsyncAPI converter](https://github.com/asyncapi/converter-js) (You can refer to [installation guide](/apps/generator//docs//installation-guide.md)). If you need to convert documents on the fly, you may use the [Node.js](https://github.com/asyncapi/converter-js) or [Go](https://github.com/asyncapi/converter-go) converters. +![npm](https://img.shields.io/npm/v/@asyncapi/generator?style=for-the-badge) ![npm](https://img.shields.io/npm/dt/@asyncapi/generator?style=for-the-badge) @@ -17,6 +17,8 @@ This is a Monorepo managed using [Turborepo](https://turbo.build/) and contains - [Overview](#overview) - [List of official generator templates](#list-of-official-generator-templates) +- [Filters](#filters) +- [Hooks](#hooks) - [Contributing](#contributing) - [Contributors ✨](#contributors-%E2%9C%A8) @@ -53,18 +55,24 @@ There is a large number of templates that are ready to use and are officially su You can find above templates and the ones provided by the community in **[this list](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate)** -# Generator Filters +## Filters -This library contains generator filters that can be reused across multiple templates, helping to avoid redundant work. These filters are designed specifically for Nunjucks templates and are included by default with the generator, so there's no need to add them to dependencies seprately. + `apps/nunjucks-filters` library contains generator filters that can be reused across multiple templates, helping to avoid redundant work. These filters are designed specifically for Nunjucks templates and are included by default with the generator, so there's no need to add them to dependencies separately. This library consists of: - Custom filters. Check out [API docs](apps/nunjucks-filters/docs/api.md) for complete list - Lodash-powered filters. For the list of all available filters check [official docs](https://lodash.com/docs/) +## Hooks + +The `apps/hooks` library contains generator filters that can be reused across multiple templates, helping to avoid redundant work. [Hooks](https://www.asyncapi.com/docs/tools/generator/hooks) are functions called by the generator at specific moments in the generation process. Hooks can be anonymous functions, but you can also assign them function names. These hooks can have arguments provided to them, or they may be expected to return a value. + +These hooks are included in the generator without adding any specific dependency to the library. You still have to enable the given hook in the configuration explicitly because some hooks can execute automatically without passing a specific parameter. [Learn more about configuration and what hooks are available out of the box](https://www.asyncapi.com/docs/tools/generator/hooks#official-library). + ## Contributing -For developement setup you can follow the detailed guide in [Developement guide](Development.md) +For the development setup, you can follow the detailed guide in [Developement guide](Development.md) Read [CONTRIBUTING](CONTRIBUTING.md) guide. diff --git a/apps/generator/docs/configuration-file.md b/apps/generator/docs/configuration-file.md index c11cfd00e..dda21e983 100644 --- a/apps/generator/docs/configuration-file.md +++ b/apps/generator/docs/configuration-file.md @@ -20,7 +20,7 @@ The `generator` property from `package.json` file must contain a JSON object tha |`nonRenderableFiles`| [String] | A list of file paths or [globs](https://en.wikipedia.org/wiki/Glob_(programming)) that must be copied "as-is" to the target directory, i.e., without performing any rendering process. This is useful when you want to copy binary files. |`generator`| [String] | A string representing the generator version-range the template is compatible with. This value must follow the [semver](https://nodejs.dev/learn/semantic-versioning-using-npm) syntax. E.g., `>=1.0.0`, `>=1.0.0 <=2.0.0`, `~1.0.0`, `^1.0.0`, `1.0.0`, etc. [Read more about semver](https://docs.npmjs.com/about-semantic-versioning). |`filters`| [String] | A list of modules containing functions that can be used as Nunjucks filters. In case of external modules, remember they need to be added as a dependency in `package.json` of your template. -|`hooks`| Object[String, String] or Object[String, Array[String]] | A list of modules containing hooks, except for the ones you keep locally in your template in default location. For each module you must specify the exact name of the hook that should be used in the template. For a single hook you can specify it as a string, for more you must pass an array of strings. In case of external modules, remember they need to be added as a dependency in `package.json` of your template. +|`hooks`| Object[String, String] or Object[String, Array[String]] | A list of modules containing hooks, except for the ones you keep locally in your template in the default location. For each module you must specify the exact name of the hook that should be used in the template. For a single hook, you can specify it as a string; for more hooks, you must pass an array of strings. In the case of external modules, remember they need to be added as a dependency in `package.json` of your template. There is also [an official hooks library](hooks#official-library) always included in the generator. As this is a library of multiple hooks, you still need to explicitly specify in the configuration which one you want to use. Use `@asyncapi/generator-hooks` as the library name. ### Example @@ -64,7 +64,8 @@ The `generator` property from `package.json` file must contain a JSON object tha "my-package-with-filters" ], "hooks": { - "@asyncapi/generator-hooks": "hookFunctionName" + "@asyncapi/generator-hooks": "hookFunctionName", + "my-custom-hooks-package": ["myHook", "andAnotherOne"] } } ``` diff --git a/apps/generator/docs/hooks.md b/apps/generator/docs/hooks.md index cf1cb1dbc..422ff2191 100644 --- a/apps/generator/docs/hooks.md +++ b/apps/generator/docs/hooks.md @@ -4,6 +4,9 @@ weight: 130 --- Hooks are functions called by the generator on a specific moment in the generation process. Hooks can be anonymous functions but you can also add function names. These hooks can have arguments provided to them or being expected to return a value. + +## Types + The following types of hooks are currently supported: |Hook type|Description| Return type | Arguments @@ -12,13 +15,15 @@ The following types of hooks are currently supported: | `generate:after` | Called at the very end of the generation. | void : Nothing is expected to be returned. | [The generator instance](/api) | `setFileTemplateName ` | Called right before saving a new file generated by [file template](./file-templates.md). | string : a new filename for the generator to use for the file template. | [The generator instance](/api) and object in the form of `{ "originalFilename" : string }` +## Location + The generator parses: - All the files in the `.hooks` directory inside the template. -- All modules listed in the template configuration and triggers only hooks that names were added to the config. You can use the official AsyncAPI [hooks library](https://github.com/asyncapi/generator-hooks). To learn how to add hooks to configuration [read more about the configuration file](https://www.asyncapi.com/docs/tools/generator/configuration-file). +- All modules listed in the template configuration and triggers only hooks whose names were added to the config. You can use an [official hooks library](#official-library) that is bundled together with the generator. To learn how to add hooks to configuration [read more about the configuration file](configuration-file). -### Examples +## Examples -> Some of the examples have names of hook functions provided and some not. Keep in mind that hook functions kept in template in default location do not require a name. Name is required only if you keep hooks in non default location or in a separate library, because such hooks need to be explicitly configured in the configuration file. For more details on hooks configuration [read more about the configuration file](https://www.asyncapi.com/docs/tools/generator/configuration-file). +> Some of the examples have names of hook functions provided and some not. Keep in mind that hook functions kept in template in default location do not require a name. Name is required only if you keep hooks in non default location or in a separate library, because such hooks need to be explicitly configured in the configuration file. For more details on hooks configuration [read more about the configuration file](configuration-file). Most basic modules with hooks look like this: ```js @@ -79,3 +84,35 @@ module.exports = { }; }; ``` + +## Official library + +It is a library of reusable hooks that you can use in your templates. You only have to add its name to the configuration: `@asyncapi/generator-hooks` and specify which hook you want to enable. + +This library consists of the following hooks: +|Hook name|Hook type|Description| +|---|---|---| +| `createAsyncapiFile` | `generate:after` | It creates an AsyncAPI file with the content of the spec file passed to the generator. By default, it creates the file in the root of the generation output directory. This hook also supports custom parameters that the user can pass to template generation. The parameter called `asyncapiFileDir` allows the user to specify the location where the spec file should be created. To make your template users use this parameter, you need to add it to the configuration of your template like other parameters | + +1. In your template configuration in `package.json` specify you want to use this library and what hook exactly: + ```json + { + "generator": { + "hooks": { + "@asyncapi/generator-hooks": "createAsyncapiFile" + } + } + } + ``` +1. Some hooks support custom parameters that template's user can use to specify different behaviour of the hook. To enable these, you need to also add them to the list of your template's parameters: + ```json + { + "generator": { + "parameters": { + "asyncapiFileDir": { + "description": "This template by default also outputs the AsyncAPI document that was passed as input. You can specify with this parameter what should be the location of this AsyncAPI document, relative to specified template output." + } + } + } + } + ``` \ No newline at end of file diff --git a/apps/generator/package.json b/apps/generator/package.json index e919b8902..fe44e9cd8 100644 --- a/apps/generator/package.json +++ b/apps/generator/package.json @@ -52,6 +52,7 @@ "@asyncapi/generator-react-sdk": "^1.1.2", "@asyncapi/multi-parser": "^2.1.1", "@asyncapi/nunjucks-filters": "*", + "@asyncapi/generator-hooks": "*", "@asyncapi/parser": "^3.0.14", "@npmcli/arborist": "5.6.3", "@npmcli/config": "^8.0.2", diff --git a/apps/generator/test/__snapshots__/integration.test.js.snap b/apps/generator/test/__snapshots__/integration.test.js.snap index e26c63097..f875aa85c 100644 --- a/apps/generator/test/__snapshots__/integration.test.js.snap +++ b/apps/generator/test/__snapshots__/integration.test.js.snap @@ -14,6 +14,400 @@ Version v1 running on production mode " `; +exports[`Integration testing generateFromFile() to make sure the result of the generation is not changend comparing to snapshot generate using React template 2`] = ` +"asyncapi: '2.3.0' + +externalDocs: + description: Find more info here + url: https://www.asyncapi.com + +info: + title: Dummy example with all spec features included + version: '0.0.1' + description: | + This is an example of AsyncAPI specification file that is suppose to include all possible features of the AsyncAPI specification. Do not use it on production. + + It's goal is to support development of documentation and code generation with the [AsyncAPI Generator](https://github.com/asyncapi/generator/) and [Template projects](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate) + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + contact: + name: API Support + url: http://www.asyncapi.com/support + email: info@asyncapi.io + x-twitter: '@AsyncAPISpec' + +tags: + - name: root-tag1 + externalDocs: + description: External docs description 1 + url: https://www.asyncapi.com/ + - name: root-tag2 + description: Description 2 + externalDocs: + url: \\"https://www.asyncapi.com/\\" + - name: root-tag3 + - name: root-tag4 + description: Description 4 + - name: root-tag5 + externalDocs: + url: \\"https://www.asyncapi.com/\\" + +servers: + dummy-mqtt: + $ref: '#/components/servers/dummyMQTT' + dummy-amqp: + url: amqp://localhost:{port} + protocol: amqp + description: dummy AMQP broker + protocolVersion: \\"0.9.1\\" + variables: + port: + enum: + - '15672' + - '5672' + security: + - user-password: [] + dummy-kafka: + url: http://localhost:{port} + protocol: kafka + description: dummy Kafka broker + variables: + port: + default: '9092' + +defaultContentType: application/json + +channels: + dummy/channel/with/{dummy}/parameter/create: + x-dummy-security: + $ref: '#/components/securitySchemes/supportedOauthFlows/flows/clientCredentials' + description: Dummy channel description. + parameters: + dummy: + $ref: '#/components/parameters/dummy' + publish: + summary: Inform whenever something dummy is created. + description: | + Longer description. + + Still dummy though. + operationId: receiveNewDummyInfo + tags: + - name: oparation-tag1 + externalDocs: + description: External docs description 1 + url: https://www.asyncapi.com/ + - name: oparation-tag2 + description: Description 2 + externalDocs: + url: \\"https://www.asyncapi.com/\\" + - name: oparation-tag3 + - name: oparation-tag4 + description: Description 4 + - name: oparation-tag5 + externalDocs: + url: \\"https://www.asyncapi.com/\\" + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/dummyCreated' + + dummy/channel/without/parameter: + $ref: '#/components/channels/dummyChannel' +components: + servers: + dummyMQTT: + url: mqtt://localhost + protocol: mqtt + description: dummy MQTT broker + bindings: + mqtt: + clientId: guest + cleanSession: true + channels: + dummyChannel: + bindings: + amqp: + is: routingKey + subscribe: + operationId: receiveSystemInfo + bindings: + amqp: + expiration: 100000 + userId: guest + cc: [ 'user.logs' ] + priority: 10 + deliveryMode: 2 + mandatory: false + bcc: [ 'external.audit' ] + replyTo: user.signedup + timestamp: true + ack: false + bindingVersion: 0.1.0 + message: + $ref: '#/components/messages/dummyInfo' + messages: + dummyCreated: + name: dummyCreated + title: Dummy created message + summary: This is just a dummy create message + correlationId: + description: This is a dummy correlation ID. + location: $message.header#/correlationId + tags: + - name: message-tag1 + externalDocs: + description: External docs description 1 + url: https://www.asyncapi.com/ + - name: message-tag2 + description: Description 2 + externalDocs: + url: \\"https://www.asyncapi.com/\\" + - name: message-tag3 + - name: message-tag4 + description: Description 4 + - name: message-tag5 + externalDocs: + url: \\"https://www.asyncapi.com/\\" + headers: + type: object + properties: + my-custom-app-header: + type: string + correlationId: + type: string + payload: + $ref: \\"#/components/schemas/dummyCreated\\" + bindings: + kafka: + key: + type: object + properties: + id: + type: string + format: uuid + type: + type: string + enum: [ 'type1', \\"type2\\" ] + bindingVersion: '0.1.0' + amqp: + contentEncoding: gzip + messageType: 'user.signup' + bindingVersion: 0.1.0 + x-response: + $ref: \\"#/components/messages/dummyInfo\\" + dummyInfo: + name: dummyInfo + title: Dummy system info + summary: This is just a dummy info message + correlationId: + location: $message.header#/correlationId + description: | + More description for a dummy message. + + It is a dummy system info message. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: \\"#/components/schemas/dummyInfo\\" + examples: + - name: option1example + summary: this is dummy summary for option1example + headers: + my-app-header: 12 + payload: + prop1: option1 + sentAt: 2020-01-31T13:24:53Z + - name: headerExample + headers: + my-app-header: 13 + - payload: + prop1: option2 + sentAt: 2020-01-31T13:24:53Z + + + schemas: + dummyCreated: + type: object + required: + - prop2 + x-schema-extensions-as-object: + type: object + properties: + prop1: + type: string + prop2: + type: integer + minimum: 0 + x-schema-extensions-as-primitive: dummy + x-schema-extensions-as-array: + - \\"item1\\" + - \\"item2\\" + properties: + prop1: + type: integer + minimum: 0 + description: Dummy prop1 + x-prop1-dummy: dummy extension + prop2: + type: string + description: Dummy prop2 + sentAt: + $ref: \\"#/components/schemas/sentAt\\" + dummyArrayWithObject: + $ref: \\"#/components/schemas/dummyArrayWithObject\\" + dummyArrayWithArray: + $ref: \\"#/components/schemas/dummyArrayWithArray\\" + dummyObject: + $ref: \\"#/components/schemas/dummyObject\\" + dummyArrayRank: + $ref: '#/components/schemas/dummyArrayRank' + dummyInfo: + type: object + required: + - prop1 + properties: + prop1: + type: string + enum: + - option1 + - option2 + description: Dummy prop1 + sentAt: + $ref: \\"#/components/schemas/sentAt\\" + dummyArrayWithObject: + type: array + items: + $ref: \\"#/components/schemas/dummyInfo\\" + dummyArrayWithArray: + type: array + items: + - $ref: \\"#/components/schemas/dummyInfo\\" + - type: string + - type: number + dummyObject: + type: object + properties: + dummyObjectProp1: + $ref: \\"#/components/schemas/sentAt\\" + dummyObjectProp2: + $ref: \\"#/components/schemas/dummyRecursiveObject\\" + dummyObjectProp3: + type: object + additionalProperties: true + dummyObjectProp4: + type: object + additionalProperties: false + dummyRecursiveObject: + type: object + properties: + dummyRecursiveProp1: + $ref: \\"#/components/schemas/dummyObject\\" + dummyRecursiveProp2: + type: string + sentAt: + type: string + format: date-time + description: Date and time when the message was sent. + dummyArrayRank: + type: object + properties: + dummyArrayValueRank: + $ref: '#/components/schemas/dummyArrayValueRank' + dummyArrayDimensions: + $ref: '#/components/schemas/dummyArrayArrayDimensions' + dummyArrayValueRank: + description: > + This Attribute indicates whether the val Attribute of the datapoint is an + array and how many dimensions the array has. + type: integer + default: -1 + examples: + - 2 + oneOf: + - const: -1 + description: 'Scalar: The value is not an array.' + - const: 0 + description: 'OneOrMoreDimensions: The value is an array with one or more dimensions.' + - const: 1 + description: 'OneDimension: The value is an array with one dimension.' + - const: 2 + description: 'The value is an array with two dimensions.' + dummyArrayArrayDimensions: + type: array + items: + type: integer + minimum: 0 + examples: + - [3, 5] + + securitySchemes: + user-password: + type: userPassword + apiKey: + type: apiKey + in: user + description: Provide your API key as the user and leave the password empty. + supportedOauthFlows: + type: oauth2 + description: Flows to support OAuth 2.0 + flows: + implicit: + authorizationUrl: 'https://authserver.example/auth' + scopes: + 'dummy:created': Ability to create dummy message + 'dymmy:read': Ability to read dummy info + password: + tokenUrl: 'https://authserver.example/token' + scopes: + 'dummy:created': Ability to create dummy message + 'dymmy:read': Ability to read dummy info + clientCredentials: + tokenUrl: 'https://authserver.example/token' + scopes: + 'dummy:created': Ability to create dummy message + 'dymmy:read': Ability to read dummy info + authorizationCode: + authorizationUrl: 'https://authserver.example/auth' + tokenUrl: 'https://authserver.example/token' + refreshUrl: 'https://authserver.example/refresh' + scopes: + 'dummy:created': Ability to create dummy message + 'dymmy:read': Ability to read dummy info + openIdConnectWellKnown: + type: openIdConnect + openIdConnectUrl: 'https://authserver.example/.well-known' + + parameters: + dummy: + description: The ID of the new dummy message. + schema: + type: string + description: Description that not be rendered, as parameter has explicit description. + + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + correlationId: + type: string + + operationTraits: + kafka: + bindings: + kafka: + groupId: my-app-group-id + clientId: my-app-client-id + bindingVersion: '0.1.0' +" +`; + exports[`Integration testing generateFromFile() to make sure the result of the generation is not changend comparing to snapshot generated using Nunjucks template 1`] = ` "This is a markdown file for my application. App name is: **Dummy example with all spec features included** diff --git a/apps/generator/test/integration.test.js b/apps/generator/test/integration.test.js index c77c03924..c2df3fac5 100644 --- a/apps/generator/test/integration.test.js +++ b/apps/generator/test/integration.test.js @@ -62,8 +62,11 @@ describe('Integration testing generateFromFile() to make sure the result of the templateParams: { version: 'v1', mode: 'production' } }); await generator.generateFromFile(dummySpecPath); - const file = await readFile(path.join(outputDir, testOutputFile), 'utf8'); - expect(file).toMatchSnapshot(); + const mdFile = await readFile(path.join(outputDir, testOutputFile), 'utf8'); + //react template has hooks lib enabled and generation of asyncapi document that was passed as input should work out of the box without adding @asyncapi/generator-hooks to dependencies + const asyncAPIFile = await readFile(path.join(outputDir, 'asyncapi.yaml'), 'utf8'); + expect(mdFile).toMatchSnapshot(); + expect(asyncAPIFile).toMatchSnapshot(); }); it('generate json based api with referenced JSON Schema', async () => { diff --git a/apps/generator/test/test-templates/react-template/package.json b/apps/generator/test/test-templates/react-template/package.json index ee383f64b..2bafb43ad 100644 --- a/apps/generator/test/test-templates/react-template/package.json +++ b/apps/generator/test/test-templates/react-template/package.json @@ -8,12 +8,18 @@ "generator": { "renderer": "react", "apiVersion": "v3", + "hooks": { + "@asyncapi/generator-hooks": "createAsyncapiFile" + }, "parameters": { "version": { "description": "Custom version to be used" }, "mode": { "description": "development or production" + }, + "asyncapiFileDir": { + "description": "This template by default also outputs the AsyncAPI document that was passed as input. You can specify with this parameter what should be the location of this AsyncAPI document, relative to specified template output." } } }, diff --git a/apps/hooks/package.json b/apps/hooks/package.json new file mode 100644 index 000000000..8e08f64fd --- /dev/null +++ b/apps/hooks/package.json @@ -0,0 +1,37 @@ +{ + "name": "@asyncapi/generator-hooks", + "version": "0.1.0", + "description": "Library with hooks function for templates using AsyncAPI Generator", + "main": "src/index.js", + "scripts": { + "test": "echo There are no tests yet", + "lint": "eslint --max-warnings 0 --config ../../.eslintrc --ignore-path ../../.eslintignore .", + "lint:fix": "eslint --fix --max-warnings 0 --config ../../.eslintrc --ignore-path ../../.eslintignore .", + "generate:assets": "echo 'No additional assets need to be generated at the moment'" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/asyncapi/generator-hooks.git" + }, + "keywords": [ + "asyncapi", + "generator" + ], + "author": "Lukasz Gornicki ", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/asyncapi/generator-hooks/issues" + }, + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/asyncapi/generator-hooks#readme", + "dependencies": { + "fs.extra": "^1.3.2" + }, + "devDependencies": { + "eslint": "^6.8.0", + "eslint-plugin-sonarjs": "^0.5.0", + "jsdoc-to-markdown": "^7.1.1" + } +} diff --git a/apps/hooks/src/index.js b/apps/hooks/src/index.js new file mode 100644 index 000000000..927d81fbf --- /dev/null +++ b/apps/hooks/src/index.js @@ -0,0 +1,33 @@ +const fs = require('fs'); +const path = require('path'); +const xfs = require('fs.extra'); + +function createAsyncapiFile(generator) { + const asyncapi = generator.originalAsyncAPI; + const targetDir = generator.targetDir; + const customDirInTarget = generator.templateParams.asyncapiFileDir; + const getCustomFileLocation = (target, dir, filename) => { + xfs.mkdirpSync(path.resolve(target, dir)); + return path.resolve(target, dir, filename); + }; + let extension; + + try { + JSON.parse(asyncapi); + extension = 'json'; + } catch (e) { + extension = 'yaml'; + } + + const outputFileName = `asyncapi.${extension}`; + + const asyncapiOutputLocation = customDirInTarget + ? getCustomFileLocation(targetDir, customDirInTarget, outputFileName) + : path.resolve(targetDir, outputFileName); + + fs.writeFileSync(asyncapiOutputLocation, asyncapi); +} + +module.exports = { + 'generate:after': createAsyncapiFile +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 434b3c16e..d543520d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "packages/*" ], "devDependencies": { + "markdown-toc": "^1.2.0", "turbo": "1.13.3" }, "engines": { @@ -23,6 +24,7 @@ "version": "2.4.1", "license": "Apache-2.0", "dependencies": { + "@asyncapi/generator-hooks": "*", "@asyncapi/generator-react-sdk": "^1.1.2", "@asyncapi/multi-parser": "^2.1.1", "@asyncapi/nunjucks-filters": "*", @@ -107,6 +109,19 @@ "node": ">= 10.0.0" } }, + "apps/hooks": { + "name": "@asyncapi/generator-hooks", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "fs.extra": "^1.3.2" + }, + "devDependencies": { + "eslint": "^6.8.0", + "eslint-plugin-sonarjs": "^0.5.0", + "jsdoc-to-markdown": "^7.1.1" + } + }, "apps/nunjucks-filters": { "name": "@asyncapi/nunjucks-filters", "version": "2.1.0", @@ -207,6 +222,10 @@ "resolved": "apps/generator", "link": true }, + "node_modules/@asyncapi/generator-hooks": { + "resolved": "apps/hooks", + "link": true + }, "node_modules/@asyncapi/generator-react-sdk": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@asyncapi/generator-react-sdk/-/generator-react-sdk-1.1.2.tgz", @@ -10625,8 +10644,9 @@ }, "node_modules/markdown-toc": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", + "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", "dev": true, - "license": "MIT", "dependencies": { "concat-stream": "^1.5.2", "diacritics-map": "^0.1.0", diff --git a/package.json b/package.json index 5cfbbe02c..89ce46a6f 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "test": "turbo run test", "lint": "turbo lint", "lint:fix": "turbo run lint:fix", + "generate:assets": "turbo run generate:assets && npm run generate:readme:toc", + "generate:readme:toc": "markdown-toc -i README.md", "generator:test": "turbo run test --filter=@asyncapi/generator", "generator:test:dev": "turbo run test:dev", "generator:test:unit": "turbo run test:unit --filter=@asyncapi/generator", @@ -22,6 +24,7 @@ "nunjucks-filters:test": "turbo run test --filter=@asyncapi/nunjucks-filters" }, "devDependencies": { + "markdown-toc": "^1.2.0", "turbo": "1.13.3" }, "engines": { diff --git a/turbo.json b/turbo.json index 922009546..2725ce8e8 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,5 @@ { - "$schema": "https://turbo.build/schema.json", + "$schema": "https://turbo.build/schema.v1.json", "pipeline": { "build": { "outputs": [] @@ -9,16 +9,20 @@ "cache": false, "persistent": true }, - "test": { "cache": false }, - "test:unit": {"cache":false}, - "test:integration": {"cache": false}, - "test:cli": {}, - "test:cleanup": {}, - "docs": {}, - "docker:build": {}, - "lint": {}, - "lint:fix":{"cache": false}, - "lint:tpl:validator": {}, - "test:integration:update":{"cache": false} + "test": { "cache": false }, + "test:unit": {"cache":false}, + "test:integration": {"cache": false}, + "test:cli": {}, + "test:cleanup": {}, + "docs": {}, + "docker:build": {}, + "lint": {}, + "lint:fix":{"cache": false}, + "lint:tpl:validator": {}, + "test:integration:update":{"cache": false}, + "generate:assets": { + "dependsOn": [], + "outputs": ["assets/**"] + } } } \ No newline at end of file