Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add TypeScript support for hooks and filters #519

Merged
merged 15 commits into from
Mar 5, 2021
9 changes: 9 additions & 0 deletions docs/authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,12 @@ async function asyncCamelCase(str, callback) {
```

In case you have more than one template and want to reuse filters, you can put them in a single library. You can configure such a library in the template configuration under `filters` property. You can also use the official AsyncAPI [filters library](https://github.com/asyncapi/generator-filters). To learn how to add such filters to configuration [read more about the configuration file](#configuration-file).

## TypeScript support

The AsyncAPI generator has TypeScript support for [hooks](#hooks) and Nunjucks's [filters](#filters). Assumptions:

- Installing the `typescript` package and creating the` tsconfig.json` file isn't necessary.
- Source code of the hook/filter must have `.ts` extension.
- Each package related to the typings for TypeScript like `@types/node` must be installed in the template under `dependencies` array. This is because the Generator transpiles the TypeScript code on-the-fly while rendering the template, and cannot use packages under `devDependencies`.
- Each template should have installed `@types/node` package to support typings for Node.
magicmatatjahu marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 8 additions & 4 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ const {
getInvalidOptions,
isReactTemplate,
isJsFile,
registerSourceMap,
registerTypeScript,
} = require('./utils');
const { registerFilters } = require('./filtersRegistry');
const { registerHooks } = require('./hooksRegistry');

parser.registerSchemaParser(openapiSchemaParser);
parser.registerSchemaParser(ramlDtParser);
parser.registerSchemaParser(avroSchemaParser);

const FILTERS_DIRNAME = 'filters';
const HOOKS_DIRNAME = 'hooks';
const CONFIG_FILENAME = 'package.json';
Expand All @@ -56,6 +54,12 @@ const shouldIgnoreDir = dirPath =>
dirPath === '.git'
|| dirPath.startsWith(`.git${path.sep}`);

parser.registerSchemaParser(openapiSchemaParser);
parser.registerSchemaParser(ramlDtParser);
parser.registerSchemaParser(avroSchemaParser);
registerSourceMap();
registerTypeScript();

class Generator {
/**
* Instantiates a new Generator object.
Expand Down
5 changes: 5 additions & 0 deletions lib/hooksRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ async function registerConfigHooks(hooks, templateDir, templateConfig) {
* @param {Array} config List of hooks configured in template configuration
*/
function addHook(hooks, mod, config) {
// for ESM `export default {}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it mean, what esm has to do with this part?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example. If you write export for hooks in CommonJS, then you have:

module.exports = {
  'generate:before': (generator) => {
  }
};

But if you are using ESM in TS, you can write:

export default {
  'generate:before': (generator) => {
  }
}

Unfortunately, then it is transpiled to:

module.exports = {
  default: {
      'generate:before': (generator) => {
      }
  }
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, ok, you just made a comment to explain the if. Can you make it more descriptive 🙏🏼 I totally didn't map it in my head, probably because I use ESM very rarely. So please write this comment for people like me too 😄

if (typeof mod.default === 'object') {
mod = mod.default;
}

Object.keys(mod).forEach(hookType => {
const moduleHooksArray = [].concat(mod[hookType]);

Expand Down
19 changes: 19 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,22 @@ utils.getInvalidOptions = (generatorOptions, options) => {
utils.isAsyncFunction = (fn) => {
return fn && fn.constructor && fn.constructor.name === 'AsyncFunction';
};

/**
* Register `source-map-support` package.
derberg marked this conversation as resolved.
Show resolved Hide resolved
* This package provides source map support for stack traces in Node - also for transpiled code from TS.
*
* @private
*/
utils.registerSourceMap = () => {
require('source-map-support').install();
};

/**
* Register TypeScript transpiler. It enables transpilation of TS filters and hooks on the fly.
*
* @private
*/
utils.registerTypeScript = () => {
require('ts-node').register();
};
43 changes: 43 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@
"nunjucks": "^3.2.0",
"resolve-pkg": "^2.0.0",
"semver": "^7.3.2",
"simple-git": "^1.131.0"
"simple-git": "^1.131.0",
"source-map-support": "^0.5.19",
"ts-node": "^9.1.1",
"typescript": "^4.2.2"
},
"devDependencies": {
"@semantic-release/commit-analyzer": "^8.0.1",
Expand Down