diff --git a/.github/workflows/update-docs-in-website.yml b/.github/workflows/update-docs-in-website.yml index 6394cefae..b39628970 100644 --- a/.github/workflows/update-docs-in-website.yml +++ b/.github/workflows/update-docs-in-website.yml @@ -5,7 +5,7 @@ on: branches: - 'master' paths: - - 'docs/*.md' + - 'apps/generator/docs/*.md' jobs: Make-PR: @@ -37,10 +37,10 @@ jobs: run: | rm -r ./markdown/docs/tools/generator mkdir -p ./markdown/docs/tools/generator - rm ../generator/docs/README.md - rm -r ../generator/docs/jsdoc2md-handlebars + rm ../generator/apps/generator/docs/README.md + rm -r ../generator/apps/generator/docs/jsdoc2md-handlebars printf "%s\ntitle: Generator\nweight: 3\n%s" "---" "---"> ../generator/docs/_section.md - mv ../generator/docs/*.md ./markdown/docs/tools/generator + mv ../generator/apps/generator/docs/*.md ./markdown/docs/tools/generator - name: Commit and push working-directory: ./website run: | diff --git a/apps/generator/.gitignore b/apps/generator/.gitignore index 670435e61..4c5f72e17 100644 --- a/apps/generator/.gitignore +++ b/apps/generator/.gitignore @@ -15,6 +15,7 @@ output out/ coverage test/temp/integrationTestResult +test/temp/reactTemplate test/test-project/package-lock.json test/test-project/verdaccio/storage/ test/test-project/storage/ diff --git a/apps/generator/CHANGELOG.md b/apps/generator/CHANGELOG.md index d83655139..2ead8e4ea 100644 --- a/apps/generator/CHANGELOG.md +++ b/apps/generator/CHANGELOG.md @@ -1,5 +1,17 @@ # @asyncapi/generator +## 2.4.1 + +### Patch Changes + +- 3a372c4: Removed the source-map-support package from the AsyncAPI Generator, as it is no longer required for version 2, which now supports Node.js version 18.12.0 and above. + +## 2.4.0 + +### Minor Changes + +- 46114d8: Add `compile` option to enable rerun of transpilation of templates build with react engine. It is set to `true` by default. In future major releases it will be set to `false` and we will explain how to publish template to include transpilation files by default. Transpiled files are already included in [`html-template`](https://github.com/asyncapi/html-template/pull/575). It means that you can run generator for `html-template` (it's latest version) with `compile=false` and this will improve the speed of HTML generation for you. + ## 2.3.0 ### Minor Changes diff --git a/apps/generator/docs/file-templates.md b/apps/generator/docs/file-templates.md index 5ba4a9687..7a7a8ce77 100644 --- a/apps/generator/docs/file-templates.md +++ b/apps/generator/docs/file-templates.md @@ -3,7 +3,11 @@ title: "File templates" weight: 140 --- -It is possible to generate files for each specific object in your AsyncAPI documentation. For example, you can specify a filename like `$$channel$$.js` to generate a file for each channel defined in your AsyncAPI. The following file-template names and extra variables in them are available: +## Generating files with the Nunjucks render engine + +> **Note**: This section applies only to the Nunjucks render engine. For information on using the React render engine, refer to the [Generating files with the React render engine](#generating-files-with-the-react-render-engine) section below. + +It is possible to generate files for each specific object in your AsyncAPI documentation using the Nunjucks render engine. For example, you can specify a filename like `$$channel$$.js` to generate a file for each channel defined in your AsyncAPI. The following file-template names and extra variables are available: - `$$channel$$`, within the template-file you have access to two variables [`channel`](https://github.com/asyncapi/parser-api/blob/master/docs/api.md#channel) and [`channelName`](https://github.com/asyncapi/parser-api/blob/master/docs/api.md#channels). Where the `channel` contains the current channel being rendered. - `$$message$$`, within the template-file you have access to two variables [`message`](https://github.com/asyncapi/parser-api/blob/master/docs/api.md#message) and [`messageName`](https://github.com/asyncapi/parser-api/blob/master/docs/api.md#message). Where `message` contains the current message being rendered. @@ -25,7 +29,7 @@ Schema name is '{{schemaName}}' and properties are: {% endfor %} ``` -With following AsyncAPI: +With the following AsyncAPI: ``` components: schemas: @@ -53,15 +57,82 @@ Schema name is 'people' and properties are: - id ``` -### React +> You can see an example of a file template that uses the Nunjucks render engine [here](https://github.com/asyncapi/template-for-generator-templates/tree/nunjucks/template/schemas). + +## Generating files with the React render engine -The above way of rendering **file templates** works for both `nunjucks` and `react` render engines, but `react` also has another, more generic way to render multiple files. It is enough to return an array of `File` components in the rendering component. See the following example: +The above method of rendering **file templates** only works for the Nunjucks render engine. To use the React render engine, you need to follow a different approach. The React render engine allows for a more generic way to render multiple files by returning an array of `File` components in the rendering component. This can be particularly useful for complex templates or when you need to generate a large number of files with varying content. + +### Example 1: Rendering hardcoded files + +The following is a simple hardcoded example of how to render multiple files using the React render engine: ```tsx +import { File} from "@asyncapi/generator-react-sdk"; + export default function({ asyncapi }) { return [ Content, Content ] } -``` \ No newline at end of file +``` + +### Example 2: Rendering files based on the AsyncAPI Schema + +In practice, to render the multiple files, that are generated from the data defined in your AsyncAPI, you'll iterate over the array of schemas and generate a file for each schema as shown in the example below: + +```js +import { File} from "@asyncapi/generator-react-sdk"; + +/* + * To render multiple files, it is enough to return an array of `File` components in the rendering component, like in following example. + */ +export default function({ asyncapi }) { + const schemas = asyncapi.allSchemas(); + const files = []; + // schemas is an instance of the Map + schemas.forEach((schema) => { + + files.push( + // We return a react file component and each time we do it, the name of the generated file will be a schema name + // Content of the file will be a variable representing schema + + const { schema.id() } = { JSON.stringify(schema._json, null, 2) } + + ); + }); + return files; +} +``` + +### Example 3: Rendering files for each channel + +Additionally, you can generate multiple files for each channel defined in your AsyncAPI specification using the React render engine as shown in the example below: + +```js +import { File, Text } from "@asyncapi/generator-react-sdk"; + + +export default function ({ asyncapi }) { + const files = []; + + // Generate files for channels + asyncapi.channels().forEach((channel) => { + const channelName = channel.id(); + + files.push( + + # Channel: {channelName} + + {channel.hasDescription() && `${channel.description()}`} + + + ); + }); + return files; +} +``` +The code snippet above uses the `Text` component to write file content to the `.md` markdown file. The `newline` property is used to ensure that the content isn't all rendered in one line in the markdown file. In summary, the code snippet above is a practical guide on generating properly formatted multiline Markdown files for each channel in an AsyncAPI document. + +> You can see an example of a file template that uses the React render engine [here](https://github.com/asyncapi/template-for-generator-templates/blob/master/template/schemas/schema.js). diff --git a/apps/generator/lib/generator.js b/apps/generator/lib/generator.js index 5910859d7..8a07a5670 100644 --- a/apps/generator/lib/generator.js +++ b/apps/generator/lib/generator.js @@ -27,7 +27,6 @@ const { fetchSpec, isReactTemplate, isJsFile, - registerSourceMap, getTemplateDetails, convertCollectionToObject, } = require('./utils'); @@ -47,7 +46,7 @@ const DEFAULT_TEMPLATES_DIR = path.resolve(ROOT_DIR, 'node_modules'); const TRANSPILED_TEMPLATE_LOCATION = '__transpiled'; const TEMPLATE_CONTENT_DIRNAME = 'template'; -const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder', 'url', 'auth', 'token', 'registry']; +const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder', 'url', 'auth', 'token', 'registry', 'compile']; const logMessage = require('./logMessages'); const shouldIgnoreFile = filePath => @@ -57,8 +56,6 @@ const shouldIgnoreDir = dirPath => dirPath === '.git' || dirPath.startsWith(`.git${path.sep}`); -registerSourceMap(); - class Generator { /** * Instantiates a new Generator object. @@ -86,6 +83,7 @@ class Generator { * @param {Boolean} [options.forceWrite=false] Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir. Default is set to false. * @param {Boolean} [options.install=false] Install the template and its dependencies, even when the template has already been installed. * @param {Boolean} [options.debug=false] Enable more specific errors in the console. At the moment it only shows specific errors about filters. Keep in mind that as a result errors about template are less descriptive. + * @param {Boolean} [options.compile=true] Whether to compile the template or use the cached transpiled version provided by template in '__transpiled' folder * @param {Object} [options.mapBaseUrlToFolder] Optional parameter to map schema references from a base url to a local base folder e.g. url=https://schema.example.com/crm/ folder=./test/docs/ . * @param {Object} [options.registry] Optional parameter with private registry configuration * @param {String} [options.registry.url] Parameter to pass npm registry url @@ -93,13 +91,14 @@ class Generator { * @param {String} [options.registry.token] Optional parameter to pass npm registry auth token that you can grab from .npmrc file */ - constructor(templateName, targetDir, { templateParams = {}, entrypoint, noOverwriteGlobs, disabledHooks, output = 'fs', forceWrite = false, install = false, debug = false, mapBaseUrlToFolder = {}, registry = {}} = {}) { + constructor(templateName, targetDir, { templateParams = {}, entrypoint, noOverwriteGlobs, disabledHooks, output = 'fs', forceWrite = false, install = false, debug = false, mapBaseUrlToFolder = {}, registry = {}, compile = true } = {}) { const options = arguments[arguments.length - 1]; this.verifyoptions(options); if (!templateName) throw new Error('No template name has been specified.'); if (!entrypoint && !targetDir) throw new Error('No target directory has been specified.'); if (!['fs', 'string'].includes(output)) throw new Error(`Invalid output type ${output}. Valid values are 'fs' and 'string'.`); - + /** @type {Boolean} Whether to compile the template or use the cached transpiled version provided by template in '__transpiled' folder. */ + this.compile = compile; /** @type {Object} Npm registry information. */ this.registry = registry; /** @type {String} Name of the template to generate. */ @@ -393,7 +392,7 @@ class Generator { * Configure the templates based the desired renderer. */ async configureTemplate() { - if (isReactTemplate(this.templateConfig)) { + if (isReactTemplate(this.templateConfig) && this.compile) { await configureReact(this.templateDir, this.templateContentDir, TRANSPILED_TEMPLATE_LOCATION); } else { this.nunjucks = configureNunjucks(this.debug, this.templateDir); diff --git a/apps/generator/lib/logMessages.js b/apps/generator/lib/logMessages.js index 2d6e855fa..0ea167d6b 100644 --- a/apps/generator/lib/logMessages.js +++ b/apps/generator/lib/logMessages.js @@ -46,6 +46,10 @@ function conditionalFilesMatched(relativeSourceFile) { return `${relativeSourceFile} was not generated because condition specified for this file in template configuration in conditionalFiles matched.`; } +function compileEnabled(dir, output_dir) { + return `Transpilation of files ${dir} into ${output_dir} started.`; +} + module.exports = { TEMPLATE_INSTALL_FLAG_MSG, TEMPLATE_INSTALL_DISK_MSG, @@ -59,5 +63,6 @@ module.exports = { templateSuccessfullyInstalled, relativeSourceFileNotGenerated, conditionalFilesMatched, + compileEnabled, skipOverwrite }; diff --git a/apps/generator/lib/renderer/react.js b/apps/generator/lib/renderer/react.js index 1a21c203c..5a8171b8b 100644 --- a/apps/generator/lib/renderer/react.js +++ b/apps/generator/lib/renderer/react.js @@ -16,9 +16,11 @@ const reactExport = module.exports; * @param {string} templateLocation located for thetemplate * @param {string} templateContentDir where the template content are located * @param {string} transpiledTemplateLocation folder for the transpiled code + * @param {Boolean} compile Whether to compile the template files or used the cached transpiled version provided by the template in the '__transpiled' folder */ reactExport.configureReact = async (templateLocation, templateContentDir, transpiledTemplateLocation) => { const outputDir = path.resolve(templateLocation, `./${transpiledTemplateLocation}`); + log.debug(logMessage.compileEnabled(templateContentDir, outputDir)); await AsyncReactSDK.transpileFiles(templateContentDir, outputDir, { recursive: true }); diff --git a/apps/generator/lib/utils.js b/apps/generator/lib/utils.js index 27e26de0f..6403eb2fd 100644 --- a/apps/generator/lib/utils.js +++ b/apps/generator/lib/utils.js @@ -134,16 +134,6 @@ utils.isAsyncFunction = (fn) => { return fn && fn.constructor && fn.constructor.name === 'AsyncFunction'; }; -/** - * Register `source-map-support` package. - * This package provides source map support for stack traces in Node - also for transpiled code from TS. - * - * @private - */ -utils.registerSourceMap = () => { - require('source-map-support').install(); -}; - /** * Register TypeScript transpiler. It enables transpilation of TS filters and hooks on the fly. * diff --git a/apps/generator/package.json b/apps/generator/package.json index e4ddc6b92..a3baeea93 100644 --- a/apps/generator/package.json +++ b/apps/generator/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/generator", - "version": "2.3.0", + "version": "2.4.1", "description": "The AsyncAPI generator. It can generate documentation, code, anything!", "main": "./lib/generator.js", "bin": { @@ -74,7 +74,6 @@ "resolve-pkg": "^2.0.0", "semver": "^7.3.2", "simple-git": "^3.3.0", - "source-map-support": "^0.5.19", "ts-node": "^10.9.1", "typescript": "^4.9.3" }, @@ -87,6 +86,7 @@ "jsdoc-to-markdown": "^7.1.1", "markdown-toc": "^1.2.0", "rimraf": "^3.0.2", - "unixify": "^1.0.0" + "unixify": "^1.0.0", + "fs-extra": "9.1.0" } } diff --git a/apps/generator/test/generator.test.js b/apps/generator/test/generator.test.js index 0cf90129d..22c67aaaf 100644 --- a/apps/generator/test/generator.test.js +++ b/apps/generator/test/generator.test.js @@ -26,6 +26,7 @@ describe('Generator', () => { expect(gen.forceWrite).toStrictEqual(false); expect(gen.install).toStrictEqual(false); expect(gen.templateParams).toStrictEqual({}); + expect(gen.compile).toStrictEqual(true); }); it('works with all the params', () => { @@ -39,6 +40,7 @@ describe('Generator', () => { templateParams: { test: true, }, + compile: false, }); expect(gen.templateName).toStrictEqual('testTemplate'); expect(gen.targetDir).toStrictEqual(__dirname); @@ -48,6 +50,7 @@ describe('Generator', () => { expect(gen.output).toStrictEqual('string'); expect(gen.forceWrite).toStrictEqual(true); expect(gen.install).toStrictEqual(true); + expect(gen.compile).toStrictEqual(false); expect(() => gen.templateParams.test).toThrow('Template parameter "test" has not been defined in the package.json file under generator property. Please make sure it\'s listed there before you use it in your template.'); // Mock params on templateConfig so it doesn't fail. diff --git a/apps/generator/test/integration.test.js b/apps/generator/test/integration.test.js index d627ca9e7..c77c03924 100644 --- a/apps/generator/test/integration.test.js +++ b/apps/generator/test/integration.test.js @@ -2,8 +2,9 @@ * @jest-environment node */ -const { mkdir, writeFile, readFile } = require('fs').promises; const path = require('path'); +const { readFile, writeFile, access, mkdir } = require('fs').promises; +const { copy } = require('fs-extra'); const Generator = require('../lib/generator'); const dummySpecPath = path.resolve(__dirname, './docs/dummy.yml'); const refSpecPath = path.resolve(__dirname, './docs/apiwithref.json'); @@ -12,6 +13,8 @@ const crypto = require('crypto'); const mainTestResultPath = 'test/temp/integrationTestResult'; const reactTemplate = 'test/test-templates/react-template'; const nunjucksTemplate = 'test/test-templates/nunjucks-template'; +//temp location where react template is copied for each test that does some mutation on template files +const copyOfReactTemplate = 'test/temp/reactTemplate'; describe('Integration testing generateFromFile() to make sure the result of the generation is not changend comparing to snapshot', () => { const generateFolderName = () => { @@ -19,9 +22,28 @@ describe('Integration testing generateFromFile() to make sure the result of the return path.resolve(mainTestResultPath, crypto.randomBytes(4).toString('hex')); }; - jest.setTimeout(60000); + const getCleanReactTemplate = async () => { + //for each test new react template is needed in unique location + const newReactTemplateLocation = path.resolve(copyOfReactTemplate, crypto.randomBytes(4).toString('hex')); + await copy(reactTemplate, newReactTemplateLocation); + return newReactTemplateLocation; + }; + + jest.setTimeout(100000); const testOutputFile = 'test-file.md'; + const tempJsContent = ` + import { File, Text } from '@asyncapi/generator-react-sdk'; + + export default function() { + return ( + + Test + + ); + } + `; + it('generated using Nunjucks template', async () => { const outputDir = generateFolderName(); const generator = new Generator(nunjucksTemplate, outputDir, { @@ -56,8 +78,52 @@ describe('Integration testing generateFromFile() to make sure the result of the expect(file).toMatchSnapshot(); }); + it('check if the temp.md file is created with compile option true', async () => { + const outputDir = generateFolderName(); + const cleanReactTemplate = await getCleanReactTemplate(); + // Create temp.md.js file dynamically + + const tempJsPath = path.join(cleanReactTemplate, 'template/temp.md.js'); + // Create temp.md.js file dynamically + await writeFile(tempJsPath, tempJsContent); + + const generator = new Generator(cleanReactTemplate, outputDir, { + forceWrite: true, + compile: true, + debug: true, + }); + await generator.generateFromFile(dummySpecPath); + + const tempMdPath = path.join(outputDir, 'temp.md'); + + // Check the content of temp.md + const tempMdContent = await readFile(tempMdPath, 'utf8'); + expect(tempMdContent.trim()).toBe('Test'); + }); + + it('check if the temp.md file is not created when compile option is false', async () => { + const outputDir = generateFolderName(); + const cleanReactTemplate = await getCleanReactTemplate(); + // Create temp.md.js file dynamically + const tempJsPath = path.join(cleanReactTemplate, 'template/temp.md.js'); + await writeFile(tempJsPath, tempJsContent); + + const generator = new Generator(cleanReactTemplate, outputDir, { + forceWrite: true, + compile: false, + debug: true + }); + await generator.generateFromFile(dummySpecPath); + + // Check if temp.md is not created in the output directory + const tempMdPath = path.join(outputDir, 'temp.md'); + const tempMdExists = await access(tempMdPath).then(() => true).catch(() => false); + expect(tempMdExists).toBe(false); + }); + it('should ignore specified files with noOverwriteGlobs', async () => { const outputDir = generateFolderName(); + const cleanReactTemplate = await getCleanReactTemplate(); // Manually create a file to test if it's not overwritten await mkdir(outputDir, { recursive: true }); // Create a variable to store the file content @@ -67,7 +133,7 @@ describe('Integration testing generateFromFile() to make sure the result of the await writeFile(testFilePath, testContent); // Manually create an output first, before generation, with additional custom file to validate if later it is still there, not overwritten - const generator = new Generator(reactTemplate, outputDir, { + const generator = new Generator(cleanReactTemplate, outputDir, { forceWrite: true, noOverwriteGlobs: [`**/${testOutputFile}`], debug: true, diff --git a/apps/generator/test/test-project/test-project.test.js b/apps/generator/test/test-project/test-project.test.js index e740df46a..ded760bd7 100644 --- a/apps/generator/test/test-project/test-project.test.js +++ b/apps/generator/test/test-project/test-project.test.js @@ -41,7 +41,7 @@ describe('Testing if markdown was generated with proper version of the template' it('Test B - generated markdown should contain new content because of explicit fresh installation of different template version (install: true)', async () => { const templateVersion = '0.0.2'; - const generator = new Generator(`${templateName}@${templateVersion}`, tempOutputResults, { forceWrite: true, install: true, debug: true, templateParams: { version: 'v1', mode: 'production' } }); + const generator = new Generator(`${templateName}@${templateVersion}`, tempOutputResults, { compile: true, forceWrite: true, install: true, debug: true, templateParams: { version: 'v1', mode: 'production' } }); await generator.generateFromFile(dummySpecPath); const file = await readFile(path.join(tempOutputResults, fileToCheck), 'utf8'); @@ -95,4 +95,4 @@ describe('Testing if markdown was generated with proper version of the template' expect(console.log).toHaveBeenCalledWith(logMessage.templateVersion(version)); expect(console.log).toHaveBeenCalledWith(logMessage.NPM_INSTALL_TRIGGER); }); -}); \ No newline at end of file +}); diff --git a/apps/generator/test/test-project/test-registry.test.js b/apps/generator/test/test-project/test-registry.test.js index 6ef8d4b5e..d4ab648a3 100644 --- a/apps/generator/test/test-project/test-registry.test.js +++ b/apps/generator/test/test-project/test-registry.test.js @@ -16,6 +16,7 @@ describe('Integration testing generateFromFile() to make sure the template can b it('generated using private registory', async () => { const generator = new Generator('react-template', tempOutputResults, { + compile: true, debug: true, install: true, forceWrite: true, @@ -41,6 +42,7 @@ describe('Integration testing generateFromFile() to make sure the template can b it('generated using private registory from npm config', async () => { const generator = new Generator('react-template', tempOutputResults, { + compile: true, debug: true, install: true, forceWrite: true, diff --git a/package-lock.json b/package-lock.json index add94e1fb..45663cb5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,6 @@ "resolve-pkg": "^2.0.0", "semver": "^7.3.2", "simple-git": "^3.3.0", - "source-map-support": "^0.5.19", "ts-node": "^10.9.1", "typescript": "^4.9.3" }, @@ -60,6 +59,7 @@ "eslint-plugin-jest": "^23.8.2", "eslint-plugin-react": "^7.34.1", "eslint-plugin-sonarjs": "^0.5.0", + "fs-extra": "9.1.0", "jest": "^27.3.1", "jsdoc-to-markdown": "^7.1.1", "markdown-toc": "^1.2.0", @@ -71,6 +71,42 @@ "npm": ">=8.19.0" } }, + "apps/generator/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "apps/generator/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "apps/generator/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "apps/nunjucks-filters": { "name": "@asyncapi/nunjucks-filters", "version": "2.1.0", @@ -4382,6 +4418,15 @@ "dev": true, "license": "MIT" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/autolinker": { "version": "0.28.1", "dev": true,