diff --git a/.gitignore b/.gitignore index bedeac419e..b23f1e0c19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +.idea/ secrets.env .DS_Store cjs/ esm/ +types/ diff --git a/frontend/packages/cli/bin/backstage-cli b/frontend/packages/cli/bin/backstage-cli new file mode 100755 index 0000000000..7aa96ef3d4 --- /dev/null +++ b/frontend/packages/cli/bin/backstage-cli @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../cjs'); diff --git a/frontend/packages/cli/cli.js b/frontend/packages/cli/cli.js deleted file mode 100755 index 4ad3db98a9..0000000000 --- a/frontend/packages/cli/cli.js +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node -const hello = require('./src/index.ts'); - -console.log('output:', hello()); diff --git a/frontend/packages/cli/package.json b/frontend/packages/cli/package.json index 8f145e4ee1..9d14a392a3 100644 --- a/frontend/packages/cli/package.json +++ b/frontend/packages/cli/package.json @@ -8,12 +8,34 @@ "scripts": { "build": "web-scripts build", "lint": "web-scripts lint", - "test": "web-scripts test" + "test": "web-scripts test", + "start": "nodemon ." }, "devDependencies": { - "@spotify/web-scripts": "^6.0.0" + "@spotify/web-scripts": "^6.0.0", + "@types/node": "^13.7.2", + "@types/inquirer": "^6.5.0", + "nodemon": "^2.0.2", + "ts-node": "^8.6.2" }, "bin": { - "backstage-cli": "cli.js" + "backstage-cli": "bin/backstage-cli" + }, + "dependencies": { + "commander": "^4.1.1", + "dashify": "^2.0.0", + "handlebars": "^4.7.3", + "replace-in-file": "^5.0.2", + "inquirer": "^7.0.4" + }, + "files": [ + "templates", + "bin", + "cjs" + ], + "nodemonConfig": { + "watch": "./src", + "exec": "ts-node", + "ext": "ts" } } diff --git a/frontend/packages/cli/src/commands/createPlugin.test.ts b/frontend/packages/cli/src/commands/createPlugin.test.ts new file mode 100644 index 0000000000..46d7bc982f --- /dev/null +++ b/frontend/packages/cli/src/commands/createPlugin.test.ts @@ -0,0 +1,52 @@ +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import { createPluginFolder, createFileFromTemplate } from './createPlugin'; + +describe('createPlugin', () => { + describe('createPluginFolder', () => { + it('should create a plugin directory in the correct place', () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); + try { + const pluginFolder = createPluginFolder(tempDir, 'foo'); + expect(fs.existsSync(pluginFolder)).toBe(true); + expect(pluginFolder).toMatch(/packages\/plugins\/foo/); + } finally { + fs.rmdirSync(tempDir, { recursive: true }); + } + }); + + it('should not create a plugin directory if it already exists', () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); + try { + const pluginFolder = createPluginFolder(tempDir, 'foo'); + expect(fs.existsSync(pluginFolder)).toBe(true); + expect(() => createPluginFolder(tempDir, 'foo')).toThrowError( + /A plugin with the same name already exists/, + ); + } finally { + fs.rmdirSync(tempDir, { recursive: true }); + } + }); + }); + + describe('createFileFromTemplate', () => { + it('should generate a valid output with inserted values', () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); + try { + const sourceData = '{"name": "@spotify-backstage/{{id}}"}'; + const targetData = '{"name": "@spotify-backstage/foo"}'; + const sourcePath = path.join(tempDir, 'in.hbs'); + const targetPath = path.join(tempDir, 'out.json'); + fs.writeFileSync(sourcePath, sourceData); + + createFileFromTemplate(sourcePath, targetPath, { id: 'foo' }); + + expect(fs.existsSync(targetPath)).toBe(true); + expect(fs.readFileSync(targetPath).toString()).toBe(targetData); + } finally { + fs.rmdirSync(tempDir, { recursive: true }); + } + }); + }); +}); diff --git a/frontend/packages/cli/src/commands/createPlugin.ts b/frontend/packages/cli/src/commands/createPlugin.ts new file mode 100644 index 0000000000..03e4366b0b --- /dev/null +++ b/frontend/packages/cli/src/commands/createPlugin.ts @@ -0,0 +1,83 @@ +import fs from 'fs'; +import path from 'path'; +import handlebars from 'handlebars'; +import inquirer from 'inquirer'; + +export const createPluginFolder = (rootDir: string, id: string): string => { + const destination = path.join(rootDir, 'packages', 'plugins', id); + + if (fs.existsSync(destination)) { + throw new Error( + `A plugin with the same name already exists: ${destination}`, + ); + } + + try { + fs.mkdirSync(destination, { recursive: true }); + return destination; + } catch (e) { + throw new Error( + `Failed to create plugin directory: ${destination}: ${e.message}`, + ); + } +}; + +export const createFileFromTemplate = ( + sourcePath: string, + destinationPath: string, + answers: { [key: string]: string }, +) => { + const template = fs.readFileSync(sourcePath); + const compiled = handlebars.compile(template.toString()); + const contents = compiled({ + name: path.basename(destinationPath), + ...answers, + }); + try { + fs.writeFileSync(destinationPath, contents); + } catch (e) { + throw new Error(`Failed to create file: ${destinationPath}: ${e.message}`); + } +}; + +const createPlugin = async (): Promise => { + const currentDir = process.argv[1]; + const questions = [ + { + type: 'input', + name: 'id', + message: 'Enter an ID for the plugin [required]', + validate: (value: any) => + value ? true : 'Please enter an ID for the plugin', + }, + ]; + + const answers: { [key: string]: string } = await inquirer.prompt(questions); + const destinationFolder = createPluginFolder( + path.join(currentDir, '..', '..', '..'), + answers.id, + ); + + const templateFolder = path.join( + currentDir, + '..', + '..', + '@spotify-backstage', + 'cli', + 'templates', + 'default-plugin', + ); + const files = [{ input: 'package.json.hbs', output: 'package.json' }]; + + files.forEach(file => { + createFileFromTemplate( + path.join(templateFolder, file.input), + path.join(destinationFolder, file.output), + answers, + ); + }); + + return destinationFolder; +}; + +export default createPlugin; diff --git a/frontend/packages/cli/src/index.ts b/frontend/packages/cli/src/index.ts index a6e3282955..ba9462dc3d 100644 --- a/frontend/packages/cli/src/index.ts +++ b/frontend/packages/cli/src/index.ts @@ -1,3 +1,22 @@ -const hello = () => 'Hello World'; +import program from 'commander'; +import createPluginCommand from './commands/createPlugin'; -module.exports = hello; +const main = (argv: string[]) => { + program + .command('create-plugin') + .description('Creates a new plugin in the current repository') + .action(createPluginCommand); + + program.on('command:*', () => { + console.error( + 'Invalid command: %s\nSee --help for a list of available commands.', + program.args.join(' '), + ); + process.exit(1); + }); + + program.parse(argv); +}; + +// main(process.argv); +main([process.argv[0], process.argv[1], 'create-plugin']); diff --git a/frontend/packages/cli/templates/default-plugin/ScaffoldPage.hbs b/frontend/packages/cli/templates/default-plugin/ScaffoldPage.hbs new file mode 100644 index 0000000000..3a02ea4403 --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/ScaffoldPage.hbs @@ -0,0 +1,32 @@ +import React{{#if ts}}, { FC }{{/if}} from 'react'; +import { Typography } from '@material-ui/core'; +import { theme } from 'core/app/PageThemeProvider'; +import InfoCard from 'shared/components/InfoCard'; +import { Content, ContentHeader, Header, HeaderLabel, Navigation, NavItem, Page } from 'shared/components/layout'; + +/** + * This component demonstrates how to render a page with its own navigation. + * You typically create layouts that can be shared between multiple pages. + * Layouts can be found in {@link src/shared/components/layout/} + */ +const {{id}}Page{{#if ts}}: FC<>{{/if}} = () => { + return ( + +
+ + +
+ + + + + + + Hello world, this is your new plugin! + + +
+ ); +}; + +export default {{id}}Page; diff --git a/frontend/packages/cli/templates/default-plugin/ScaffoldPage.test.hbs b/frontend/packages/cli/templates/default-plugin/ScaffoldPage.test.hbs new file mode 100644 index 0000000000..b1f12525c9 --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/ScaffoldPage.test.hbs @@ -0,0 +1,14 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { wrapInThemedTestApp } from 'testUtils'; + +import {{id}}Page from './{{id}}Page'; + +const minProps = {}; + +describe('<{{id}}Page />', () => { + it('renders without exploding', () => { + const { getByText } = render(wrapInThemedTestApp(<{{id}}Page {...minProps} />)); + expect(getByText(/Hello world.*/)).toBeInTheDocument(); + }); +}); diff --git a/frontend/packages/cli/templates/default-plugin/ScaffoldPlugin.hbs b/frontend/packages/cli/templates/default-plugin/ScaffoldPlugin.hbs new file mode 100644 index 0000000000..770e379565 --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/ScaffoldPlugin.hbs @@ -0,0 +1,14 @@ +import React from 'react'; +import { createPlugin } from 'shared/pluginApi'; + +const {{id}}Page = React.lazy( + /* istanbul ignore next */ () => import(/* webpackChunkName: "{{folder}}" */ 'plugins/{{folder}}/{{id}}Page'), +); + +export default createPlugin({ + manifest: require('./plugin-info.yaml'), + + register({ router }) { + router.registerRoute('{{route}}', {{id}}Page); + } +}); diff --git a/frontend/packages/cli/templates/default-plugin/index.hbs b/frontend/packages/cli/templates/default-plugin/index.hbs new file mode 100644 index 0000000000..07b190dcb7 --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/index.hbs @@ -0,0 +1 @@ +export { default as {{id}} } from 'plugins/{{folder}}/{{id}}Plugin'; diff --git a/frontend/packages/cli/templates/default-plugin/index.js b/frontend/packages/cli/templates/default-plugin/index.js new file mode 100644 index 0000000000..3243cf662b --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/index.js @@ -0,0 +1 @@ +export { default as ScaffoldPlugin } from 'plugins/scaffold/ScaffoldPlugin'; diff --git a/frontend/packages/cli/templates/default-plugin/package.json.hbs b/frontend/packages/cli/templates/default-plugin/package.json.hbs new file mode 100644 index 0000000000..d091fbc1ec --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/package.json.hbs @@ -0,0 +1,28 @@ +{ + "name": "@spotify-backstage/{{id}}", + "version": "{{version}}", + "main": "src/index.ts", + "main:src": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "scripts": { + "build": "web-scripts build", + "lint": "web-scripts lint", + "test": "web-scripts test", + "start": "nodemon ." + }, + "devDependencies": { + "@spotify/web-scripts": "^6.0.0", + "@types/node": "^13.7.2", + "nodemon": "^2.0.2", + "ts-node": "^8.6.2" + }, + "bin": { + "backstage-{{id}}": "bin/backstage-{{id}}" + }, + "nodemonConfig": { + "watch": "./src", + "exec": "ts-node", + "ext": "ts" + } +} diff --git a/frontend/packages/cli/templates/default-plugin/plugin-info.hbs b/frontend/packages/cli/templates/default-plugin/plugin-info.hbs new file mode 100644 index 0000000000..125d7a66f3 --- /dev/null +++ b/frontend/packages/cli/templates/default-plugin/plugin-info.hbs @@ -0,0 +1,10 @@ +--- +id: {{id}} +title: {{title}} +description: {{desc}} +owner: {{owner}} +visibility: public +facts: + support_channel: {{support_channel}} + authors: + - slack: {{author}} diff --git a/frontend/packages/cli/tsconfig.json b/frontend/packages/cli/tsconfig.json new file mode 100644 index 0000000000..84b9319b94 --- /dev/null +++ b/frontend/packages/cli/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@spotify/web-scripts/config/tsconfig.json", + "include": ["src"], + "compilerOptions": { + "baseUrl": "src", + "paths": { + "*": ["src/*"] + } + } +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b3c7d0d45b..9d0342195a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2860,6 +2860,14 @@ resolved "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860" integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw== +"@types/inquirer@^6.5.0": + version "6.5.0" + resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz#b83b0bf30b88b8be7246d40e51d32fe9d10e09be" + integrity sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw== + dependencies: + "@types/through" "*" + rxjs "^6.4.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.1" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -2920,6 +2928,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9" integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA== +"@types/node@^13.7.2": + version "13.7.2" + resolved "https://registry.npmjs.org/@types/node/-/node-13.7.2.tgz#50375b95b5845a34efda2ffb3a087c7becbc46c6" + integrity sha512-uvilvAQbdJvnSBFcKJ2td4016urcGvsiR+N4dHGU87ml8O2Vl6l+ErOi9w0kXSPiwJ1AYlIW+0pDXDWWMOiWbw== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -3004,6 +3017,13 @@ "@types/react-dom" "*" "@types/testing-library__dom" "*" +"@types/through@*": + version "0.0.30" + resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" @@ -3510,6 +3530,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -4506,7 +4531,7 @@ chokidar@^2.0.2, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.3.0: +chokidar@^3.2.2, chokidar@^3.3.0: version "3.3.1" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== @@ -4823,7 +4848,7 @@ commander@^2.11.0, commander@^2.20.0, commander@~2.20.3: resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.1: +commander@^4.0.1, commander@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -5636,6 +5661,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +dashify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/dashify/-/dashify-2.0.0.tgz#fff270ca2868ca427fee571de35691d6e437a648" + integrity sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A== + data-urls@^1.0.0, data-urls@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" @@ -5676,7 +5706,7 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5884,6 +5914,11 @@ diff-sequences@^25.1.0: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32" integrity sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -7661,7 +7696,7 @@ handle-thing@^2.0.0: resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== -handlebars@^4.4.0: +handlebars@^4.4.0, handlebars@^4.7.3: version "4.7.3" resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz#8ece2797826886cf8082d1726ff21d2a022550ee" integrity sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg== @@ -8082,6 +8117,11 @@ iferr@^1.0.2: resolved "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz#e9fde49a9da06dc4a4194c6c9ed6d08305037a6d" integrity sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + ignore-walk@^3.0.1: version "3.0.3" resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" @@ -8268,7 +8308,7 @@ inquirer@6.5.0: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@7.0.4, inquirer@^7.0.0: +inquirer@7.0.4, inquirer@^7.0.0, inquirer@^7.0.4: version "7.0.4" resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== @@ -10611,6 +10651,11 @@ make-error@1.x: resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + make-fetch-happen@^5.0.0: version "5.0.2" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" @@ -11328,6 +11373,22 @@ node-releases@^1.1.47: dependencies: semver "^6.3.0" +nodemon@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz#9c7efeaaf9b8259295a97e5d4585ba8f0cbe50b0" + integrity sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw== + dependencies: + chokidar "^3.2.2" + debug "^3.2.6" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.7" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.2" + update-notifier "^2.5.0" + nopt@^4.0.1, nopt@~4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -11336,6 +11397,13 @@ nopt@^4.0.1, nopt@~4.0.1: abbrev "1" osenv "^0.1.4" +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.4.0, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -13273,6 +13341,11 @@ psl@^1.1.24, psl@^1.1.28: resolved "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== +pstree.remy@^1.1.7: + version "1.1.7" + resolved "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3" + integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A== + public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" @@ -14001,6 +14074,15 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" +replace-in-file@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/replace-in-file/-/replace-in-file-5.0.2.tgz#bd26203b66dfb5b8112ae36a2d2cf928ea4cfe12" + integrity sha512-1Vc7Sbr/rTuHgU1PZuBb7tGsFx3D4NKdhV4BpEF2MuN/6+SoXcFtx+dZ1Zz+5Dq4k5x9js87Y+gXQYPTQ9ppkA== + dependencies: + chalk "^3.0.0" + glob "^7.1.6" + yargs "^15.0.2" + request-promise-core@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" @@ -15337,7 +15419,7 @@ supports-color@^2.0.0: resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^5.3.0: +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -15686,6 +15768,13 @@ toidentifier@1.0.0: resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -15759,6 +15848,17 @@ ts-jest@^25.0.0: semver "^5.5" yargs-parser "10.x" +ts-node@^8.6.2: + version "8.6.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" + integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "3.1.1" + ts-pnp@1.1.5, ts-pnp@^1.1.2: version "1.1.5" resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" @@ -15886,6 +15986,13 @@ umask@^1.1.0, umask@~1.1.0: resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= +undefsafe@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" + integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== + dependencies: + debug "^2.2.0" + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -16900,7 +17007,7 @@ yargs@^14.2.2: y18n "^4.0.0" yargs-parser "^15.0.0" -yargs@^15.0.0, yargs@^15.0.1: +yargs@^15.0.0, yargs@^15.0.1, yargs@^15.0.2: version "15.1.0" resolved "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219" integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg== @@ -16936,6 +17043,11 @@ yargs@^8.0.2: y18n "^3.2.1" yargs-parser "^7.0.0" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + zen-observable@^0.8.15: version "0.8.15" resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"