├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── README.md
├── cli.js
├── lib
├── beautifier.js
├── codegen.js
├── generator.js
├── helpers
│ └── handlebars.js
└── parser.js
├── logo.ai
├── logo.png
├── package-lock.json
├── package.json
├── templates
├── .editorconfig
├── .eslintrc
├── .npmignore
├── Dockerfile
├── README.md
├── __.gitignore
├── config
│ └── common.yml
├── package.json
└── src
│ ├── api
│ ├── adapters
│ │ └── ___adapter.js
│ ├── index.js
│ ├── middlewares
│ │ ├── buffer2string.js
│ │ ├── logger.js
│ │ └── string2json.js
│ ├── routes
│ │ └── ___route.js
│ └── services
│ │ └── ___service.js
│ └── lib
│ └── config.js
└── tests
├── sample.yml
└── test.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig
2 |
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | indent_style = space
11 | indent_size = 2
12 |
13 | [Makefile]
14 | indent_style = tab
15 |
16 | [*.md]
17 | trim_trailing_whitespace = false
18 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asyncapi-archived-repos/node-codegen/8cb999827f135e5cb1ee1b1cfe6eac460c13b424/.eslintignore
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | parserOptions:
2 | ecmaVersion: 2017
3 |
4 | env:
5 | node: true
6 | es6: true
7 |
8 | ecmaFeatures:
9 | forOf: true
10 |
11 | rules:
12 | # Ignore Rules
13 | strict: 0
14 | no-underscore-dangle: 0
15 | no-mixed-requires: 0
16 | no-process-exit: 0
17 | no-warning-comments: 0
18 | curly: 0
19 | no-multi-spaces: 0
20 | no-alert: 0
21 | consistent-return: 0
22 | consistent-this: [0, self]
23 | func-style: 0
24 | max-nested-callbacks: 0
25 |
26 | # Warnings
27 | no-debugger: 1
28 | no-empty: 1
29 | no-invalid-regexp: 1
30 | no-unused-expressions: 1
31 | no-native-reassign: 1
32 | no-fallthrough: 1
33 | camelcase: 0
34 |
35 | # Errors
36 | eqeqeq: 2
37 | no-undef: 2
38 | no-dupe-keys: 2
39 | no-empty-character-class: 2
40 | no-self-compare: 2
41 | valid-typeof: 2
42 | no-unused-vars: [2, { "args": "none" }]
43 | handle-callback-err: 2
44 | no-shadow-restricted-names: 2
45 | no-new-require: 2
46 | no-mixed-spaces-and-tabs: 2
47 | block-scoped-var: 2
48 | no-else-return: 2
49 | no-throw-literal: 2
50 | no-void: 2
51 | radix: 2
52 | wrap-iife: [2, outside]
53 | no-shadow: 0
54 | no-use-before-define: [2, nofunc]
55 | no-path-concat: 2
56 | valid-jsdoc: [0, {requireReturn: false, requireParamDescription: false, requireReturnDescription: false}]
57 |
58 | # stylistic errors
59 | no-spaced-func: 2
60 | semi-spacing: 2
61 | quotes: [2, 'single']
62 | key-spacing: [2, { beforeColon: false, afterColon: true }]
63 | indent: [2, 2]
64 | no-lonely-if: 2
65 | no-floating-decimal: 2
66 | brace-style: [2, 1tbs, { allowSingleLine: true }]
67 | comma-style: [2, last]
68 | new-cap: [2, {capIsNewExceptions: ["express.Router"]}]
69 | no-multiple-empty-lines: [2, {max: 1}]
70 | no-nested-ternary: 2
71 | operator-assignment: [2, always]
72 | padded-blocks: [2, never]
73 | quote-props: [2, as-needed]
74 | space-before-function-paren: [2, always]
75 | keyword-spacing: [2, {'before': true, 'after': true, 'overrides': {}}]
76 | space-before-blocks: [2, always]
77 | array-bracket-spacing: [2, never]
78 | computed-property-spacing: [2, never]
79 | space-in-parens: [2, never]
80 | space-unary-ops: [2, {words: true, nonwords: false}]
81 | wrap-regex: 2
82 | linebreak-style: [2, unix]
83 | semi: [2, always]
84 | arrow-spacing: [2, {before: true, after: true}]
85 | no-class-assign: 2
86 | no-const-assign: 2
87 | no-dupe-class-members: 2
88 | no-this-before-super: 2
89 | no-var: 2
90 | object-shorthand: [2, always]
91 | prefer-arrow-callback: 2
92 | prefer-const: 2
93 | prefer-spread: 2
94 | prefer-template: 2
95 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | npm-debug.log
4 | tests/generated
5 | tests/generated-yaml
6 | tests/generated-json
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test/
2 | .DS_Store
3 | *.swp
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | script:
5 | - npm install
6 | - npm run test
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecation notice
2 |
3 | This package is deprecated and maintanance has been stopped in favor of the Node.js template at [github.com/asyncapi/generator](https://github.com/asyncapi/generator).
4 |
5 | ---
6 |
7 |

8 |
9 | AsyncAPI Node.js
Code Generator
10 |
11 |
12 | Use your AsyncAPI definition to
generate the code for your API.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | The generated code features:
21 |
22 | - Default Node.js template, featuring:
23 | * ES7
24 | * ESLint
25 | * YAML config file
26 | * Hermes
27 | * No transpiling
28 | - Custom templates. Check `--templates` option in the [Usage section](#usage). Kudos to [@jantoniucci](https://github.com/jantoniucci).
29 |
30 | ## Install
31 |
32 | To use it from the CLI:
33 |
34 | ```bash
35 | npm install -g asyncapi-node-codegen
36 | ```
37 |
38 | To use it as a module in your project:
39 |
40 | ```bash
41 | npm install --save asyncapi-node-codegen
42 | ```
43 |
44 | ## Usage
45 |
46 | ### From the command-line interface (CLI)
47 |
48 | ```bash
49 | Usage: anc [options]
50 |
51 |
52 | Options:
53 |
54 | -V, --version output the version number
55 | -o, --output directory where to put the generated files (defaults to current directory)
56 | -t, --templates directory where templates are located (defaults to internal nodejs templates)
57 | -h, --help output usage information
58 | ```
59 |
60 | #### Examples
61 |
62 | The shortest possible syntax:
63 | ```bash
64 | anc asyncapi.yaml
65 | ```
66 |
67 | Specify where to put the generated code:
68 | ```bash
69 | anc asyncapi.yaml -o ./my-api
70 | ```
71 |
72 | Specify where to find the code templates:
73 | ```bash
74 | anc asyncapi.yaml -t ../my-specific-templates-dir -o ./my-api
75 | ```
76 |
77 | ### As a module in your project
78 |
79 | ```js
80 | const path = require('path');
81 | const codegen = require('asyncapi-node-codegen');
82 | const asyncapi = '/path/to/asyncapi.yaml'; // Or a path to a JSON file
83 |
84 | codegen.process(asyncapi, path.resolve(__dirname, './my-api')).then(() => {
85 | console.log('Done!');
86 | }).catch(err => {
87 | console.error(`Something went wrong: ${err.message}`);
88 | });
89 | ```
90 |
91 | #### Using async/await
92 |
93 | The function `codegen.process` returns a Promise, so it means you can use async/await:
94 |
95 | ```js
96 | const path = require('path');
97 | const codegen = require('asyncapi-node-codegen');
98 | const asyncapi = '/path/to/asyncapi.yaml'; // Or a path to a JSON file
99 |
100 | try {
101 | await codegen.process(asyncapi, path.resolve(__dirname, './my-api'));
102 | console.log('Done!');
103 | } catch (err) {
104 | console.error(`Something went wrong: ${err.message}`);
105 | }
106 | ```
107 |
108 | ## Author
109 |
110 | Fran Méndez ([@fmvilas](http://twitter.com/fmvilas))
111 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const path = require('path');
4 | const program = require('commander');
5 | const mkdirp = require('mkdirp');
6 | const packageInfo = require('./package.json');
7 | const generate = require('./lib/codegen').process;
8 |
9 | const red = text => `\x1b[31m${text}\x1b[0m`;
10 | const magenta = text => `\x1b[35m${text}\x1b[0m`;
11 | const yellow = text => `\x1b[33m${text}\x1b[0m`;
12 | const green = text => `\x1b[32m${text}\x1b[0m`;
13 |
14 | let asyncAPIFile;
15 |
16 | const parseOutput = dir => path.resolve(dir);
17 |
18 | const showError = err => {
19 | console.error(red('Something went wrong:'));
20 | console.error(red(err.stack || err.message));
21 | };
22 |
23 | program
24 | .version(packageInfo.version)
25 | .arguments('')
26 | .action((asyncAPIFilePath) => {
27 | asyncAPIFile = path.resolve(asyncAPIFilePath);
28 | })
29 | .option('-o, --output ', 'directory where to put the generated files (defaults to current directory)', parseOutput, process.cwd())
30 | .option('-t, --templates ', 'directory where templates are located (defaults to internal nodejs templates)')
31 | .parse(process.argv);
32 |
33 | if (!asyncAPIFile) {
34 | console.error(red('> Path to AsyncAPI file not provided.'));
35 | program.help(); // This exits the process
36 | }
37 |
38 | mkdirp(program.output, err => {
39 | if (err) return showError(err);
40 |
41 | generate(asyncAPIFile, program.output, program.templates).then(() => {
42 | console.log(green('Done! ✨'));
43 | console.log(yellow('Check out your shiny new API at ') + magenta(program.output) + yellow('.'));
44 | }).catch(showError);
45 | });
46 |
47 | process.on('unhandledRejection', showError);
48 |
--------------------------------------------------------------------------------
/lib/beautifier.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const wrap = require('word-wrap');
3 | const topicParser = require('asyncapi-topic-parser');
4 |
5 | const getOperationId = (parsedTopic) => {
6 | const name = `${parsedTopic.resources.join('-')}-${parsedTopic.operation}`;
7 | const id = parsedTopic.status ? `${name}-${parsedTopic.status}` : name;
8 | return _.camelCase(parsedTopic.type === 'event' ? `on-${id}` : id);
9 | };
10 |
11 | const sharedStart = (array) => {
12 | const A = array.concat().sort();
13 | const a1 = A[0], a2= A[A.length-1], L= a1.length;
14 | let i = 0;
15 | while (i {
20 | return `\x1b[33m${text}\x1b[0m`;
21 | };
22 |
23 | module.exports = (asyncapi) => {
24 | const services = [];
25 | const usedOperations = [];
26 | asyncapi.baseTopic = asyncapi.baseTopic || '';
27 | asyncapi.__descriptionInOneLine = (asyncapi.info.description || '').replace(/\n/g, ' ');
28 |
29 | asyncapi.__schemes = {};
30 |
31 | _.each(asyncapi.servers || [], server => {
32 | asyncapi.__schemes[server.scheme.toLowerCase()] = 1;
33 | });
34 |
35 | asyncapi.__schemes = Object.keys(asyncapi.__schemes);
36 |
37 | _.each(asyncapi.topics, (topic, topicName) => {
38 | const baseTopic = asyncapi.baseTopic.trim();
39 |
40 | let newTopicName = `${baseTopic}.${topicName}`;
41 | newTopicName = newTopicName.replace(/\{(.*)\}/, ':$1');
42 |
43 | if (newTopicName !== topicName) {
44 | asyncapi.topics[newTopicName] = topic;
45 | delete asyncapi.topics[topicName];
46 | topicName = newTopicName;
47 | }
48 |
49 | const parsedTopic = topicParser.parse(topicName);
50 |
51 | _.each(topic, (operation, operationName) => {
52 | if (!usedOperations.includes(operationName)) usedOperations.push(operationName);
53 | operation['operationId'] = operation['operationId'] || getOperationId(parsedTopic);
54 | const description = operation['summary'] || operation['description'] || '';
55 | const descriptionLines = description ? wrap(description, { width: 60, indent: '' }).split(/\n/) : [];
56 | operation['descriptionLines'] = descriptionLines;
57 | });
58 |
59 | topic['serviceName'] = parsedTopic.service;
60 | if (!services.includes(parsedTopic.service)) services.push(parsedTopic.service);
61 |
62 | if (topic.publish && topic.subscribe) {
63 | console.log(yellow('WARNING:'), `Topic ${topicName} is using publish and subscribe operations. It means that you'll receive the messages you publish. Please double-check this is the behaviour you want achieve.`);
64 | }
65 | });
66 |
67 | asyncapi.__usedOperations = usedOperations;
68 | asyncapi.__services = services;
69 |
70 | const commonPrefix = sharedStart(Object.keys(asyncapi.topics));
71 | const levels = commonPrefix.split('.').length - 1;
72 | asyncapi.__commonPrefix = commonPrefix.split('.').slice(0, levels).join('.');
73 | asyncapi.__commonTopic = `${asyncapi.__commonPrefix}.#`;
74 |
75 | return asyncapi;
76 | };
77 |
--------------------------------------------------------------------------------
/lib/codegen.js:
--------------------------------------------------------------------------------
1 | const parse = require('./parser');
2 | const generate = require('./generator');
3 | const beautify = require('./beautifier');
4 | const codegen = module.exports = {};
5 |
6 | codegen.process = async (filePath, targetDir, customTemplateDir) => {
7 | const json = await parse(filePath);
8 | const asyncapi = beautify(json);
9 | const templatesDir = customTemplateDir ? '../' + customTemplateDir : '../templates';
10 |
11 | await generate.package(asyncapi, targetDir, templatesDir);
12 | await generate.APIindex(asyncapi, targetDir, templatesDir);
13 | await generate.adapters(asyncapi, targetDir, templatesDir);
14 | await generate.topics(asyncapi, targetDir, templatesDir);
15 | await generate.config(asyncapi, targetDir, templatesDir);
16 | await generate.readme(asyncapi, targetDir, templatesDir);
17 | await generate.static(targetDir, templatesDir);
18 |
19 | };
20 |
--------------------------------------------------------------------------------
/lib/generator.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const util = require('util');
4 | const Handlebars = require('handlebars');
5 | const helpers = require('./helpers/handlebars');
6 | const _ = require('lodash');
7 | const mkdirp = require('mkdirp');
8 | const generate = module.exports = {};
9 |
10 | generate.package = async (asyncapi, targetDir, templatesDir) => {
11 | fs.readFile(path.resolve(__dirname, `${templatesDir}/package.json`), 'utf8', (err, data) => {
12 | if (err) throw err;
13 |
14 | const targetFile = path.resolve(targetDir, 'package.json');
15 | const template = Handlebars.compile(data.toString());
16 | const content = template({ asyncapi });
17 |
18 | mkdirp(path.dirname(targetFile), err => {
19 | if (err) return console.error(err);
20 |
21 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
22 | if (err) throw err;
23 | });
24 | });
25 | });
26 | };
27 |
28 | generate.APIindex = async (asyncapi, targetDir, templatesDir) => {
29 | fs.readFile(path.resolve(__dirname, `${templatesDir}/src/api/index.js`), 'utf8', (err, data) => {
30 | if (err) throw err;
31 |
32 | const targetFile = path.resolve(targetDir, 'src/api/', 'index.js');
33 | const template = Handlebars.compile(data.toString());
34 | const content = template({ asyncapi });
35 |
36 | mkdirp(path.dirname(targetFile), err => {
37 | if (err) return console.error(err);
38 |
39 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
40 | if (err) throw err;
41 | });
42 | });
43 | });
44 | };
45 |
46 | generate.adapters = async (asyncapi, targetDir, templatesDir) => {
47 | fs.readFile(path.resolve(__dirname, `${templatesDir}/src/api/adapters/___adapter.js`), 'utf8', (err, data) => {
48 | if (err) throw err;
49 |
50 | asyncapi.__schemes.map(adapter => {
51 | const targetFile = path.resolve(targetDir, 'src/api/adapters/', `${adapter}.js`);
52 | const template = Handlebars.compile(data.toString());
53 | const content = template({ asyncapi, adapter });
54 |
55 | mkdirp(path.dirname(targetFile), err => {
56 | if (err) return console.error(err);
57 |
58 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
59 | if (err) throw err;
60 | });
61 | });
62 | });
63 | });
64 | };
65 |
66 | generate.topics = async (asyncapi, targetDir, templatesDir) => {
67 | await generate.routes({asyncapi, targetDir, templatesDir});
68 | await generate.services({asyncapi, targetDir, templatesDir});
69 | };
70 |
71 | generate.routes = async ({asyncapi, targetDir, templatesDir}) => {
72 | fs.readFile(path.resolve(__dirname, `${templatesDir}/src/api/routes/___route.js`), 'utf8', (err, data) => {
73 | if (err) throw err;
74 |
75 | asyncapi.__services.map(service => {
76 | const targetFile = path.resolve(targetDir, 'src/api/routes/', `${service}.js`);
77 | const template = Handlebars.compile(data.toString());
78 | const content = template({ asyncapi, service });
79 |
80 | mkdirp(path.dirname(targetFile), err => {
81 | if (err) return console.error(err);
82 |
83 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
84 | if (err) throw err;
85 | });
86 | });
87 | });
88 | });
89 | };
90 |
91 | generate.services = async ({asyncapi, targetDir, templatesDir}) => {
92 | fs.readFile(path.resolve(__dirname, `${templatesDir}/src/api/services/___service.js`), 'utf8', (err, data) => {
93 | if (err) throw err;
94 |
95 | asyncapi.__services.map(service => {
96 | const targetFile = path.resolve(targetDir, 'src/api/services/', `${service}.js`);
97 | const template = Handlebars.compile(data.toString());
98 | const content = template({ asyncapi, service });
99 |
100 | mkdirp(path.dirname(targetFile), err => {
101 | if (err) return console.error(err);
102 |
103 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
104 | if (err) throw err;
105 | });
106 | });
107 | });
108 | });
109 | };
110 |
111 | generate.config = async (asyncapi, targetDir, templatesDir) => {
112 | fs.readFile(path.resolve(__dirname, `${templatesDir}/config/common.yml`), 'utf8', (err, data) => {
113 | if (err) throw err;
114 |
115 | const targetFile = path.resolve(targetDir, 'config/common.yml');
116 | const template = Handlebars.compile(data.toString());
117 | const content = template({ asyncapi });
118 |
119 | mkdirp(path.dirname(targetFile), err => {
120 | if (err) return console.error(err);
121 |
122 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
123 | if (err) throw err;
124 | });
125 | });
126 | });
127 | };
128 |
129 | generate.readme = async (asyncapi, targetDir, templatesDir) => {
130 | fs.readFile(path.resolve(__dirname, `${templatesDir}/README.md`), 'utf8', (err, data) => {
131 | if (err) throw err;
132 |
133 | const targetFile = path.resolve(targetDir, 'README.md');
134 | const template = Handlebars.compile(data.toString());
135 | const content = template({ asyncapi });
136 |
137 | mkdirp(path.dirname(targetFile), err => {
138 | if (err) return console.error(err);
139 |
140 | fs.writeFile(targetFile, content, { encoding: 'utf8', flag: 'w' }, (err) => {
141 | if (err) throw err;
142 | });
143 | });
144 | });
145 | };
146 |
147 | generate.static = async (targetDir, templatesDir) => {
148 | const files = [
149 | '.editorconfig',
150 | '.eslintrc',
151 | '__.gitignore',
152 | 'Dockerfile',
153 | 'src/lib/config.js',
154 | 'src/api/middlewares/string2json.js',
155 | 'src/api/middlewares/logger.js',
156 | 'src/api/middlewares/buffer2string.js'
157 | ];
158 |
159 | files.map(file => {
160 | const targetFile = path.resolve(targetDir, file.substr(0, 2) === '__' ? file.substr(2) : file);
161 |
162 | mkdirp(path.dirname(targetFile), err => {
163 | if (err) return console.error(err);
164 |
165 | fs.createReadStream(path.resolve(__dirname, `${templatesDir}/`, file))
166 | .pipe(fs.createWriteStream(targetFile));
167 | });
168 | });
169 | };
170 |
--------------------------------------------------------------------------------
/lib/helpers/handlebars.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const Handlebars = require('handlebars');
3 |
4 | Handlebars.registerHelper('equal', (lvalue, rvalue, options) => {
5 | if (arguments.length < 3)
6 | throw new Error('Handlebars Helper equal needs 2 parameters');
7 | if (lvalue!==rvalue) {
8 | return options.inverse(this);
9 | }
10 |
11 | return options.fn(this);
12 | });
13 |
14 | Handlebars.registerHelper('validOperation', (operation, options) => {
15 | const authorized_operations = ['PUBLISH', 'SUBSCRIBE'];
16 |
17 | if (arguments.length < 3)
18 | throw new Error('Handlebars Helper validOperation needs 1 parameter');
19 | if (!authorized_operations.includes(operation.toUpperCase())) {
20 | return options.inverse(this);
21 | }
22 |
23 | return options.fn(this);
24 | });
25 |
26 | Handlebars.registerHelper('match', (lvalue, rvalue, options) => {
27 | if (arguments.length < 3)
28 | throw new Error('Handlebars Helper match needs 2 parameters');
29 | if (!lvalue.match(rvalue)) {
30 | return options.inverse(this);
31 | }
32 |
33 | return options.fn(this);
34 | });
35 |
36 | Handlebars.registerHelper('compare', (lvalue, operator, rvalue, options) => {
37 | if (arguments.length < 4) throw new Error('Handlerbars Helper "compare" needs 3 parameters');
38 |
39 | const operators = {
40 | '==': (l,r) => { return l == r; },
41 | '===': (l,r) => { return l === r; },
42 | '!=': (l,r) => { return l != r; },
43 | '<': (l,r) => { return l < r; },
44 | '>': (l,r) => { return l > r; },
45 | '<=': (l,r) => { return l <= r; },
46 | '>=': (l,r) => { return l >= r; },
47 | typeof: (l,r) => { return typeof l === r; }
48 | };
49 |
50 | if (!operators[operator]) throw new Error(`Handlerbars Helper 'compare' doesn't know the operator ${operator}`);
51 | const result = operators[operator](lvalue,rvalue);
52 |
53 | if (result) return options.fn(this);
54 |
55 | return options.inverse(this);
56 | });
57 |
58 | Handlebars.registerHelper('capitalize', (str) => {
59 | return _.capitalize(str);
60 | });
61 |
62 | Handlebars.registerHelper('toMQTT', (str) => {
63 | return String(str).split('.').join('/');
64 | });
65 |
66 | Handlebars.registerHelper('snakecase', (str) => {
67 | return _.snakeCase(String(str).toLowerCase());
68 | });
69 |
70 | Handlebars.registerHelper('kebabcase', (str) => {
71 | return _.kebabCase(String(str).toLowerCase());
72 | });
73 |
74 | Handlebars.registerHelper('queueName', (title, version) => {
75 | return _.kebabCase(`${title}-${version}`.toLowerCase()).split('-').join('.');
76 | });
77 |
78 | Handlebars.registerHelper('shouldSubscribe', (asyncapi) => {
79 | return asyncapi.__usedOperations.includes('subscribe');
80 | });
81 |
82 | Handlebars.registerHelper('docline', (field, fieldName, scopePropName) => {
83 | const buildLine = (f, fName, pName) => {
84 | const type = f.type ? _.capitalize(f.type) : 'String';
85 | const description = f.description ? ` - ${f.description.replace(/\r?\n|\r/g, '')}` : '';
86 | let def = f.default;
87 |
88 | if (def && type === 'String') def = `'${def}'`;
89 |
90 | let line;
91 | if (def !== undefined) {
92 | line = ` * @param {${type}} [${pName ? `${pName}.` : ''}${fName}=${def}]`;
93 | } else {
94 | line = ` * @param {${type}} ${pName ? `${pName}.` : ''}${fName}`;
95 | }
96 |
97 | if (type === 'Object') {
98 | let lines = `${line}\n`;
99 | let first = true;
100 | for (const propName in f.properties) {
101 | lines = `${lines}${first ? '' : '\n'}${buildLine(f.properties[propName], propName, `${pName ? `${pName}.` : ''}${fName}`)}`;
102 | first = false;
103 | }
104 | return lines;
105 | }
106 |
107 | return `${line}${description}`;
108 | };
109 |
110 | return buildLine(field, fieldName, scopePropName);
111 | });
112 |
113 | Handlebars.registerHelper('dotsToSlashes', (topicName) => {
114 | return topicName.replace(/\./g, '/');
115 | });
116 |
--------------------------------------------------------------------------------
/lib/parser.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const ZSchema = require('z-schema');
4 | const YAML = require('js-yaml');
5 | const RefParser = require('json-schema-ref-parser');
6 | const asyncAPIschema = require('asyncapi');
7 |
8 | const validator = new ZSchema();
9 |
10 | async function getFileContent (filePath) {
11 | return new Promise((resolve, reject) => {
12 | fs.readFile(path.resolve(__dirname, filePath), (err, content) => {
13 | if (err) return reject(err);
14 | resolve(content);
15 | });
16 | });
17 | }
18 |
19 | function parseContent (content) {
20 | content = content.toString('utf8');
21 | try {
22 | return JSON.parse(content);
23 | } catch (e) {
24 | return YAML.safeLoad(content);
25 | }
26 | }
27 |
28 | async function dereference (json) {
29 | return RefParser.dereference(json, {
30 | dereference: {
31 | circular: 'ignore'
32 | }
33 | });
34 | }
35 |
36 | async function bundle (json) {
37 | return RefParser.bundle(json, {
38 | dereference: {
39 | circular: 'ignore'
40 | }
41 | });
42 | }
43 |
44 | async function validate (json, schema) {
45 | return new Promise((resolve, reject) => {
46 | validator.validate(json, schema, (err, valid) => {
47 | if (err) return reject(err);
48 | return resolve(json);
49 | });
50 | });
51 | }
52 |
53 | async function parse (filePath) {
54 | let content, parsedContent, dereferencedJSON, bundledJSON, parsed;
55 |
56 | try {
57 | content = await getFileContent(filePath);
58 | } catch (e) {
59 | console.error('Can not load the content of the AsyncAPI specification file');
60 | console.error(e);
61 | return;
62 | }
63 |
64 | try {
65 | parsedContent = parseContent(content);
66 | } catch (e) {
67 | console.error('Can not parse the content of the AsyncAPI specification file');
68 | console.error(e);
69 | return;
70 | }
71 |
72 | try {
73 | dereferencedJSON = await dereference(parsedContent);
74 | } catch (e) {
75 | console.error('Can not dereference the JSON obtained from the content of the AsyncAPI specification file');
76 | console.error(e);
77 | return;
78 | }
79 |
80 | try {
81 | bundledJSON = await bundle(dereferencedJSON);
82 | } catch (e) {
83 | console.error('Can not bundle the JSON obtained from the content of the AsyncAPI specification file');
84 | console.error(e);
85 | return;
86 | }
87 |
88 | try {
89 | parsed = await validate(bundledJSON, asyncAPIschema);
90 | } catch (e) {
91 | console.error('Invalid JSON obtained from the content of the AsyncAPI specification file');
92 | console.error(e);
93 | return;
94 | }
95 |
96 | return JSON.parse(JSON.stringify(parsed));
97 | };
98 |
99 | module.exports = parse;
100 |
--------------------------------------------------------------------------------
/logo.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asyncapi-archived-repos/node-codegen/8cb999827f135e5cb1ee1b1cfe6eac460c13b424/logo.ai
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asyncapi-archived-repos/node-codegen/8cb999827f135e5cb1ee1b1cfe6eac460c13b424/logo.png
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "asyncapi-node-codegen",
3 | "version": "1.3.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "align-text": {
8 | "version": "0.1.4",
9 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
10 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
11 | "requires": {
12 | "kind-of": "^3.0.2",
13 | "longest": "^1.0.1",
14 | "repeat-string": "^1.5.2"
15 | }
16 | },
17 | "amdefine": {
18 | "version": "1.0.1",
19 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
20 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
21 | },
22 | "ansi-regex": {
23 | "version": "2.1.1",
24 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
25 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
26 | },
27 | "argparse": {
28 | "version": "1.0.9",
29 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
30 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
31 | "requires": {
32 | "sprintf-js": "~1.0.2"
33 | }
34 | },
35 | "async": {
36 | "version": "1.5.2",
37 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
38 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
39 | },
40 | "asyncapi": {
41 | "version": "1.0.0",
42 | "resolved": "https://registry.npmjs.org/asyncapi/-/asyncapi-1.0.0.tgz",
43 | "integrity": "sha1-GuhdCDxfO8ZII/8dUflmVrEgIGQ=",
44 | "requires": {
45 | "js-yaml": "^3.7.0",
46 | "json-schema-ref-parser": "^3.1.2",
47 | "load-json-file": "^2.0.0",
48 | "mdv": "^1.0.5",
49 | "z-schema": "^3.18.2"
50 | }
51 | },
52 | "asyncapi-topic-parser": {
53 | "version": "0.2.0",
54 | "resolved": "https://registry.npmjs.org/asyncapi-topic-parser/-/asyncapi-topic-parser-0.2.0.tgz",
55 | "integrity": "sha1-ldVoe0bsndhIdIk8wNGhfPaDU34="
56 | },
57 | "boolbase": {
58 | "version": "1.0.0",
59 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
60 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
61 | },
62 | "buffer-shims": {
63 | "version": "1.0.0",
64 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
65 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
66 | },
67 | "builtin-modules": {
68 | "version": "1.1.1",
69 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
70 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
71 | },
72 | "call-me-maybe": {
73 | "version": "1.0.1",
74 | "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
75 | "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms="
76 | },
77 | "camelcase": {
78 | "version": "3.0.0",
79 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
80 | "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
81 | },
82 | "center-align": {
83 | "version": "0.1.3",
84 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
85 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
86 | "optional": true,
87 | "requires": {
88 | "align-text": "^0.1.3",
89 | "lazy-cache": "^1.0.3"
90 | }
91 | },
92 | "cheerio": {
93 | "version": "0.22.0",
94 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
95 | "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=",
96 | "requires": {
97 | "css-select": "~1.2.0",
98 | "dom-serializer": "~0.1.0",
99 | "entities": "~1.1.1",
100 | "htmlparser2": "^3.9.1",
101 | "lodash.assignin": "^4.0.9",
102 | "lodash.bind": "^4.1.4",
103 | "lodash.defaults": "^4.0.1",
104 | "lodash.filter": "^4.4.0",
105 | "lodash.flatten": "^4.2.0",
106 | "lodash.foreach": "^4.3.0",
107 | "lodash.map": "^4.4.0",
108 | "lodash.merge": "^4.4.0",
109 | "lodash.pick": "^4.2.1",
110 | "lodash.reduce": "^4.4.0",
111 | "lodash.reject": "^4.4.0",
112 | "lodash.some": "^4.4.0"
113 | }
114 | },
115 | "cliui": {
116 | "version": "3.2.0",
117 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
118 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
119 | "requires": {
120 | "string-width": "^1.0.1",
121 | "strip-ansi": "^3.0.1",
122 | "wrap-ansi": "^2.0.0"
123 | }
124 | },
125 | "code-point-at": {
126 | "version": "1.1.0",
127 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
128 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
129 | },
130 | "commander": {
131 | "version": "2.12.2",
132 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz",
133 | "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA=="
134 | },
135 | "core-util-is": {
136 | "version": "1.0.2",
137 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
138 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
139 | },
140 | "css-select": {
141 | "version": "1.2.0",
142 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
143 | "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
144 | "requires": {
145 | "boolbase": "~1.0.0",
146 | "css-what": "2.1",
147 | "domutils": "1.5.1",
148 | "nth-check": "~1.0.1"
149 | }
150 | },
151 | "css-what": {
152 | "version": "2.1.0",
153 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
154 | "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0="
155 | },
156 | "debug": {
157 | "version": "2.6.8",
158 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
159 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
160 | "requires": {
161 | "ms": "2.0.0"
162 | }
163 | },
164 | "decamelize": {
165 | "version": "1.2.0",
166 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
167 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
168 | },
169 | "dom-serializer": {
170 | "version": "0.1.0",
171 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
172 | "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
173 | "requires": {
174 | "domelementtype": "~1.1.1",
175 | "entities": "~1.1.1"
176 | },
177 | "dependencies": {
178 | "domelementtype": {
179 | "version": "1.1.3",
180 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
181 | "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
182 | }
183 | }
184 | },
185 | "domelementtype": {
186 | "version": "1.3.0",
187 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
188 | "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
189 | },
190 | "domhandler": {
191 | "version": "2.4.1",
192 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz",
193 | "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=",
194 | "requires": {
195 | "domelementtype": "1"
196 | }
197 | },
198 | "domutils": {
199 | "version": "1.5.1",
200 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
201 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
202 | "requires": {
203 | "dom-serializer": "0",
204 | "domelementtype": "1"
205 | }
206 | },
207 | "entities": {
208 | "version": "1.1.1",
209 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
210 | "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
211 | },
212 | "error-ex": {
213 | "version": "1.3.1",
214 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
215 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
216 | "requires": {
217 | "is-arrayish": "^0.2.1"
218 | }
219 | },
220 | "es6-promise": {
221 | "version": "3.3.1",
222 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
223 | "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
224 | },
225 | "esprima": {
226 | "version": "3.1.3",
227 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
228 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
229 | },
230 | "find-up": {
231 | "version": "1.1.2",
232 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
233 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
234 | "requires": {
235 | "path-exists": "^2.0.0",
236 | "pinkie-promise": "^2.0.0"
237 | }
238 | },
239 | "get-caller-file": {
240 | "version": "1.0.2",
241 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
242 | "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U="
243 | },
244 | "graceful-fs": {
245 | "version": "4.1.11",
246 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
247 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
248 | },
249 | "handlebars": {
250 | "version": "4.0.8",
251 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.8.tgz",
252 | "integrity": "sha1-Irh1zT8ObL6jAxTxROgrx6cv9CA=",
253 | "requires": {
254 | "async": "^1.4.0",
255 | "optimist": "^0.6.1",
256 | "source-map": "^0.4.4",
257 | "uglify-js": "^2.6"
258 | }
259 | },
260 | "hosted-git-info": {
261 | "version": "2.4.2",
262 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz",
263 | "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc="
264 | },
265 | "htmlparser2": {
266 | "version": "3.9.2",
267 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
268 | "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=",
269 | "requires": {
270 | "domelementtype": "^1.3.0",
271 | "domhandler": "^2.3.0",
272 | "domutils": "^1.5.1",
273 | "entities": "^1.1.1",
274 | "inherits": "^2.0.1",
275 | "readable-stream": "^2.0.2"
276 | }
277 | },
278 | "inherits": {
279 | "version": "2.0.3",
280 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
281 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
282 | },
283 | "invert-kv": {
284 | "version": "1.0.0",
285 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
286 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
287 | },
288 | "is-arrayish": {
289 | "version": "0.2.1",
290 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
291 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
292 | },
293 | "is-buffer": {
294 | "version": "1.1.5",
295 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
296 | "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw="
297 | },
298 | "is-builtin-module": {
299 | "version": "1.0.0",
300 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
301 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
302 | "requires": {
303 | "builtin-modules": "^1.0.0"
304 | }
305 | },
306 | "is-fullwidth-code-point": {
307 | "version": "1.0.0",
308 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
309 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
310 | "requires": {
311 | "number-is-nan": "^1.0.0"
312 | }
313 | },
314 | "is-utf8": {
315 | "version": "0.2.1",
316 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
317 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
318 | },
319 | "isarray": {
320 | "version": "1.0.0",
321 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
322 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
323 | },
324 | "js-yaml": {
325 | "version": "3.8.4",
326 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz",
327 | "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=",
328 | "requires": {
329 | "argparse": "^1.0.7",
330 | "esprima": "^3.1.1"
331 | }
332 | },
333 | "json-schema-ref-parser": {
334 | "version": "3.1.2",
335 | "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-3.1.2.tgz",
336 | "integrity": "sha1-o47Ld3T4fzLn65cj1ZITkOdqmkI=",
337 | "requires": {
338 | "call-me-maybe": "^1.0.1",
339 | "debug": "^2.2.0",
340 | "es6-promise": "^3.1.2",
341 | "js-yaml": "^3.6.0",
342 | "ono": "^2.2.1",
343 | "z-schema": "^3.17.0"
344 | }
345 | },
346 | "kind-of": {
347 | "version": "3.2.2",
348 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
349 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
350 | "requires": {
351 | "is-buffer": "^1.1.5"
352 | }
353 | },
354 | "lazy-cache": {
355 | "version": "1.0.4",
356 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
357 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
358 | "optional": true
359 | },
360 | "lcid": {
361 | "version": "1.0.0",
362 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
363 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
364 | "requires": {
365 | "invert-kv": "^1.0.0"
366 | }
367 | },
368 | "linkify-it": {
369 | "version": "2.0.3",
370 | "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
371 | "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=",
372 | "requires": {
373 | "uc.micro": "^1.0.1"
374 | }
375 | },
376 | "load-json-file": {
377 | "version": "2.0.0",
378 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
379 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
380 | "requires": {
381 | "graceful-fs": "^4.1.2",
382 | "parse-json": "^2.2.0",
383 | "pify": "^2.0.0",
384 | "strip-bom": "^3.0.0"
385 | }
386 | },
387 | "lodash": {
388 | "version": "4.17.4",
389 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
390 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
391 | },
392 | "lodash.assignin": {
393 | "version": "4.2.0",
394 | "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
395 | "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI="
396 | },
397 | "lodash.bind": {
398 | "version": "4.2.1",
399 | "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
400 | "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
401 | },
402 | "lodash.defaults": {
403 | "version": "4.2.0",
404 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
405 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
406 | },
407 | "lodash.filter": {
408 | "version": "4.6.0",
409 | "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
410 | "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4="
411 | },
412 | "lodash.flatten": {
413 | "version": "4.4.0",
414 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
415 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
416 | },
417 | "lodash.foreach": {
418 | "version": "4.5.0",
419 | "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
420 | "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
421 | },
422 | "lodash.get": {
423 | "version": "4.4.2",
424 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
425 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
426 | },
427 | "lodash.isequal": {
428 | "version": "4.5.0",
429 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
430 | "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
431 | },
432 | "lodash.map": {
433 | "version": "4.6.0",
434 | "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
435 | "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="
436 | },
437 | "lodash.merge": {
438 | "version": "4.6.0",
439 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz",
440 | "integrity": "sha1-aYhLoUSsM/5plzemCG3v+t0PicU="
441 | },
442 | "lodash.pick": {
443 | "version": "4.4.0",
444 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
445 | "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
446 | },
447 | "lodash.reduce": {
448 | "version": "4.6.0",
449 | "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
450 | "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
451 | },
452 | "lodash.reject": {
453 | "version": "4.6.0",
454 | "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
455 | "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU="
456 | },
457 | "lodash.some": {
458 | "version": "4.6.0",
459 | "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
460 | "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
461 | },
462 | "longest": {
463 | "version": "1.0.1",
464 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
465 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
466 | },
467 | "markdown-it": {
468 | "version": "8.3.1",
469 | "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.3.1.tgz",
470 | "integrity": "sha1-L0tiKUjM3Bk9ZvPKLUMSWsSscyM=",
471 | "requires": {
472 | "argparse": "^1.0.7",
473 | "entities": "~1.1.1",
474 | "linkify-it": "^2.0.0",
475 | "mdurl": "^1.0.1",
476 | "uc.micro": "^1.0.3"
477 | }
478 | },
479 | "mdurl": {
480 | "version": "1.0.1",
481 | "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
482 | "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
483 | },
484 | "mdv": {
485 | "version": "1.0.5",
486 | "resolved": "https://registry.npmjs.org/mdv/-/mdv-1.0.5.tgz",
487 | "integrity": "sha1-OXp0O6MzPfHDaR3p3MJy1ppMIUg=",
488 | "requires": {
489 | "cheerio": "^0.22.0",
490 | "markdown-it": "^8.3.1",
491 | "yargs": "^7.0.2"
492 | }
493 | },
494 | "minimist": {
495 | "version": "0.0.10",
496 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
497 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
498 | },
499 | "mkdirp": {
500 | "version": "0.5.1",
501 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
502 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
503 | "requires": {
504 | "minimist": "0.0.8"
505 | },
506 | "dependencies": {
507 | "minimist": {
508 | "version": "0.0.8",
509 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
510 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
511 | }
512 | }
513 | },
514 | "ms": {
515 | "version": "2.0.0",
516 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
517 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
518 | },
519 | "normalize-package-data": {
520 | "version": "2.3.8",
521 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz",
522 | "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=",
523 | "requires": {
524 | "hosted-git-info": "^2.1.4",
525 | "is-builtin-module": "^1.0.0",
526 | "semver": "2 || 3 || 4 || 5",
527 | "validate-npm-package-license": "^3.0.1"
528 | }
529 | },
530 | "nth-check": {
531 | "version": "1.0.1",
532 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
533 | "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
534 | "requires": {
535 | "boolbase": "~1.0.0"
536 | }
537 | },
538 | "number-is-nan": {
539 | "version": "1.0.1",
540 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
541 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
542 | },
543 | "ono": {
544 | "version": "2.2.4",
545 | "resolved": "https://registry.npmjs.org/ono/-/ono-2.2.4.tgz",
546 | "integrity": "sha1-9sHZ6mTaB6VIY5hlNdo95n5QJpY="
547 | },
548 | "optimist": {
549 | "version": "0.6.1",
550 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
551 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
552 | "requires": {
553 | "minimist": "~0.0.1",
554 | "wordwrap": "~0.0.2"
555 | }
556 | },
557 | "os-locale": {
558 | "version": "1.4.0",
559 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
560 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
561 | "requires": {
562 | "lcid": "^1.0.0"
563 | }
564 | },
565 | "parse-json": {
566 | "version": "2.2.0",
567 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
568 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
569 | "requires": {
570 | "error-ex": "^1.2.0"
571 | }
572 | },
573 | "path-exists": {
574 | "version": "2.1.0",
575 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
576 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
577 | "requires": {
578 | "pinkie-promise": "^2.0.0"
579 | }
580 | },
581 | "path-type": {
582 | "version": "1.1.0",
583 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
584 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
585 | "requires": {
586 | "graceful-fs": "^4.1.2",
587 | "pify": "^2.0.0",
588 | "pinkie-promise": "^2.0.0"
589 | }
590 | },
591 | "pify": {
592 | "version": "2.3.0",
593 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
594 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
595 | },
596 | "pinkie": {
597 | "version": "2.0.4",
598 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
599 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
600 | },
601 | "pinkie-promise": {
602 | "version": "2.0.1",
603 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
604 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
605 | "requires": {
606 | "pinkie": "^2.0.0"
607 | }
608 | },
609 | "process-nextick-args": {
610 | "version": "1.0.7",
611 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
612 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
613 | },
614 | "read-pkg": {
615 | "version": "1.1.0",
616 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
617 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
618 | "requires": {
619 | "load-json-file": "^1.0.0",
620 | "normalize-package-data": "^2.3.2",
621 | "path-type": "^1.0.0"
622 | },
623 | "dependencies": {
624 | "load-json-file": {
625 | "version": "1.1.0",
626 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
627 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
628 | "requires": {
629 | "graceful-fs": "^4.1.2",
630 | "parse-json": "^2.2.0",
631 | "pify": "^2.0.0",
632 | "pinkie-promise": "^2.0.0",
633 | "strip-bom": "^2.0.0"
634 | }
635 | },
636 | "strip-bom": {
637 | "version": "2.0.0",
638 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
639 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
640 | "requires": {
641 | "is-utf8": "^0.2.0"
642 | }
643 | }
644 | }
645 | },
646 | "read-pkg-up": {
647 | "version": "1.0.1",
648 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
649 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
650 | "requires": {
651 | "find-up": "^1.0.0",
652 | "read-pkg": "^1.0.0"
653 | }
654 | },
655 | "readable-stream": {
656 | "version": "2.2.9",
657 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz",
658 | "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=",
659 | "requires": {
660 | "buffer-shims": "~1.0.0",
661 | "core-util-is": "~1.0.0",
662 | "inherits": "~2.0.1",
663 | "isarray": "~1.0.0",
664 | "process-nextick-args": "~1.0.6",
665 | "string_decoder": "~1.0.0",
666 | "util-deprecate": "~1.0.1"
667 | }
668 | },
669 | "repeat-string": {
670 | "version": "1.6.1",
671 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
672 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
673 | },
674 | "require-directory": {
675 | "version": "2.1.1",
676 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
677 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
678 | },
679 | "require-main-filename": {
680 | "version": "1.0.1",
681 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
682 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
683 | },
684 | "right-align": {
685 | "version": "0.1.3",
686 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
687 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
688 | "optional": true,
689 | "requires": {
690 | "align-text": "^0.1.1"
691 | }
692 | },
693 | "safe-buffer": {
694 | "version": "5.0.1",
695 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
696 | "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
697 | },
698 | "semver": {
699 | "version": "5.3.0",
700 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
701 | "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
702 | },
703 | "set-blocking": {
704 | "version": "2.0.0",
705 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
706 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
707 | },
708 | "source-map": {
709 | "version": "0.4.4",
710 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
711 | "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
712 | "requires": {
713 | "amdefine": ">=0.0.4"
714 | }
715 | },
716 | "spdx-correct": {
717 | "version": "1.0.2",
718 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
719 | "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
720 | "requires": {
721 | "spdx-license-ids": "^1.0.2"
722 | }
723 | },
724 | "spdx-expression-parse": {
725 | "version": "1.0.4",
726 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
727 | "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw="
728 | },
729 | "spdx-license-ids": {
730 | "version": "1.2.2",
731 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
732 | "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc="
733 | },
734 | "sprintf-js": {
735 | "version": "1.0.3",
736 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
737 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
738 | },
739 | "string-width": {
740 | "version": "1.0.2",
741 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
742 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
743 | "requires": {
744 | "code-point-at": "^1.0.0",
745 | "is-fullwidth-code-point": "^1.0.0",
746 | "strip-ansi": "^3.0.0"
747 | }
748 | },
749 | "string_decoder": {
750 | "version": "1.0.1",
751 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz",
752 | "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=",
753 | "requires": {
754 | "safe-buffer": "^5.0.1"
755 | }
756 | },
757 | "strip-ansi": {
758 | "version": "3.0.1",
759 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
760 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
761 | "requires": {
762 | "ansi-regex": "^2.0.0"
763 | }
764 | },
765 | "strip-bom": {
766 | "version": "3.0.0",
767 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
768 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
769 | },
770 | "uc.micro": {
771 | "version": "1.0.3",
772 | "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz",
773 | "integrity": "sha1-ftUNXg+an7ClczeSWfKndFjVAZI="
774 | },
775 | "uglify-js": {
776 | "version": "2.8.26",
777 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.26.tgz",
778 | "integrity": "sha1-Oh24rgoKun+S4d2trb0Ck9VJ+Q4=",
779 | "optional": true,
780 | "requires": {
781 | "source-map": "~0.5.1",
782 | "uglify-to-browserify": "~1.0.0",
783 | "yargs": "~3.10.0"
784 | },
785 | "dependencies": {
786 | "camelcase": {
787 | "version": "1.2.1",
788 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
789 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
790 | "optional": true
791 | },
792 | "cliui": {
793 | "version": "2.1.0",
794 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
795 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
796 | "optional": true,
797 | "requires": {
798 | "center-align": "^0.1.1",
799 | "right-align": "^0.1.1",
800 | "wordwrap": "0.0.2"
801 | }
802 | },
803 | "source-map": {
804 | "version": "0.5.6",
805 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
806 | "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
807 | "optional": true
808 | },
809 | "wordwrap": {
810 | "version": "0.0.2",
811 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
812 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
813 | "optional": true
814 | },
815 | "yargs": {
816 | "version": "3.10.0",
817 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
818 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
819 | "optional": true,
820 | "requires": {
821 | "camelcase": "^1.0.2",
822 | "cliui": "^2.1.0",
823 | "decamelize": "^1.0.0",
824 | "window-size": "0.1.0"
825 | }
826 | }
827 | }
828 | },
829 | "uglify-to-browserify": {
830 | "version": "1.0.2",
831 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
832 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
833 | "optional": true
834 | },
835 | "util-deprecate": {
836 | "version": "1.0.2",
837 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
838 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
839 | },
840 | "validate-npm-package-license": {
841 | "version": "3.0.1",
842 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
843 | "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
844 | "requires": {
845 | "spdx-correct": "~1.0.0",
846 | "spdx-expression-parse": "~1.0.0"
847 | }
848 | },
849 | "validator": {
850 | "version": "6.3.0",
851 | "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
852 | "integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g="
853 | },
854 | "which-module": {
855 | "version": "1.0.0",
856 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
857 | "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
858 | },
859 | "window-size": {
860 | "version": "0.1.0",
861 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
862 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
863 | "optional": true
864 | },
865 | "word-wrap": {
866 | "version": "1.2.2",
867 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.2.tgz",
868 | "integrity": "sha1-j6eMO9o+MTjHeX+rzq5wmWiBS0E="
869 | },
870 | "wordwrap": {
871 | "version": "0.0.3",
872 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
873 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
874 | },
875 | "wrap-ansi": {
876 | "version": "2.1.0",
877 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
878 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
879 | "requires": {
880 | "string-width": "^1.0.1",
881 | "strip-ansi": "^3.0.1"
882 | }
883 | },
884 | "y18n": {
885 | "version": "3.2.1",
886 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
887 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
888 | },
889 | "yargs": {
890 | "version": "7.1.0",
891 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
892 | "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
893 | "requires": {
894 | "camelcase": "^3.0.0",
895 | "cliui": "^3.2.0",
896 | "decamelize": "^1.1.1",
897 | "get-caller-file": "^1.0.1",
898 | "os-locale": "^1.4.0",
899 | "read-pkg-up": "^1.0.1",
900 | "require-directory": "^2.1.1",
901 | "require-main-filename": "^1.0.1",
902 | "set-blocking": "^2.0.0",
903 | "string-width": "^1.0.2",
904 | "which-module": "^1.0.0",
905 | "y18n": "^3.2.1",
906 | "yargs-parser": "^5.0.0"
907 | }
908 | },
909 | "yargs-parser": {
910 | "version": "5.0.0",
911 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
912 | "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
913 | "requires": {
914 | "camelcase": "^3.0.0"
915 | }
916 | },
917 | "z-schema": {
918 | "version": "3.18.2",
919 | "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.2.tgz",
920 | "integrity": "sha1-5CIZa17+YLRq3vPD8q7y3qqREWE=",
921 | "requires": {
922 | "commander": "^2.7.1",
923 | "lodash.get": "^4.1.2",
924 | "lodash.isequal": "^4.4.0",
925 | "validator": "^6.0.0"
926 | }
927 | }
928 | }
929 | }
930 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "asyncapi-node-codegen",
3 | "version": "1.3.2",
4 | "description": "An AsyncAPI codegen for Node.js",
5 | "scripts": {
6 | "test": "node tests/test"
7 | },
8 | "bin": {
9 | "asyncapi-node-codegen": "./cli.js",
10 | "anc": "./cli.js"
11 | },
12 | "preferGlobal": true,
13 | "bugs": {
14 | "url": "https://github.com/asyncapi/node-codegen/issues"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/asyncapi/node-codegen.git"
19 | },
20 | "keywords": [
21 | "asyncapi",
22 | "codegen",
23 | "generator"
24 | ],
25 | "author": {
26 | "name": "Fran Mendez",
27 | "email": "fmvilas@gmail.com"
28 | },
29 | "homepage": "https://github.com/asyncapi/node-codegen",
30 | "dependencies": {
31 | "asyncapi": "1.0.0",
32 | "asyncapi-topic-parser": "^0.2.0",
33 | "commander": "^2.12.2",
34 | "handlebars": "^4.0.6",
35 | "js-yaml": "^3.8.3",
36 | "json-schema-ref-parser": "^3.1.2",
37 | "lodash": "^4.17.4",
38 | "mkdirp": "^0.5.1",
39 | "word-wrap": "^1.2.1",
40 | "z-schema": "^3.18.2"
41 | },
42 | "devDependencies": {}
43 | }
44 |
--------------------------------------------------------------------------------
/templates/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | indent_style = space
9 | indent_size = 2
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/templates/.eslintrc:
--------------------------------------------------------------------------------
1 | extends: 'eslint:recommended'
2 |
3 | parserOptions:
4 | ecmaVersion: 8
5 |
6 | env:
7 | node: true
8 | es6: true
9 |
10 | ecmaFeatures:
11 | forOf: true
12 | modules: true
13 |
14 | rules:
15 | # Possible Errors
16 | no-console: 0
17 | valid-jsdoc: [0, {requireReturn: false, requireParamDescription: false, requireReturnDescription: false}]
18 |
19 | # Best Practices
20 | consistent-return: 0
21 | curly: 0
22 | block-scoped-var: 2
23 | no-else-return: 2
24 | no-process-env: 2
25 | no-self-compare: 2
26 | no-throw-literal: 2
27 | no-void: 2
28 | radix: 2
29 | wrap-iife: [2, outside]
30 |
31 | # Variables
32 | no-shadow: 0
33 | no-use-before-define: [2, nofunc]
34 |
35 | # Node.js
36 | no-process-exit: 0
37 | handle-callback-err: [2, err]
38 | no-new-require: 2
39 | no-path-concat: 2
40 |
41 | # Stylistic Issues
42 | quotes: [2, single]
43 | camelcase: 0
44 | indent: [2, 2]
45 | no-lonely-if: 2
46 | no-floating-decimal: 2
47 | brace-style: [2, 1tbs, { "allowSingleLine": true }]
48 | comma-style: [2, last]
49 | consistent-this: [0, self]
50 | func-style: 0
51 | max-nested-callbacks: 0
52 | new-cap: 2
53 | no-multiple-empty-lines: [2, {max: 1}]
54 | no-nested-ternary: 2
55 | semi-spacing: [2, {before: false, after: true}]
56 | operator-assignment: [2, always]
57 | padded-blocks: [2, never]
58 | quote-props: [2, as-needed]
59 | space-before-function-paren: [2, always]
60 | keyword-spacing: [2, {"before": true, "after": true}]
61 | space-before-blocks: [2, always]
62 | array-bracket-spacing: [2, never]
63 | computed-property-spacing: [2, never]
64 | space-in-parens: [2, never]
65 | space-unary-ops: [2, {words: true, nonwords: false}]
66 | #spaced-line-comment: [2, always]
67 | wrap-regex: 2
68 | linebreak-style: [2, unix]
69 | semi: [2, always]
70 |
71 | # ECMAScript 6
72 | arrow-spacing: [2, {before: true, after: true}]
73 | no-class-assign: 2
74 | no-const-assign: 2
75 | no-dupe-class-members: 2
76 | no-this-before-super: 2
77 | no-var: 2
78 | object-shorthand: [2, always]
79 | prefer-arrow-callback: 2
80 | prefer-const: 2
81 | prefer-spread: 2
82 | prefer-template: 2
83 |
--------------------------------------------------------------------------------
/templates/.npmignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asyncapi-archived-repos/node-codegen/8cb999827f135e5cb1ee1b1cfe6eac460c13b424/templates/.npmignore
--------------------------------------------------------------------------------
/templates/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:7-alpine
2 |
3 | # Create app directory
4 | RUN mkdir -p /usr/src/app
5 | WORKDIR /usr/src/app
6 |
7 | # Install app dependencies
8 | COPY package.json /usr/src/app/
9 | RUN npm install
10 |
11 | # Copy app source
12 | COPY . /usr/src/app
13 |
14 | CMD [ "npm", "start" ]
15 |
--------------------------------------------------------------------------------
/templates/README.md:
--------------------------------------------------------------------------------
1 | # {{asyncapi.info.title}}
2 |
3 | {{asyncapi.info.description}}
4 |
--------------------------------------------------------------------------------
/templates/__.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | npm-debug.log
4 |
--------------------------------------------------------------------------------
/templates/config/common.yml:
--------------------------------------------------------------------------------
1 | default:
2 | broker:
3 | {{#each asyncapi.__schemes as |scheme|}}
4 | {{#compare scheme '===' 'amqp'}}
5 | amqp:
6 | exchange:
7 | username:
8 | password:
9 | host: localhost
10 | port:
11 | topic: {{../../asyncapi.__commonTopic}}
12 | queue: {{queueName ../../asyncapi.info.title ../../asyncapi.info.version}}
13 | queue_options:
14 | exclusive: false
15 | durable: true
16 | autoDelete: true
17 | {{/compare}}
18 | {{#compare scheme '===' 'mqtt'}}
19 | mqtt:
20 | host_url: mqtt://localhost
21 | topics: {{toMQTT ../../asyncapi.__commonTopic}}
22 | qos:
23 | protocol: mqtt
24 | retain:
25 | {{/compare}}
26 | {{/each}}
27 |
28 |
29 | development:
30 |
31 | test:
32 |
33 | staging:
34 |
35 | production:
36 | broker:
37 | {{#each asyncapi.__schemes as |scheme|}}
38 | {{#compare scheme '===' 'amqp'}}
39 | amqp:
40 | host: {{../../asyncapi.host}}
41 | {{/compare}}
42 | {{#compare scheme '===' 'mqtt'}}
43 | mqtt:
44 | host_url: mqtt://{{../../asyncapi.host}}
45 | {{/compare}}
46 | {{/each}}
47 |
--------------------------------------------------------------------------------
/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{snakecase asyncapi.info.title}}",
3 | "description": "{{asyncapi.__descriptionInOneLine}}",
4 | "version": "{{asyncapi.info.version}}",
5 | "scripts": {
6 | "start": "node src/api/index.js"
7 | },
8 | "dependencies": {
9 | "hermesjs": "1.x",
10 | "hermesjs-router": "1.x",
11 | {{#each asyncapi.__schemes as |scheme|}}
12 | "hermesjs-{{scheme}}": "1.x",
13 | {{/each}}
14 | "node-yaml-config": "0.0.4"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/templates/src/api/adapters/___adapter.js:
--------------------------------------------------------------------------------
1 | const config = require('../../lib/config');
2 | {{#compare adapter '===' 'amqp' }}
3 | const hermesAMQP = require('hermesjs-amqp');
4 |
5 | const adapter = hermesAMQP({
6 | exchange: config.broker.amqp.exchange,
7 | username: config.broker.amqp.username,
8 | password: config.broker.amqp.password,
9 | host: config.broker.amqp.host,
10 | port: config.broker.amqp.port,
11 | topic: config.broker.amqp.topic,
12 | queue: config.broker.amqp.queue,
13 | queue_options: config.broker.amqp.queue_options,
14 | subscribe: {{shouldSubscribe ../asyncapi}} // ATTENTION: If subscribe is true you might receive the messages you send.
15 | });
16 | {{/compare}}
17 | {{#compare adapter '===' 'mqtt' }}
18 | const hermesMQTT = require('hermesjs-mqtt');
19 |
20 | const adapter = hermesMQTT({
21 | host_url: config.broker.mqtt.host_url,
22 | topics: config.broker.mqtt.topics,
23 | qos: config.broker.mqtt.qos,
24 | protocol: config.broker.mqtt.protocol,
25 | retain: config.broker.mqtt.retain,
26 | subscribe: {{shouldSubscribe ../asyncapi}} // ATTENTION: If subscribe is true you might receive the messages you send.
27 | });
28 | {{/compare}}
29 |
30 | module.exports = adapter;
31 |
--------------------------------------------------------------------------------
/templates/src/api/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const hermes = require('hermesjs')();
4 | const buffer2string = require('./middlewares/buffer2string');
5 | const string2json = require('./middlewares/string2json');
6 | const logger = require('./middlewares/logger');
7 | {{#each asyncapi.__schemes as |scheme|}}
8 | const {{capitalize scheme}}Adapter = require('./adapters/{{scheme}}');
9 | {{/each}}
10 | {{#each asyncapi.__services as |service|}}
11 | const {{service}} = require('./routes/{{service}}.js');
12 | {{/each}}
13 |
14 | {{#each asyncapi.__schemes as |scheme|}}
15 | hermes.add('broker', {{capitalize scheme}}Adapter);
16 | {{/each}}
17 |
18 | hermes.on('broker:ready', ({name}) => {
19 | console.log(`${name} is listening...`);
20 | });
21 |
22 | hermes.use(buffer2string);
23 | hermes.use(string2json);
24 | hermes.use(logger);
25 |
26 | {{#each asyncapi.__services as |service|}}
27 | hermes.use({{service}});
28 | {{/each}}
29 |
30 | hermes.use((err, message, next) => {
31 | console.error(err);
32 | next();
33 | });
34 |
35 | hermes.listen();
36 |
--------------------------------------------------------------------------------
/templates/src/api/middlewares/buffer2string.js:
--------------------------------------------------------------------------------
1 | module.exports = (message, next) => {
2 | if (message.payload instanceof Buffer) {
3 | message.payload = message.payload.toString();
4 | }
5 |
6 | next();
7 | };
8 |
--------------------------------------------------------------------------------
/templates/src/api/middlewares/logger.js:
--------------------------------------------------------------------------------
1 | const util = require('util');
2 |
3 | function yellow (text) {
4 | return `\x1b[33m${text}\x1b[0m`;
5 | }
6 |
7 | module.exports = (message, next) => {
8 | const action = message.from.broker ? 'received from' : 'sent to';
9 | console.log(`${yellow(message.topic)} was ${action} broker:`);
10 | console.log(util.inspect(message.payload, { depth: null, colors: true }));
11 | next();
12 | };
13 |
--------------------------------------------------------------------------------
/templates/src/api/middlewares/string2json.js:
--------------------------------------------------------------------------------
1 | module.exports = (message, next) => {
2 | try {
3 | message.payload = JSON.parse(message.payload);
4 | } catch (e) {
5 | // We did our best...
6 | }
7 |
8 | next();
9 | };
10 |
--------------------------------------------------------------------------------
/templates/src/api/routes/___route.js:
--------------------------------------------------------------------------------
1 | const router = require('hermesjs-router')();
2 | const {{service}} = require('../services/{{service}}');
3 |
4 | {{#each asyncapi.topics as |topic topicName|}}
5 | {{#if topic.publish}}
6 | {{#if topic.publish.descriptionLines}}
7 | /**
8 | {{#each topic.publish.descriptionLines}}
9 | * {{this}}
10 | {{/each}}
11 | */
12 | {{/if}}
13 | router.use('{{dotsToSlashes topicName}}', async (message, next) => {
14 | await {{topic.serviceName}}.{{topic.publish.operationId}}({message});
15 | next();
16 | });
17 |
18 | {{/if}}
19 | {{#if topic.subscribe}}
20 | {{#if topic.subscribe.descriptionLines}}
21 | /**
22 | {{#each topic.subscribe.descriptionLines}}
23 | * {{this}}
24 | {{/each}}
25 | */
26 | {{/if}}
27 | router.use('{{dotsToSlashes topicName}}', async (message, next) => {
28 | await {{topic.serviceName}}.{{topic.subscribe.operationId}}({message});
29 | next();
30 | });
31 |
32 | {{/if}}
33 | {{/each}}
34 | module.exports = router;
35 |
--------------------------------------------------------------------------------
/templates/src/api/services/___service.js:
--------------------------------------------------------------------------------
1 | const {{service}} = module.exports = {};
2 |
3 | {{#each asyncapi.topics as |topic topicName|}}
4 | {{#if topic.publish}}
5 | /**
6 | {{#if topic.publish.descriptionLines}}
7 | {{#each topic.publish.descriptionLines}}
8 | * {{this}}
9 | {{/each}}
10 | *
11 | {{/if}}
12 | * @param {Object} options
13 | * @param {Object} options.message
14 | {{#if topic.publish.headers}}
15 | {{#each topic.publish.headers.properties as |field fieldName|}}
16 | {{{docline field fieldName 'options.message.headers'}}}
17 | {{/each}}
18 | {{/if}}
19 | {{#each topic.publish.payload.properties as |field fieldName|}}
20 | {{{docline field fieldName 'options.message.payload'}}}
21 | {{/each}}
22 | */
23 | {{topic.serviceName}}.{{topic.publish.operationId}} = async ({message}) => {
24 | // Implement your business logic here...
25 | };
26 |
27 | {{/if}}
28 | {{#if topic.subscribe}}
29 | /**
30 | {{#if topic.subscribe.descriptionLines}}
31 | {{#each topic.subscribe.descriptionLines}}
32 | * {{this}}
33 | {{/each}}
34 | *
35 | {{/if}}
36 | * @param {Object} options
37 | * @param {Object} options.message
38 | {{#if topic.subscribe.headers}}
39 | {{#each topic.subscribe.headers.properties as |field fieldName|}}
40 | {{{docline field fieldName 'options.message.headers'}}}
41 | {{/each}}
42 | {{/if}}
43 | {{#each topic.subscribe.payload.properties as |field fieldName|}}
44 | {{{docline field fieldName 'options.message.payload'}}}
45 | {{/each}}
46 | */
47 | {{topic.serviceName}}.{{topic.subscribe.operationId}} = async ({message}) => {
48 | // Implement your business logic here...
49 | };
50 |
51 | {{/if}}
52 | {{/each}}
53 |
--------------------------------------------------------------------------------
/templates/src/lib/config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const yamlConfig = require('node-yaml-config');
3 |
4 | const config = yamlConfig.load(path.join(__dirname, '../../config/common.yml'));
5 |
6 | module.exports = config;
7 |
--------------------------------------------------------------------------------
/tests/sample.yml:
--------------------------------------------------------------------------------
1 | asyncapi: "1.0.0"
2 | info:
3 | title: AsyncAPI Sample
4 | version: "1.0.0"
5 | description: |
6 | This is a simple example of an _AsyncAPI_ document.
7 | termsOfService: https://api.company.com/terms
8 | baseTopic: 'hitch'
9 |
10 | servers:
11 | - url: api.company.com:{port}/{app-id}
12 | description: Allows you to connect using the MQTT protocol.
13 | scheme: mqtt
14 | variables:
15 | app-id:
16 | default: demo
17 | description: You can find your `app-id` in our control panel, under the auth tab.
18 | port:
19 | enum:
20 | - '5676'
21 | - '5677'
22 | default: '5676'
23 | - url: api.company.com:{port}/{app-id}
24 | description: Allows you to connect using the AMQP protocol.
25 | scheme: amqp
26 | variables:
27 | app-id:
28 | default: demo
29 | description: You can find your `app-id` in our control panel, under the auth tab.
30 | port:
31 | enum:
32 | - '5676'
33 | - '5677'
34 | default: '5676'
35 |
36 | topics:
37 | accounts.1.0.action.user.signup:
38 | publish:
39 | $ref: "#/components/messages/userSignUp"
40 | accounts.1.0.event.user.signup:
41 | subscribe:
42 | $ref: "#/components/messages/userSignedUp"
43 |
44 | components:
45 | messages:
46 | userSignUp:
47 | deprecated: true
48 | summary: Action to sign a user up.
49 | description: |
50 | Multiline description of what this action does. **It allows Markdown.**
51 | tags:
52 | - name: user
53 | - name: signup
54 | headers:
55 | type: object
56 | properties:
57 | qos:
58 | $ref: "#/components/schemas/MQTTQoSHeader"
59 | retainFlag:
60 | $ref: "#/components/schemas/MQTTRetainHeader"
61 | payload:
62 | type: object
63 | properties:
64 | user:
65 | $ref: "#/components/schemas/userCreate"
66 | signup:
67 | $ref: "#/components/schemas/signup"
68 |
69 |
70 | userSignedUp:
71 | payload:
72 | type: object
73 | properties:
74 | test:
75 | type: array
76 | items:
77 | type: object
78 | properties:
79 | key1:
80 | type: string
81 | key2:
82 | type: integer
83 | user:
84 | $ref: "#/components/schemas/user"
85 | signup:
86 | $ref: "#/components/schemas/signup"
87 | schemas:
88 | id:
89 | title: id
90 | description: Resource identifier
91 | type: string
92 | username:
93 | title: username
94 | description: User handle
95 | type: string
96 | datetime:
97 | title: datetime
98 | description: Date and Time of the message
99 | type: string
100 | format: date-time
101 | MQTTQoSHeader:
102 | title: qos
103 | description: Quality of Service
104 | type: integer
105 | format: int32
106 | default: 1
107 | enum:
108 | - 0
109 | - 2
110 | MQTTRetainHeader:
111 | title: retainFlag
112 | description: |
113 | This flag determines if the message will be saved by the broker for the specified
114 | topic as last known good value. New clients that subscribe to that topic will receive
115 | the last retained message on that topic instantly after subscribing. More on retained messages
116 | and best practices in one of the next posts.
117 | type: boolean
118 | default: false
119 | user:
120 | type: object
121 | required:
122 | - id
123 | - username
124 | properties:
125 | id:
126 | description: User Id
127 | $ref: "#/components/schemas/id"
128 | full_name:
129 | description: User full name
130 | type: string
131 | username:
132 | $ref: "#/components/schemas/username"
133 | userCreate:
134 | type: object
135 | required:
136 | - username
137 | properties:
138 | full_name:
139 | description: User full name
140 | type: string
141 | username:
142 | $ref: "#/components/schemas/username"
143 |
144 | signup:
145 | type: object
146 | required:
147 | - method
148 | - datetime
149 | properties:
150 | method:
151 | description: Signup method
152 | type: string
153 | enum:
154 | - email
155 | - facebook
156 | - twitter
157 | - github
158 | - google
159 | datetime:
160 | $ref: "#/components/schemas/datetime"
161 |
--------------------------------------------------------------------------------
/tests/test.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const process = require('../lib/codegen').process;
3 |
4 | process(path.resolve(__dirname, 'sample.yml'), path.resolve(__dirname, './output/'));
5 |
--------------------------------------------------------------------------------