├── .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 | --------------------------------------------------------------------------------