diff --git a/.changeset/few-penguins-watch.md b/.changeset/few-penguins-watch.md new file mode 100644 index 0000000000..3d7d9fb457 --- /dev/null +++ b/.changeset/few-penguins-watch.md @@ -0,0 +1,45 @@ +--- +'@backstage/plugin-catalog-import': minor +--- + +Add initial support for customizing the catalog import page. + +It is now possible to pass a custom layout to the import page, as it's already +supported by the search page. If no custom layout is passed, the default layout +is used. + +```typescript +}> + +
+ + + + Start tracking your component in Backstage by adding it to the + software catalog. + + + + + + Hello World + + + + + + + + + +``` + +Previously it was possible to disable and customize the automatic pull request +feature by passing options to `` (`pullRequest.disable` and +`pullRequest.preparePullRequest`). This functionality is moved to the +`CatalogImportApi` which now provides an optional `preparePullRequest()` +function. The function can either be overridden to generate a different content +for the pull request, or removed to disable this feature. + +The export of the long term deprecated legacy `` is removed, migrate to +`` instead. diff --git a/.changeset/proud-adults-bathe.md b/.changeset/proud-adults-bathe.md new file mode 100644 index 0000000000..a8f15b18a3 --- /dev/null +++ b/.changeset/proud-adults-bathe.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-ldap': patch +--- + +Alters LDAP processor to handle one SearchEntry at a time diff --git a/.changeset/spicy-yaks-notice.md b/.changeset/spicy-yaks-notice.md new file mode 100644 index 0000000000..ae5c3ba1a5 --- /dev/null +++ b/.changeset/spicy-yaks-notice.md @@ -0,0 +1,19 @@ +--- +'@backstage/backend-common': patch +'@backstage/catalog-model': patch +'@backstage/cli': patch +'@backstage/config': patch +'@backstage/core-components': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-catalog-backend-module-ldap': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-circleci': patch +'@backstage/plugin-kafka-backend': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-rollbar': patch +'@backstage/plugin-rollbar-backend': patch +'@backstage/plugin-search-backend-module-pg': patch +--- + +Bump `lodash` to remediate `SNYK-JS-LODASH-590103` security vulnerability diff --git a/.changeset/techdocs-shy-beans-prove.md b/.changeset/techdocs-shy-beans-prove.md new file mode 100644 index 0000000000..5aa9db1da7 --- /dev/null +++ b/.changeset/techdocs-shy-beans-prove.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Make techdocs context search bar width adjust on smaller screens. diff --git a/microsite/package.json b/microsite/package.json index b3048612a7..dafab893e2 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -19,7 +19,7 @@ "@spotify/prettier-config": "^11.0.0", "docusaurus": "^2.0.0-alpha.70", "js-yaml": "^4.1.0", - "prettier": "^2.4.0", + "prettier": "^2.4.1", "yarn-lock-check": "^1.0.5" }, "prettier": "@spotify/prettier-config" diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index ec80bbc01f..56b6c122ae 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -306,7 +306,15 @@ class Index extends React.Component { src={`${baseUrl}animations/backstage-techdocs-icon-1.gif`} /> - Backstage TechDocs + + Backstage TechDocs{' '} + + (beta) + + Docs like code ; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (ae-forgotten-export) The symbol "SearchCallback" needs to be exported by the entry point index.d.ts + searchStreaming( + dn: string, + options: SearchOptions, + f: SearchCallback, + ): Promise; } // Warning: (ae-missing-release-tag) "LdapOrgEntityProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 5909c28a98..2787287b62 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -34,7 +34,7 @@ "@backstage/plugin-catalog-backend": "^0.13.3", "@types/ldapjs": "^2.2.0", "ldapjs": "^2.2.0", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "winston": "^3.2.1" }, "devDependencies": { diff --git a/plugins/catalog-backend-module-ldap/src/ldap/client.ts b/plugins/catalog-backend-module-ldap/src/ldap/client.ts index 5418577381..048aa40dc1 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/client.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/client.ts @@ -24,6 +24,10 @@ import { LdapVendor, } from './vendors'; +export interface SearchCallback { + (entry: SearchEntry): void; +} + /** * Basic wrapper for the ldapjs library. * @@ -120,6 +124,53 @@ export class LdapClient { } } + /** + * Performs an LDAP search operation, calls a function on each entry to limit memory usage + * + * @param dn The fully qualified base DN to search within + * @param options The search options + * @param f The callback to call on each search entry + */ + async searchStreaming( + dn: string, + options: SearchOptions, + f: SearchCallback, + ): Promise { + try { + return await new Promise((resolve, reject) => { + this.client.search(dn, options, (err, res) => { + if (err) { + reject(new Error(errorString(err))); + } + + res.on('searchReference', () => { + reject(new Error('Unable to handle referral')); + }); + + res.on('searchEntry', entry => { + f(entry); + }); + + res.on('error', e => { + reject(new Error(errorString(e))); + }); + + res.on('end', r => { + if (!r) { + throw new Error('Null response'); + } else if (r.status !== 0) { + throw new Error(`Got status ${r.status}: ${r.errorMessage}`); + } else { + resolve(); + } + }); + }); + }); + } catch (e) { + throw new Error(`LDAP search at DN "${dn}" failed, ${e.message}`); + } + } + /** * Get the Server Vendor. * Currently only detects Microsoft Active Directory Servers. diff --git a/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts b/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts index 49afd65c30..c4f203c5db 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts @@ -67,7 +67,7 @@ function searchEntry( describe('readLdapUsers', () => { const client: jest.Mocked = { - search: jest.fn(), + searchStreaming: jest.fn(), getVendor: jest.fn(), } as any; @@ -75,18 +75,20 @@ describe('readLdapUsers', () => { it('transfers all attributes from a default ldap vendor', async () => { client.getVendor.mockResolvedValue(DefaultLdapVendor); - client.search.mockResolvedValue([ - searchEntry({ - uid: ['uid-value'], - description: ['description-value'], - cn: ['cn-value'], - mail: ['mail-value'], - avatarUrl: ['avatarUrl-value'], - memberOf: ['x', 'y', 'z'], - entryDN: ['dn-value'], - entryUUID: ['uuid-value'], - }), - ]); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn( + searchEntry({ + uid: ['uid-value'], + description: ['description-value'], + cn: ['cn-value'], + mail: ['mail-value'], + avatarUrl: ['avatarUrl-value'], + memberOf: ['x', 'y', 'z'], + entryDN: ['dn-value'], + entryUUID: ['uuid-value'], + }), + ); + }); const config: UserConfig = { dn: 'ddd', options: {}, @@ -129,23 +131,25 @@ describe('readLdapUsers', () => { it('transfers all attributes from Microsoft Active Directory', async () => { client.getVendor.mockResolvedValue(ActiveDirectoryVendor); - client.search.mockResolvedValue([ - searchEntry({ - uid: ['uid-value'], - description: ['description-value'], - cn: ['cn-value'], - mail: ['mail-value'], - avatarUrl: ['avatarUrl-value'], - memberOf: ['x', 'y', 'z'], - distinguishedName: ['dn-value'], - objectGUID: [ - Buffer.from([ - 68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160, - 152, - ]), - ], - }), - ]); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn( + searchEntry({ + uid: ['uid-value'], + description: ['description-value'], + cn: ['cn-value'], + mail: ['mail-value'], + avatarUrl: ['avatarUrl-value'], + memberOf: ['x', 'y', 'z'], + distinguishedName: ['dn-value'], + objectGUID: [ + Buffer.from([ + 68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160, + 152, + ]), + ], + }), + ); + }); const config: UserConfig = { dn: 'ddd', options: {}, @@ -189,7 +193,7 @@ describe('readLdapUsers', () => { describe('readLdapGroups', () => { const client: jest.Mocked = { - search: jest.fn(), + searchStreaming: jest.fn(), getVendor: jest.fn(), } as any; @@ -197,19 +201,21 @@ describe('readLdapGroups', () => { it('transfers all attributes from a default ldap vendor', async () => { client.getVendor.mockResolvedValue(DefaultLdapVendor); - client.search.mockResolvedValue([ - searchEntry({ - cn: ['cn-value'], - description: ['description-value'], - tt: ['type-value'], - mail: ['mail-value'], - avatarUrl: ['avatarUrl-value'], - memberOf: ['x', 'y', 'z'], - member: ['e', 'f', 'g'], - entryDN: ['dn-value'], - entryUUID: ['uuid-value'], - }), - ]); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn( + searchEntry({ + cn: ['cn-value'], + description: ['description-value'], + tt: ['type-value'], + mail: ['mail-value'], + avatarUrl: ['avatarUrl-value'], + memberOf: ['x', 'y', 'z'], + member: ['e', 'f', 'g'], + entryDN: ['dn-value'], + entryUUID: ['uuid-value'], + }), + ); + }); const config: GroupConfig = { dn: 'ddd', options: {}, @@ -260,24 +266,26 @@ describe('readLdapGroups', () => { }); it('transfers all attributes from Microsoft Active Directory', async () => { client.getVendor.mockResolvedValue(ActiveDirectoryVendor); - client.search.mockResolvedValue([ - searchEntry({ - cn: ['cn-value'], - description: ['description-value'], - tt: ['type-value'], - mail: ['mail-value'], - avatarUrl: ['avatarUrl-value'], - memberOf: ['x', 'y', 'z'], - member: ['e', 'f', 'g'], - distinguishedName: ['dn-value'], - objectGUID: [ - Buffer.from([ - 68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160, - 152, - ]), - ], - }), - ]); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn( + searchEntry({ + cn: ['cn-value'], + description: ['description-value'], + tt: ['type-value'], + mail: ['mail-value'], + avatarUrl: ['avatarUrl-value'], + memberOf: ['x', 'y', 'z'], + member: ['e', 'f', 'g'], + distinguishedName: ['dn-value'], + objectGUID: [ + Buffer.from([ + 68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160, + 152, + ]), + ], + }), + ); + }); const config: GroupConfig = { dn: 'ddd', options: {}, diff --git a/plugins/catalog-backend-module-ldap/src/ldap/read.ts b/plugins/catalog-backend-module-ldap/src/ldap/read.ts index 77ca43a519..2d6e3c1608 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/read.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/read.ts @@ -107,13 +107,11 @@ export async function readLdapUsers( const transformer = opts?.transformer ?? defaultUserTransformer; - const entries = await client.search(dn, options); - - for (const user of entries) { + await client.searchStreaming(dn, options, async user => { const entity = await transformer(vendor, config, user); if (!entity) { - continue; + return; } mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => { @@ -121,7 +119,7 @@ export async function readLdapUsers( }); entities.push(entity); - } + }); return { users: entities, userMemberOf }; } @@ -210,24 +208,26 @@ export async function readLdapGroups( const transformer = opts?.transformer ?? defaultGroupTransformer; - const entries = await client.search(dn, options); - - for (const group of entries) { - const entity = await transformer(vendor, config, group); - - if (!entity) { - continue; + await client.searchStreaming(dn, options, async entry => { + if (!entry) { + return; } - mapReferencesAttr(group, vendor, map.memberOf, (myDn, vs) => { + const entity = await transformer(vendor, config, entry); + + if (!entity) { + return; + } + + mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => { ensureItems(groupMemberOf, myDn, vs); }); - mapReferencesAttr(group, vendor, map.members, (myDn, vs) => { + mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => { ensureItems(groupMember, myDn, vs); }); groups.push(entity); - } + }); return { groups, diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 4209630cc5..397d4fffb3 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -35,7 +35,7 @@ "@backstage/plugin-catalog-backend": "^0.13.5", "@microsoft/microsoft-graph-types": "^1.25.0", "cross-fetch": "^3.0.6", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "p-limit": "^3.0.2", "winston": "^3.2.1", "qs": "^6.9.4" diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 9aba035f78..16f61c2b9a 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -51,7 +51,7 @@ "git-url-parse": "^11.6.0", "glob": "^7.1.6", "knex": "^0.95.1", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "luxon": "^2.0.2", "morgan": "^1.10.0", "p-limit": "^3.0.2", diff --git a/plugins/catalog-import/api-report.md b/plugins/catalog-import/api-report.md index 6752f95748..753244b28f 100644 --- a/plugins/catalog-import/api-report.md +++ b/plugins/catalog-import/api-report.md @@ -68,6 +68,11 @@ export interface CatalogImportApi { // (undocumented) analyzeUrl(url: string): Promise; // (undocumented) + preparePullRequest?(): Promise<{ + title: string; + body: string; + }>; + // (undocumented) submitPullRequest(options: { repositoryUrl: string; fileContent: string; @@ -94,10 +99,16 @@ export class CatalogImportClient implements CatalogImportApi { identityApi: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; catalogApi: CatalogApi; + configApi: ConfigApi; }); // (undocumented) analyzeUrl(url: string): Promise; // (undocumented) + preparePullRequest(): Promise<{ + title: string; + body: string; + }>; + // (undocumented) submitPullRequest({ repositoryUrl, fileContent, @@ -114,11 +125,10 @@ export class CatalogImportClient implements CatalogImportApi { }>; } -// Warning: (ae-forgotten-export) The symbol "StepperProviderOpts" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "CatalogImportPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const CatalogImportPage: (opts: StepperProviderOpts) => JSX.Element; +export const CatalogImportPage: () => JSX.Element; // Warning: (ae-missing-release-tag) "catalogImportPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -144,6 +154,11 @@ export function defaultGenerateStepper( defaults: StepperProvider, ): StepperProvider; +// Warning: (ae-missing-release-tag) "DefaultImportPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const DefaultImportPage: () => JSX.Element; + // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "EntityListComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -155,7 +170,12 @@ export const EntityListComponent: ({ onItemClick, firstListItem, withLinks, -}: Props_2) => JSX.Element; +}: Props) => JSX.Element; + +// Warning: (ae-missing-release-tag) "ImportInfoCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ImportInfoCard: () => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ImportStepper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -165,8 +185,7 @@ export const ImportStepper: ({ initialUrl, generateStepper, variant, - opts, -}: Props) => JSX.Element; +}: Props_2) => JSX.Element; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -203,11 +222,6 @@ export const PreviewPullRequestComponent: ({ classes, }: Props_7) => JSX.Element; -// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const Router: (opts: StepperProviderOpts) => JSX.Element; - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -230,8 +244,6 @@ export const StepPrepareCreatePullRequest: ({ onPrepare, onGoBack, renderFormFields, - defaultTitle, - defaultBody, }: Props_8) => JSX.Element; // Warnings were encountered during analysis: diff --git a/plugins/catalog-import/dev/index.tsx b/plugins/catalog-import/dev/index.tsx index 48d9320ca9..a0cf034782 100644 --- a/plugins/catalog-import/dev/index.tsx +++ b/plugins/catalog-import/dev/index.tsx @@ -29,7 +29,7 @@ import { EntityListComponent, ImportStepper, } from '../src'; -import { ImportComponentPage } from '../src/components/ImportComponentPage'; +import { ImportPage } from '../src/components/ImportPage'; import { Content, Header, InfoCard, Page } from '@backstage/core-components'; const getEntityNames = (url: string): EntityName[] => [ @@ -252,7 +252,7 @@ createDevApp() }) .addPage({ title: 'Catalog Import', - element: , + element: , }) .addPage({ title: 'Catalog Import 2', diff --git a/plugins/catalog-import/src/api/CatalogImportApi.ts b/plugins/catalog-import/src/api/CatalogImportApi.ts index 04e98f3d7c..a4645dc7ef 100644 --- a/plugins/catalog-import/src/api/CatalogImportApi.ts +++ b/plugins/catalog-import/src/api/CatalogImportApi.ts @@ -15,8 +15,8 @@ */ import { EntityName } from '@backstage/catalog-model'; -import { PartialEntity } from '../types'; import { createApiRef } from '@backstage/core-plugin-api'; +import { PartialEntity } from '../types'; export const catalogImportApiRef = createApiRef({ id: 'plugin.catalog-import.service', @@ -42,6 +42,10 @@ export type AnalyzeResult = export interface CatalogImportApi { analyzeUrl(url: string): Promise; + preparePullRequest?(): Promise<{ + title: string; + body: string; + }>; submitPullRequest(options: { repositoryUrl: string; fileContent: string; diff --git a/plugins/catalog-import/src/api/CatalogImportClient.test.ts b/plugins/catalog-import/src/api/CatalogImportClient.test.ts index 2936f956e4..0782b44147 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.test.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.test.ts @@ -115,6 +115,11 @@ describe('CatalogImportClient', () => { scmIntegrationsApi, identityApi, catalogApi, + configApi: new ConfigReader({ + app: { + baseUrl: 'https://demo.backstage.io/', + }, + }), }); }); @@ -443,4 +448,13 @@ describe('CatalogImportClient', () => { }); }); }); + + describe('preparePullRequest', () => { + test('should prepare pull request details', async () => { + await expect(catalogImportClient.preparePullRequest()).resolves.toEqual({ + title: 'Add catalog-info.yaml config file', + body: expect.any(String), + }); + }); + }); }); diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts index 2497af99d8..242f8440d1 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.ts @@ -17,6 +17,7 @@ import { CatalogApi } from '@backstage/catalog-client'; import { EntityName } from '@backstage/catalog-model'; import { + ConfigApi, DiscoveryApi, IdentityApi, OAuthApi, @@ -37,6 +38,7 @@ export class CatalogImportClient implements CatalogImportApi { private readonly githubAuthApi: OAuthApi; private readonly scmIntegrationsApi: ScmIntegrationRegistry; private readonly catalogApi: CatalogApi; + private readonly configApi: ConfigApi; constructor(options: { discoveryApi: DiscoveryApi; @@ -44,12 +46,14 @@ export class CatalogImportClient implements CatalogImportApi { identityApi: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; catalogApi: CatalogApi; + configApi: ConfigApi; }) { this.discoveryApi = options.discoveryApi; this.githubAuthApi = options.githubAuthApi; this.identityApi = options.identityApi; this.scmIntegrationsApi = options.scmIntegrationsApi; this.catalogApi = options.catalogApi; + this.configApi = options.configApi; } async analyzeUrl(url: string): Promise { @@ -114,6 +118,24 @@ export class CatalogImportClient implements CatalogImportApi { }; } + async preparePullRequest(): Promise<{ + title: string; + body: string; + }> { + const appTitle = + this.configApi.getOptionalString('app.title') ?? 'Backstage'; + const appBaseUrl = this.configApi.getString('app.baseUrl'); + + return { + title: 'Add catalog-info.yaml config file', + body: `This pull request adds a **Backstage entity metadata file** \ +to this repository so that the component can be added to the \ +[${appTitle} software catalog](${appBaseUrl}).\n\nAfter this pull request is merged, \ +the component will become available.\n\nFor more information, read an \ +[overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview).`, + }; + } + async submitPullRequest({ repositoryUrl, fileContent, diff --git a/plugins/catalog-import/src/components/ImportComponentPage.test.tsx b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx similarity index 74% rename from plugins/catalog-import/src/components/ImportComponentPage.test.tsx rename to plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx index 2e572c8936..57376c33f2 100644 --- a/plugins/catalog-import/src/components/ImportComponentPage.test.tsx +++ b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx @@ -15,21 +15,19 @@ */ import { CatalogClient } from '@backstage/catalog-client'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import { wrapInTestApp } from '@backstage/test-utils'; -import { act, render } from '@testing-library/react'; -import React from 'react'; -import { catalogImportApiRef, CatalogImportClient } from '../api'; -import { ImportComponentPage } from './ImportComponentPage'; - import { ApiProvider, ApiRegistry, ConfigReader, } from '@backstage/core-app-api'; import { configApiRef } from '@backstage/core-plugin-api'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { catalogImportApiRef, CatalogImportClient } from '../../api'; +import { DefaultImportPage } from './DefaultImportPage'; -describe('', () => { +describe('', () => { const identityApi = { getUserId: () => { return 'user'; @@ -63,23 +61,20 @@ describe('', () => { identityApi, scmIntegrationsApi: {} as any, catalogApi: {} as any, + configApi: {} as any, }), ); }); it('renders without exploding', async () => { - await act(async () => { - const { getByText } = render( - wrapInTestApp( - - - , - ), - ); + const { getByText } = await renderInTestApp( + + + , + ); - expect( - await getByText('Start tracking your component in Backstage'), - ).toBeInTheDocument(); - }); + expect( + getByText('Start tracking your component in Backstage'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.tsx b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.tsx new file mode 100644 index 0000000000..8693895ec0 --- /dev/null +++ b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.tsx @@ -0,0 +1,57 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Content, + ContentHeader, + Header, + Page, + SupportButton, +} from '@backstage/core-components'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import { Grid } from '@material-ui/core'; +import React from 'react'; +import { ImportInfoCard } from '../ImportInfoCard'; +import { ImportStepper } from '../ImportStepper'; + +export const DefaultImportPage = () => { + const configApi = useApi(configApiRef); + const appTitle = configApi.getOptional('app.title') || 'Backstage'; + + return ( + +
+ + + + Start tracking your component in {appTitle} by adding it to the + software catalog. + + + + + + + + + + + + + + + ); +}; diff --git a/plugins/catalog-import/src/components/DefaultImportPage/index.ts b/plugins/catalog-import/src/components/DefaultImportPage/index.ts new file mode 100644 index 0000000000..5a4d4907e3 --- /dev/null +++ b/plugins/catalog-import/src/components/DefaultImportPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { DefaultImportPage } from './DefaultImportPage'; diff --git a/plugins/catalog-import/src/components/ImportComponentPage.tsx b/plugins/catalog-import/src/components/ImportComponentPage.tsx deleted file mode 100644 index bf53af134f..0000000000 --- a/plugins/catalog-import/src/components/ImportComponentPage.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Chip, Grid, Typography } from '@material-ui/core'; -import React from 'react'; -import { ImportStepper } from './ImportStepper'; -import { StepperProviderOpts } from './ImportStepper/defaults'; - -import { configApiRef, useApi } from '@backstage/core-plugin-api'; -import { - Content, - ContentHeader, - Header, - InfoCard, - Page, - SupportButton, -} from '@backstage/core-components'; - -export const ImportComponentPage = (opts: StepperProviderOpts) => { - const configApi = useApi(configApiRef); - const appTitle = configApi.getOptional('app.title') || 'Backstage'; - - const integrations = configApi.getConfig('integrations'); - const hasGithubIntegration = integrations.has('github'); - - return ( - -
- - - - Start tracking your component in {appTitle} by adding it to the - software catalog. - - - - - - - - Enter the URL to your source code repository to add it to{' '} - {appTitle}. - - - Link to an existing entity file - - - Example:{' '} - - https://github.com/backstage/backstage/blob/master/catalog-info.yaml - - - - The wizard analyzes the file, previews the entities, and adds - them to the {appTitle} catalog. - - {hasGithubIntegration && ( - <> - - Link to a repository{' '} - - - - Example: https://github.com/backstage/backstage - - - The wizard discovers all catalog-info.yaml{' '} - files in the repository, previews the entities, and adds - them to the {appTitle} catalog. - - {!opts?.pullRequest?.disable && ( - - If no entities are found, the wizard will prepare a Pull - Request that adds an example{' '} - catalog-info.yaml and prepares the {appTitle}{' '} - catalog to load all entities as soon as the Pull Request - is merged. - - )} - - )} - - - - - - - - - - ); -}; diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx new file mode 100644 index 0000000000..4e0c3694cd --- /dev/null +++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ApiProvider, + ApiRegistry, + ConfigReader, +} from '@backstage/core-app-api'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { CatalogImportApi, catalogImportApiRef } from '../../api'; +import { ImportInfoCard } from './ImportInfoCard'; + +describe('', () => { + let apis: ApiRegistry; + let catalogImportApi: jest.Mocked; + + beforeEach(() => { + catalogImportApi = { + analyzeUrl: jest.fn(), + submitPullRequest: jest.fn(), + }; + + apis = ApiRegistry.with( + configApiRef, + new ConfigReader({ + integrations: { + github: [{ token: 'my-token' }], + }, + }), + ).with(catalogImportApiRef, catalogImportApi); + }); + + it('renders without exploding', async () => { + apis = ApiRegistry.with( + configApiRef, + new ConfigReader({ integrations: {} }), + ).with(catalogImportApiRef, catalogImportApi); + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText('Register an existing component')).toBeInTheDocument(); + }); + + it('renders section on GitHub discovery if supported', async () => { + catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' }); + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText(/The wizard discovers all/)).toBeInTheDocument(); + }); + + it('renders section on pull requests if supported', async () => { + catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' }); + + const { getByText } = await renderInTestApp( + + + , + ); + + expect( + getByText(/the wizard will prepare a Pull Request/), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx new file mode 100644 index 0000000000..2fab223f20 --- /dev/null +++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InfoCard } from '@backstage/core-components'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import { Chip, Typography } from '@material-ui/core'; +import React from 'react'; +import { catalogImportApiRef } from '../../api'; + +export const ImportInfoCard = () => { + const configApi = useApi(configApiRef); + const appTitle = configApi.getOptional('app.title') || 'Backstage'; + const catalogImportApi = useApi(catalogImportApiRef); + + const integrations = configApi.getConfig('integrations'); + const hasGithubIntegration = integrations.has('github'); + + return ( + + + Enter the URL to your source code repository to add it to {appTitle}. + + Link to an existing entity file + + Example:{' '} + + https://github.com/backstage/backstage/blob/master/catalog-info.yaml + + + + The wizard analyzes the file, previews the entities, and adds them to + the {appTitle} catalog. + + {hasGithubIntegration && ( + <> + + Link to a repository{' '} + + + + Example: https://github.com/backstage/backstage + + + The wizard discovers all catalog-info.yaml files in the + repository, previews the entities, and adds them to the {appTitle}{' '} + catalog. + + {catalogImportApi.preparePullRequest && ( + + If no entities are found, the wizard will prepare a Pull Request + that adds an example catalog-info.yaml and prepares + the {appTitle} catalog to load all entities as soon as the Pull + Request is merged. + + )} + + )} + + ); +}; diff --git a/plugins/catalog-import/src/components/ImportInfoCard/index.ts b/plugins/catalog-import/src/components/ImportInfoCard/index.ts new file mode 100644 index 0000000000..c82e88f7e8 --- /dev/null +++ b/plugins/catalog-import/src/components/ImportInfoCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ImportInfoCard } from './ImportInfoCard'; diff --git a/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx b/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx new file mode 100644 index 0000000000..fe3c3673f8 --- /dev/null +++ b/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx @@ -0,0 +1,100 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CatalogClient } from '@backstage/catalog-client'; +import { + ApiProvider, + ApiRegistry, + ConfigReader, +} from '@backstage/core-app-api'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { useOutlet } from 'react-router'; +import { catalogImportApiRef, CatalogImportClient } from '../../api'; +import { ImportPage } from './ImportPage'; + +jest.mock('react-router', () => ({ + ...jest.requireActual('react-router'), + useOutlet: jest.fn(), +})); + +describe('', () => { + const identityApi = { + getUserId: () => { + return 'user'; + }, + getProfile: () => { + return {}; + }, + getIdToken: () => { + return Promise.resolve('token'); + }, + signOut: () => { + return Promise.resolve(); + }, + }; + + let apis: ApiRegistry; + + beforeEach(() => { + apis = ApiRegistry.with( + configApiRef, + new ConfigReader({ integrations: {} }), + ) + .with(catalogApiRef, new CatalogClient({ discoveryApi: {} as any })) + .with( + catalogImportApiRef, + new CatalogImportClient({ + discoveryApi: {} as any, + githubAuthApi: { + getAccessToken: async () => 'token', + }, + identityApi, + scmIntegrationsApi: {} as any, + catalogApi: {} as any, + configApi: new ConfigReader({}), + }), + ); + }); + + afterEach(() => jest.resetAllMocks()); + + it('renders without exploding', async () => { + const { getByText } = await renderInTestApp( + + + , + ); + + expect( + getByText('Start tracking your component in Backstage'), + ).toBeInTheDocument(); + }); + + it('renders with custom children', async () => { + (useOutlet as jest.Mock).mockReturnValue(
Hello World
); + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText('Hello World')).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog-import/src/components/Router.tsx b/plugins/catalog-import/src/components/ImportPage/ImportPage.tsx similarity index 66% rename from plugins/catalog-import/src/components/Router.tsx rename to plugins/catalog-import/src/components/ImportPage/ImportPage.tsx index c8f1ae65a3..467bd710f0 100644 --- a/plugins/catalog-import/src/components/Router.tsx +++ b/plugins/catalog-import/src/components/ImportPage/ImportPage.tsx @@ -15,12 +15,11 @@ */ import React from 'react'; -import { Route, Routes } from 'react-router-dom'; -import { ImportComponentPage } from './ImportComponentPage'; -import { StepperProviderOpts } from './ImportStepper/defaults'; +import { useOutlet } from 'react-router'; +import { DefaultImportPage } from '../DefaultImportPage'; -export const Router = (opts: StepperProviderOpts) => ( - - } /> - -); +export const ImportPage = () => { + const outlet = useOutlet(); + + return outlet || ; +}; diff --git a/plugins/catalog-import/src/components/ImportPage/index.ts b/plugins/catalog-import/src/components/ImportPage/index.ts new file mode 100644 index 0000000000..891c5302db --- /dev/null +++ b/plugins/catalog-import/src/components/ImportPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ImportPage } from './ImportPage'; diff --git a/plugins/catalog-import/src/components/ImportStepper/ImportStepper.tsx b/plugins/catalog-import/src/components/ImportStepper/ImportStepper.tsx index 48ee23e444..4112775c2d 100644 --- a/plugins/catalog-import/src/components/ImportStepper/ImportStepper.tsx +++ b/plugins/catalog-import/src/components/ImportStepper/ImportStepper.tsx @@ -14,21 +14,20 @@ * limitations under the License. */ +import { InfoCard, InfoCardVariants } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; import { Step, StepContent, Stepper } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import React, { useMemo } from 'react'; +import { catalogImportApiRef } from '../../api'; import { ImportFlows, ImportState, useImportState } from '../useImportState'; import { defaultGenerateStepper, defaultStepper, StepConfiguration, StepperProvider, - StepperProviderOpts, } from './defaults'; -import { configApiRef, useApi } from '@backstage/core-plugin-api'; -import { InfoCard, InfoCardVariants } from '@backstage/core-components'; - const useStyles = makeStyles(() => ({ stepperRoot: { padding: 0, @@ -42,16 +41,14 @@ type Props = { defaults: StepperProvider, ) => StepperProvider; variant?: InfoCardVariants; - opts?: StepperProviderOpts; }; export const ImportStepper = ({ initialUrl, generateStepper = defaultGenerateStepper, variant, - opts, }: Props) => { - const configApi = useApi(configApiRef); + const catalogImportApi = useApi(catalogImportApiRef); const classes = useStyles(); const state = useImportState({ initialUrl }); @@ -79,25 +76,25 @@ export const ImportStepper = ({ {render( states.analyze( state as Extract, - { apis: { configApi }, opts }, + { apis: { catalogImportApi } }, ), )} {render( states.prepare( state as Extract, - { apis: { configApi }, opts }, + { apis: { catalogImportApi } }, ), )} {render( states.review( state as Extract, - { apis: { configApi }, opts }, + { apis: { catalogImportApi } }, ), )} {render( states.finish( state as Extract, - { apis: { configApi }, opts }, + { apis: { catalogImportApi } }, ), )} diff --git a/plugins/catalog-import/src/components/ImportStepper/defaults.tsx b/plugins/catalog-import/src/components/ImportStepper/defaults.tsx index 79d859ce32..357ed64e14 100644 --- a/plugins/catalog-import/src/components/ImportStepper/defaults.tsx +++ b/plugins/catalog-import/src/components/ImportStepper/defaults.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { ConfigApi } from '@backstage/core-plugin-api'; import { Box, Checkbox, @@ -35,22 +34,9 @@ import { } from '../StepPrepareCreatePullRequest'; import { StepPrepareSelectLocations } from '../StepPrepareSelectLocations'; import { StepReviewLocation } from '../StepReviewLocation'; +import { StepperApis } from '../types'; import { ImportFlows, ImportState } from '../useImportState'; -export type StepperProviderOpts = { - pullRequest?: { - disable?: boolean; - preparePullRequest?: (apis: StepperApis) => { - title?: string; - body?: string; - }; - }; -}; - -type StepperApis = { - configApi: ConfigApi; -}; - export type StepConfiguration = { stepLabel: React.ReactElement; content: React.ReactElement; @@ -59,41 +45,22 @@ export type StepConfiguration = { export type StepperProvider = { analyze: ( s: Extract, - opts: { apis: StepperApis; opts?: StepperProviderOpts }, + opts: { apis: StepperApis }, ) => StepConfiguration; prepare: ( s: Extract, - opts: { apis: StepperApis; opts?: StepperProviderOpts }, + opts: { apis: StepperApis }, ) => StepConfiguration; review: ( s: Extract, - opts: { apis: StepperApis; opts?: StepperProviderOpts }, + opts: { apis: StepperApis }, ) => StepConfiguration; finish: ( s: Extract, - opts: { apis: StepperApis; opts?: StepperProviderOpts }, + opts: { apis: StepperApis }, ) => StepConfiguration; }; -function defaultPreparePullRequest( - apis: StepperApis, - { title, body }: { title?: string; body?: string } = {}, -) { - const appTitle = apis.configApi.getOptionalString('app.title') ?? 'Backstage'; - const appBaseUrl = apis.configApi.getString('app.baseUrl'); - - return { - title: title ?? 'Add catalog-info.yaml config file', - body: - body ?? - `This pull request adds a **Backstage entity metadata file** \ -to this repository so that the component can be added to the \ -[${appTitle} software catalog](${appBaseUrl}).\n\nAfter this pull request is merged, \ -the component will become available.\n\nFor more information, read an \ -[overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview).`, - }; -} - /** * The default stepper generation function. * @@ -169,13 +136,6 @@ export function defaultGenerateStepper( return defaults.prepare(state, opts); } - const preparePullRequest = - opts?.opts?.pullRequest?.preparePullRequest; - const { title, body } = defaultPreparePullRequest( - opts.apis, - preparePullRequest ? preparePullRequest(opts.apis) : {}, - ); - return { stepLabel: Create Pull Request, content: ( @@ -183,8 +143,6 @@ export function defaultGenerateStepper( analyzeResult={state.analyzeResult} onPrepare={state.onPrepare} onGoBack={state.onGoBack} - defaultTitle={title} - defaultBody={body} renderFormFields={({ values, setValue, @@ -299,14 +257,14 @@ export function defaultGenerateStepper( } export const defaultStepper: StepperProvider = { - analyze: (state, { opts }) => ({ + analyze: (state, { apis }) => ({ stepLabel: Select URL, content: ( ), }), diff --git a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx index 9cc9532a3d..4a72a653ea 100644 --- a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx +++ b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx @@ -14,15 +14,14 @@ * limitations under the License. */ +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { errorApiRef } from '@backstage/core-plugin-api'; import { act, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { AnalyzeResult, catalogImportApiRef } from '../../api/'; import { StepInitAnalyzeUrl } from './StepInitAnalyzeUrl'; -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; -import { errorApiRef } from '@backstage/core-plugin-api'; - describe('', () => { const catalogImportApi: jest.Mocked = { analyzeUrl: jest.fn(), diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx index 08b8177291..2212daf0c0 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx @@ -14,6 +14,8 @@ * limitations under the License. */ +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { errorApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { TextField } from '@material-ui/core'; import { act, render, screen } from '@testing-library/react'; @@ -25,12 +27,12 @@ import { generateEntities, StepPrepareCreatePullRequest, } from './StepPrepareCreatePullRequest'; -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; describe('', () => { const catalogImportApi: jest.Mocked = { analyzeUrl: jest.fn(), submitPullRequest: jest.fn(), + preparePullRequest: jest.fn(), }; const catalogApi: jest.Mocked = { @@ -44,12 +46,16 @@ describe('', () => { removeEntityByUid: jest.fn(), }; + const errorApi: jest.Mocked = { + error$: jest.fn(), + post: jest.fn(), + }; + const Wrapper = ({ children }: { children?: React.ReactNode }) => ( {children} @@ -77,16 +83,19 @@ describe('', () => { beforeEach(() => { jest.resetAllMocks(); + + (catalogImportApi.preparePullRequest! as jest.Mock).mockResolvedValue({ + title: 'My title', + body: 'My **body**', + }); }); it('renders without exploding', async () => { catalogApi.getEntities.mockReturnValue(Promise.resolve({ items: [] })); await act(async () => { - const { getByText } = render( + const { findByText } = render( { @@ -105,8 +114,8 @@ describe('', () => { }, ); - const title = getByText('My title'); - const description = getByText('body', { selector: 'strong' }); + const title = await findByText('My title'); + const description = await findByText('body', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); @@ -124,10 +133,8 @@ describe('', () => { ); await act(async () => { - await render( + render( { @@ -154,11 +161,9 @@ describe('', () => { }, ); - await userEvent.type(await screen.getByLabelText('name'), '-changed'); - await userEvent.type(await screen.getByLabelText('owner'), '-changed'); - await userEvent.click( - await screen.getByRole('button', { name: /Create PR/i }), - ); + userEvent.type(await screen.findByLabelText('name'), '-changed'); + userEvent.type(await screen.findByLabelText('owner'), '-changed'); + userEvent.click(screen.getByRole('button', { name: /Create PR/i })); }); expect(catalogImportApi.submitPullRequest).toBeCalledTimes(1); @@ -212,10 +217,8 @@ spec: ); await act(async () => { - await render( + render( { @@ -234,8 +237,8 @@ spec: }, ); - await userEvent.click( - await screen.getByRole('button', { name: /Create PR/i }), + userEvent.click( + await screen.findByRole('button', { name: /Create PR/i }), ); }); @@ -261,10 +264,8 @@ spec: ); await act(async () => { - await render( + render( void; onGoBack?: () => void; - defaultTitle: string; - defaultBody: string; - renderFormFields: ( props: Pick< UseFormReturn, @@ -99,16 +96,30 @@ export const StepPrepareCreatePullRequest = ({ onPrepare, onGoBack, renderFormFields, - defaultTitle, - defaultBody, }: Props) => { const classes = useStyles(); const catalogApi = useApi(catalogApiRef); - const catalogInfoApi = useApi(catalogImportApiRef); + const catalogImportApi = useApi(catalogImportApiRef); + const errorApi = useApi(errorApiRef); const [submitted, setSubmitted] = useState(false); const [error, setError] = useState(); + const { + loading: prDefaultsLoading, + value: prDefaults, + error: prDefaultsError, + } = useAsync( + () => catalogImportApi.preparePullRequest!(), + [catalogImportApi.preparePullRequest], + ); + + useEffect(() => { + if (prDefaultsError) { + errorApi.post(prDefaultsError); + } + }, [prDefaultsError, errorApi]); + const { loading: groupsLoading, value: groups } = useAsync(async () => { const groupEntities = await catalogApi.getEntities({ filter: { kind: 'group' }, @@ -124,7 +135,7 @@ export const StepPrepareCreatePullRequest = ({ setSubmitted(true); try { - const pr = await catalogInfoApi.submitPullRequest({ + const pr = await catalogImportApi.submitPullRequest({ repositoryUrl: analyzeResult.url, title: data.title, body: data.body, @@ -171,7 +182,7 @@ export const StepPrepareCreatePullRequest = ({ analyzeResult.generatedEntities, analyzeResult.integrationType, analyzeResult.url, - catalogInfoApi, + catalogImportApi, onPrepare, ], ); @@ -184,79 +195,81 @@ export const StepPrepareCreatePullRequest = ({ a Pull Request that creates one. - - onSubmit={handleResult} - defaultValues={{ - title: defaultTitle, - body: defaultBody, - owner: - (analyzeResult.generatedEntities[0]?.spec?.owner as string) || '', - componentName: - analyzeResult.generatedEntities[0]?.metadata?.name || '', - useCodeowners: false, - }} - render={({ values, formState, register, setValue }) => ( - <> - {renderFormFields({ - values, - formState, - register, - setValue, - groups: groups ?? [], - groupsLoading, - })} + {!prDefaultsLoading && ( + + onSubmit={handleResult} + defaultValues={{ + title: prDefaults?.title ?? '', + body: prDefaults?.body ?? '', + owner: + (analyzeResult.generatedEntities[0]?.spec?.owner as string) || '', + componentName: + analyzeResult.generatedEntities[0]?.metadata?.name || '', + useCodeowners: false, + }} + render={({ values, formState, register, setValue }) => ( + <> + {renderFormFields({ + values, + formState, + register, + setValue, + groups: groups ?? [], + groupsLoading, + })} - - Preview Pull Request - + + Preview Pull Request + - + - - Preview Entities - + + Preview Entities + - - - {error && {error}} - - - {onGoBack && ( - - )} - - Create PR - - - - )} - /> + repositoryUrl={analyzeResult.url} + classes={{ + card: classes.previewCard, + cardContent: classes.previewCardContent, + }} + /> + + {error && {error}} + + + {onGoBack && ( + + )} + + Create PR + + + + )} + /> + )} ); }; diff --git a/plugins/catalog-import/src/components/index.ts b/plugins/catalog-import/src/components/index.ts index 9b53200317..886c679d2d 100644 --- a/plugins/catalog-import/src/components/index.ts +++ b/plugins/catalog-import/src/components/index.ts @@ -14,7 +14,9 @@ * limitations under the License. */ -export * from './ImportStepper'; +export * from './DefaultImportPage'; export * from './EntityListComponent'; +export * from './ImportInfoCard'; +export * from './ImportStepper'; export * from './StepInitAnalyzeUrl'; export * from './StepPrepareCreatePullRequest'; diff --git a/plugins/catalog-import/src/components/types.ts b/plugins/catalog-import/src/components/types.ts new file mode 100644 index 0000000000..baa279ff21 --- /dev/null +++ b/plugins/catalog-import/src/components/types.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CatalogImportApi } from '../api'; + +export type StepperApis = { + catalogImportApi: CatalogImportApi; +}; diff --git a/plugins/catalog-import/src/index.ts b/plugins/catalog-import/src/index.ts index adee41bdbd..149c3e55c6 100644 --- a/plugins/catalog-import/src/index.ts +++ b/plugins/catalog-import/src/index.ts @@ -25,6 +25,5 @@ export { catalogImportPlugin as plugin, CatalogImportPage, } from './plugin'; -export { Router } from './components/Router'; export * from './components'; export * from './api'; diff --git a/plugins/catalog-import/src/plugin.ts b/plugins/catalog-import/src/plugin.ts index 3ab999ac07..d7d6370de3 100644 --- a/plugins/catalog-import/src/plugin.ts +++ b/plugins/catalog-import/src/plugin.ts @@ -14,10 +14,8 @@ * limitations under the License. */ -import { scmIntegrationsApiRef } from '@backstage/integration-react'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import { catalogImportApiRef, CatalogImportClient } from './api'; import { + configApiRef, createApiFactory, createPlugin, createRoutableExtension, @@ -26,6 +24,9 @@ import { githubAuthApiRef, identityApiRef, } from '@backstage/core-plugin-api'; +import { scmIntegrationsApiRef } from '@backstage/integration-react'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { catalogImportApiRef, CatalogImportClient } from './api'; export const rootRouteRef = createRouteRef({ path: '', @@ -43,6 +44,7 @@ export const catalogImportPlugin = createPlugin({ identityApi: identityApiRef, scmIntegrationsApi: scmIntegrationsApiRef, catalogApi: catalogApiRef, + configApi: configApiRef, }, factory: ({ discoveryApi, @@ -50,6 +52,7 @@ export const catalogImportPlugin = createPlugin({ identityApi, scmIntegrationsApi, catalogApi, + configApi, }) => new CatalogImportClient({ discoveryApi, @@ -57,6 +60,7 @@ export const catalogImportPlugin = createPlugin({ scmIntegrationsApi, identityApi, catalogApi, + configApi, }), }), ], @@ -67,7 +71,7 @@ export const catalogImportPlugin = createPlugin({ export const CatalogImportPage = catalogImportPlugin.provide( createRoutableExtension({ - component: () => import('./components/Router').then(m => m.Router), + component: () => import('./components/ImportPage').then(m => m.ImportPage), mountPoint: rootRouteRef, }), ); diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 9388ef3670..54d8af11b6 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -41,7 +41,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "@types/react": "*", "jwt-decode": "^3.1.0", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "qs": "^6.9.4", "react": "^16.13.1", "react-router": "6.0.0-beta.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index bdc2946177..bf60f4de8e 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -42,7 +42,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "circleci-api": "^4.0.0", "humanize-duration": "^3.27.0", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 0952e335cb..0583bccf68 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -40,7 +40,7 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "kafkajs": "^1.16.0-beta.6", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "winston": "^3.2.1" }, "devDependencies": { diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index fdba331c07..3154cab61d 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -47,7 +47,7 @@ "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", "helmet": "^4.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "morgan": "^1.10.0", "stream-buffers": "^3.0.2", "winston": "^3.2.1", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 75eb7b9773..e6f92d4065 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -42,7 +42,7 @@ "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", "helmet": "^4.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "morgan": "^1.10.0", "winston": "^3.2.1", "yn": "^4.0.0" diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 49bd185dde..66037f7b8b 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -40,7 +40,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", diff --git a/plugins/scaffolder-backend-module-yeoman/.eslintrc.js b/plugins/scaffolder-backend-module-yeoman/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/scaffolder-backend-module-yeoman/README.md b/plugins/scaffolder-backend-module-yeoman/README.md new file mode 100644 index 0000000000..bacbb87648 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/README.md @@ -0,0 +1,156 @@ +# scaffolder-backend-module-yeoman + +Welcome to the `run:yeoman` action for the `scaffolder-backend`. + +## Getting started + +You need to configure the action in your backend: + +## From your Backstage root directory + +``` +cd packages/backend +yarn add @backstage/plugin-scaffolder-backend-module-yeoman +``` + +Configure the action: +(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): + +```typescript +// packages/backend/src/plugins/scaffolder.ts + +const actions = [ + createRunYeomanAction(), + ...createBuiltInActions({ + containerRunner, + integrations, + config, + catalogClient, + reader, + }), +]; + +return await createRouter({ + containerRunner, + logger, + config, + database, + catalogClient, + reader, + actions, +}); +``` + +After that you can use the action in your template: + +```yaml +apiVersion: backstage.io/v1beta2 +kind: Template +metadata: + name: yeoman-demo + title: Yeoman Test + description: Cookiecutter example +spec: + owner: backstage/techdocs-core + type: service + + parameters: + - title: Fill in some steps + required: + - name + - owner + properties: + name: + title: Name + type: string + description: Unique name of the component + ui:autofocus: true + ui:options: + rows: 5 + owner: + title: Owner + type: string + description: Owner of the component + ui:field: OwnerPicker + ui:options: + allowedKinds: + - Group + system: + title: System + type: string + description: System of the component + ui:field: EntityPicker + ui:options: + allowedKinds: + - System + defaultKind: System + + - title: Choose a location + required: + - repoUrl + - dryRun + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com + dryRun: + title: Only perform a dry run, don't publish anything + type: boolean + default: false + + steps: + - id: yeoman + name: Yeoman + action: run:yeoman + input: + namespace: 'org:codeowners' + options: + codeowners: '@{{ parameters.owner }}' + + - id: publish + if: '{{ not parameters.dryRun }}' + name: Publish + action: publish:github + input: + allowedHosts: ['github.com'] + description: 'This is {{ parameters.name }}' + repoUrl: '{{ parameters.repoUrl }}' + + - id: register + if: '{{ not parameters.dryRun }}' + name: Register + action: catalog:register + input: + repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' + catalogInfoPath: '/catalog-info.yaml' + + - name: Results + if: '{{ parameters.dryRun }}' + action: debug:log + input: + listWorkspace: true + + output: + links: + - title: Repository + url: '{{ steps.publish.output.remoteUrl }}' + - title: Open in catalog + icon: 'catalog' + entityRef: '{{ steps.register.output.entityRef }}' +``` + +You can also visit the `/create/actions` route in your Backstage application to find out more about the parameters this action accepts when it's installed to configure how you like. + +### Yeoman Generators setup + +Yeoman generator should be installed a dependency of your `backstage/packages/backend` in `package.json` + +```package.json +"generator-name": "^1.2.3" +``` + +Alternatively it can be installed globally in the environment using, e.g.: `npm install -g generator-name`. diff --git a/plugins/scaffolder-backend-module-yeoman/api-report.md b/plugins/scaffolder-backend-module-yeoman/api-report.md new file mode 100644 index 0000000000..1f38d3945c --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/api-report.md @@ -0,0 +1,14 @@ +## API Report File for "@backstage/plugin-scaffolder-backend-module-yeoman" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { TemplateAction } from '@backstage/plugin-scaffolder-backend'; + +// Warning: (ae-missing-release-tag) "createRunYeomanAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function createRunYeomanAction(): TemplateAction; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json new file mode 100644 index 0000000000..eb339b90d5 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -0,0 +1,35 @@ +{ + "name": "@backstage/plugin-scaffolder-backend-module-yeoman", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/config": "^0.1.8", + "@backstage/plugin-scaffolder-backend": "^0.15.2", + "winston": "^3.2.1", + "yeoman-environment": "^3.6.0" + }, + "devDependencies": { + "@backstage/backend-common": "^0.9.2", + "@types/jest": "^26.0.7" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/scaffolder-backend-module-yeoman/src/actions/index.ts b/plugins/scaffolder-backend-module-yeoman/src/actions/index.ts new file mode 100644 index 0000000000..61bfea8510 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/src/actions/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './run'; diff --git a/plugins/scaffolder-backend-module-yeoman/src/actions/run/index.ts b/plugins/scaffolder-backend-module-yeoman/src/actions/run/index.ts new file mode 100644 index 0000000000..52d3f3ac92 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/src/actions/run/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { createRunYeomanAction } from './yeoman'; diff --git a/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.test.ts b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.test.ts new file mode 100644 index 0000000000..6e61ae77c4 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.test.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { yeomanRun } from './yeomanRun'; + +jest.mock('./yeomanRun'); + +import { getVoidLogger } from '@backstage/backend-common'; +import os from 'os'; +import { PassThrough } from 'stream'; +import { createRunYeomanAction } from './yeoman'; +import type { ActionContext } from '@backstage/plugin-scaffolder-backend'; +import { JsonObject } from '@backstage/config'; + +describe('run:yeoman', () => { + const mockTmpDir = os.tmpdir(); + + let mockContext: ActionContext<{ + namespace: string; + args?: string[]; + options?: JsonObject; + }>; + + const action = createRunYeomanAction(); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should call yeomanRun with the correct variables', async () => { + const namespace = 'whatever:app'; + const args = ['aa', 'bb']; + const options = { + code: 'owner', + }; + mockContext = { + input: { + namespace, + args, + options, + }, + workspacePath: mockTmpDir, + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir), + }; + + await action.handler(mockContext); + expect(yeomanRun).toHaveBeenCalledWith( + mockTmpDir, + namespace, + args, + options, + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts new file mode 100644 index 0000000000..c8bd49f3d3 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeoman.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JsonObject } from '@backstage/config'; +import { createTemplateAction } from '@backstage/plugin-scaffolder-backend'; +import { yeomanRun } from './yeomanRun'; + +export function createRunYeomanAction() { + return createTemplateAction<{ + namespace: string; + args?: string[]; + options?: JsonObject; + }>({ + id: 'run:yeoman', + description: 'Runs Yeoman on an installed Yeoman generator', + schema: { + input: { + type: 'object', + required: ['namespace'], + properties: { + namespace: { + title: 'Generator Namespace', + description: 'Yeoman generator namespace, e.g: node:app', + type: 'string', + }, + args: { + title: 'Generator Arguments', + description: 'Arguments to pass on to Yeoman for templating', + type: 'array', + items: { + type: 'string', + }, + }, + options: { + title: 'Generator Options', + description: 'Options to pass on to Yeoman for templating', + type: 'object', + }, + }, + }, + }, + async handler(ctx) { + ctx.logger.info( + `Templating using Yeoman generator: ${ctx.input.namespace}`, + ); + await yeomanRun( + ctx.workspacePath, + ctx.input.namespace, + ctx.input.args, + ctx.input.options, + ); + }, + }); +} diff --git a/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeomanRun.ts b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeomanRun.ts new file mode 100644 index 0000000000..ff11634f81 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/src/actions/run/yeomanRun.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JsonObject } from '@backstage/config'; + +/* + * This module should use '@types/yeoman-environment' eventually as soon as '@types/yeoman-environment' supports + * the latest version of Yeoman -> 3.x. Then it will be possible to test this module with jest. + */ + +export async function yeomanRun( + workspace: string, + namespace: string, + args?: string[], + opts?: JsonObject, +) { + const yeoman = require('yeoman-environment'); + const generator = yeoman.lookupGenerator(namespace); + const env = yeoman.createEnv(undefined, { cwd: workspace }); + env.register(generator, namespace); + const yeomanArgs = [namespace, ...(args ?? [])]; + await env.run(yeomanArgs, opts); +} diff --git a/plugins/scaffolder-backend-module-yeoman/src/index.ts b/plugins/scaffolder-backend-module-yeoman/src/index.ts new file mode 100644 index 0000000000..4f06b14a86 --- /dev/null +++ b/plugins/scaffolder-backend-module-yeoman/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './actions'; diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 1bd4c9fee5..3dfb1fe307 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -23,7 +23,7 @@ "@backstage/backend-common": "^0.9.1", "@backstage/search-common": "^0.2.0", "@backstage/plugin-search-backend-node": "^0.4.2", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "knex": "^0.95.1" }, "devDependencies": { diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 5fe395e131..b1205e7dd9 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -64,6 +64,10 @@ const useStyles = makeStyles(theme => ({ marginLeft: '20rem', maxWidth: 'calc(100% - 20rem * 2 - 3rem)', marginTop: theme.spacing(1), + '@media screen and (max-width: 76.1875em)': { + marginLeft: '10rem', + maxWidth: 'calc(100% - 10rem)', + }, }, })); diff --git a/yarn.lock b/yarn.lock index 223f8023f6..526fc1406d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2847,6 +2847,11 @@ dependencies: yaml-ast-parser "0.0.43" +"@gar/promisify@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" + integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== + "@gitbeaker/core@^30.2.0", "@gitbeaker/core@^30.3.0": version "30.3.0" resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-30.3.0.tgz#d005891d47cfacb41d4a3cc8bf3ee8c68c53d378" @@ -4655,11 +4660,56 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@npmcli/arborist@^2.2.2": + version "2.8.3" + resolved "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.8.3.tgz#5569e7d2038f6893abc81f9c879f497b506e6980" + integrity sha512-miFcxbZjmQqeFTeRSLLh+lc/gxIKDO5L4PVCp+dp+kmcwJmYsEJmF7YvHR2yi3jF+fxgvLf3CCFzboPIXAuabg== + dependencies: + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/map-workspaces" "^1.0.2" + "@npmcli/metavuln-calculator" "^1.1.0" + "@npmcli/move-file" "^1.1.0" + "@npmcli/name-from-folder" "^1.0.1" + "@npmcli/node-gyp" "^1.0.1" + "@npmcli/package-json" "^1.0.1" + "@npmcli/run-script" "^1.8.2" + bin-links "^2.2.1" + cacache "^15.0.3" + common-ancestor-path "^1.0.1" + json-parse-even-better-errors "^2.3.1" + json-stringify-nice "^1.1.4" + mkdirp "^1.0.4" + mkdirp-infer-owner "^2.0.0" + npm-install-checks "^4.0.0" + npm-package-arg "^8.1.5" + npm-pick-manifest "^6.1.0" + npm-registry-fetch "^11.0.0" + pacote "^11.3.5" + parse-conflict-json "^1.1.1" + proc-log "^1.0.0" + promise-all-reject-late "^1.0.0" + promise-call-limit "^1.0.1" + read-package-json-fast "^2.0.2" + readdir-scoped-modules "^1.1.0" + rimraf "^3.0.2" + semver "^7.3.5" + ssri "^8.0.1" + treeverse "^1.0.4" + walk-up-path "^1.0.0" + "@npmcli/ci-detect@^1.0.0": version "1.3.0" resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== +"@npmcli/fs@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f" + integrity sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + "@npmcli/git@^2.0.1": version "2.0.6" resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.0.6.tgz#47b97e96b2eede3f38379262fa3bdfa6eae57bf2" @@ -4675,7 +4725,21 @@ unique-filename "^1.1.1" which "^2.0.2" -"@npmcli/installed-package-contents@^1.0.6": +"@npmcli/git@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" + integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== + dependencies: + "@npmcli/promise-spawn" "^1.3.2" + lru-cache "^6.0.0" + mkdirp "^1.0.4" + npm-pick-manifest "^6.1.1" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^2.0.2" + +"@npmcli/installed-package-contents@^1.0.6", "@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== @@ -4683,6 +4747,25 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" +"@npmcli/map-workspaces@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-1.0.4.tgz#915708b55afa25e20bc2c14a766c124c2c5d4cab" + integrity sha512-wVR8QxhyXsFcD/cORtJwGQodeeaDf0OxcHie8ema4VgFeqwYkFsDPnSrIRSytX8xR6nKPAH89WnwTcaU608b/Q== + dependencies: + "@npmcli/name-from-folder" "^1.0.1" + glob "^7.1.6" + minimatch "^3.0.4" + read-package-json-fast "^2.0.1" + +"@npmcli/metavuln-calculator@^1.1.0": + version "1.1.1" + resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.1.1.tgz#2f95ff3c6d88b366dd70de1c3f304267c631b458" + integrity sha512-9xe+ZZ1iGVaUovBVFI9h3qW+UuECUzhvZPxK9RaEA2mjU26o5D0JloGYWwLYvQELJNmBdQB6rrpuN8jni6LwzQ== + dependencies: + cacache "^15.0.5" + pacote "^11.1.11" + semver "^7.3.2" + "@npmcli/move-file@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" @@ -4690,11 +4773,31 @@ dependencies: mkdirp "^1.0.4" -"@npmcli/node-gyp@^1.0.2": +"@npmcli/move-file@^1.1.0": + version "1.1.2" + resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@npmcli/name-from-folder@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" + integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== + +"@npmcli/node-gyp@^1.0.1", "@npmcli/node-gyp@^1.0.2": version "1.0.2" resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== +"@npmcli/package-json@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-1.0.1.tgz#1ed42f00febe5293c3502fd0ef785647355f6e89" + integrity sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg== + dependencies: + json-parse-even-better-errors "^2.3.1" + "@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" @@ -6552,6 +6655,11 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/expect@^1.20.4": + version "1.20.4" + resolved "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" + integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.21": version "4.17.24" resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" @@ -6993,6 +7101,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-12.12.58.tgz#46dae9b2b9ee5992818c8f7cee01ff4ce03ab44c" integrity sha512-Be46CNIHWAagEfINOjmriSxuv7IVcqbGe+sDSg2SYCEz/0CRBy7LRASGfRbD8KZkqoePU73Wsx3UvOSFcq/9hA== +"@types/node@^15.6.1": + version "15.14.9" + resolved "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" + integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== + "@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" @@ -7507,6 +7620,14 @@ resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== +"@types/vinyl@^2.0.4": + version "2.0.6" + resolved "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.6.tgz#b2d134603557a7c3d2b5d3dc23863ea2b5eb29b0" + integrity sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g== + dependencies: + "@types/expect" "^1.20.4" + "@types/node" "*" + "@types/webpack-dev-server@^3.11.5": version "3.11.5" resolved "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.11.5.tgz#f4a254a3dd0667c8ee4af90d42afdb4ad1d607f3" @@ -8137,6 +8258,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + agentkeepalive@^4.1.3, agentkeepalive@^4.1.4: version "4.1.4" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" @@ -8593,6 +8721,14 @@ archiver@^5.0.2, archiver@^5.3.0: tar-stream "^2.2.0" zip-stream "^4.1.0" +are-we-there-yet@^1.1.5: + version "1.1.7" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -8840,6 +8976,11 @@ async-retry@^1.2.1, async-retry@^1.3.1: dependencies: retry "0.12.0" +async@0.9.x: + version "0.9.2" + resolved "https://registry.npmjs.org/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= + async@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -9402,6 +9543,18 @@ bignumber.js@^9.0.0: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== +bin-links@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/bin-links/-/bin-links-2.2.1.tgz#347d9dbb48f7d60e6c11fe68b77a424bee14d61b" + integrity sha512-wFzVTqavpgCCYAh8SVBdnZdiQMxTkGR+T3b14CNpBXIBe2neJWaMGAZ55XWWHELJJ89dscuq0VCBqcVaIOgCMg== + dependencies: + cmd-shim "^4.0.1" + mkdirp "^1.0.3" + npm-normalize-package-bin "^1.0.0" + read-cmd-shim "^2.0.0" + rimraf "^3.0.0" + write-file-atomic "^3.0.3" + binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -9425,6 +9578,11 @@ binary@~0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" +binaryextensions@^4.15.0, binaryextensions@^4.16.0: + version "4.18.0" + resolved "https://registry.npmjs.org/binaryextensions/-/binaryextensions-4.18.0.tgz#22aeada2d14de062c60e8ca59a504a5636a76ceb" + integrity sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw== + bindings@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -9821,6 +9979,30 @@ cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^15.0.3, cacache@^15.2.0: + version "15.3.0" + resolved "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cacache@^15.0.5: version "15.0.5" resolved "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" @@ -10352,6 +10534,13 @@ cli-table3@0.6.0, cli-table3@~0.6.0: optionalDependencies: colors "^1.1.2" +cli-table@^0.3.1: + version "0.3.6" + resolved "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc" + integrity sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ== + dependencies: + colors "1.0.3" + cli-truncate@2.1.0, cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -10400,6 +10589,11 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clone-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + clone-deep@^0.2.4: version "0.2.4" resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" @@ -10427,7 +10621,12 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone@2.x: +clone-stats@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + +clone@2.x, clone@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= @@ -10437,12 +10636,21 @@ clone@^1.0.2: resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +cloneable-readable@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" + integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== + dependencies: + inherits "^2.0.1" + process-nextick-args "^2.0.0" + readable-stream "^2.3.5" + clsx@^1.0.1, clsx@^1.0.2, clsx@^1.0.4, clsx@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== -cmd-shim@^4.1.0: +cmd-shim@^4.0.1, cmd-shim@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== @@ -10585,6 +10793,11 @@ colorette@^1.2.1, colorette@^1.2.2: resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +colors@1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + colors@^1.1.2, colors@^1.2.1: version "1.4.0" resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -10628,6 +10841,11 @@ command-exists@^1.2.9: resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== +commander@7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" + integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg== + commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.7.1, commander@~2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -10653,6 +10871,11 @@ commander@^7.1.0, commander@^7.2.0: resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== + common-tags@1.8.0, common-tags@^1.8.0: version "1.8.0" resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -11855,6 +12078,11 @@ dateformat@^3.0.0, dateformat@^3.0.3: resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +dateformat@^4.5.0: + version "4.5.1" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c" + integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q== + dayjs@^1.10.4: version "1.10.4" resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" @@ -12556,6 +12784,13 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +ejs@^3.1.6: + version "3.1.6" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" + integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== + dependencies: + jake "^10.6.1" + elastic-builder@^2.16.0: version "2.16.0" resolved "https://registry.npmjs.org/elastic-builder/-/elastic-builder-2.16.0.tgz#684757ab9e6a4214653d23d84cec5ab8d185892f" @@ -12753,6 +12988,11 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.1.1" +error@^10.4.0: + version "10.4.0" + resolved "https://registry.npmjs.org/error/-/error-10.4.0.tgz#6fcf0fd64bceb1e750f8ed9a3dd880f00e46a487" + integrity sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw== + es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: version "1.18.0" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" @@ -13792,6 +14032,13 @@ filefy@0.1.10: resolved "https://registry.npmjs.org/filefy/-/filefy-0.1.10.tgz#174677c8e2fa5bc39a3af0ed6fb492f16b8fbf42" integrity sha512-VgoRVOOY1WkTpWH+KBy8zcU1G7uQTVsXqhWEgzryB9A5hg2aqCyZ6aQ/5PSzlqM5+6cnVrX6oYV0XqD3HZSnmQ== +filelist@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" + integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + dependencies: + minimatch "^3.0.4" + filesize@6.1.0: version "6.1.0" resolved "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" @@ -13910,6 +14157,13 @@ find-yarn-workspace-root2@1.2.16: micromatch "^4.0.2" pkg-dir "^4.2.0" +first-chunk-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" + integrity sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA= + dependencies: + readable-stream "^2.0.2" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -14951,6 +15205,11 @@ graphql@^15.3.0, graphql@^15.4.0: resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad" integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw== +grouped-queue@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/grouped-queue/-/grouped-queue-2.0.0.tgz#a2c6713f2171e45db2c300a3a9d7c119d694dac8" + integrity sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw== + growly@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -15268,6 +15527,13 @@ hosted-git-info@^3.0.6: dependencies: lru-cache "^6.0.0" +hosted-git-info@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + dependencies: + lru-cache "^6.0.0" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -15856,6 +16122,26 @@ inquirer@^7.0.4, inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" +inquirer@^8.0.0: + version "8.1.5" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz#2dc5159203c826d654915b5fe6990fd17f54a150" + integrity sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.2.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + inquirer@^8.1.0: version "8.1.1" resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.1.1.tgz#7c53d94c6d03011c7bb2a947f0dca3b98246c26a" @@ -16439,6 +16725,13 @@ is-root@2.1.0: resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +is-scoped@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz#fef0713772658bdf5bee418608267ddae6d3566d" + integrity sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ== + dependencies: + scoped-regex "^2.0.0" + is-set@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" @@ -16534,7 +16827,7 @@ is-upper-case@^2.0.2: dependencies: tslib "^2.0.3" -is-utf8@^0.2.0: +is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= @@ -16634,9 +16927,9 @@ isomorphic-form-data@^2.0.0: form-data "^2.3.2" isomorphic-git@^1.8.0: - version "1.9.2" - resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.9.2.tgz#0e492dbcd9873070b2a57eef257a45b90020ed72" - integrity sha512-puCXcGgtkDXdMYLZlAEGbpkbmHn/Q4Lsl2uMFwMLOKmmr8Qe7Fe3+c6k2+aHW3rMdJYg9xTv95BJ+PRzR8Ydww== + version "1.10.0" + resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.10.0.tgz#59a4604d1190d1e7fc52172085da25e6a428bc07" + integrity sha512-CijspEYaOQAnsHWXyq8ICZXzLJ/1wYQAa0jdfLcugA/68oNzrxykjGZz8Up7B8huA1VfkFHm4VviExtj/zpViw== dependencies: async-lock "^1.1.0" clean-git-ref "^2.0.1" @@ -16719,6 +17012,16 @@ iterate-value@^1.0.0: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +jake@^10.6.1: + version "10.8.2" + resolved "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" + integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== + dependencies: + async "0.9.x" + chalk "^2.4.2" + filelist "^1.0.1" + minimatch "^3.0.4" + jenkins@^0.28.1: version "0.28.1" resolved "https://registry.npmjs.org/jenkins/-/jenkins-0.28.1.tgz#f7951798ee5d2bb501a831979b6b5ecc1a922a64" @@ -17399,7 +17702,7 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.3.0: +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== @@ -17468,6 +17771,11 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" +json-stringify-nice@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" + integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== + json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -17684,6 +17992,16 @@ junk@^3.1.0: resolved "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +just-diff-apply@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-3.0.0.tgz#a77348d24f0694e378b57293dceb65bdf5a91c4f" + integrity sha512-K2MLc+ZC2DVxX4V61bIKPeMUUfj1YYZ3h0myhchDXOW1cKoPZMnjIoNCqv9bF2n5Oob1PFxuR2gVJxkxz4e58w== + +just-diff@^3.0.1: + version "3.1.1" + resolved "https://registry.npmjs.org/just-diff/-/just-diff-3.1.1.tgz#d50c597c6fd4776495308c63bdee1b6839082647" + integrity sha512-sdMWKjRq8qWZEjDcVA6llnUT8RDEBIfOiGpYFPYa9u+2c39JCsejktSP7mj5eRid5EIvTzIpQ2kDOCw1Nq9BjQ== + just-extend@^4.0.2: version "4.2.1" resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" @@ -18651,6 +18969,28 @@ make-fetch-happen@^8.0.9: socks-proxy-agent "^5.0.0" ssri "^8.0.0" +make-fetch-happen@^9.0.1: + version "9.1.0" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^6.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -18895,6 +19235,32 @@ media-typer@0.3.0: resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +"mem-fs-editor@^8.1.2 || ^9.0.0": + version "9.3.0" + resolved "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-9.3.0.tgz#85ce80541b1961d1d9f433e275c7cee9d0a1c9a2" + integrity sha512-QKFbPwGCh1ypmc2H8BUYpbapwT/x2AOCYZQogzSui4rUNes7WVMagQXsirPIfp18EarX0SSY9Fpg426nSjew4Q== + dependencies: + binaryextensions "^4.16.0" + commondir "^1.0.1" + deep-extend "^0.6.0" + ejs "^3.1.6" + globby "^11.0.3" + isbinaryfile "^4.0.8" + minimatch "^3.0.4" + multimatch "^5.0.0" + normalize-path "^3.0.0" + textextensions "^5.13.0" + +"mem-fs@^1.2.0 || ^2.0.0": + version "2.2.1" + resolved "https://registry.npmjs.org/mem-fs/-/mem-fs-2.2.1.tgz#c87bc8a53fb17971b129d4bcd59a9149fb78c5b1" + integrity sha512-yiAivd4xFOH/WXlUi6v/nKopBh1QLzwjFi36NK88cGt/PRXI8WeBASqY+YSjIVWvQTx3hR8zHKDBMV6hWmglNA== + dependencies: + "@types/node" "^15.6.1" + "@types/vinyl" "^2.0.4" + vinyl "^2.0.1" + vinyl-file "^3.0.0" + mem@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122" @@ -19650,7 +20016,7 @@ needle@^2.2.1, needle@^2.5.0: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.2: +negotiator@0.6.2, negotiator@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== @@ -20023,6 +20389,15 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0: semver "^7.0.0" validate-npm-package-name "^3.0.0" +npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: + version "8.1.5" + resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== + dependencies: + hosted-git-info "^4.0.1" + semver "^7.3.4" + validate-npm-package-name "^3.0.0" + npm-packlist@^1.1.6: version "1.4.8" resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" @@ -20051,6 +20426,28 @@ npm-pick-manifest@^6.0.0: npm-package-arg "^8.0.0" semver "^7.0.0" +npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1: + version "6.1.1" + resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== + dependencies: + npm-install-checks "^4.0.0" + npm-normalize-package-bin "^1.0.1" + npm-package-arg "^8.1.2" + semver "^7.3.4" + +npm-registry-fetch@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== + dependencies: + make-fetch-happen "^9.0.1" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" + npm-registry-fetch@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" @@ -20368,7 +20765,7 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^5.3.0: +ora@^5.3.0, ora@^5.4.1: version "5.4.1" resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== @@ -20637,6 +21034,31 @@ packet-reader@1.0.0: resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== +pacote@^11.1.11, pacote@^11.3.5: + version "11.3.5" + resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" + integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== + dependencies: + "@npmcli/git" "^2.1.0" + "@npmcli/installed-package-contents" "^1.0.6" + "@npmcli/promise-spawn" "^1.2.0" + "@npmcli/run-script" "^1.8.2" + cacache "^15.0.5" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.3" + mkdirp "^1.0.3" + npm-package-arg "^8.0.1" + npm-packlist "^2.1.4" + npm-pick-manifest "^6.0.0" + npm-registry-fetch "^11.0.0" + promise-retry "^2.0.1" + read-package-json-fast "^2.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.1.0" + pacote@^11.2.6: version "11.2.6" resolved "https://registry.npmjs.org/pacote/-/pacote-11.2.6.tgz#c0426e5d5c8d33aeea3461a75e1390f1ba78f953" @@ -20708,6 +21130,15 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-conflict-json@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-1.1.1.tgz#54ec175bde0f2d70abf6be79e0e042290b86701b" + integrity sha512-4gySviBiW5TRl7XHvp1agcS7SOe0KZOjC//71dzZVWJrY9hCrgtvl5v3SyIxCZ4fZF47TxD9nfzmxcx76xmbUw== + dependencies: + json-parse-even-better-errors "^2.3.0" + just-diff "^3.0.1" + just-diff-apply "^3.0.0" + parse-entities@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" @@ -21815,6 +22246,16 @@ preferred-pm@^3.0.0: path-exists "^4.0.0" which-pm "2.0.0" +preferred-pm@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6" + integrity sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ== + dependencies: + find-up "^5.0.0" + find-yarn-workspace-root2 "1.2.16" + path-exists "^4.0.0" + which-pm "2.0.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -21845,7 +22286,7 @@ prettier@~2.2.1: resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== -pretty-bytes@^5.6.0: +pretty-bytes@^5.3.0, pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== @@ -21898,7 +22339,12 @@ private@^0.1.8: resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -process-nextick-args@~2.0.0: +proc-log@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77" + integrity sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg== + +process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== @@ -21920,6 +22366,16 @@ prom-client@^13.2.0: dependencies: tdigest "^0.1.1" +promise-all-reject-late@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" + integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== + +promise-call-limit@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" + integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -22837,6 +23293,14 @@ read-package-json-fast@^2.0.1: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" +read-package-json-fast@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" + integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + read-package-json@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz#16aa66c59e7d4dad6288f179dd9295fd59bb98f1" @@ -22991,7 +23455,7 @@ readdir-glob@^1.0.0: dependencies: minimatch "^3.0.4" -readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -23396,6 +23860,11 @@ replace-ext@1.0.0: resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + replace-in-file@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.1.0.tgz#9f9ddd7bb70d6ad231d2ad692e1b646e73d06647" @@ -23806,7 +24275,7 @@ rxjs@^6.6.6: dependencies: tslib "^1.9.0" -rxjs@^7.1.0: +rxjs@^7.1.0, rxjs@^7.2.0: version "7.3.0" resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6" integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw== @@ -23942,6 +24411,11 @@ schema-utils@^3.1.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +scoped-regex@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/scoped-regex/-/scoped-regex-2.1.0.tgz#7b9be845d81fd9d21d1ec97c61a0b7cf86d2015f" + integrity sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ== + screenfull@^5.0.0, screenfull@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.1.0.tgz#85c13c70f4ead4c1b8a935c70010dfdcd2c0e5c8" @@ -24421,6 +24895,15 @@ socks-proxy-agent@^5.0.0: debug "4" socks "^2.3.3" +socks-proxy-agent@^6.0.0: + version "6.1.0" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz#869cf2d7bd10fea96c7ad3111e81726855e285c3" + integrity sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg== + dependencies: + agent-base "^6.0.2" + debug "^4.3.1" + socks "^2.6.1" + socks@^2.3.3: version "2.5.1" resolved "https://registry.npmjs.org/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" @@ -24429,6 +24912,14 @@ socks@^2.3.3: ip "^1.1.5" smart-buffer "^4.1.0" +socks@^2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + sonic-boom@^0.7.5: version "0.7.7" resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.7.7.tgz#d921de887428208bfa07b0ae32c278de043f350a" @@ -25069,6 +25560,21 @@ strip-ansi@^7.0.0: dependencies: ansi-regex "^6.0.1" +strip-bom-buf@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz#1cb45aaf57530f4caf86c7f75179d2c9a51dd572" + integrity sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI= + dependencies: + is-utf8 "^0.2.1" + +strip-bom-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" + integrity sha1-+H217yYT9paKpUWr/h7HKLaoKco= + dependencies: + first-chunk-stream "^2.0.0" + strip-bom "^2.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -25723,6 +26229,11 @@ text-table@0.2.0, text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +textextensions@^5.12.0, textextensions@^5.13.0: + version "5.14.0" + resolved "https://registry.npmjs.org/textextensions/-/textextensions-5.14.0.tgz#a6ff6aee5faaa751e6157d422c722a2bfd59eedf" + integrity sha512-4cAYwNFNYlIAHBUo7p6zw8POUvWbZor+/R0Tanv+rIhsauEyV9QSrEXL40pI+GfTQxKX8k6Tyw6CmdSDSmASrg== + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -25992,6 +26503,11 @@ tree-kill@^1.2.2: resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== +treeverse@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f" + integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -26971,6 +27487,29 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vinyl-file@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz#b104d9e4409ffa325faadd520642d0a3b488b365" + integrity sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U= + dependencies: + graceful-fs "^4.1.2" + pify "^2.3.0" + strip-bom-buf "^1.0.0" + strip-bom-stream "^2.0.0" + vinyl "^2.0.1" + +vinyl@^2.0.1: + version "2.2.1" + resolved "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" + integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== + dependencies: + clone "^2.1.1" + clone-buffer "^1.0.0" + clone-stats "^1.0.0" + cloneable-readable "^1.0.0" + remove-trailing-separator "^1.0.1" + replace-ext "^1.0.0" + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -27011,6 +27550,11 @@ wait-on@6.0.0: minimist "^1.2.5" rxjs "^7.1.0" +walk-up-path@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" + integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -27857,6 +28401,46 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +yeoman-environment@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-3.6.0.tgz#91267ced0d474c4dec330b83f232e72e796c0730" + integrity sha512-X16N9lhzRdUKFT8MZrpwjLDKsdgAUqh4VPR2wAXeAqjJJaUxYBxCQGFxtZVTf3vbyNuIHXPunwOLtK60bpapbg== + dependencies: + "@npmcli/arborist" "^2.2.2" + are-we-there-yet "^1.1.5" + arrify "^2.0.1" + binaryextensions "^4.15.0" + chalk "^4.1.0" + cli-table "^0.3.1" + commander "7.1.0" + dateformat "^4.5.0" + debug "^4.1.1" + diff "^5.0.0" + error "^10.4.0" + escape-string-regexp "^4.0.0" + execa "^5.0.0" + find-up "^5.0.0" + globby "^11.0.1" + grouped-queue "^2.0.0" + inquirer "^8.0.0" + is-scoped "^2.1.0" + lodash "^4.17.10" + log-symbols "^4.0.0" + mem-fs "^1.2.0 || ^2.0.0" + mem-fs-editor "^8.1.2 || ^9.0.0" + minimatch "^3.0.4" + npmlog "^4.1.2" + p-queue "^6.6.2" + pacote "^11.2.6" + preferred-pm "^3.0.3" + pretty-bytes "^5.3.0" + semver "^7.1.3" + slash "^3.0.0" + strip-ansi "^6.0.0" + text-table "^0.2.0" + textextensions "^5.12.0" + untildify "^4.0.0" + yml-loader@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz#b976b8691b537b6d3dc7d92a9a7d34b90de10870"