diff --git a/.changeset/afraid-trainers-know.md b/.changeset/afraid-trainers-know.md new file mode 100644 index 0000000000..5df3d3eced --- /dev/null +++ b/.changeset/afraid-trainers-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Fix a bug preventing an access token to be refreshed a second time with the GitLab provider. diff --git a/.changeset/brave-eggs-rush.md b/.changeset/brave-eggs-rush.md new file mode 100644 index 0000000000..8e1a7ca86d --- /dev/null +++ b/.changeset/brave-eggs-rush.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +--- + +Change the styling of the `` to have more contrast in light +mode. Nodes now have a design similar to material UI buttons. diff --git a/.changeset/breezy-camels-bake.md b/.changeset/breezy-camels-bake.md deleted file mode 100644 index 4ec6e7dab1..0000000000 --- a/.changeset/breezy-camels-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-todo': patch ---- - -Fix TodoList table column widths diff --git a/.changeset/breezy-gifts-wave.md b/.changeset/breezy-gifts-wave.md deleted file mode 100644 index 2bea8e8cd1..0000000000 --- a/.changeset/breezy-gifts-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Fix issue #7021 scaffolder action fetch:template preserves templates file permissions diff --git a/.changeset/bright-birds-jog.md b/.changeset/bright-birds-jog.md deleted file mode 100644 index 20954eca77..0000000000 --- a/.changeset/bright-birds-jog.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Fix warning produced by BottomLink component - -During development, we noticed warnings such as: - -``` -react_devtools_backend.js:2842 Warning: validateDOMNesting(...):
cannot appear as a descendant of

. -``` - -The BottomLink component renders a Box component within a Typography component which leads to a div tag within a p tag. -This change inverts that ordering without changing the visual appearance. diff --git a/.changeset/cuddly-rabbits-look.md b/.changeset/cuddly-rabbits-look.md new file mode 100644 index 0000000000..421c97bbff --- /dev/null +++ b/.changeset/cuddly-rabbits-look.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Action needed: If you are using the templates located at https://github.com/backstage/backstage/tree/master/ in your Backstage app directly using the URL via the `app-config.yaml`, you should copy over the templates inside your org and import from there. The templates have now been moved to https://github.com/backstage/software-templates. See https://github.com/backstage/backstage/issues/6415 for explanation. diff --git a/.changeset/dull-trains-shout.md b/.changeset/dull-trains-shout.md deleted file mode 100644 index bfd9a461d3..0000000000 --- a/.changeset/dull-trains-shout.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/backend-test-utils': patch -'@backstage/catalog-client': patch -'@backstage/catalog-model': patch -'@backstage/cli-common': patch -'@backstage/config-loader': patch -'@backstage/dev-utils': patch -'@backstage/errors': patch ---- - -Properly export all used types. diff --git a/.changeset/fast-actors-grow.md b/.changeset/fast-actors-grow.md new file mode 100644 index 0000000000..c1da84f744 --- /dev/null +++ b/.changeset/fast-actors-grow.md @@ -0,0 +1,16 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +Bump `passport-saml` to version 3. This is a breaking change, in that it [now requires](https://github.com/node-saml/passport-saml/pull/548) the `auth.saml.cert` parameter to be set. If you are not using SAML auth, you can ignore this. + +To update your settings, add something similar to the following to your app-config: + +```yaml +auth: + saml: + # ... other settings ... + cert: 'MIICizCCAfQCCQCY8tKaMc0BMjANBgkqh ... W==' +``` + +For more information, see the [library README](https://github.com/node-saml/passport-saml#security-and-signatures). diff --git a/.changeset/fast-wasps-matter.md b/.changeset/fast-wasps-matter.md deleted file mode 100644 index 6b6a682a37..0000000000 --- a/.changeset/fast-wasps-matter.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Updated the search configuration class to use the static `fromConfig`-based constructor for the `DefaultCatalogCollator`. - -To apply this change to an existing app, replace the following line in `search.ts`: - -```diff --collator: new DefaultCatalogCollator({ discovery }) -+collator: DefaultCatalogCollator.fromConfig(config, { discovery }) -``` - -The `config` parameter was not needed before, so make sure you also add that in the signature of `createPlugin` -in `search.ts`: - -```diff -export default async function createPlugin({ - logger, - discovery, -+ config, -}: PluginEnvironment) { -``` diff --git a/.changeset/forty-moons-boil.md b/.changeset/forty-moons-boil.md deleted file mode 100644 index 0000374546..0000000000 --- a/.changeset/forty-moons-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-todo': patch ---- - -All types are now properly documented and exported. diff --git a/.changeset/good-singers-flourish.md b/.changeset/good-singers-flourish.md deleted file mode 100644 index d8b6e1a140..0000000000 --- a/.changeset/good-singers-flourish.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-home': minor ---- - -Rename RandomJokeHomePageComponent to HomePageRandomJoke to fit convention, and update example app accordingly. -**NOTE**: If you're using the RandomJoke component in your instance, it now has to be renamed to `HomePageRandomJoke` diff --git a/.changeset/great-balloons-walk.md b/.changeset/great-balloons-walk.md deleted file mode 100644 index 7675a2badd..0000000000 --- a/.changeset/great-balloons-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/config': patch ---- - -Documented `Config` interface and mark types as public. diff --git a/.changeset/grumpy-beds-design.md b/.changeset/grumpy-beds-design.md new file mode 100644 index 0000000000..afca4e0eb3 --- /dev/null +++ b/.changeset/grumpy-beds-design.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Fix import for `createPlugin` in example snippet diff --git a/.changeset/happy-cups-tap.md b/.changeset/happy-cups-tap.md deleted file mode 100644 index 051f9def1c..0000000000 --- a/.changeset/happy-cups-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Allow the catalog search collator to filter the entities that it indexes diff --git a/.changeset/hot-otters-taste.md b/.changeset/hot-otters-taste.md new file mode 100644 index 0000000000..1e41f980f1 --- /dev/null +++ b/.changeset/hot-otters-taste.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-newrelic': patch +--- + +Added pagination and searching to the table diff --git a/.changeset/late-mangos-allow.md b/.changeset/late-mangos-allow.md new file mode 100644 index 0000000000..e1e4fe1d56 --- /dev/null +++ b/.changeset/late-mangos-allow.md @@ -0,0 +1,28 @@ +--- +'@backstage/plugin-home': minor +--- + +The homepage `

` is now part of the composable canvas (allowing you to add the , for example). + +You will need to wrap your existing composed `` component in ``, `
`, and `` components, like this: + +```diff +// app/src/components/home/HomePage.tsx + ++ import { Content, Header, Page, HomePageTimer } from '@backstage/core-components'; + +export const HomePage = () => ( ++ ++
++ ++
++ + + + + + // ... ++ ++
+); +``` diff --git a/.changeset/long-walls-tie.md b/.changeset/long-walls-tie.md deleted file mode 100644 index c23c34b4ce..0000000000 --- a/.changeset/long-walls-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -GitHub Webhook action in Scaffolder Backend has been improved to validate event names against Octokit Webhook event names list. diff --git a/.changeset/neat-mugs-add.md b/.changeset/neat-mugs-add.md new file mode 100644 index 0000000000..b90c95440d --- /dev/null +++ b/.changeset/neat-mugs-add.md @@ -0,0 +1,7 @@ +--- +'@backstage/integration': patch +--- + +Fix Azure URL handling to properly support both repo shorthand (`/owner/_git/project`) and full URLs (`/owner/project/_git/repo`). + +Fix Azure DevOps Server URL handling by being able to parse URLs with hosts other than `dev.azure.com`. Note that the `api-version` used for API requests is currently `6.0`, meaning you need to support at least this version in your Azure DevOps Server instance. diff --git a/.changeset/perfect-keys-hide.md b/.changeset/perfect-keys-hide.md new file mode 100644 index 0000000000..cab9967363 --- /dev/null +++ b/.changeset/perfect-keys-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fill in most missing type exports. diff --git a/.changeset/polite-dragons-allow.md b/.changeset/polite-dragons-allow.md new file mode 100644 index 0000000000..05eac53a1f --- /dev/null +++ b/.changeset/polite-dragons-allow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Use a more informative error message if the configured OIDC identity provider does not provide a `userinfo_endpoint` in its metadata. diff --git a/.changeset/poor-weeks-act.md b/.changeset/poor-weeks-act.md new file mode 100644 index 0000000000..152ce4bfc6 --- /dev/null +++ b/.changeset/poor-weeks-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Change the Categories filter to a dropdown component diff --git a/.changeset/real-beans-collect.md b/.changeset/real-beans-collect.md new file mode 100644 index 0000000000..43dae9cd9a --- /dev/null +++ b/.changeset/real-beans-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Fix Azure `readTree` and `search` handling to properly support paths. diff --git a/.changeset/short-mugs-drive.md b/.changeset/short-mugs-drive.md new file mode 100644 index 0000000000..f2b34fd2c4 --- /dev/null +++ b/.changeset/short-mugs-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Improve UX for Login pop-up diff --git a/.changeset/six-crabs-sit.md b/.changeset/six-crabs-sit.md new file mode 100644 index 0000000000..9389906ee9 --- /dev/null +++ b/.changeset/six-crabs-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +remove hard coded min height in page header diff --git a/.changeset/strong-geese-hope.md b/.changeset/strong-geese-hope.md new file mode 100644 index 0000000000..27da35743a --- /dev/null +++ b/.changeset/strong-geese-hope.md @@ -0,0 +1,13 @@ +--- +'@backstage/backend-common': patch +'@backstage/integration': patch +'@backstage/techdocs-common': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-techdocs': patch +--- + +Upgrade git-parse-url to v11.6.0 diff --git a/.changeset/strong-pears-wink.md b/.changeset/strong-pears-wink.md deleted file mode 100644 index 811eb56519..0000000000 --- a/.changeset/strong-pears-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Fix display error when it fails to load a template (/create) page diff --git a/.changeset/techdocs-small-bugs-exist.md b/.changeset/techdocs-small-bugs-exist.md new file mode 100644 index 0000000000..5e7c01c1a4 --- /dev/null +++ b/.changeset/techdocs-small-bugs-exist.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-home': patch +--- + +Adjust dependencies to `@types/react` and `react-router` to follow the pattern +used by all other Backstage packages. diff --git a/.changeset/ten-dolls-ring.md b/.changeset/ten-dolls-ring.md new file mode 100644 index 0000000000..8142d1f3c8 --- /dev/null +++ b/.changeset/ten-dolls-ring.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-explore': patch +--- + +Make styling of groups tab in the explore page use the Backstage theme instead +of hard coded colors. Change the layout to be full screen. diff --git a/.changeset/tidy-shoes-occur.md b/.changeset/tidy-shoes-occur.md new file mode 100644 index 0000000000..9ffac60f56 --- /dev/null +++ b/.changeset/tidy-shoes-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Handle pagination on return results when fetch GitHub API to list selected repos diff --git a/.changeset/twenty-ravens-fetch.md b/.changeset/twenty-ravens-fetch.md deleted file mode 100644 index 28393b7936..0000000000 --- a/.changeset/twenty-ravens-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search': patch ---- - -Add Home Page Search Bar Component, to be included in composable Home Page. diff --git a/.changeset/wise-monkeys-enjoy.md b/.changeset/wise-monkeys-enjoy.md deleted file mode 100644 index 446d5bbfad..0000000000 --- a/.changeset/wise-monkeys-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Properly export all used types. diff --git a/.changeset/yellow-hats-sniff.md b/.changeset/yellow-hats-sniff.md deleted file mode 100644 index b360cfef29..0000000000 --- a/.changeset/yellow-hats-sniff.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Support importing font files in tests. -This fixes remaining issues from [#7019](https://github.com/backstage/backstage/pull/7019). diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 781feb1f04..0854635a2b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,18 +8,18 @@ /docs/features/techdocs @backstage/techdocs-core /docs/features/search @backstage/techdocs-core /docs/assets/search @backstage/techdocs-core -/plugins/code-coverage @alde @nissayeva -/plugins/code-coverage-backend @alde @nissayeva +/plugins/code-coverage @backstage/reviewers @alde @nissayeva +/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva /plugins/cost-insights @backstage/silver-lining -/plugins/cloudbuild @trivago/ebarrios +/plugins/cloudbuild @backstage/reviewers @trivago/ebarrios /plugins/search @backstage/techdocs-core /plugins/search-* @backstage/techdocs-core /plugins/techdocs @backstage/techdocs-core /plugins/techdocs-backend @backstage/techdocs-core -/plugins/ilert @yacut +/plugins/ilert @backstage/reviewers @yacut /plugins/home @backstage/techdocs-core /packages/search-common @backstage/techdocs-core /packages/techdocs-common @backstage/techdocs-core -/.changeset/cost-insights-* @backstage/silver-lining +/.changeset/cost-insights-* @backstage/reviewers @backstage/silver-lining /.changeset/search-* @backstage/techdocs-core /.changeset/techdocs-* @backstage/techdocs-core diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index dd9224fc84..5c9d46ea8a 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -68,7 +68,6 @@ devs discoverability Discoverability dls -docgen Dockerfile dockerfiles Dockerize diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8361654038..d203fafa2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,9 +105,6 @@ jobs: run: git diff --quiet origin/master HEAD -- yarn.lock continue-on-error: true - - name: verify doc links - run: node scripts/verify-links.js - - name: prettier run: yarn prettier:check '!ADOPTERS.md' @@ -123,8 +120,12 @@ jobs: - name: type checking and declarations run: yarn tsc:full - - name: check api reports - run: yarn build:api-reports:only --ci + # We need to generate the API references as well, so that we can verify the doc links + - name: check api reports and generate API reference + run: yarn build:api-reports:only --ci --docs + + - name: verify doc links + run: node scripts/verify-links.js - name: build changed packages if: ${{ steps.yarn-lock.outcome == 'success' }} diff --git a/.github/workflows/microsite-build-check.yml b/.github/workflows/microsite-build-check.yml index 68a6c08cd9..73f63ed63a 100644 --- a/.github/workflows/microsite-build-check.yml +++ b/.github/workflows/microsite-build-check.yml @@ -27,9 +27,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: verify doc links - run: node scripts/verify-links.js - # Skip caching of microsite dependencies, it keeps the global cache size # smaller, which make Windows builds a lot faster for the rest of the project. - name: yarn install diff --git a/.github/workflows/microsite-with-storybook-deploy.yml b/.github/workflows/microsite-with-storybook-deploy.yml index e8886b7d6b..47e18340eb 100644 --- a/.github/workflows/microsite-with-storybook-deploy.yml +++ b/.github/workflows/microsite-with-storybook-deploy.yml @@ -4,12 +4,6 @@ on: push: branches: - master - paths: - - '.github/workflows/microsite-with-storybook-deploy.yml' - - 'packages/storybook/**' - - 'packages/core-components/src/**' - - 'microsite/**' - - 'docs/**' jobs: deploy-microsite-and-storybook: @@ -41,6 +35,9 @@ jobs: run: yarn install --frozen-lockfile working-directory: microsite + - name: build API reference + run: yarn build:api-docs + - name: build microsite run: yarn build working-directory: microsite diff --git a/.github/workflows/snyk-monitor.yml b/.github/workflows/snyk-monitor.yml new file mode 100644 index 0000000000..0adb6c5bcb --- /dev/null +++ b/.github/workflows/snyk-monitor.yml @@ -0,0 +1,51 @@ +name: Snyk Monitoring + +on: + workflow_dispatch: + push: + branches: [master] + paths: + - '.github/workflows/snyk-monitor.yml' + - '**/.snyk' + - '**/package.json' + - 'yarn.lock' + +# This workflow synchronizes the packages in this repo along with policies in +# each .snyk file with the remote state in our snyk org. It allows us to define +# ignore policies in the .snyk files and then have them show up in the snyk web +# UI, and also automatically adds any new packages that are created. + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Monitor and Synchronize Snyk Policies + uses: snyk/actions/node@master + with: + command: monitor + args: > + --yarn-workspaces + --org=backstage-dgh + --strict-out-of-sync=false + --remote-repo-url=https://github.com/backstage/backstage + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + + # Above we run the `monitor` command, this runs the `test` command which is + # the one that generates the SARIF report that we can upload to GitHub. + - name: Create Snyk report + uses: snyk/actions/node@master + continue-on-error: true # To make sure that SARIF upload gets called + with: + args: > + --yarn-workspaces + --org=backstage-dgh + --strict-out-of-sync=false + --sarif-file-output=snyk.sarif + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + - name: Upload Snyk report + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: snyk.sarif diff --git a/SECURITY.md b/SECURITY.md index b410404bcf..ba96c694ee 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -26,3 +26,13 @@ In the event of a vulnerability the runbook for the maintainers is as follows: 1. Create one or multiple Pull Requests with fixes towards the fork. Note that these PRs will not have CI checks run on them, so more care should be taken to run local validation. The PRs are also not merged like normal PRs, but are instead merged straight into the main repo all at once when the merge button is hit on the security advisory. 4. Once the fix is ready in a PR or private fork and it is time to release it, there are a couple of options. Either merge into the main branch with a changeset and wait for a regular release, or do a quick release by manually bumping the version in `package.json` of the affected package, along with a manual `CHANGELOG.md` entry. Note that a quick release will only work if the package does not have any other pending changes that depend on pending changes in other packages, so be sure to manually check that first, and fall back to an early regular release if needed. In general it's best to stick with the regular release flow, with the quick release being used only for time sensitive fixes. 5. Finalize and publish the security advisory. Note that once you hit the publish button it's no longer possible to edit the advisory. Just like the CVE number this can take up to 72h, and expect it to be slower than the CVE number request. + +## Vulnerability Policies + +Backstage uses Snyk vulnerability scans in order to make sure we minimize vulnerabilities in our dependencies and get notified of new vulnerabilities. + +There are many situations where a vulnerability does not affect a particular dependency because of how the vulnerable package is used. In that situation the package authors may choose to stay at the current version rather than bumping the dependency, leading to a warning in the vulnerability scans but no actual vulnerability. + +To work around this and other similar issues, Snyk provides a method to ignore vulnerabilities. In order to provide the best visibility and most utility to adopters of Backstage, we store these ignore rules in `.snyk` policy files. This allows adopters to rely on our ignore policies if they wish to do so. + +Adding a new ignore policy is done by creating or modifying an existing `.snyk` file within a package root. See the [Snyk Documentation](https://support.snyk.io/hc/en-us/articles/360007487097-The-snyk-file) for details on the syntax. Always include a description, full path, and time limit of the ignore policy. diff --git a/app-config.yaml b/app-config.yaml index c5c4a23bdd..2fb75f718e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -312,6 +312,7 @@ auth: saml: entryPoint: 'http://localhost:7001/' issuer: 'passport-saml' + cert: 'fake-cert-base64' okta: development: clientId: ${AUTH_OKTA_CLIENT_ID} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000..90b1e2c7ef --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +# This is generated by build:api-docs in the root +reference diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index f04202b79b..1b364bcdae 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -12,25 +12,32 @@ however always be a need for plugins to communicate outside of its boundaries, both with other plugins and the app itself. Backstage provides two primary methods for plugins to communicate across their -boundaries in client-side code. The first one being the `createPlugin` API and -the registration hooks passed to the `register` method, and the second one being -Utility APIs. While the `createPlugin` API is focused on the initialization -plugins and the app, the Utility APIs provide ways for plugins to communicate -during their entire life cycle. +boundaries in client-side code. The first one being the +[createPlugin](../reference/core-plugin-api.createplugin.md) API along with the +extensions that it can provide, and the second one being Utility APIs. While the +[createPlugin](../reference/core-plugin-api.createplugin.md) API is focused on +the initialization plugins and the app, the Utility APIs provide ways for +plugins to communicate during their entire life cycle. ## Consuming APIs -Each Utility API is tied to an `ApiRef` instance, which is a global singleton -object without any additional state or functionality, its only purpose is to -reference Utility APIs. `ApiRef`s are created using `createApiRef`, which is -exported by `@backstage/core-plugin-api`. There are many -[predefined Utility APIs](../reference/utility-apis/README.md) defined in -`@backstage/core-plugin-api`, and they're all exported with a name of the -pattern `*ApiRef`, for example `errorApiRef`. +Each Utility API is tied to an [ApiRef](../reference/core-plugin-api.apiref.md) +instance, which is a global singleton object without any additional state or +functionality, its only purpose is to reference Utility APIs. +[ApiRef](../reference/core-plugin-api.apiref.md)s are created using +[createApiRef](../reference/core-plugin-api.createapiref.md), which is exported +by [@backstage/core-plugin-api](../reference/core-plugin-api.md). There are also +many predefined Utility APIs in +[@backstage/core-plugin-api](../reference/core-plugin-api.md), and they're all +exported with a name of the pattern `*ApiRef`, for example +[errorApiRef](../reference/core-plugin-api.errorapiref.md). -To access one of the Utility APIs inside a React component, use the `useApi` -hook exported by `@backstage/core-plugin-api`, or the `withApis` HOC if you -prefer class components. For example, the `ErrorApi` can be accessed like this: +To access one of the Utility APIs inside a React component, use the +[useApi](../reference/core-plugin-api.useapi.md) hook exported by +[@backstage/core-plugin-api](../reference/core-plugin-api.md), or the +[withApis](../reference/core-plugin-api.withapis.md) HOC if you prefer class +components. For example, the +[ErrorApi](../reference/core-plugin-api.errorapi.md) can be accessed like this: ```tsx import React from 'react'; @@ -48,24 +55,31 @@ export const MyComponent = () => { }; ``` -Note that there is no explicit type given for `ErrorApi`. This is because the -`errorApiRef` has the type embedded, and `useApi` is able to infer the type. +Note that there is no explicit type given for +[ErrorApi](../reference/core-plugin-api.errorapi.md). This is because the +[errorApiRef](../reference/core-plugin-api.errorapiref.md) has the type +embedded, and [useApi](../reference/core-plugin-api.useapi.md) is able to infer +the type. Also note that consuming Utility APIs is not limited to plugins, it can be done from any component inside Backstage, including the ones in -`@backstage/core-plugin-api`. The only requirement is that they are beneath the -`AppProvider` in the react tree. +[@backstage/core-plugin-api](../reference/core-plugin-api.md). The only +requirement is that they are beneath the `AppProvider` in the react tree. ## Supplying APIs ### API Factories -APIs are registered in the form of `ApiFactories`, which encapsulate the process -of instantiating an API. It is a collection of three things: the `ApiRef` of the -API to instantiate, a list of all required dependencies, and a factory function -that returns a new API instance. +APIs are registered in the form of +[ApiFactories](../reference/core-plugin-api.apifactory.md), which encapsulate +the process of instantiating an API. It is a collection of three things: the +[ApiRef](../reference/core-plugin-api.apiref.md) of the API to instantiate, a +list of all required dependencies, and a factory function that returns a new API +instance. -For example, this is the default `ApiFactory` for the `ErrorApi`: +For example, this is the default +[ApiFactory](../reference/core-plugin-api.apifactory.md) for the +[ErrorApi](../reference/core-plugin-api.errorapi.md): ```ts createApiFactory({ @@ -79,18 +93,25 @@ createApiFactory({ }); ``` -In this example the `errorApiRef` is our API, which encapsulates the `ErrorApi` -type. The `alertApiRef` is our single dependency, which we give the name -`alertApi`, and is then passed on to the factory function, which returns an -implementation of the `ErrorApi`. +In this example the [errorApiRef](../reference/core-plugin-api.errorapiref.md) +is our API, which encapsulates the +[ErrorApi](../reference/core-plugin-api.errorapi.md) type. The +[alertApiRef](../reference/core-plugin-api.alertapiref.md) is our single +dependency, which we give the name `alertApi`, and is then passed on to the +factory function, which returns an implementation of the +[ErrorApi](../reference/core-plugin-api.errorapi.md). -The `createApiFactory` function is a thin wrapper that enables TypeScript type -inference. You may notice that there are no type annotations in the above -example, and that is because we're able to infer all types from the `ApiRef`s. -TypeScript will make sure that the return value of the `factory` function -matches the type embedded in `api`'s `ApiRef`, in this case the `ErrorApi`. It -will also match the types between the `deps` and the parameters of the `factory` -function, again using the type embedded within the `ApiRef`s. +The [createApiFactory](../reference/core-plugin-api.createapifactory.md) +function is a thin wrapper that enables TypeScript type inference. You may +notice that there are no type annotations in the above example, and that is +because we're able to infer all types from the +[ApiRef](../reference/core-plugin-api.apiref.md)s. TypeScript will make sure +that the return value of the `factory` function matches the type embedded in +`api`'s [ApiRef](../reference/core-plugin-api.apiref.md), in this case the +[ErrorApi](../reference/core-plugin-api.errorapi.md). It will also match the +types between the `deps` and the parameters of the `factory` function, again +using the type embedded within the +[ApiRef](../reference/core-plugin-api.apiref.md)s. ## Registering API Factories @@ -102,24 +123,27 @@ app, and the app itself. Starting with the Backstage core library, it provides implementations for all of the core APIs. The core APIs are the ones exported by -`@backstage/core-plugin-api`, such as the `errorApiRef` and `configApiRef`. You -can find a full list of them [here](../reference/utility-apis/README.md). +[@backstage/core-plugin-api](../reference/core-plugin-api.md), such as the +[errorApiRef](../reference/core-plugin-api.errorapiref.md) and +[configApiRef](../reference/core-plugin-api.configapiref.md). -The core APIs are loaded for any app created with `createApp` from -`@backstage/core-plugin-api`, which means that there is no step that needs to be -taken to include these APIs in an app. +The core APIs are loaded for any app created with +[createApp](../reference/core-app-api.createapp.md) from +[@backstage/core-plugin-api](../reference/core-plugin-api.md), which means that +there is no step that needs to be taken to include these APIs in an app. ### Plugin APIs In addition to the core APIs, plugins can define and export their own APIs. While doing so they should usually also provide default implementations of their own APIs, for example, the `catalog` plugin exports `catalogApiRef`, and also -supplies a default `ApiFactory` of that API using the `CatalogClient`. There is -one restriction to plugin-provided API Factories: plugins may not supply -factories for core APIs, trying to do so will cause the app to refuse to start. +supplies a default [ApiFactory](../reference/core-plugin-api.apifactory.md) of +that API using the `CatalogClient`. There is one restriction to plugin-provided +API Factories: plugins may not supply factories for core APIs, trying to do so +will cause the app to refuse to start. -Plugins supply their APIs through the `apis` option of `createPlugin`, for -example: +Plugins supply their APIs through the `apis` option of +[createPlugin](../reference/core-plugin-api.createplugin.md), for example: ```ts export const techdocsPlugin = createPlugin({ @@ -144,7 +168,8 @@ Lastly, the app itself is the final point where APIs can be added, and what has the final say in what APIs will be loaded at runtime. The app may override the factories for any of the core or plugin APIs, with the exception of the config, app theme, and identity APIs. These are static APIs that are tied into the -`createApp` implementation, and therefore not possible to override. +[createApp](../reference/core-app-api.createapp.md) implementation, and +therefore not possible to override. Overriding APIs is useful for apps that want to switch out behavior to tailor it to their environment. In some cases plugins may also export multiple @@ -206,16 +231,19 @@ const app = createApp({ ``` Note that the above line will cause an error if `IgnoreErrorApi` does not fully -implement the `ErrorApi`, as it is checked by the type embedded in the -`errorApiRef` at compile time. +implement the [ErrorApi](../reference/core-plugin-api.errorapi.md), as it is +checked by the type embedded in the +[errorApiRef](../reference/core-plugin-api.errorapiref.md) at compile time. ## Defining custom Utility APIs Plugins are free to define their own Utility APIs. Simply define the TypeScript -interface for the API, and create an `ApiRef` using `createApiRef` exported from -`@backstage/core-plugin-api`. Also be sure to provide at least one -implementation of the API, and to declare a default factory for the API in -`createPlugin`. +interface for the API, and create an +[ApiRef](../reference/core-plugin-api.apiref.md) using +[createApiRef](../reference/core-plugin-api.createapiref.md) exported from +[@backstage/core-plugin-api](../reference/core-plugin-api.md). Also be sure to +provide at least one implementation of the API, and to declare a default factory +for the API in [createPlugin](../reference/core-plugin-api.createplugin.md). Custom Utility APIs can be either public or private, which is up to the plugin to choose. Private APIs do not expose an external API surface, and it's @@ -226,15 +254,18 @@ plugin to override the API in the app. It is however important to maintain backwards compatibility of public APIs, as you may otherwise break apps that are using your plugin. -To make an API public, simply export the `ApiRef` of the API, and any associated -types. To make an API private, just avoid exporting the `ApiRef`, but still be -sure to supply a default factory to `createPlugin`. +To make an API public, simply export the +[ApiRef](../reference/core-plugin-api.apiref.md) of the API, and any associated +types. To make an API private, just avoid exporting the +[ApiRef](../reference/core-plugin-api.apiref.md), but still be sure to supply a +default factory to [createPlugin](../reference/core-plugin-api.createplugin.md). Private APIs are useful for plugins that want to depend on other APIs outside of React components, but not have to expose an entire API surface to maintain. When using private APIs, it is fine to use the `typeof` of an implementing class as -the type parameter passed to `createApiRef`, while public APIs should always -define a separate TypeScript interface type. +the type parameter passed to +[createApiRef](../reference/core-plugin-api.createapiref.md), while public APIs +should always define a separate TypeScript interface type. Plugins may depend on APIs from other plugins, both in React components and as dependencies to API factories. Do however be sure to not cause circular @@ -242,13 +273,14 @@ dependencies between plugins. ## Architecture -The `ApiRef` instances mentioned above provide a point of indirection between -consumers and producers of Utility APIs. It allows for plugins and components to -depend on APIs in a type-safe way, without having a direct reference to a -concrete implementation of the APIs. The Apps are also given a lot of -flexibility in what implementations to provide. As long as they adhere to the -contract established by an `ApiRef`, they are free to choose any implementation -they want. +The [ApiRef](../reference/core-plugin-api.apiref.md) instances mentioned above +provide a point of indirection between consumers and producers of Utility APIs. +It allows for plugins and components to depend on APIs in a type-safe way, +without having a direct reference to a concrete implementation of the APIs. The +Apps are also given a lot of flexibility in what implementations to provide. As +long as they adhere to the contract established by an +[ApiRef](../reference/core-plugin-api.apiref.md), they are free to choose any +implementation they want. The figure below shows the relationship between different Apps, that provide @@ -271,14 +303,17 @@ directly tied to React. The indirection provided by Utility APIs also makes it straightforward to test components that depend on APIs, and to provide a standard common development environment for plugins. A proper test wrapper with mocked API implementations -is not yet ready, but it will be provided as a part of `@backstage/test-utils`. -It will provide mocked variants of APIs, with additional methods for asserting a -component's interaction with the API. +is not yet ready, but it will be provided as a part of +[@backstage/test-utils](../reference/test-utils.md). It will provide mocked +variants of APIs, with additional methods for asserting a component's +interaction with the API. The common development environment for plugins is included in -`@backstage/dev-utils`, where the exported `createDevApp` function creates an +[@backstage/dev-utils](../reference/dev-utils.md), where the exported +[createDevApp](../reference/dev-utils.createdevapp.md) function creates an application with implementations for all core APIs already present. Contrary to the method for wiring up Utility API implementations in an app created with -`createApp`, `createDevApp` uses automatic dependency injection. This is to make -it possible to replace any API implementation, and having that be reflected in -dependents of that API. +[createApp](../reference/core-app-api.createapp.md), +[createDevApp](../reference/dev-utils.createdevapp.md) uses automatic dependency +injection. This is to make it possible to replace any API implementation, and +having that be reflected in dependents of that API. diff --git a/docs/auth/index.md b/docs/auth/index.md index 76cd79b0fd..0c03e33900 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -60,9 +60,8 @@ small update to show this provider as a login option. The `SignInPage` component handles this, and takes either a `provider` or `providers` (array) prop of `SignInProviderConfig` definitions. -These reference the [ApiRef](../reference/utility-apis/README.md) exported by -the provider. Again, an example using GitHub that can be adapted to any of the -built-in providers: +These reference the `ApiRef` exported by the provider. Again, an example using +GitHub that can be adapted to any of the built-in providers: ```diff # packages/app/src/App.tsx diff --git a/docs/auth/using-auth.md b/docs/auth/using-auth.md index e4f87a12f9..067c43e8a0 100644 --- a/docs/auth/using-auth.md +++ b/docs/auth/using-auth.md @@ -28,7 +28,8 @@ OAuth helps in that regard. The method with which frontend plugins request access to third party services is through [Utility APIs](../api/utility-apis.md) for each service provider. For a full list of providers, see the -[Utility API References](../reference/utility-apis/README.md). +[@backstage/core-plugin-api](../reference/core-plugin-api.md#variables) +reference. ### Identity - WIP diff --git a/docs/conf/reading.md b/docs/conf/reading.md index 6ac10a8c8b..fd1ce4c7a6 100644 --- a/docs/conf/reading.md +++ b/docs/conf/reading.md @@ -7,7 +7,7 @@ description: Documentation on Reading Backstage Configuration ## Config API There's a common configuration API for by both frontend and backend plugins. An -API reference can be found [here](../reference/utility-apis/Config.md). +API reference can be found [here](../reference/config.config.md). The configuration API is tailored towards failing fast in case of missing or bad config. That's because configuration errors can always be considered programming @@ -110,9 +110,18 @@ example `getString`. These will throw an error if there is no value available. ## Accessing ConfigApi in Frontend Plugins -The [ConfigApi](../reference/utility-apis/Config.md) in the frontend is a +The [ConfigApi](../reference/core-plugin-api.configapi.md) in the frontend is a [UtilityApi](../api/utility-apis.md). It's accessible as usual via the -`configApiRef` exported from `@backstage/core-plugin-api`. +`configApiRef` exported from `@backstage/core-plugin-api`: + +``` +import { useApi, configApiRef } from '@backstage/core-plugin-api'; +... +const MyReactComponent = (...) => { + const config = useApi(configApiRef); + ... +} +``` Depending on the config api in another API is slightly different though, as the `ConfigApi` implementation is supplied via the App itself and not instantiated diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index e542b02ebf..599c01a0ce 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -113,6 +113,9 @@ browser at `http://localhost:7000` ## Multi-stage Build +> NOTE: The `.dockerignore` is different in this setup, read on for more +> details. + This section describes how to set up a multi-stage Docker build that builds the entire project within Docker. This is typically slower than a host build, but is sometimes desired because Docker in Docker is not available in the build @@ -137,6 +140,7 @@ WORKDIR /app COPY package.json yarn.lock ./ COPY packages packages +# Comment this out if you don't have any internal plugins COPY plugins plugins RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -exec rm -rf {} \+ @@ -182,8 +186,9 @@ end up being properly installed. To speed up the build when not running in a fresh clone of the repo you should set up a `.dockerignore`. This one is different than the host build one, because -we want to have access to the source code of all packages for the build, but can -ignore any existing build output or dependencies: +we want to have access to the source code of all packages for the build. We can +however ignore any existing build output or dependencies on the host. For our +new `.dockerignore`, replace the contents of your existing one with this: ```text node_modules diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md index aa5aed0540..ed106bb5dd 100644 --- a/docs/features/software-catalog/extending-the-model.md +++ b/docs/features/software-catalog/extending-the-model.md @@ -393,3 +393,39 @@ of interest to you, you might consider contacting the maintainers on Discord or my making a GitHub issue describing your use case. [This issue](https://github.com/backstage/backstage/issues/2292) also contains more context. + +## Referencing different environments with the model + +Example intent: + +> "I have multiple versions of my API deployed in different environments so I +> want to have `mytool-dev` and `mytool-prod` as different entities." + +While it's possible to have different versions of the same thing represented as +separate entities, it's something we generally recommend against. We believe +that a developer should be able to just find for example one `Component` +representing a service, and to be able to see the different code versions that +are deployed throughout your stack within its view. This reasoning works +similarly for other kinds as well, such as `API`. + +That being said - sometimes the differences between versions are so large, that +they represent what is for all intents and purposes an entirely new entity as +seen from the consumer's point of view. This can happen for example for +different _significant_ major versions of an API, and in particular if the two +major versions coexist in the ecosystem for some time. In those cases, it can be +motivated to have one `my-api-v2` and one `my-api-v3` named entity. This matches +the end user's expectations when searching for the API, and matches the desire +to maybe have separate documentation for the two and similar. But use this +sparingly - only do it if the extra modelling burden is outweighed by any +potential better clarity for users. + +When writing your custom plugins, we encourage designing them such that they can +show all the different variations through environments etc under one canonical +reference to your software in the catalog. For example for a continuous +deployment plugin, a user is likely to be greatly helped by being able to see +the entity's versions deployed in all different environments next to each other +in one view. That is also where they might be offered the ability to promote +from one environment to the other, do rollbacks, see their relative performance +metrics, and similar. This coherency and collection of tooling in one place is +where something like Backstage can offer the most value and effectiveness of +use. Splitting your entities apart into small islands makes this harder. diff --git a/docs/features/software-catalog/life-of-an-entity.md b/docs/features/software-catalog/life-of-an-entity.md index 3b7b3e8117..9e8a3f0563 100644 --- a/docs/features/software-catalog/life-of-an-entity.md +++ b/docs/features/software-catalog/life-of-an-entity.md @@ -183,6 +183,64 @@ The stitching is currently a fixed process, that cannot be modified or extended. This means that any modifications you want to make on the final result, has to happen during ingestion or processing. -## Deletion +## Errors -> TODO +> TODO: Describe how errors are exposed through entities + +## Orphaning + +As mentioned earlier, entities internally form a graph. The edges go from +processed parent entities, to child entities emitted while processing the +parent. + +The processing loop runs continuously, so these edges are reconsidered over +time. If processing a parent entity no longer emits a given child entity, then +that former edge is severed. If that child has no other edges pointing at it +either, it becomes _orphaned_. The end result is as follows: + +- The stitching process injects a `backstage.io/orphan: 'true'` annotation on + the child entity. +- The child entity is _not_ removed from the catalog, but stays around until + explicitly deleted via the catalog API, or "reclaimed" by the original parent + or another parent starting to reference it. +- The catalog page in Backstage for the child entity detects the new annotation + and informs users about the orphan status. + +Orphaning can occur in several different scenarios. One common cause is that the +end user edited a corresponding catalog catalog-info YAML file removing the +entity's entry. In the case of a `Location` parent entity, orphaning can happen +if removing the target line pointing to the file containing the child entity. +Another common cause is large batch processors such as the ones that crawl +through remote systems looking for entities, no longer finding something that it +used to find before. Maybe the data was moved, or deleted, in the remote system. +So for example when a person leaves the company an LDAP org discovery processor +might leave an orphaned `User` entity behind. Note that this only applies to +processors - ingestion that happens using entity providers work differently, +described below. + +> Note that removing a file, or accidentally corrupting a file so that it cannot +> be read successfully, does _not_ lead to orphaning. Hard errors, including the +> inability to find or read a distinct remote, are marked as such on the entity +> to inform the owner that something is wrong. But processing and other +> behaviors continue as usual. + +The reason that the orphaning mechanism exists instead of having an eager +deletion triggered, is safety. Scenarios like these can happen purely by +accident, due to the asynchronous nature of the system and the fallible nature +of humans. In particular when external systems start consuming and relying on +the catalog, there could be substantial consequences to suddenly dropping +entities without explicit owner consent. The catalog therefore takes the stance +that entities that often were added by direct user action should also be deleted +only by direct user action. + +It is possible to use the catalog API to build automated "reaper" systems that +finally delete entities that are orphaned. This is however not something that's +provided out of the box. + +## Implicit Deletion + +> TODO: Describe the process of entity providers eagerly deleting entities + +## Explicit Deletion + +> TODO: Describe direct deletion via the catalog API diff --git a/docs/getting-started/project-structure.md b/docs/getting-started/project-structure.md index 1d5d58c2c9..bf2cc93ea5 100644 --- a/docs/getting-started/project-structure.md +++ b/docs/getting-started/project-structure.md @@ -147,11 +147,6 @@ are separated out into their own folder, see further down. Helps you setup a plugin for isolated development so that it can be served separately. -- [`docgen/`](https://github.com/backstage/backstage/tree/master/packages/docgen) - - Uses the - [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) - to read out definitions and generate documentation for it. - - [`e2e-test/`](https://github.com/backstage/backstage/tree/master/packages/e2e-test) - Another CLI that can be run to try out what would happen if you build all the packages, publish them, create a new app, and then run them. CI uses this for diff --git a/docs/overview/stability-index.md b/docs/overview/stability-index.md index ee2628a893..8e710d4e7c 100644 --- a/docs/overview/stability-index.md +++ b/docs/overview/stability-index.md @@ -145,12 +145,6 @@ Provides utilities for developing plugins in isolation. Stability: `0`. This package is largely broken and needs updates. -### `docgen` [GitHub](https://github.com/backstage/backstage/tree/master/packages/docgen/) - -Internal CLI utility for generating API Documentation. - -Stability: `N/A` - ### `e2e-test` [GitHub](https://github.com/backstage/backstage/tree/master/packages/e2e-test/) Internal CLI utility for running e2e tests. diff --git a/docs/plugins/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md index 99a55745c5..83fcfbddba 100644 --- a/docs/plugins/structure-of-a-plugin.md +++ b/docs/plugins/structure-of-a-plugin.md @@ -82,8 +82,8 @@ export const ExamplePage = examplePlugin.provide( This is where the plugin is created and where it creates and exports extensions that can be imported and used the app. See reference docs for -[createPlugin](../reference/createPlugin.md) or introduction to the new -[Composability System](./composability.md). +[createPlugin](../reference/core-plugin-api.createplugin.md) or introduction to +the new [Composability System](./composability.md). ## Components diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md deleted file mode 100644 index d550ba10a9..0000000000 --- a/docs/reference/createPlugin-feature-flags.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: createPlugin-feature-flags -title: createPlugin - feature flags -description: Documentation on createPlugin - feature flags ---- - -The `featureFlags` object passed to the `register` function makes it possible -for plugins to register Feature Flags in Backstage for users to opt into. You -can use this to split out logic in your code for manual A/B testing, etc. - -Here's a code sample: - -```typescript -import { createPlugin } from '@backstage/core-plugin-api'; - -export default createPlugin({ - id: 'plugin-name', - register({ featureFlags }) { - featureFlags.register('enable-example-feature'); - }, -}); -``` - -## Using with useApi - -To inspect the state of a feature flag inside your plugin, you can use the -`FeatureFlagsApi`, accessed via the `featureFlagsApiRef`. For example: - -```tsx -import React from 'react'; -import { Button } from '@material-ui/core'; -import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api'; - -const ExamplePage = () => { - const featureFlags = useApi(featureFlagsApiRef); - - return ( -
- - { featureFlags.isActive('enable-example-feature') && } -
- ); -}; -``` diff --git a/docs/reference/createPlugin.md b/docs/reference/createPlugin.md deleted file mode 100644 index 6602b0b2cc..0000000000 --- a/docs/reference/createPlugin.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -id: createPlugin -title: createPlugin -description: Documentation on createPlugin ---- - -Takes a plugin config as an argument and returns a new plugin. - -## Plugin Config - -```typescript -function createPlugin(config: PluginConfig): BackstagePlugin; - -type PluginConfig = { - id: string; - register?(hooks: PluginHooks): void; -}; - -type PluginHooks = { - featureFlags: FeatureFlagsHooks; -}; -``` - -- [Read more about feature flags here](createPlugin-feature-flags.md) - -## Example Uses - -### Creating a basic plugin - -Showcasing adding a feature flag. - -```jsx -import { createPlugin } from '@backstage/core-plugin-api'; - -export default createPlugin({ - id: 'new-plugin', - register({ router, featureFlags }) { - featureFlags.register('enable-example-component'); - }, -}); -``` diff --git a/docs/reference/utility-apis/AlertApi.md b/docs/reference/utility-apis/AlertApi.md deleted file mode 100644 index 8d1851cc44..0000000000 --- a/docs/reference/utility-apis/AlertApi.md +++ /dev/null @@ -1,114 +0,0 @@ -# AlertApi - -The AlertApi type is defined at -[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/AlertApi.ts#L29). - -The following Utility API implements this type: [alertApiRef](./README.md#alert) - -## Members - -### post() - -Post an alert for handling by the application. - -
-post(alert: AlertMessage): void
-
- -### alert\$() - -Observe alerts posted by other parts of the application. - -
-alert$(): Observable<AlertMessage>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AlertMessage - -
-export type AlertMessage = {
-  message: string;
-  // Severity will default to success since that is what material ui defaults the value to.
-  severity?: 'success' | 'info' | 'warning' | 'error';
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/AlertApi.ts#L19). - -Referenced by: [post](#post), [alert\$](#alert). - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L53). - -Referenced by: [alert\$](#alert). - -### Observer - -This file contains non-react related core types used throughout Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/AppThemeApi.md b/docs/reference/utility-apis/AppThemeApi.md deleted file mode 100644 index a662b4cb70..0000000000 --- a/docs/reference/utility-apis/AppThemeApi.md +++ /dev/null @@ -1,271 +0,0 @@ -# AppThemeApi - -The AppThemeApi type is defined at -[packages/core-api/src/apis/definitions/AppThemeApi.ts:56](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/AppThemeApi.ts#L56). - -The following Utility API implements this type: -[appThemeApiRef](./README.md#apptheme) - -## Members - -### getInstalledThemes() - -Get a list of available themes. - -
-getInstalledThemes(): AppTheme[]
-
- -### activeThemeId\$() - -Observe the currently selected theme. A value of undefined means no specific -theme has been selected. - -
-activeThemeId$(): Observable<string | undefined>
-
- -### getActiveThemeId() - -Get the current theme ID. Returns undefined if no specific theme is selected. - -
-getActiveThemeId(): string | undefined
-
- -### setActiveThemeId() - -Set a specific theme to use in the app, overriding the default theme selection. - -Clear the selection by passing in undefined. - -
-setActiveThemeId(themeId?: string): void
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AppTheme - -Describes a theme provided by the app. - -
-export type AppTheme = {
-  /**
-   * ID used to remember theme selections.
-   */
-  id: string;
-
-  /**
-   * Title of the theme
-   */
-  title: string;
-
-  /**
-   * Theme variant
-   */
-  variant: 'light' | 'dark';
-
-  /**
-   * The specialized MaterialUI theme instance.
-   */
-  theme: BackstageTheme;
-
-  /**
-   * An Icon for the theme mode setting.
-   */
-  icon?: React.ReactElement<SvgIconProps>;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/AppThemeApi.ts:25](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/AppThemeApi.ts#L25). - -Referenced by: [getInstalledThemes](#getinstalledthemes). - -### BackstagePalette - -
-export type BackstagePalette = Palette & PaletteAdditions
-
- -Defined at -[packages/theme/src/types.ts:74](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/theme/src/types.ts#L74). - -Referenced by: [BackstageTheme](#backstagetheme). - -### BackstageTheme - -
-export interface BackstageTheme extends Theme {
-  palette: BackstagePalette;
-  page: PageTheme;
-  getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme;
-}
-
- -Defined at -[packages/theme/src/types.ts:81](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/theme/src/types.ts#L81). - -Referenced by: [AppTheme](#apptheme). - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L53). - -Referenced by: [activeThemeId\$](#activethemeid). - -### Observer - -This file contains non-react related core types used throughout Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### PageTheme - -
-export type PageTheme = {
-  colors: string[];
-  shape: string;
-  backgroundImage: string;
-}
-
- -Defined at -[packages/theme/src/types.ts:103](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/theme/src/types.ts#L103). - -Referenced by: [BackstageTheme](#backstagetheme). - -### PageThemeSelector - -
-export type PageThemeSelector = {
-  themeId: string;
-}
-
- -Defined at -[packages/theme/src/types.ts:77](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/theme/src/types.ts#L77). - -Referenced by: [BackstageTheme](#backstagetheme). - -### PaletteAdditions - -
-type PaletteAdditions = {
-  status: {
-    ok: string;
-    warning: string;
-    error: string;
-    pending: string;
-    running: string;
-    aborted: string;
-  };
-  border: string;
-  textContrast: string;
-  textVerySubtle: string;
-  textSubtle: string;
-  highlight: string;
-  errorBackground: string;
-  warningBackground: string;
-  infoBackground: string;
-  errorText: string;
-  infoText: string;
-  warningText: string;
-  linkHover: string;
-  link: string;
-  gold: string;
-  navigation: {
-    background: string;
-    indicator: string;
-    color: string;
-    selectedColor: string;
-  };
-  tabbar: {
-    indicator: string;
-  };
-  bursts: {
-    fontColor: string;
-    slackChannelText: string;
-    backgroundColor: {
-      default: string;
-    };
-  };
-  pinSidebarButton: {
-    icon: string;
-    background: string;
-  };
-  banner: {
-    info: string;
-    error: string;
-    text: string;
-    link: string;
-  };
-}
-
- -Defined at -[packages/theme/src/types.ts:23](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/theme/src/types.ts#L23). - -Referenced by: [BackstagePalette](#backstagepalette). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/BackstageIdentityApi.md b/docs/reference/utility-apis/BackstageIdentityApi.md deleted file mode 100644 index 80a40d427e..0000000000 --- a/docs/reference/utility-apis/BackstageIdentityApi.md +++ /dev/null @@ -1,100 +0,0 @@ -# BackstageIdentityApi - -The BackstageIdentityApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:134](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L134). - -The following Utility APIs implement this type: - -- [auth0AuthApiRef](./README.md#auth0auth) - -- [githubAuthApiRef](./README.md#githubauth) - -- [gitlabAuthApiRef](./README.md#gitlabauth) - -- [googleAuthApiRef](./README.md#googleauth) - -- [microsoftAuthApiRef](./README.md#microsoftauth) - -- [oauth2ApiRef](./README.md#oauth2) - -- [oidcAuthApiRef](./README.md#oidcauth) - -- [oktaAuthApiRef](./README.md#oktaauth) - -- [oneloginAuthApiRef](./README.md#oneloginauth) - -- [samlAuthApiRef](./README.md#samlauth) - -## Members - -### getBackstageIdentity() - -Get the user's identity within Backstage. This should normally not be called -directly, use the @IdentityApi instead. - -If the optional flag is not set, a session is guaranteed to be returned, while -if the optional flag is set, the session may be undefined. See -@AuthRequestOptions for more details. - -
-getBackstageIdentity(
-    options?: AuthRequestOptions,
-  ): Promise<BackstageIdentity | undefined>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AuthRequestOptions - -
-export type AuthRequestOptions = {
-  /**
-   * If this is set to true, the user will not be prompted to log in,
-   * and an empty response will be returned if there is no existing session.
-   *
-   * This can be used to perform a check whether the user is logged in, or if you don't
-   * want to force a user to be logged in, but provide functionality if they already are.
-   *
-   * @default false
-   */
-  optional?: boolean;
-
-  /**
-   * If this is set to true, the request will bypass the regular oauth login modal
-   * and open the login popup directly.
-   *
-   * The method must be called synchronously from a user action for this to work in all browsers.
-   *
-   * @default false
-   */
-  instantPopup?: boolean;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L40). - -Referenced by: [getBackstageIdentity](#getbackstageidentity). - -### BackstageIdentity - -
-export type BackstageIdentity = {
-  /**
-   * The backstage user ID.
-   */
-  id: string;
-
-  /**
-   * An ID token that can be used to authenticate the user within Backstage.
-   */
-  idToken: string;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:147](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L147). - -Referenced by: [getBackstageIdentity](#getbackstageidentity). diff --git a/docs/reference/utility-apis/Config.md b/docs/reference/utility-apis/Config.md deleted file mode 100644 index c5b20610e7..0000000000 --- a/docs/reference/utility-apis/Config.md +++ /dev/null @@ -1,187 +0,0 @@ -# Config - -The Config type is defined at -[packages/config/src/types.ts:32](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/config/src/types.ts#L32). - -The following Utility API implements this type: -[configApiRef](./README.md#config) - -## Members - -### has() - -
-has(key: string): boolean
-
- -### keys() - -
-keys(): string[]
-
- -### get() - -
-get(key?: string): JsonValue
-
- -### getOptional() - -
-getOptional(key?: string): JsonValue | undefined
-
- -### getConfig() - -
-getConfig(key: string): Config
-
- -### getOptionalConfig() - -
-getOptionalConfig(key: string): Config | undefined
-
- -### getConfigArray() - -
-getConfigArray(key: string): Config[]
-
- -### getOptionalConfigArray() - -
-getOptionalConfigArray(key: string): Config[] | undefined
-
- -### getNumber() - -
-getNumber(key: string): number
-
- -### getOptionalNumber() - -
-getOptionalNumber(key: string): number | undefined
-
- -### getBoolean() - -
-getBoolean(key: string): boolean
-
- -### getOptionalBoolean() - -
-getOptionalBoolean(key: string): boolean | undefined
-
- -### getString() - -
-getString(key: string): string
-
- -### getOptionalString() - -
-getOptionalString(key: string): string | undefined
-
- -### getStringArray() - -
-getStringArray(key: string): string[]
-
- -### getOptionalStringArray() - -
-getOptionalStringArray(key: string): string[] | undefined
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### Config - -
-export type Config = {
-  has(key: string): boolean;
-
-  keys(): string[];
-
-  get(key?: string): JsonValue;
-  getOptional(key?: string): JsonValue | undefined;
-
-  getConfig(key: string): Config;
-  getOptionalConfig(key: string): Config | undefined;
-
-  getConfigArray(key: string): Config[];
-  getOptionalConfigArray(key: string): Config[] | undefined;
-
-  getNumber(key: string): number;
-  getOptionalNumber(key: string): number | undefined;
-
-  getBoolean(key: string): boolean;
-  getOptionalBoolean(key: string): boolean | undefined;
-
-  getString(key: string): string;
-  getOptionalString(key: string): string | undefined;
-
-  getStringArray(key: string): string[];
-  getOptionalStringArray(key: string): string[] | undefined;
-}
-
- -Defined at -[packages/config/src/types.ts:32](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/config/src/types.ts#L32). - -Referenced by: [getConfig](#getconfig), [getOptionalConfig](#getoptionalconfig), -[getConfigArray](#getconfigarray), -[getOptionalConfigArray](#getoptionalconfigarray), [Config](#config). - -### JsonArray - -
-export type JsonArray = JsonValue[]
-
- -Defined at -[packages/config/src/types.ts:18](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/config/src/types.ts#L18). - -Referenced by: [JsonValue](#jsonvalue). - -### JsonObject - -
-export type JsonObject = { [key in string]?: JsonValue }
-
- -Defined at -[packages/config/src/types.ts:17](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/config/src/types.ts#L17). - -Referenced by: [JsonValue](#jsonvalue). - -### JsonValue - -
-export type JsonValue =
-  | JsonObject
-  | JsonArray
-  | number
-  | string
-  | boolean
-  | null
-
- -Defined at -[packages/config/src/types.ts:19](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/config/src/types.ts#L19). - -Referenced by: [get](#get), [getOptional](#getoptional), -[JsonObject](#jsonobject), [JsonArray](#jsonarray), [Config](#config). diff --git a/docs/reference/utility-apis/DiscoveryApi.md b/docs/reference/utility-apis/DiscoveryApi.md deleted file mode 100644 index 0d888c1daa..0000000000 --- a/docs/reference/utility-apis/DiscoveryApi.md +++ /dev/null @@ -1,24 +0,0 @@ -# DiscoveryApi - -The DiscoveryApi type is defined at -[packages/core-api/src/apis/definitions/DiscoveryApi.ts:30](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L30). - -The following Utility API implements this type: -[discoveryApiRef](./README.md#discovery) - -## Members - -### getBaseUrl() - -Returns the HTTP base backend URL for a given plugin, without a trailing slash. - -This method must always be called just before making a request, as opposed to -fetching the URL when constructing an API client. That is to ensure that more -flexible routing patterns can be supported. - -For example, asking for the URL for `auth` may return something like -`https://backstage.example.com/api/auth` - -
-getBaseUrl(pluginId: string): Promise<string>
-
diff --git a/docs/reference/utility-apis/ErrorApi.md b/docs/reference/utility-apis/ErrorApi.md deleted file mode 100644 index 1aaecdb47e..0000000000 --- a/docs/reference/utility-apis/ErrorApi.md +++ /dev/null @@ -1,134 +0,0 @@ -# ErrorApi - -The ErrorApi type is defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/ErrorApi.ts#L53). - -The following Utility API implements this type: [errorApiRef](./README.md#error) - -## Members - -### post() - -Post an error for handling by the application. - -
-post(error: Error, context?: ErrorContext): void
-
- -### error\$() - -Observe errors posted by other parts of the application. - -
-error$(): Observable<{ error: Error; context?: ErrorContext }>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### Error - -Mirrors the JavaScript Error class, for the purpose of providing documentation -and optional fields. - -
-type Error = {
-  name: string;
-  message: string;
-  stack?: string;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/ErrorApi.ts#L24). - -Referenced by: [post](#post), [error\$](#error). - -### ErrorContext - -Provides additional information about an error that was posted to the -application. - -
-export type ErrorContext = {
-  // If set to true, this error should not be displayed to the user. Defaults to false.
-  hidden?: boolean;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/ErrorApi.ts#L33). - -Referenced by: [post](#post), [error\$](#error). - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L53). - -Referenced by: [error\$](#error). - -### Observer - -This file contains non-react related core types used throughout Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/FeatureFlagsApi.md b/docs/reference/utility-apis/FeatureFlagsApi.md deleted file mode 100644 index 1e8979c02e..0000000000 --- a/docs/reference/utility-apis/FeatureFlagsApi.md +++ /dev/null @@ -1,113 +0,0 @@ -# FeatureFlagsApi - -The FeatureFlagsApi type is defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:60](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L60). - -The following Utility API implements this type: -[featureFlagsApiRef](./README.md#featureflags) - -## Members - -### registerFlag() - -Registers a new feature flag. Once a feature flag has been registered it can be -toggled by users, and read back to enable or disable features. - -
-registerFlag(flag: FeatureFlag): void
-
- -### getRegisteredFlags() - -Get a list of all registered flags. - -
-getRegisteredFlags(): FeatureFlag[]
-
- -### isActive() - -Whether the feature flag with the given name is currently activated for the -user. - -
-isActive(name: string): boolean
-
- -### save() - -Save the user's choice of feature flag states. - -
-save(options: FeatureFlagsSaveOptions): void
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### FeatureFlag - -The feature flags API is used to toggle functionality to users across plugins -and Backstage. - -Plugins can use this API to register feature flags that they have available for -users to enable/disable, and this API will centralize the current user's state -of which feature flags they would like to enable. - -This is ideal for Backstage plugins, as well as your own App, to trial -incomplete or unstable upcoming features. Although there will be a common -interface for users to enable and disable feature flags, this API acts as -another way to enable/disable. - -
-export type FeatureFlag = {
-  name: string;
-  pluginId: string;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:31](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L31). - -Referenced by: [registerFlag](#registerflag), -[getRegisteredFlags](#getregisteredflags). - -### FeatureFlagState - -
-export enum FeatureFlagState {
-  None = 0,
-  Active = 1,
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:36](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L36). - -Referenced by: [FeatureFlagsSaveOptions](#featureflagssaveoptions). - -### FeatureFlagsSaveOptions - -Options to use when saving feature flags. - -
-export type FeatureFlagsSaveOptions = {
-  /**
-   * The new feature flag states to save.
-   */
-  states: Record<string, FeatureFlagState>;
-
-  /**
-   * Whether the saves states should be merged into the existing ones, or replace them.
-   *
-   * Defaults to false.
-   */
-  merge?: boolean;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:44](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L44). - -Referenced by: [save](#save). diff --git a/docs/reference/utility-apis/IdentityApi.md b/docs/reference/utility-apis/IdentityApi.md deleted file mode 100644 index aa2a4dd9a0..0000000000 --- a/docs/reference/utility-apis/IdentityApi.md +++ /dev/null @@ -1,81 +0,0 @@ -# IdentityApi - -The IdentityApi type is defined at -[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/IdentityApi.ts#L22). - -The following Utility API implements this type: -[identityApiRef](./README.md#identity) - -## Members - -### getUserId() - -The ID of the signed in user. This ID is not meant to be presented to the user, -but used as an opaque string to pass on to backends or use in frontend logic. - -TODO: The intention of the user ID is to be able to tie the user to an identity -that is known by the catalog and/or identity backend. It should for example be -possible to fetch all owned components using this ID. - -
-getUserId(): string
-
- -### getProfile() - -The profile of the signed in user. - -
-getProfile(): ProfileInfo
-
- -### getIdToken() - -An OpenID Connect ID Token which proves the identity of the signed in user. - -The ID token will be undefined if the signed in user does not have a verified -identity, such as a demo user or mocked user for e2e tests. - -
-getIdToken(): Promise<string | undefined>
-
- -### signOut() - -Sign out the current user - -
-signOut(): Promise<void>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### ProfileInfo - -Profile information of the user. - -
-export type ProfileInfo = {
-  /**
-   * Email ID.
-   */
-  email?: string;
-
-  /**
-   * Display name that can be presented to the user.
-   */
-  displayName?: string;
-
-  /**
-   * URL to an avatar image of the user.
-   */
-  picture?: string;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L162). - -Referenced by: [getProfile](#getprofile). diff --git a/docs/reference/utility-apis/OAuthApi.md b/docs/reference/utility-apis/OAuthApi.md deleted file mode 100644 index 9af6b3bb53..0000000000 --- a/docs/reference/utility-apis/OAuthApi.md +++ /dev/null @@ -1,117 +0,0 @@ -# OAuthApi - -The OAuthApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L67). - -The following Utility APIs implement this type: - -- [githubAuthApiRef](./README.md#githubauth) - -- [gitlabAuthApiRef](./README.md#gitlabauth) - -- [googleAuthApiRef](./README.md#googleauth) - -- [microsoftAuthApiRef](./README.md#microsoftauth) - -- [oauth2ApiRef](./README.md#oauth2) - -- [oidcAuthApiRef](./README.md#oidcauth) - -- [oktaAuthApiRef](./README.md#oktaauth) - -- [oneloginAuthApiRef](./README.md#oneloginauth) - -## Members - -### getAccessToken() - -Requests an OAuth 2 Access Token, optionally with a set of scopes. The access -token allows you to make requests on behalf of the user, and the copes may grant -you broader access, depending on the auth provider. - -Each auth provider has separate handling of scope, so you need to look at the -documentation for each one to know what scope you need to request. - -This method is cheap and should be called each time an access token is used. Do -not for example store the access token in React component state, as that could -cause the token to expire. Instead fetch a new access token for each request. - -Be sure to include all required scopes when requesting an access token. When -testing your implementation it is best to log out the Backstage session and then -visit your plugin page directly, as you might already have some required scopes -in your existing session. Not requesting the correct scopes can lead to 403 or -other authorization errors, which can be tricky to debug. - -If the user has not yet granted access to the provider and the set of requested -scopes, the user will be prompted to log in. The returned promise will not -resolve until the user has successfully logged in. The returned promise can be -rejected, but only if the user rejects the login request. - -
-getAccessToken(
-    scope?: OAuthScope,
-    options?: AuthRequestOptions,
-  ): Promise<string>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AuthRequestOptions - -
-export type AuthRequestOptions = {
-  /**
-   * If this is set to true, the user will not be prompted to log in,
-   * and an empty response will be returned if there is no existing session.
-   *
-   * This can be used to perform a check whether the user is logged in, or if you don't
-   * want to force a user to be logged in, but provide functionality if they already are.
-   *
-   * @default false
-   */
-  optional?: boolean;
-
-  /**
-   * If this is set to true, the request will bypass the regular oauth login modal
-   * and open the login popup directly.
-   *
-   * The method must be called synchronously from a user action for this to work in all browsers.
-   *
-   * @default false
-   */
-  instantPopup?: boolean;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L40). - -Referenced by: [getAccessToken](#getaccesstoken). - -### OAuthScope - -This file contains declarations for common interfaces of auth-related APIs. The -declarations should be used to signal which type of authentication and -authorization methods each separate auth provider supports. - -For example, a Google OAuth provider that supports OAuth 2 and OpenID Connect, -would be declared as follows: - -const googleAuthApiRef = createApiRef({ ... }) - -An array of scopes, or a scope string formatted according to the auth provider, -which is typically a space separated list. - -See the documentation for each auth provider for the list of scopes supported by -each provider. - -
-export type OAuthScope = string | string[]
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L38). - -Referenced by: [getAccessToken](#getaccesstoken). diff --git a/docs/reference/utility-apis/OAuthRequestApi.md b/docs/reference/utility-apis/OAuthRequestApi.md deleted file mode 100644 index 5f521cf288..0000000000 --- a/docs/reference/utility-apis/OAuthRequestApi.md +++ /dev/null @@ -1,233 +0,0 @@ -# OAuthRequestApi - -The OAuthRequestApi type is defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99). - -The following Utility API implements this type: -[oauthRequestApiRef](./README.md#oauthrequest) - -## Members - -### createAuthRequester() - -A utility for showing login popups or similar things, and merging together -multiple requests for different scopes into one request that includes all -scopes. - -The passed in options provide information about the login provider, and how to -handle auth requests. - -The returned AuthRequester function is used to request login with new scopes. -These requests are merged together and forwarded to the auth handler, as soon as -a consumer of auth requests triggers an auth flow. - -See AuthRequesterOptions, AuthRequester, and handleAuthRequests for more info. - -
-createAuthRequester<AuthResponse>(
-    options: AuthRequesterOptions<AuthResponse>,
-  ): AuthRequester<AuthResponse>
-
- -### authRequest\$() - -Observers pending auth requests. The returned observable will emit all current -active auth request, at most one for each created auth requester. - -Each request has its own info about the login provider, forwarded from the auth -requester options. - -Depending on user interaction, the request should either be rejected, or used to -trigger the auth handler. If the request is rejected, all pending AuthRequester -calls will fail with a "RejectedError". If a auth is triggered, and the auth -handler resolves successfully, then all currently pending AuthRequester calls -will resolve to the value returned by the onAuthRequest call. - -
-authRequest$(): Observable<PendingAuthRequest[]>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AuthProvider - -Information about the auth provider that we're requesting a login towards. - -This should be shown to the user so that they can be informed about what login -is being requested before a popup is shown. - -
-export type AuthProvider = {
-  /**
-   * Title for the auth provider, for example "GitHub"
-   */
-  title: string;
-
-  /**
-   * Icon for the auth provider.
-   */
-  icon: IconComponent;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27). - -Referenced by: [AuthRequesterOptions](#authrequesteroptions), -[PendingAuthRequest](#pendingauthrequest). - -### AuthRequester - -Function used to trigger new auth requests for a set of scopes. - -The returned promise will resolve to the same value returned by the -onAuthRequest in the AuthRequesterOptions. Or rejected, if the request is -rejected. - -This function can be called multiple times before the promise resolves. All -calls will be merged into one request, and the scopes forwarded to the -onAuthRequest will be the union of all requested scopes. - -
-export type AuthRequester<AuthResponse> = (
-  scopes: Set<string>,
-) => Promise<AuthResponse>
-
- -Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66). - -Referenced by: [createAuthRequester](#createauthrequester). - -### AuthRequesterOptions - -Describes how to handle auth requests. Both how to show them to the user, and -what to do when the user accesses the auth request. - -
-export type AuthRequesterOptions<AuthResponse> = {
-  /**
-   * Information about the auth provider, which will be forwarded to auth requests.
-   */
-  provider: AuthProvider;
-
-  /**
-   * Implementation of the auth flow, which will be called synchronously when
-   * trigger() is called on an auth requests.
-   */
-  onAuthRequest(scopes: Set<string>): Promise<AuthResponse>;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43). - -Referenced by: [createAuthRequester](#createauthrequester). - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L53). - -Referenced by: [authRequest\$](#authrequest). - -### Observer - -This file contains non-react related core types used throughout Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### PendingAuthRequest - -An pending auth request for a single auth provider. The request will remain in -this pending state until either reject() or trigger() is called. - -Any new requests for the same provider are merged into the existing pending -request, meaning there will only ever be a single pending request for a given -provider. - -
-export type PendingAuthRequest = {
-  /**
-   * Information about the auth provider, as given in the AuthRequesterOptions
-   */
-  provider: AuthProvider;
-
-  /**
-   * Rejects the request, causing all pending AuthRequester calls to fail with "RejectedError".
-   */
-  reject: () => void;
-
-  /**
-   * Trigger the auth request to continue the auth flow, by for example showing a popup.
-   *
-   * Synchronously calls onAuthRequest with all scope currently in the request.
-   */
-  trigger(): Promise<void>;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77). - -Referenced by: [authRequest\$](#authrequest). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/OpenIdConnectApi.md b/docs/reference/utility-apis/OpenIdConnectApi.md deleted file mode 100644 index 6d05af6189..0000000000 --- a/docs/reference/utility-apis/OpenIdConnectApi.md +++ /dev/null @@ -1,75 +0,0 @@ -# OpenIdConnectApi - -The OpenIdConnectApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:99](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L99). - -The following Utility APIs implement this type: - -- [auth0AuthApiRef](./README.md#auth0auth) - -- [googleAuthApiRef](./README.md#googleauth) - -- [microsoftAuthApiRef](./README.md#microsoftauth) - -- [oauth2ApiRef](./README.md#oauth2) - -- [oidcAuthApiRef](./README.md#oidcauth) - -- [oktaAuthApiRef](./README.md#oktaauth) - -- [oneloginAuthApiRef](./README.md#oneloginauth) - -## Members - -### getIdToken() - -Requests an OpenID Connect ID Token. - -This method is cheap and should be called each time an ID token is used. Do not -for example store the id token in React component state, as that could cause the -token to expire. Instead fetch a new id token for each request. - -If the user has not yet logged in to Google inside Backstage, the user will be -prompted to log in. The returned promise will not resolve until the user has -successfully logged in. The returned promise can be rejected, but only if the -user rejects the login request. - -
-getIdToken(options?: AuthRequestOptions): Promise<string>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AuthRequestOptions - -
-export type AuthRequestOptions = {
-  /**
-   * If this is set to true, the user will not be prompted to log in,
-   * and an empty response will be returned if there is no existing session.
-   *
-   * This can be used to perform a check whether the user is logged in, or if you don't
-   * want to force a user to be logged in, but provide functionality if they already are.
-   *
-   * @default false
-   */
-  optional?: boolean;
-
-  /**
-   * If this is set to true, the request will bypass the regular oauth login modal
-   * and open the login popup directly.
-   *
-   * The method must be called synchronously from a user action for this to work in all browsers.
-   *
-   * @default false
-   */
-  instantPopup?: boolean;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L40). - -Referenced by: [getIdToken](#getidtoken). diff --git a/docs/reference/utility-apis/ProfileInfoApi.md b/docs/reference/utility-apis/ProfileInfoApi.md deleted file mode 100644 index 1a2f94d031..0000000000 --- a/docs/reference/utility-apis/ProfileInfoApi.md +++ /dev/null @@ -1,104 +0,0 @@ -# ProfileInfoApi - -The ProfileInfoApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:117](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L117). - -The following Utility APIs implement this type: - -- [auth0AuthApiRef](./README.md#auth0auth) - -- [githubAuthApiRef](./README.md#githubauth) - -- [gitlabAuthApiRef](./README.md#gitlabauth) - -- [googleAuthApiRef](./README.md#googleauth) - -- [microsoftAuthApiRef](./README.md#microsoftauth) - -- [oauth2ApiRef](./README.md#oauth2) - -- [oidcAuthApiRef](./README.md#oidcauth) - -- [oktaAuthApiRef](./README.md#oktaauth) - -- [oneloginAuthApiRef](./README.md#oneloginauth) - -- [samlAuthApiRef](./README.md#samlauth) - -## Members - -### getProfile() - -Get profile information for the user as supplied by this auth provider. - -If the optional flag is not set, a session is guaranteed to be returned, while -if the optional flag is set, the session may be undefined. See -@AuthRequestOptions for more details. - -
-getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### AuthRequestOptions - -
-export type AuthRequestOptions = {
-  /**
-   * If this is set to true, the user will not be prompted to log in,
-   * and an empty response will be returned if there is no existing session.
-   *
-   * This can be used to perform a check whether the user is logged in, or if you don't
-   * want to force a user to be logged in, but provide functionality if they already are.
-   *
-   * @default false
-   */
-  optional?: boolean;
-
-  /**
-   * If this is set to true, the request will bypass the regular oauth login modal
-   * and open the login popup directly.
-   *
-   * The method must be called synchronously from a user action for this to work in all browsers.
-   *
-   * @default false
-   */
-  instantPopup?: boolean;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L40). - -Referenced by: [getProfile](#getprofile). - -### ProfileInfo - -Profile information of the user. - -
-export type ProfileInfo = {
-  /**
-   * Email ID.
-   */
-  email?: string;
-
-  /**
-   * Display name that can be presented to the user.
-   */
-  displayName?: string;
-
-  /**
-   * URL to an avatar image of the user.
-   */
-  picture?: string;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:162](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L162). - -Referenced by: [getProfile](#getprofile). diff --git a/docs/reference/utility-apis/README.md b/docs/reference/utility-apis/README.md deleted file mode 100644 index aefbb4b925..0000000000 --- a/docs/reference/utility-apis/README.md +++ /dev/null @@ -1,202 +0,0 @@ -# Backstage Core Utility APIs - -The following is a list of all Utility APIs defined by `@backstage/core`. They -are available to use by plugins and components, and can be accessed using the -`useApi` hook, also provided by `@backstage/core`. For more information, see -https://github.com/backstage/backstage/blob/master/docs/api/utility-apis.md. - -### alert - -Used to report alerts and forward them to the app - -Implemented type: [AlertApi](./AlertApi.md) - -ApiRef: -[alertApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/AlertApi.ts#L41) - -### appTheme - -API Used to configure the app theme, and enumerate options - -Implemented type: [AppThemeApi](./AppThemeApi.md) - -ApiRef: -[appThemeApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/AppThemeApi.ts#L80) - -### auth0Auth - -Provides authentication towards Auth0 APIs - -Implemented types: [OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[auth0AuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L275) - -### config - -Used to access runtime configuration - -Implemented type: [Config](./Config.md) - -ApiRef: -[configApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/ConfigApi.ts#L25) - -### discovery - -Provides service discovery of backend plugins - -Implemented type: [DiscoveryApi](./DiscoveryApi.md) - -ApiRef: -[discoveryApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L44) - -### error - -Used to report errors and forward them to the app - -Implemented type: [ErrorApi](./ErrorApi.md) - -ApiRef: -[errorApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/ErrorApi.ts#L65) - -### featureFlags - -Used to toggle functionality in features across Backstage - -Implemented type: [FeatureFlagsApi](./FeatureFlagsApi.md) - -ApiRef: -[featureFlagsApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L83) - -### githubAuth - -Provides authentication towards GitHub APIs - -Implemented types: [OAuthApi](./OAuthApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[githubAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L232) - -### gitlabAuth - -Provides authentication towards GitLab APIs - -Implemented types: [OAuthApi](./OAuthApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[gitlabAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L262) - -### googleAuth - -Provides authentication towards Google APIs and identities - -Implemented types: [OAuthApi](./OAuthApi.md), -[OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[googleAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L215) - -### identity - -Provides access to the identity of the signed in user - -Implemented type: [IdentityApi](./IdentityApi.md) - -ApiRef: -[identityApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/IdentityApi.ts#L53) - -### microsoftAuth - -Provides authentication towards Microsoft APIs and identities - -Implemented types: [OAuthApi](./OAuthApi.md), -[OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[microsoftAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L289) - -### oauth2 - -Example of how to use oauth2 custom provider - -Implemented types: [OAuthApi](./OAuthApi.md), -[OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[oauth2ApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L303) - -### oauthRequest - -An API for implementing unified OAuth flows in Backstage - -Implemented type: [OAuthRequestApi](./OAuthRequestApi.md) - -ApiRef: -[oauthRequestApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130) - -### oidcAuth - -Example of how to use oidc custom provider - -Implemented types: [OAuthApi](./OAuthApi.md), -[OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[oidcAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L317) - -### oktaAuth - -Provides authentication towards Okta APIs - -Implemented types: [OAuthApi](./OAuthApi.md), -[OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[oktaAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L245) - -### oneloginAuth - -Provides authentication towards OneLogin APIs and identities - -Implemented types: [OAuthApi](./OAuthApi.md), -[OpenIdConnectApi](./OpenIdConnectApi.md), -[ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[oneloginAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L338) - -### samlAuth - -Example of how to use SAML custom provider - -Implemented types: [ProfileInfoApi](./ProfileInfoApi.md), -[BackstageIdentityApi](./BackstageIdentityApi.md), [SessionApi](./SessionApi.md) - -ApiRef: -[samlAuthApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L331) - -### storage - -Provides the ability to store data which is unique to the user - -Implemented type: [StorageApi](./StorageApi.md) - -ApiRef: -[storageApiRef](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/StorageApi.ts#L68) diff --git a/docs/reference/utility-apis/SessionApi.md b/docs/reference/utility-apis/SessionApi.md deleted file mode 100644 index 4b584d92c8..0000000000 --- a/docs/reference/utility-apis/SessionApi.md +++ /dev/null @@ -1,144 +0,0 @@ -# SessionApi - -The SessionApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:190](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L190). - -The following Utility APIs implement this type: - -- [auth0AuthApiRef](./README.md#auth0auth) - -- [githubAuthApiRef](./README.md#githubauth) - -- [gitlabAuthApiRef](./README.md#gitlabauth) - -- [googleAuthApiRef](./README.md#googleauth) - -- [microsoftAuthApiRef](./README.md#microsoftauth) - -- [oauth2ApiRef](./README.md#oauth2) - -- [oidcAuthApiRef](./README.md#oidcauth) - -- [oktaAuthApiRef](./README.md#oktaauth) - -- [oneloginAuthApiRef](./README.md#oneloginauth) - -- [samlAuthApiRef](./README.md#samlauth) - -## Members - -### signIn() - -Sign in with a minimum set of permissions. - -
-signIn(): Promise<void>
-
- -### signOut() - -Sign out from the current session. This will reload the page. - -
-signOut(): Promise<void>
-
- -### sessionState\$() - -Observe the current state of the auth session. Emits the current state on -subscription. - -
-sessionState$(): Observable<SessionState>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L53). - -Referenced by: [sessionState\$](#sessionstate). - -### Observer - -This file contains non-react related core types used throughout Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### SessionState - -Session state values passed to subscribers of the SessionApi. - -
-export enum SessionState {
-  SignedIn = 'SignedIn',
-  SignedOut = 'SignedOut',
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:182](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/auth.ts#L182). - -Referenced by: [sessionState\$](#sessionstate). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/SessionStateApi.md b/docs/reference/utility-apis/SessionStateApi.md deleted file mode 100644 index a8a4c3bc3d..0000000000 --- a/docs/reference/utility-apis/SessionStateApi.md +++ /dev/null @@ -1,119 +0,0 @@ -# SessionStateApi - -The SessionStateApi type is defined at -[packages/core-api/src/apis/definitions/auth.ts:201](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L201). - -The following Utility APIs implement this type: - -- [auth0AuthApiRef](./README.md#auth0auth) - -- [githubAuthApiRef](./README.md#githubauth) - -- [gitlabAuthApiRef](./README.md#gitlabauth) - -- [googleAuthApiRef](./README.md#googleauth) - -- [microsoftAuthApiRef](./README.md#microsoftauth) - -- [oauth2ApiRef](./README.md#oauth2) - -- [oktaAuthApiRef](./README.md#oktaauth) - -## Members - -### sessionState\$() - -
-sessionState$(): Observable<SessionState>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53). - -Referenced by: [sessionState\$](#sessionstate). - -### Observer - -This file contains non-react related core types used through Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### SessionState - -Session state values passed to subscribers of the SessionStateApi. - -
-export enum SessionState {
-  SignedIn = 'SignedIn',
-  SignedOut = 'SignedOut',
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/auth.ts:192](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L192). - -Referenced by: [sessionState\$](#sessionstate). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/docs/reference/utility-apis/StorageApi.md b/docs/reference/utility-apis/StorageApi.md deleted file mode 100644 index 3247d28f60..0000000000 --- a/docs/reference/utility-apis/StorageApi.md +++ /dev/null @@ -1,186 +0,0 @@ -# StorageApi - -The StorageApi type is defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/StorageApi.ts#L31). - -The following Utility API implements this type: -[storageApiRef](./README.md#storage) - -## Members - -### forBucket() - -Create a bucket to store data in. - -
-forBucket(name: string): StorageApi
-
- -### get() - -Get the current value for persistent data, use observe\$ to be notified of -updates. - -
-get<T>(key: string): T | undefined
-
- -### remove() - -Remove persistent data. - -
-remove(key: string): Promise<void>
-
- -### set() - -Save persistent data, and emit messages to anyone that is using observe\$ for -this key - -
-set(key: string, data: any): Promise<void>
-
- -### observe\$() - -Observe changes on a particular key in the bucket - -
-observe$<T>(key: string): Observable<StorageValueChange<T>>
-
- -## Supporting types - -These types are part of the API declaration, but may not be unique to this API. - -### Observable - -Observable sequence of values and errors, see TC39. - -https://github.com/tc39/proposal-observable - -This is used as a common return type for observable values and can be created -using many different observable implementations, such as zen-observable or -RxJS 5. - -
-export type Observable<T> = {
-  /**
-   * Subscribes to this observable to start receiving new values.
-   */
-  subscribe(observer: Observer<T>): Subscription;
-  subscribe(
-    onNext: (value: T) => void,
-    onError?: (error: Error) => void,
-    onComplete?: () => void,
-  ): Subscription;
-}
-
- -Defined at -[packages/core-api/src/types.ts:53](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L53). - -Referenced by: [observe\$](#observe), [StorageApi](#storageapi). - -### Observer - -This file contains non-react related core types used throughout Backstage. - -Observer interface for consuming an Observer, see TC39. - -
-export type Observer<T> = {
-  next?(value: T): void;
-  error?(error: Error): void;
-  complete?(): void;
-}
-
- -Defined at -[packages/core-api/src/types.ts:24](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L24). - -Referenced by: [Observable](#observable). - -### StorageApi - -
-export interface StorageApi {
-  /**
-   * Create a bucket to store data in.
-   * @param {String} name Namespace for the storage to be stored under,
-   *                      will inherit previous namespaces too
-   */
-  forBucket(name: string): StorageApi;
-
-  /**
-   * Get the current value for persistent data, use observe$ to be notified of updates.
-   *
-   * @param {String} key Unique key associated with the data.
-   * @return {Object} data The data that should is stored.
-   */
-  get<T>(key: string): T | undefined;
-
-  /**
-   * Remove persistent data.
-   *
-   * @param {String} key Unique key associated with the data.
-   */
-  remove(key: string): Promise<void>;
-
-  /**
-   * Save persistent data, and emit messages to anyone that is using observe$ for this key
-   *
-   * @param {String} key Unique key associated with the data.
-   */
-  set(key: string, data: any): Promise<void>;
-
-  /**
-   * Observe changes on a particular key in the bucket
-   * @param {String} key Unique key associated with the data
-   */
-  observe$<T>(key: string): Observable<StorageValueChange<T>>;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/StorageApi.ts#L31). - -Referenced by: [forBucket](#forbucket). - -### StorageValueChange - -
-export type StorageValueChange<T = any> = {
-  key: string;
-  newValue?: T;
-}
-
- -Defined at -[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/apis/definitions/StorageApi.ts#L21). - -Referenced by: [observe\$](#observe), [StorageApi](#storageapi). - -### Subscription - -Subscription returned when subscribing to an Observable, see TC39. - -
-export type Subscription = {
-  /**
-   * Cancels the subscription
-   */
-  unsubscribe(): void;
-
-  /**
-   * Value indicating whether the subscription is closed.
-   */
-  readonly closed: Boolean;
-}
-
- -Defined at -[packages/core-api/src/types.ts:33](https://github.com/backstage/backstage/blob/a4dbd8353cfa4d4d4334473e2c33afcda64e130d/packages/core-api/src/types.ts#L33). - -Referenced by: [Observable](#observable). diff --git a/microsite/package.json b/microsite/package.json index 532bb62d15..b3048612a7 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.3.2", + "prettier": "^2.4.0", "yarn-lock-check": "^1.0.5" }, "prettier": "@spotify/prettier-config" diff --git a/microsite/scripts/verify-sidebars.js b/microsite/scripts/verify-sidebars.js index 8d643b15a2..301a1cb4c0 100755 --- a/microsite/scripts/verify-sidebars.js +++ b/microsite/scripts/verify-sidebars.js @@ -28,15 +28,18 @@ try { } const errors = []; -const ids = Object.keys(metadata); -for (let id of ids) { + +// reference/index is generated, so make sure this goes through even if it's not there +const knownIds = new Set([...Object.keys(metadata), 'reference/index']); + +for (const id in metadata) { const { next, previous } = metadata[id]; - if (next && !ids.includes(next)) { + if (next && !knownIds.has(next)) { errors.push(`Next ${next} does not exist in ${id}.`); } - if (previous && !ids.includes(previous)) { + if (previous && !knownIds.has(previous)) { errors.push(`Previous ${previous} does not exist in ${id}.`); } } diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 6569f41c20..554f324230 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -237,21 +237,16 @@ "dls/contributing-to-storybook", "dls/figma" ], - "API references": [ + "API Reference": [ { "type": "subcategory", - "label": "TypeScript API", - "ids": [ - "api/utility-apis", - "reference/utility-apis/README", - "reference/createPlugin", - "reference/createPlugin-feature-flags" - ] + "label": "Guides", + "ids": ["api/utility-apis"] }, { "type": "subcategory", - "label": "Backend APIs", - "ids": ["api/backend"] + "label": "API Reference", + "ids": ["reference/index"] } ], "Tutorials": [ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 0f7ddb9c60..2973f364b0 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -5207,10 +5207,10 @@ prepend-http@^2.0.0: resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" - integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== +prettier@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba" + integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ== prismjs@^1.22.0: version "1.23.0" diff --git a/mkdocs.yml b/mkdocs.yml index 41e4d16c3f..48d1d49dcf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -157,14 +157,9 @@ nav: - Design: 'dls/design.md' - Contributing to Storybook: 'dls/contributing-to-storybook.md' - Figma: 'dls/figma.md' - - API references: - - TypeScript API: + - API Reference: + - Guides: - Utility APIs: 'api/utility-apis.md' - - reference/utility-apis/README: 'reference/utility-apis/README.md' - - createPlugin: 'reference/createPlugin.md' - - createPlugin -feature flags: 'reference/createPlugin-feature-flags.md' - - Backend APIs: - - Backend: 'api/backend.md' - Tutorials: - Future developer journey: 'tutorials/journey.md' - Migrating away from @backstage/core: 'tutorials/migrating-away-from-core.md' diff --git a/package.json b/package.json index c9a33f78e6..d362338370 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "lint:docs": "node ./scripts/check-docs-quality", "lint:all": "lerna run lint --", "lint:type-deps": "node scripts/check-type-dependencies.js", - "docgen": "lerna run docgen", "docker-build": "yarn tsc && yarn workspace example-backend build --build-dependencies && yarn workspace example-backend build-image", "create-plugin": "backstage-cli create-plugin --scope backstage --no-private", "remove-plugin": "backstage-cli remove-plugin", @@ -54,9 +53,10 @@ }, "version": "1.0.0", "dependencies": { - "@microsoft/api-documenter": "^7.13.30", - "@microsoft/api-extractor": "^7.18.1", - "@microsoft/api-extractor-model": "^7.13.3" + "@microsoft/api-documenter": "^7.13.47", + "@microsoft/api-extractor": "^7.18.7", + "@microsoft/api-extractor-model": "^7.13.5", + "@microsoft/tsdoc": "^0.13.2" }, "devDependencies": { "@types/webpack": "^5.28.0", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 7de19802e3..9b6dc1956a 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,21 @@ # example-app +## 0.2.45 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-todo@0.1.9 + - @backstage/core-components@0.4.1 + - @backstage/catalog-model@0.9.2 + - @backstage/plugin-home@0.3.0 + - @backstage/plugin-scaffolder@0.11.3 + - @backstage/plugin-org@0.3.22 + - @backstage/core-app-api@0.1.12 + - @backstage/plugin-search@0.4.10 + - @backstage/core-plugin-api@0.1.7 + - @backstage/cli@0.7.11 + ## 0.2.44 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 011fcb59af..db7a99e632 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,14 +1,14 @@ { "name": "example-app", - "version": "0.2.44", + "version": "0.2.45", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.9.1", - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/catalog-model": "^0.9.2", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/integration-react": "^0.1.8", "@backstage/plugin-api-docs": "^0.6.7", "@backstage/plugin-badges": "^0.2.8", @@ -23,22 +23,22 @@ "@backstage/plugin-gcp-projects": "^0.3.3", "@backstage/plugin-github-actions": "^0.4.17", "@backstage/plugin-graphiql": "^0.2.15", - "@backstage/plugin-home": "^0.2.1", + "@backstage/plugin-home": "^0.3.0", "@backstage/plugin-jenkins": "^0.5.5", "@backstage/plugin-kafka": "^0.2.14", "@backstage/plugin-kubernetes": "^0.4.12", "@backstage/plugin-lighthouse": "^0.2.24", "@backstage/plugin-newrelic": "^0.3.3", - "@backstage/plugin-org": "^0.3.21", + "@backstage/plugin-org": "^0.3.22", "@backstage/plugin-pagerduty": "0.3.12", "@backstage/plugin-rollbar": "^0.3.13", - "@backstage/plugin-scaffolder": "^0.11.2", - "@backstage/plugin-search": "^0.4.9", + "@backstage/plugin-scaffolder": "^0.11.3", + "@backstage/plugin-search": "^0.4.10", "@backstage/plugin-sentry": "^0.3.20", "@backstage/plugin-shortcuts": "^0.1.7", "@backstage/plugin-tech-radar": "^0.4.6", "@backstage/plugin-techdocs": "^0.11.1", - "@backstage/plugin-todo": "^0.1.8", + "@backstage/plugin-todo": "^0.1.9", "@backstage/plugin-user-settings": "^0.3.4", "@backstage/search-common": "^0.2.0", "@backstage/theme": "^0.2.10", diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx index 5831b49d2c..ebb486790d 100644 --- a/packages/app/src/components/home/HomePage.tsx +++ b/packages/app/src/components/home/HomePage.tsx @@ -22,52 +22,68 @@ import { ComponentTabs, ComponentTab, } from '@backstage/plugin-home'; +import { + Content, + Header, + Page, + HomepageTimer, +} from '@backstage/core-components'; import { HomePageSearchBar } from '@backstage/plugin-search'; export const HomePage = () => ( - - - - - - - - - - - - - - ( - - ), - }, - { - label: 'Any', - Component: () => ( - - ), - }, - ]} - /> - - + +
+ +
+ + + + + + + + + + + + + + + ( + + ), + }, + { + label: 'Any', + Component: () => ( + + ), + }, + ]} + /> + + + +
); diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index dc83e3aa3c..7ffd6e52dc 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/backend-common +## 0.9.2 + +### Patch Changes + +- 9e5ed27ec: Properly export all used types. +- Updated dependencies + - @backstage/cli-common@0.1.3 + - @backstage/config-loader@0.6.8 + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + ## 0.9.1 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 87d1053165..6ec2c8b131 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.9.1", + "version": "0.9.2", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.2", - "@backstage/config": "^0.1.8", - "@backstage/config-loader": "^0.6.7", - "@backstage/errors": "^0.1.1", + "@backstage/cli-common": "^0.1.3", + "@backstage/config": "^0.1.9", + "@backstage/config-loader": "^0.6.8", + "@backstage/errors": "^0.1.2", "@backstage/integration": "^0.6.3", "@google-cloud/storage": "^5.8.0", "@octokit/rest": "^18.5.3", @@ -49,7 +49,7 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "helmet": "^4.0.0", "isomorphic-git": "^1.8.0", "keyv": "^4.0.3", @@ -77,7 +77,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.7.10", + "@backstage/cli": "^0.7.11", "@backstage/test-utils": "^0.1.17", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", diff --git a/packages/backend-common/src/reading/AzureUrlReader.test.ts b/packages/backend-common/src/reading/AzureUrlReader.test.ts index c7c8853929..e014fa2e75 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.test.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.test.ts @@ -81,14 +81,14 @@ describe('AzureUrlReader', () => { url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml&version=GBmaster', config: createConfig(), response: expect.objectContaining({ - url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml&version=master', + url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml&version=master', }), }, { url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml', config: createConfig(), response: expect.objectContaining({ - url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml', + url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml', }), }, { @@ -125,14 +125,12 @@ describe('AzureUrlReader', () => { { url: 'https://api.com/a/b/blob/master/path/to/c.yaml', config: createConfig(), - error: - 'Incorrect URL: https://api.com/a/b/blob/master/path/to/c.yaml, Error: Wrong Azure Devops URL or Invalid file path', + error: 'Azure URL must point to a git repository', }, { url: 'com/a/b/blob/master/path/to/c.yaml', config: createConfig(), - error: - 'Incorrect URL: com/a/b/blob/master/path/to/c.yaml, TypeError: Invalid URL: com/a/b/blob/master/path/to/c.yaml', + error: 'Invalid URL: com/a/b/blob/master/path/to/c.yaml', }, { url: '', diff --git a/packages/backend-common/src/reading/AzureUrlReader.ts b/packages/backend-common/src/reading/AzureUrlReader.ts index 910e8e04a5..6015b044bc 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.ts @@ -23,11 +23,9 @@ import { ScmIntegrations, } from '@backstage/integration'; import fetch from 'cross-fetch'; -import parseGitUrl from 'git-url-parse'; import { Minimatch } from 'minimatch'; import { Readable } from 'stream'; import { NotFoundError, NotModifiedError } from '@backstage/errors'; -import { stripFirstDirectoryFromPath } from './tree/util'; import { ReadTreeResponseFactory, ReaderFactory, @@ -129,28 +127,38 @@ export class AzureUrlReader implements UrlReader { throw new Error(message); } + // When downloading a zip archive from azure on a subpath we get an extra directory + // layer added at the top. With for example the file /a/b/c.txt and a download of + // /a/b, we'll see /b/c.txt in the zip archive. This picks out /b so that we can remove it. + let subpath; + const path = new URL(url).searchParams.get('path'); + if (path) { + subpath = path.split('/').filter(Boolean).slice(-1)[0]; + } + return await this.deps.treeResponseFactory.fromZipArchive({ stream: archiveAzureResponse.body as unknown as Readable, etag: commitSha, filter: options?.filter, + subpath, }); } async search(url: string, options?: SearchOptions): Promise { - const { filepath } = parseGitUrl(url); - const matcher = new Minimatch(filepath); + const treeUrl = new URL(url); + + const path = treeUrl.searchParams.get('path'); + const matcher = path && new Minimatch(path.replace(/^\/+/, '')); // TODO(freben): For now, read the entire repo and filter through that. In // a future improvement, we could be smart and try to deduce that non-glob // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used // to get just that part of the repo. - const treeUrl = new URL(url); treeUrl.searchParams.delete('path'); - treeUrl.pathname = treeUrl.pathname.replace(/\/+$/, ''); const tree = await this.readTree(treeUrl.toString(), { etag: options?.etag, - filter: path => matcher.match(stripFirstDirectoryFromPath(path)), + filter: p => (matcher ? matcher.match(p) : true), }); const files = await tree.files(); diff --git a/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts b/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts index f6f9a7845a..a54bac0c4a 100644 --- a/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts +++ b/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts @@ -62,36 +62,6 @@ describe('ZipArchiveResponse', () => { ]); }); - it('should read files and strip root dir if requested', async () => { - const stream = fs.createReadStream('/test-archive-with-extra-root-dir.zip'); - - const res = new ZipArchiveResponse( - stream, - '', - '/tmp', - 'etag', - undefined, - true, - ); - const files = await res.files(); - - expect(files).toEqual([ - { - path: 'mkdocs.yml', - content: expect.any(Function), - }, - { - path: 'docs/index.md', - content: expect.any(Function), - }, - ]); - const contents = await Promise.all(files.map(f => f.content())); - expect(contents.map(c => c.toString('utf8').trim())).toEqual([ - 'site_name: Test', - '# Test', - ]); - }); - it('should read files with filter', async () => { const stream = fs.createReadStream('/test-archive.zip'); diff --git a/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts b/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts index fb22f24b94..e07cedde87 100644 --- a/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts @@ -24,7 +24,6 @@ import { ReadTreeResponseDirOptions, ReadTreeResponseFile, } from '../types'; -import { stripFirstDirectoryFromPath } from './util'; /** * Wraps a zip archive stream into a tree response reader. @@ -38,7 +37,6 @@ export class ZipArchiveResponse implements ReadTreeResponse { private readonly workDir: string, public readonly etag: string, private readonly filter?: (path: string, info: { size: number }) => boolean, - private readonly stripFirstDirectory?: boolean, ) { if (subPath) { if (!subPath.endsWith('/')) { @@ -68,12 +66,8 @@ export class ZipArchiveResponse implements ReadTreeResponse { } private shouldBeIncluded(entry: Entry): boolean { - const strippedPath = this.stripFirstDirectory - ? stripFirstDirectoryFromPath(entry.path) - : entry.path; - if (this.subPath) { - if (!strippedPath.startsWith(this.subPath)) { + if (!entry.path.startsWith(this.subPath)) { return false; } } @@ -102,11 +96,7 @@ export class ZipArchiveResponse implements ReadTreeResponse { if (this.shouldBeIncluded(entry)) { files.push({ - path: this.getInnerPath( - this.stripFirstDirectory - ? stripFirstDirectoryFromPath(entry.path) - : entry.path, - ), + path: this.getInnerPath(entry.path), content: () => entry.buffer(), }); } else { @@ -154,11 +144,7 @@ export class ZipArchiveResponse implements ReadTreeResponse { // Ignore directory entries since we handle that with the file entries // as a zip can have files with directories without directory entries if (entry.type === 'File' && this.shouldBeIncluded(entry)) { - const entryPath = this.getInnerPath( - this.stripFirstDirectory - ? stripFirstDirectoryFromPath(entry.path) - : entry.path, - ); + const entryPath = this.getInnerPath(entry.path); const dirname = platformPath.dirname(entryPath); if (dirname) { await fs.mkdirp(platformPath.join(dir, dirname)); diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 6d33537e7c..6e10d188df 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-test-utils +## 0.1.7 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/config@0.1.9 + - @backstage/backend-common@0.9.2 + - @backstage/cli@0.7.11 + ## 0.1.6 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index cd2855b833..1713f05d25 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.0", - "@backstage/cli": "^0.7.8", - "@backstage/config": "^0.1.8", + "@backstage/backend-common": "^0.9.2", + "@backstage/cli": "^0.7.11", + "@backstage/config": "^0.1.9", "knex": "^0.95.1", "mysql2": "^2.2.5", "pg": "^8.3.0", @@ -41,7 +41,7 @@ "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.8", + "@backstage/cli": "^0.7.11", "jest": "^26.0.1" }, "files": [ diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index d04d1dc936..b32007a3e2 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/catalog-client +## 0.3.19 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/catalog-model@0.9.2 + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + ## 0.3.18 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index cdbee32305..2040090fde 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "0.3.18", + "version": "0.3.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,13 +29,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.0", - "@backstage/config": "^0.1.5", - "@backstage/errors": "^0.1.1", + "@backstage/catalog-model": "^0.9.2", + "@backstage/config": "^0.1.9", + "@backstage/errors": "^0.1.2", "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.7.6", + "@backstage/cli": "^0.7.11", "@types/jest": "^26.0.7", "msw": "^0.29.0" }, diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 3559c661ef..6e6a7193c1 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/catalog-model +## 0.9.2 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + ## 0.9.1 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 5a16a1d310..a20fb235c6 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.9.1", + "version": "0.9.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.5", - "@backstage/errors": "^0.1.1", + "@backstage/config": "^0.1.9", + "@backstage/errors": "^0.1.2", "@types/json-schema": "^7.0.5", "@types/yup": "^0.29.8", "ajv": "^7.0.3", @@ -40,7 +40,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.7.10", + "@backstage/cli": "^0.7.11", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli-common/CHANGELOG.md b/packages/cli-common/CHANGELOG.md index 52a247f57a..8a2a903efd 100644 --- a/packages/cli-common/CHANGELOG.md +++ b/packages/cli-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/cli-common +## 0.1.3 + +### Patch Changes + +- d1da88a19: Properly export all used types. + ## 0.1.2 ### Patch Changes diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 16f10717f9..c643192df3 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.2", + "version": "0.1.3", "private": false, "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/cli/.snyk b/packages/cli/.snyk new file mode 100644 index 0000000000..612b4b0525 --- /dev/null +++ b/packages/cli/.snyk @@ -0,0 +1,70 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-ANSIHTML-1296849: + - 'webpack-dev-server > ansi-html': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-ANSIREGEX-1583908: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-CSSWHAT-1298035: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-BROWSERSLIST-1090194: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-ISSVG-1243891: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-ISSVG-1085627: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-POSTCSS-1255640: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-POSTCSS-1090595: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-WS-1296835: + - '*': + reason: Developer tools are not a valid target for ReDoS attacks + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-IMMER-1540542: + - '*': + reason: Prototype pollution is not an effective attack against a CLI as it already executes arbitrary code + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-SETVALUE-1540541: + - '*': + reason: Prototype pollution is not an effective attack against a CLI as it already executes arbitrary code + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z +patch: {} diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 47f4545ea2..f404b49e48 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/cli +## 0.7.11 + +### Patch Changes + +- 13895db37: Support importing font files in tests. + This fixes remaining issues from [#7019](https://github.com/backstage/backstage/pull/7019). +- Updated dependencies + - @backstage/cli-common@0.1.3 + - @backstage/config-loader@0.6.8 + - @backstage/config@0.1.9 + ## 0.7.10 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 831a4af0c3..12cc584e2d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.7.10", + "version": "0.7.11", "private": false, "publishConfig": { "access": "public" @@ -30,9 +30,9 @@ "dependencies": { "@babel/core": "^7.4.4", "@babel/plugin-transform-modules-commonjs": "^7.4.4", - "@backstage/cli-common": "^0.1.2", - "@backstage/config": "^0.1.8", - "@backstage/config-loader": "^0.6.7", + "@backstage/cli-common": "^0.1.3", + "@backstage/config": "^0.1.9", + "@backstage/config-loader": "^0.6.8", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -120,12 +120,12 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.1", - "@backstage/config": "^0.1.8", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/backend-common": "^0.9.2", + "@backstage/config": "^0.1.9", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@backstage/theme": "^0.2.10", "@types/diff": "^5.0.0", diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index e0b3bfb693..aa21ebc3a1 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/codemods +## 0.1.13 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/cli-common@0.1.3 + - @backstage/core-app-api@0.1.12 + - @backstage/core-plugin-api@0.1.7 + ## 0.1.12 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index b14d3e880e..0fbafe7fe2 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.12", + "version": "0.1.13", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "backstage-codemods": "bin/backstage-codemods" }, "dependencies": { - "@backstage/cli-common": "0.1.2", + "@backstage/cli-common": "0.1.3", "@backstage/core-app-api": "*", "@backstage/core-components": "*", "@backstage/core-plugin-api": "*", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index aee4cf453f..88479bbb18 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/config-loader +## 0.6.8 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/cli-common@0.1.3 + - @backstage/config@0.1.9 + ## 0.6.7 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index f810841d3e..0f5bac2ffc 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.6.7", + "version": "0.6.8", "private": false, "publishConfig": { "access": "public", @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.1", - "@backstage/config": "^0.1.7", + "@backstage/cli-common": "^0.1.3", + "@backstage/config": "^0.1.9", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "chokidar": "^3.5.2", diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 742ae86a57..7691ed2865 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/config +## 0.1.9 + +### Patch Changes + +- f88b2c7db: Documented `Config` interface and mark types as public. + ## 0.1.8 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index 6adee62ca3..969bc44045 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "0.1.8", + "version": "0.1.9", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 55b20aac8b..9020661647 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-app-api +## 0.1.12 + +### Patch Changes + +- 841666a19: Removed deprecated internal functions. +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/config@0.1.9 + - @backstage/core-plugin-api@0.1.7 + ## 0.1.11 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index e0801b45db..5223d2b27e 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "0.1.11", + "version": "0.1.12", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/config": "^0.1.8", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/config": "^0.1.9", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,7 +44,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.10", + "@backstage/cli": "^0.7.11", "@backstage/test-utils": "^0.1.17", "@backstage/test-utils-core": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/core-app-api/src/extensions/componentData.test.tsx b/packages/core-app-api/src/extensions/componentData.test.tsx deleted file mode 100644 index 808ab08cf1..0000000000 --- a/packages/core-app-api/src/extensions/componentData.test.tsx +++ /dev/null @@ -1,118 +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 React from 'react'; -import { attachComponentData, getComponentData } from './componentData'; - -describe('elementData', () => { - it('should attach a single piece of data', () => { - const data = { foo: 'bar' }; - const Component = () => null; - attachComponentData(Component, 'my-data', data); - - const element = ; - expect(getComponentData(element, 'my-data')).toBe(data); - }); - - it('should attach several distinct pieces of data', () => { - const data1 = { foo: 'bar' }; - const data2 = { test: 'value' }; - const Component = () => null; - attachComponentData(Component, 'my-data', data1); - attachComponentData(Component, 'second', data2); - - const element = ; - expect(getComponentData(element, 'my-data')).toBe(data1); - expect(getComponentData(element, 'second')).toBe(data2); - }); - - it('returns undefined for missing data', () => { - const data = { foo: 'bar' }; - const Component1 = () => null; - const Component2 = () => null; - attachComponentData(Component2, 'my-data', data); - - const element1 = ; - const element2 = ; - expect(getComponentData(element1, 'missing')).toBeUndefined(); - expect(getComponentData(element2, 'missing')).toBeUndefined(); - }); - - it('should throw when attempting to overwrite data', () => { - const data = { foo: 'bar' }; - const MyComponent = () => null; - attachComponentData(MyComponent, 'my-data', data); - expect(() => attachComponentData(MyComponent, 'my-data', data)).toThrow( - 'Attempted to attach duplicate data "my-data" to component "MyComponent"', - ); - }); - - describe('works across versions', () => { - function getDataSymbol() { - const Component = () => null; - attachComponentData(Component, 'my-data', {}); - const [symbol] = Object.getOwnPropertySymbols(Component); - return symbol; - } - - it('should should be able to get data from older versions', () => { - const symbol = getDataSymbol(); - - const data = { foo: 'bar' }; - const Component = () => null; - attachComponentData(Component, 'my-data', data); - - const element = ; - expect((element as any).type[symbol].map.get('my-data')).toBe(data); - }); - - it('should should be able to attach data for older versions', () => { - const symbol = getDataSymbol(); - - const data = { foo: 'bar' }; - const Component = () => null; - (Component as any)[symbol] = { - map: new Map([['my-data', data]]), - }; - - const element = ; - expect(getComponentData(element, 'my-data')).toBe(data); - }); - - it('should be able to get data from newer versions', () => { - const data = { foo: 'bar' }; - const Component = () => null; - attachComponentData(Component, 'my-data', data); - - const element = ; - const container = (global as any)[ - '__@backstage/component-data-store__' - ].get(element.type); - expect(container.map.get('my-data')).toBe(data); - }); - - it('should should be able to attach data for newer versions', () => { - const data = { foo: 'bar' }; - const Component = () => null; - (global as any)['__@backstage/component-data-store__'].set(Component, { - map: new Map([['my-data', data]]), - }); - - const element = ; - expect(getComponentData(element, 'my-data')).toBe(data); - }); - }); -}); diff --git a/packages/core-app-api/src/extensions/componentData.tsx b/packages/core-app-api/src/extensions/componentData.tsx deleted file mode 100644 index d4975d9eef..0000000000 --- a/packages/core-app-api/src/extensions/componentData.tsx +++ /dev/null @@ -1,84 +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 { ComponentType, ReactNode } from 'react'; -import { getOrCreateGlobalSingleton } from '../lib/globalObject'; - -// TODO(Rugvip): Access via symbol is deprecated, remove once on 0.3.x -const DATA_KEY = Symbol('backstage-component-data'); - -type ComponentWithData

= ComponentType

& { - [DATA_KEY]?: DataContainer; -}; - -type DataContainer = { - map: Map; -}; - -type MaybeComponentNode = ReactNode & { - type?: ComponentType & { [DATA_KEY]?: DataContainer }; -}; - -// The store is bridged across versions using the global object -const store = getOrCreateGlobalSingleton( - 'component-data-store', - () => new WeakMap, DataContainer>(), -); - -export function attachComponentData

( - component: ComponentType

, - type: string, - data: unknown, -) { - const dataComponent = component as ComponentWithData

; - - let container = store.get(component) || dataComponent[DATA_KEY]; - if (!container) { - container = { map: new Map() }; - store.set(component, container); - dataComponent[DATA_KEY] = container; - } - - if (container.map.has(type)) { - const name = component.displayName || component.name; - throw new Error( - `Attempted to attach duplicate data "${type}" to component "${name}"`, - ); - } - - container.map.set(type, data); -} - -export function getComponentData( - node: ReactNode, - type: string, -): T | undefined { - if (!node) { - return undefined; - } - - const component = (node as MaybeComponentNode).type; - if (!component) { - return undefined; - } - - const container = store.get(component) || component[DATA_KEY]; - if (!container) { - return undefined; - } - - return container.map.get(type) as T | undefined; -} diff --git a/packages/core-app-api/src/extensions/extensions.tsx b/packages/core-app-api/src/extensions/extensions.tsx deleted file mode 100644 index 199430c619..0000000000 --- a/packages/core-app-api/src/extensions/extensions.tsx +++ /dev/null @@ -1,54 +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 { Extension, RouteRef } from '@backstage/core-plugin-api'; - -type ComponentLoader = - | { - lazy: () => Promise; - } - | { - sync: T; - }; - -const ERROR_MESSAGE = 'Import this from @backstage/core-plugin-api'; - -/** @deprecated Import from @backstage/core-plugin-api instead */ -export function createRoutableExtension< - T extends (props: any) => JSX.Element | null, ->(_options: { - component: () => Promise; - mountPoint: RouteRef; -}): Extension { - throw new Error(ERROR_MESSAGE); -} - -/** @deprecated Import from @backstage/core-plugin-api instead */ -export function createComponentExtension< - T extends (props: any) => JSX.Element | null, ->(_options: { component: ComponentLoader }): Extension { - throw new Error(ERROR_MESSAGE); -} - -/** @deprecated Import from @backstage/core-plugin-api instead */ -export function createReactExtension< - T extends (props: any) => JSX.Element | null, ->(_options: { - component: ComponentLoader; - data?: Record; -}): Extension { - throw new Error(ERROR_MESSAGE); -} diff --git a/packages/core-app-api/src/extensions/index.ts b/packages/core-app-api/src/extensions/index.ts deleted file mode 100644 index 914d76aebf..0000000000 --- a/packages/core-app-api/src/extensions/index.ts +++ /dev/null @@ -1,22 +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. - */ - -export { attachComponentData, getComponentData } from './componentData'; -export { - createReactExtension, - createRoutableExtension, - createComponentExtension, -} from './extensions'; diff --git a/packages/core-app-api/src/plugins/collectors.ts b/packages/core-app-api/src/plugins/collectors.ts index 11e5cd51b3..0f8b65ad62 100644 --- a/packages/core-app-api/src/plugins/collectors.ts +++ b/packages/core-app-api/src/plugins/collectors.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { getComponentData } from '../extensions'; +import { BackstagePlugin, getComponentData } from '@backstage/core-plugin-api'; import { createCollector } from '../extensions/traversal'; export const pluginCollector = createCollector( diff --git a/packages/core-app-api/src/routing/collectors.test.tsx b/packages/core-app-api/src/routing/collectors.test.tsx index d37b5e3016..36d99d2938 100644 --- a/packages/core-app-api/src/routing/collectors.test.tsx +++ b/packages/core-app-api/src/routing/collectors.test.tsx @@ -31,8 +31,8 @@ import { createRouteRef, createPlugin, RouteRef, + attachComponentData, } from '@backstage/core-plugin-api'; -import { attachComponentData } from '../extensions'; import { MemoryRouter, Routes, Route } from 'react-router-dom'; const MockComponent = ({ children }: PropsWithChildren<{ path?: string }>) => ( diff --git a/packages/core-app-api/src/routing/collectors.tsx b/packages/core-app-api/src/routing/collectors.tsx index fcbe3ca923..0ba83e90f8 100644 --- a/packages/core-app-api/src/routing/collectors.tsx +++ b/packages/core-app-api/src/routing/collectors.tsx @@ -15,9 +15,8 @@ */ import { isValidElement, ReactElement, ReactNode } from 'react'; -import { RouteRef } from '@backstage/core-plugin-api'; +import { RouteRef, getComponentData } from '@backstage/core-plugin-api'; import { BackstageRouteObject } from './types'; -import { getComponentData } from '../extensions'; import { createCollector } from '../extensions/traversal'; import { FeatureFlagged, FeatureFlaggedProps } from './FeatureFlagged'; diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 0f89c26682..1c195cdd2c 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/core-components +## 0.4.1 + +### Patch Changes + +- 06e275705: Fix warning produced by BottomLink component + + During development, we noticed warnings such as: + + ``` + react_devtools_backend.js:2842 Warning: validateDOMNesting(...):

cannot appear as a descendant of

. + ``` + + The BottomLink component renders a Box component within a Typography component which leads to a div tag within a p tag. + This change inverts that ordering without changing the visual appearance. + +- Updated dependencies + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + - @backstage/core-plugin-api@0.1.7 + ## 0.4.0 ### Minor Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index f73eee1f13..f3652d98cf 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -9,10 +9,8 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; import { ButtonProps } from '@material-ui/core'; -import { ButtonTypeMap } from '@material-ui/core'; import { CardHeaderProps } from '@material-ui/core'; import { Column } from '@material-table/core'; -import { CommonProps } from '@material-ui/core/OverridableComponent'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; import { Context } from 'react'; @@ -50,7 +48,7 @@ import { WithStyles } from '@material-ui/core'; // Warning: (ae-missing-release-tag) "AlertDisplay" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const AlertDisplay: () => JSX.Element | null; +export function AlertDisplay(_props: {}): JSX.Element | null; // Warning: (ae-missing-release-tag) "Alignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -70,416 +68,89 @@ enum Alignment { // Warning: (ae-missing-release-tag) "Avatar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Avatar: ({ - displayName, - picture, - customStyles, -}: AvatarProps) => JSX.Element; +export function Avatar(props: AvatarProps): 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) "Breadcrumbs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Breadcrumbs: ({ children, ...props }: Props_24) => JSX.Element; +export function Breadcrumbs(props: Props_24): JSX.Element; +// Warning: (ae-forgotten-export) The symbol "IconComponentProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "BrokenImageIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const BrokenImageIcon: IconComponent; +export function BrokenImageIcon(props: IconComponentProps): 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) "Button" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "ButtonType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public -export const Button: React_2.ForwardRefExoticComponent< - Pick< - Props, - | 'replace' - | 'media' - | 'hidden' - | 'dir' - | 'form' - | 'slot' - | 'title' - | 'disabled' - | 'color' - | 'size' - | 'underline' - | 'display' - | 'translate' - | 'prefix' - | 'children' - | 'key' - | 'value' - | 'id' - | 'name' - | 'action' - | 'defaultChecked' - | 'defaultValue' - | 'suppressContentEditableWarning' - | 'suppressHydrationWarning' - | 'accessKey' - | 'contentEditable' - | 'contextMenu' - | 'draggable' - | 'lang' - | 'placeholder' - | 'spellCheck' - | 'tabIndex' - | 'radioGroup' - | 'role' - | 'about' - | 'datatype' - | 'inlist' - | 'property' - | 'resource' - | 'typeof' - | 'vocab' - | 'autoCapitalize' - | 'autoCorrect' - | 'autoSave' - | 'itemProp' - | 'itemScope' - | 'itemType' - | 'itemID' - | 'itemRef' - | 'results' - | 'security' - | 'unselectable' - | 'inputMode' - | 'is' - | 'aria-activedescendant' - | 'aria-atomic' - | 'aria-autocomplete' - | 'aria-busy' - | 'aria-checked' - | 'aria-colcount' - | 'aria-colindex' - | 'aria-colspan' - | 'aria-controls' - | 'aria-current' - | 'aria-describedby' - | 'aria-details' - | 'aria-disabled' - | 'aria-dropeffect' - | 'aria-errormessage' - | 'aria-expanded' - | 'aria-flowto' - | 'aria-grabbed' - | 'aria-haspopup' - | 'aria-hidden' - | 'aria-invalid' - | 'aria-keyshortcuts' - | 'aria-label' - | 'aria-labelledby' - | 'aria-level' - | 'aria-live' - | 'aria-modal' - | 'aria-multiline' - | 'aria-multiselectable' - | 'aria-orientation' - | 'aria-owns' - | 'aria-placeholder' - | 'aria-posinset' - | 'aria-pressed' - | 'aria-readonly' - | 'aria-relevant' - | 'aria-required' - | 'aria-roledescription' - | 'aria-rowcount' - | 'aria-rowindex' - | 'aria-rowspan' - | 'aria-selected' - | 'aria-setsize' - | 'aria-sort' - | 'aria-valuemax' - | 'aria-valuemin' - | 'aria-valuenow' - | 'aria-valuetext' - | 'dangerouslySetInnerHTML' - | 'onCopy' - | 'onCopyCapture' - | 'onCut' - | 'onCutCapture' - | 'onPaste' - | 'onPasteCapture' - | 'onCompositionEnd' - | 'onCompositionEndCapture' - | 'onCompositionStart' - | 'onCompositionStartCapture' - | 'onCompositionUpdate' - | 'onCompositionUpdateCapture' - | 'onFocus' - | 'onFocusCapture' - | 'onBlur' - | 'onBlurCapture' - | 'onChange' - | 'onChangeCapture' - | 'onBeforeInput' - | 'onBeforeInputCapture' - | 'onInput' - | 'onInputCapture' - | 'onReset' - | 'onResetCapture' - | 'onSubmit' - | 'onSubmitCapture' - | 'onInvalid' - | 'onInvalidCapture' - | 'onLoad' - | 'onLoadCapture' - | 'onError' - | 'onErrorCapture' - | 'onKeyDown' - | 'onKeyDownCapture' - | 'onKeyPress' - | 'onKeyPressCapture' - | 'onKeyUp' - | 'onKeyUpCapture' - | 'onAbort' - | 'onAbortCapture' - | 'onCanPlay' - | 'onCanPlayCapture' - | 'onCanPlayThrough' - | 'onCanPlayThroughCapture' - | 'onDurationChange' - | 'onDurationChangeCapture' - | 'onEmptied' - | 'onEmptiedCapture' - | 'onEncrypted' - | 'onEncryptedCapture' - | 'onEnded' - | 'onEndedCapture' - | 'onLoadedData' - | 'onLoadedDataCapture' - | 'onLoadedMetadata' - | 'onLoadedMetadataCapture' - | 'onLoadStart' - | 'onLoadStartCapture' - | 'onPause' - | 'onPauseCapture' - | 'onPlay' - | 'onPlayCapture' - | 'onPlaying' - | 'onPlayingCapture' - | 'onProgress' - | 'onProgressCapture' - | 'onRateChange' - | 'onRateChangeCapture' - | 'onSeeked' - | 'onSeekedCapture' - | 'onSeeking' - | 'onSeekingCapture' - | 'onStalled' - | 'onStalledCapture' - | 'onSuspend' - | 'onSuspendCapture' - | 'onTimeUpdate' - | 'onTimeUpdateCapture' - | 'onVolumeChange' - | 'onVolumeChangeCapture' - | 'onWaiting' - | 'onWaitingCapture' - | 'onAuxClick' - | 'onAuxClickCapture' - | 'onClick' - | 'onClickCapture' - | 'onContextMenu' - | 'onContextMenuCapture' - | 'onDoubleClick' - | 'onDoubleClickCapture' - | 'onDrag' - | 'onDragCapture' - | 'onDragEnd' - | 'onDragEndCapture' - | 'onDragEnter' - | 'onDragEnterCapture' - | 'onDragExit' - | 'onDragExitCapture' - | 'onDragLeave' - | 'onDragLeaveCapture' - | 'onDragOver' - | 'onDragOverCapture' - | 'onDragStart' - | 'onDragStartCapture' - | 'onDrop' - | 'onDropCapture' - | 'onMouseDown' - | 'onMouseDownCapture' - | 'onMouseEnter' - | 'onMouseLeave' - | 'onMouseMove' - | 'onMouseMoveCapture' - | 'onMouseOut' - | 'onMouseOutCapture' - | 'onMouseOver' - | 'onMouseOverCapture' - | 'onMouseUp' - | 'onMouseUpCapture' - | 'onSelect' - | 'onSelectCapture' - | 'onTouchCancel' - | 'onTouchCancelCapture' - | 'onTouchEnd' - | 'onTouchEndCapture' - | 'onTouchMove' - | 'onTouchMoveCapture' - | 'onTouchStart' - | 'onTouchStartCapture' - | 'onPointerDown' - | 'onPointerDownCapture' - | 'onPointerMove' - | 'onPointerMoveCapture' - | 'onPointerUp' - | 'onPointerUpCapture' - | 'onPointerCancel' - | 'onPointerCancelCapture' - | 'onPointerEnter' - | 'onPointerEnterCapture' - | 'onPointerLeave' - | 'onPointerLeaveCapture' - | 'onPointerOver' - | 'onPointerOverCapture' - | 'onPointerOut' - | 'onPointerOutCapture' - | 'onGotPointerCapture' - | 'onGotPointerCaptureCapture' - | 'onLostPointerCapture' - | 'onLostPointerCaptureCapture' - | 'onScroll' - | 'onScrollCapture' - | 'onWheel' - | 'onWheelCapture' - | 'onAnimationStart' - | 'onAnimationStartCapture' - | 'onAnimationEnd' - | 'onAnimationEndCapture' - | 'onAnimationIteration' - | 'onAnimationIterationCapture' - | 'onTransitionEnd' - | 'onTransitionEndCapture' - | 'component' - | 'variant' - | 'download' - | 'href' - | 'hrefLang' - | 'ping' - | 'rel' - | 'target' - | 'type' - | 'referrerPolicy' - | 'disableElevation' - | 'fullWidth' - | 'startIcon' - | 'endIcon' - | 'noWrap' - | 'gutterBottom' - | 'paragraph' - | 'autoFocus' - | 'formAction' - | 'formEncType' - | 'formMethod' - | 'formNoValidate' - | 'formTarget' - | 'disableFocusRipple' - | 'buttonRef' - | 'centerRipple' - | 'disableRipple' - | 'disableTouchRipple' - | 'focusRipple' - | 'focusVisibleClassName' - | 'onFocusVisible' - | 'TouchRippleProps' - | 'align' - | 'variantMapping' - | 'to' - | 'state' - | 'TypographyClasses' - | keyof CommonProps> - > & - React_2.RefAttributes ->; +// @public (undocumented) +export function Button(props: Props): JSX.Element; // Warning: (ae-forgotten-export) The symbol "CardTabProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "CardTab" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const CardTab: ({ - children, - ...props -}: PropsWithChildren) => JSX.Element; +export function CardTab(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "CatalogIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const CatalogIcon: IconComponent; +export function CatalogIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-missing-release-tag) "ChatIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ChatIcon: IconComponent; +export function ChatIcon(props: IconComponentProps): 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) "CodeSnippet" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const CodeSnippet: ({ - text, - language, - showLineNumbers, - showCopyCodeButton, - highlightedNumbers, - customStyle, -}: Props_2) => JSX.Element; +export const CodeSnippet: (props: Props_2) => 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) "Content" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Content: ({ - className, - stretch, - noPadding, - children, - ...props -}: PropsWithChildren) => JSX.Element; +export function Content(props: PropsWithChildren): JSX.Element; // Warning: (ae-forgotten-export) The symbol "ContentHeaderProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ContentHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ContentHeader: ({ - description, - title, - titleComponent: TitleComponent, - children, - textAlign, -}: PropsWithChildren) => JSX.Element; +export function ContentHeader( + props: PropsWithChildren, +): 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) "CopyTextButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // Warning: (ae-missing-release-tag) "CopyTextButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const CopyTextButton: { - (props: Props_3): JSX.Element; - propTypes: { - text: PropTypes.Validator; - tooltipDelay: PropTypes.Requireable; - tooltipText: PropTypes.Requireable; - }; -}; +export function CopyTextButton(props: Props_3): JSX.Element; + +// @public (undocumented) +export namespace CopyTextButton { + var // (undocumented) + propTypes: { + text: PropTypes.Validator; + tooltipDelay: PropTypes.Requireable; + tooltipText: PropTypes.Requireable; + }; +} // Warning: (ae-forgotten-export) The symbol "CreateButtonProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "CreateButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const CreateButton: ({ - title, - to, -}: CreateButtonProps) => JSX.Element | null; +export function CreateButton(props: CreateButtonProps): JSX.Element | null; // Warning: (ae-missing-release-tag) "DashboardIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const DashboardIcon: IconComponent; +export function DashboardIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "CustomType" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "DependencyEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -495,27 +166,7 @@ type DependencyEdge = T & { // Warning: (ae-missing-release-tag) "DependencyGraph" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function DependencyGraph({ - edges, - nodes, - renderNode, - direction, - align, - nodeMargin, - edgeMargin, - rankMargin, - paddingX, - paddingY, - acyclicer, - ranker, - labelPosition, - labelOffset, - edgeRanks, - edgeWeight, - renderLabel, - defs, - ...svgProps -}: DependencyGraphProps): JSX.Element; +export function DependencyGraph(props: DependencyGraphProps): JSX.Element; declare namespace DependencyGraphTypes { export { @@ -561,17 +212,12 @@ enum Direction { // Warning: (ae-missing-release-tag) "DismissableBanner" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const DismissableBanner: ({ - variant, - message, - id, - fixed, -}: Props_4) => JSX.Element; +export const DismissableBanner: (props: Props_4) => JSX.Element; // Warning: (ae-missing-release-tag) "DocsIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const DocsIcon: IconComponent; +export function DocsIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-missing-release-tag) "EdgeProperties" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -590,18 +236,13 @@ type EdgeProperties = { // Warning: (ae-missing-release-tag) "EmailIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const EmailIcon: IconComponent; +export function EmailIcon(props: IconComponentProps): 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) "EmptyState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const EmptyState: ({ - title, - description, - missing, - action, -}: Props_5) => JSX.Element; +export function EmptyState(props: Props_5): JSX.Element; // Warning: (ae-forgotten-export) The symbol "State" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ErrorBoundary" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -621,21 +262,14 @@ export type ErrorBoundaryProps = { // Warning: (ae-missing-release-tag) "ErrorPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ErrorPage: ({ - status, - statusMessage, - additionalInfo, -}: IErrorPageProps) => JSX.Element; +export function ErrorPage(props: IErrorPageProps): JSX.Element; // Warning: (ae-missing-release-tag) "ErrorPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const ErrorPanel: ({ - title, - error, - defaultExpanded, - children, -}: PropsWithChildren) => JSX.Element; +export function ErrorPanel( + props: PropsWithChildren, +): JSX.Element; // Warning: (ae-missing-release-tag) "ErrorPanelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -650,29 +284,26 @@ export type ErrorPanelProps = { // Warning: (ae-missing-release-tag) "FeatureCalloutCircular" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const FeatureCalloutCircular: ({ - featureId, - title, - description, - children, -}: PropsWithChildren) => JSX.Element; +export function FeatureCalloutCircular( + props: PropsWithChildren, +): 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) "Gauge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Gauge: (props: Props_14) => JSX.Element; +export function Gauge(props: Props_14): 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) "GaugeCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const GaugeCard: (props: Props_13) => JSX.Element; +export function GaugeCard(props: Props_13): JSX.Element; // Warning: (ae-missing-release-tag) "GitHubIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const GitHubIcon: IconComponent; +export function GitHubIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-missing-release-tag) "GraphEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -689,66 +320,49 @@ type GraphNode = dagre_2.Node>; // Warning: (ae-missing-release-tag) "GroupIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const GroupIcon: IconComponent; +export function GroupIcon(props: IconComponentProps): 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) "Header" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Header: ({ - children, - pageTitleOverride, - style, - subtitle, - title, - tooltip, - type, - typeLink, -}: PropsWithChildren) => JSX.Element; +export function Header(props: PropsWithChildren): 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) "HeaderIconLinkRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HeaderIconLinkRow: ({ links }: Props_8) => JSX.Element; +export function HeaderIconLinkRow(props: Props_8): JSX.Element; // Warning: (ae-forgotten-export) The symbol "HeaderLabelProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "HeaderLabel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HeaderLabel: ({ - label, - value, - url, -}: HeaderLabelProps) => JSX.Element; +export function HeaderLabel(props: HeaderLabelProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "HeaderTabsProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "HeaderTabs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HeaderTabs: ({ - tabs, - onChange, - selectedIndex, -}: HeaderTabsProps) => JSX.Element; +export function HeaderTabs(props: HeaderTabsProps): JSX.Element; // Warning: (ae-missing-release-tag) "HelpIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HelpIcon: IconComponent; +export function HelpIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-missing-release-tag) "HomepageTimer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HomepageTimer: () => JSX.Element | null; +export function HomepageTimer(_props: {}): JSX.Element | null; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "HorizontalScrollGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HorizontalScrollGrid: ( +export function HorizontalScrollGrid( props: PropsWithChildren, -) => JSX.Element; +): JSX.Element; // Warning: (ae-missing-release-tag) "IconLinkVerticalProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -767,26 +381,7 @@ export type IconLinkVerticalProps = { // Warning: (ae-missing-release-tag) "InfoCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const InfoCard: ({ - title, - subheader, - divider, - deepLink, - slackChannel, - errorBoundaryProps, - variant, - children, - headerStyle, - headerProps, - action, - actionsClassName, - actions, - cardClassName, - actionsTopRight, - className, - noPadding, - titleTypographyProps, -}: Props_19) => JSX.Element; +export function InfoCard(props: Props_19): JSX.Element; // Warning: (ae-missing-release-tag) "InfoCardVariants" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -797,7 +392,7 @@ export type InfoCardVariants = 'flex' | 'fullHeight' | 'gridItem'; // Warning: (ae-missing-release-tag) "IntroCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const IntroCard: (props: IntroCardProps) => JSX.Element; +export function IntroCard(props: IntroCardProps): JSX.Element; // Warning: (tsdoc-malformed-html-name) Invalid HTML element: Expecting an HTML name // Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag @@ -805,16 +400,7 @@ export const IntroCard: (props: IntroCardProps) => JSX.Element; // Warning: (ae-missing-release-tag) "ItemCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated -export const ItemCard: ({ - description, - tags, - title, - type, - subtitle, - label, - onClick, - href, -}: ItemCardProps) => JSX.Element; +export function ItemCard(props: ItemCardProps): JSX.Element; // Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag @@ -825,7 +411,7 @@ export const ItemCard: ({ // Warning: (ae-missing-release-tag) "ItemCardGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const ItemCardGrid: (props: ItemCardGridProps) => JSX.Element; +export function ItemCardGrid(props: ItemCardGridProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "styles" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ItemCardGridProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -844,7 +430,7 @@ export type ItemCardGridProps = Partial> & { // Warning: (ae-missing-release-tag) "ItemCardHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const ItemCardHeader: (props: ItemCardHeaderProps) => JSX.Element; +export function ItemCardHeader(props: ItemCardHeaderProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "styles" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ItemCardHeaderProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -872,302 +458,18 @@ enum LabelPosition { // Warning: (ae-missing-release-tag) "Lifecycle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Lifecycle: (props: Props_10) => JSX.Element; +export function Lifecycle(props: Props_10): 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) "LinearGauge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const LinearGauge: ({ value }: Props_15) => JSX.Element | null; +export function LinearGauge(props: Props_15): JSX.Element | null; -// Warning: (ae-missing-release-tag) "Link" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "LinkType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public -export const Link: React_2.ForwardRefExoticComponent< - Pick< - LinkProps, - | 'replace' - | 'media' - | 'hidden' - | 'dir' - | 'slot' - | 'style' - | 'title' - | 'color' - | 'underline' - | 'display' - | 'translate' - | 'prefix' - | 'children' - | 'key' - | 'id' - | 'classes' - | 'defaultChecked' - | 'defaultValue' - | 'suppressContentEditableWarning' - | 'suppressHydrationWarning' - | 'accessKey' - | 'className' - | 'contentEditable' - | 'contextMenu' - | 'draggable' - | 'lang' - | 'placeholder' - | 'spellCheck' - | 'tabIndex' - | 'radioGroup' - | 'role' - | 'about' - | 'datatype' - | 'inlist' - | 'property' - | 'resource' - | 'typeof' - | 'vocab' - | 'autoCapitalize' - | 'autoCorrect' - | 'autoSave' - | 'itemProp' - | 'itemScope' - | 'itemType' - | 'itemID' - | 'itemRef' - | 'results' - | 'security' - | 'unselectable' - | 'inputMode' - | 'is' - | 'aria-activedescendant' - | 'aria-atomic' - | 'aria-autocomplete' - | 'aria-busy' - | 'aria-checked' - | 'aria-colcount' - | 'aria-colindex' - | 'aria-colspan' - | 'aria-controls' - | 'aria-current' - | 'aria-describedby' - | 'aria-details' - | 'aria-disabled' - | 'aria-dropeffect' - | 'aria-errormessage' - | 'aria-expanded' - | 'aria-flowto' - | 'aria-grabbed' - | 'aria-haspopup' - | 'aria-hidden' - | 'aria-invalid' - | 'aria-keyshortcuts' - | 'aria-label' - | 'aria-labelledby' - | 'aria-level' - | 'aria-live' - | 'aria-modal' - | 'aria-multiline' - | 'aria-multiselectable' - | 'aria-orientation' - | 'aria-owns' - | 'aria-placeholder' - | 'aria-posinset' - | 'aria-pressed' - | 'aria-readonly' - | 'aria-relevant' - | 'aria-required' - | 'aria-roledescription' - | 'aria-rowcount' - | 'aria-rowindex' - | 'aria-rowspan' - | 'aria-selected' - | 'aria-setsize' - | 'aria-sort' - | 'aria-valuemax' - | 'aria-valuemin' - | 'aria-valuenow' - | 'aria-valuetext' - | 'dangerouslySetInnerHTML' - | 'onCopy' - | 'onCopyCapture' - | 'onCut' - | 'onCutCapture' - | 'onPaste' - | 'onPasteCapture' - | 'onCompositionEnd' - | 'onCompositionEndCapture' - | 'onCompositionStart' - | 'onCompositionStartCapture' - | 'onCompositionUpdate' - | 'onCompositionUpdateCapture' - | 'onFocus' - | 'onFocusCapture' - | 'onBlur' - | 'onBlurCapture' - | 'onChange' - | 'onChangeCapture' - | 'onBeforeInput' - | 'onBeforeInputCapture' - | 'onInput' - | 'onInputCapture' - | 'onReset' - | 'onResetCapture' - | 'onSubmit' - | 'onSubmitCapture' - | 'onInvalid' - | 'onInvalidCapture' - | 'onLoad' - | 'onLoadCapture' - | 'onError' - | 'onErrorCapture' - | 'onKeyDown' - | 'onKeyDownCapture' - | 'onKeyPress' - | 'onKeyPressCapture' - | 'onKeyUp' - | 'onKeyUpCapture' - | 'onAbort' - | 'onAbortCapture' - | 'onCanPlay' - | 'onCanPlayCapture' - | 'onCanPlayThrough' - | 'onCanPlayThroughCapture' - | 'onDurationChange' - | 'onDurationChangeCapture' - | 'onEmptied' - | 'onEmptiedCapture' - | 'onEncrypted' - | 'onEncryptedCapture' - | 'onEnded' - | 'onEndedCapture' - | 'onLoadedData' - | 'onLoadedDataCapture' - | 'onLoadedMetadata' - | 'onLoadedMetadataCapture' - | 'onLoadStart' - | 'onLoadStartCapture' - | 'onPause' - | 'onPauseCapture' - | 'onPlay' - | 'onPlayCapture' - | 'onPlaying' - | 'onPlayingCapture' - | 'onProgress' - | 'onProgressCapture' - | 'onRateChange' - | 'onRateChangeCapture' - | 'onSeeked' - | 'onSeekedCapture' - | 'onSeeking' - | 'onSeekingCapture' - | 'onStalled' - | 'onStalledCapture' - | 'onSuspend' - | 'onSuspendCapture' - | 'onTimeUpdate' - | 'onTimeUpdateCapture' - | 'onVolumeChange' - | 'onVolumeChangeCapture' - | 'onWaiting' - | 'onWaitingCapture' - | 'onAuxClick' - | 'onAuxClickCapture' - | 'onClick' - | 'onClickCapture' - | 'onContextMenu' - | 'onContextMenuCapture' - | 'onDoubleClick' - | 'onDoubleClickCapture' - | 'onDrag' - | 'onDragCapture' - | 'onDragEnd' - | 'onDragEndCapture' - | 'onDragEnter' - | 'onDragEnterCapture' - | 'onDragExit' - | 'onDragExitCapture' - | 'onDragLeave' - | 'onDragLeaveCapture' - | 'onDragOver' - | 'onDragOverCapture' - | 'onDragStart' - | 'onDragStartCapture' - | 'onDrop' - | 'onDropCapture' - | 'onMouseDown' - | 'onMouseDownCapture' - | 'onMouseEnter' - | 'onMouseLeave' - | 'onMouseMove' - | 'onMouseMoveCapture' - | 'onMouseOut' - | 'onMouseOutCapture' - | 'onMouseOver' - | 'onMouseOverCapture' - | 'onMouseUp' - | 'onMouseUpCapture' - | 'onSelect' - | 'onSelectCapture' - | 'onTouchCancel' - | 'onTouchCancelCapture' - | 'onTouchEnd' - | 'onTouchEndCapture' - | 'onTouchMove' - | 'onTouchMoveCapture' - | 'onTouchStart' - | 'onTouchStartCapture' - | 'onPointerDown' - | 'onPointerDownCapture' - | 'onPointerMove' - | 'onPointerMoveCapture' - | 'onPointerUp' - | 'onPointerUpCapture' - | 'onPointerCancel' - | 'onPointerCancelCapture' - | 'onPointerEnter' - | 'onPointerEnterCapture' - | 'onPointerLeave' - | 'onPointerLeaveCapture' - | 'onPointerOver' - | 'onPointerOverCapture' - | 'onPointerOut' - | 'onPointerOutCapture' - | 'onGotPointerCapture' - | 'onGotPointerCaptureCapture' - | 'onLostPointerCapture' - | 'onLostPointerCaptureCapture' - | 'onScroll' - | 'onScrollCapture' - | 'onWheel' - | 'onWheelCapture' - | 'onAnimationStart' - | 'onAnimationStartCapture' - | 'onAnimationEnd' - | 'onAnimationEndCapture' - | 'onAnimationIteration' - | 'onAnimationIterationCapture' - | 'onTransitionEnd' - | 'onTransitionEndCapture' - | 'component' - | 'variant' - | 'innerRef' - | 'download' - | 'href' - | 'hrefLang' - | 'ping' - | 'rel' - | 'target' - | 'type' - | 'referrerPolicy' - | 'noWrap' - | 'gutterBottom' - | 'paragraph' - | 'align' - | 'variantMapping' - | 'to' - | 'state' - | 'TypographyClasses' - > & - React_2.RefAttributes ->; +// @public (undocumented) +export function Link(props: LinkProps): JSX.Element; // Warning: (ae-missing-release-tag) "LinkProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1181,52 +483,45 @@ export type LinkProps = LinkProps_2 & // Warning: (ae-missing-release-tag) "MarkdownContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const MarkdownContent: ({ content, dialect }: Props_11) => JSX.Element; +export function MarkdownContent(props: Props_11): 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) "MissingAnnotationEmptyState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const MissingAnnotationEmptyState: ({ - annotation, -}: Props_6) => JSX.Element; +export function MissingAnnotationEmptyState(props: Props_6): JSX.Element; // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const OAuthRequestDialog: () => JSX.Element; +export function OAuthRequestDialog(_props: {}): 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) "OverflowTooltip" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const OverflowTooltip: (props: Props_12) => JSX.Element; +export function OverflowTooltip(props: Props_12): 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) "Page" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Page: ({ - themeId, - children, -}: PropsWithChildren) => JSX.Element; +export function Page(props: PropsWithChildren): JSX.Element; // Warning: (ae-forgotten-export) The symbol "PageWithHeaderProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "PageWithHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const PageWithHeader: ({ - themeId, - children, - ...props -}: PropsWithChildren) => JSX.Element; +export function PageWithHeader( + props: PropsWithChildren, +): JSX.Element; // Warning: (ae-missing-release-tag) "Progress" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Progress: ( +export function Progress( props: PropsWithChildren, -) => JSX.Element; +): JSX.Element; // Warning: (ae-missing-release-tag) "Ranker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1267,40 +562,24 @@ type RenderNodeProps = { // Warning: (ae-missing-release-tag) "ResponseErrorPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const ResponseErrorPanel: ({ - title, - error, - defaultExpanded, -}: ErrorPanelProps) => JSX.Element; +export function ResponseErrorPanel(props: ErrorPanelProps): JSX.Element; // Warning: (ae-missing-release-tag) "RoutedTabs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const RoutedTabs: ({ routes }: { routes: SubRoute_2[] }) => JSX.Element; +export function RoutedTabs(props: { routes: SubRoute_2[] }): JSX.Element; // Warning: (ae-forgotten-export) The symbol "SelectProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SelectComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Select: ({ - multiple, - items, - label, - placeholder, - selected, - onChange, - triggerReset, -}: SelectProps) => JSX.Element; +export function Select(props: SelectProps): 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) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Sidebar: ({ - openDelayMs, - closeDelayMs, - children, -}: PropsWithChildren) => JSX.Element; +export function Sidebar(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1610,7 +889,7 @@ export const SidebarDivider: React_2.ComponentType< // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SidebarIntro: () => JSX.Element | null; +export function SidebarIntro(_props: {}): JSX.Element | null; // Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1623,7 +902,7 @@ export const SidebarItem: React_2.ForwardRefExoticComponent< // Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SidebarPage: (props: PropsWithChildren<{}>) => JSX.Element; +export function SidebarPage(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "SidebarPinStateContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1911,9 +1190,7 @@ export const SidebarScrollWrapper: React_2.ComponentType< // Warning: (ae-missing-release-tag) "SidebarSearchField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SidebarSearchField: ( - props: SidebarSearchFieldProps, -) => JSX.Element; +export function SidebarSearchField(props: SidebarSearchFieldProps): JSX.Element; // Warning: (ae-missing-release-tag) "SidebarSpace" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2457,7 +1734,7 @@ export const SidebarSpacer: React_2.ComponentType< // Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SignInPage: (props: Props_22) => JSX.Element; +export function SignInPage(props: Props_22): JSX.Element; // Warning: (ae-missing-release-tag) "SignInProviderConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2473,82 +1750,65 @@ export type SignInProviderConfig = { // Warning: (ae-missing-release-tag) "SimpleStepper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SimpleStepper: ({ - children, - elevated, - onStepChange, - activeStep, -}: PropsWithChildren) => JSX.Element; +export function SimpleStepper( + props: PropsWithChildren, +): JSX.Element; // Warning: (ae-forgotten-export) The symbol "StepProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SimpleStepperStep" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SimpleStepperStep: ({ - title, - children, - end, - actions, - ...muiProps -}: PropsWithChildren) => JSX.Element; +export function SimpleStepperStep( + props: PropsWithChildren, +): JSX.Element; // Warning: (ae-missing-release-tag) "StatusAborted" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StatusAborted: (props: PropsWithChildren<{}>) => JSX.Element; +export function StatusAborted(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "StatusError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StatusError: (props: PropsWithChildren<{}>) => JSX.Element; +export function StatusError(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "StatusOK" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StatusOK: (props: PropsWithChildren<{}>) => JSX.Element; +export function StatusOK(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "StatusPending" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StatusPending: (props: PropsWithChildren<{}>) => JSX.Element; +export function StatusPending(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "StatusRunning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StatusRunning: (props: PropsWithChildren<{}>) => JSX.Element; +export function StatusRunning(props: PropsWithChildren<{}>): JSX.Element; // Warning: (ae-missing-release-tag) "StatusWarning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StatusWarning: (props: PropsWithChildren<{}>) => JSX.Element; +export function StatusWarning(props: PropsWithChildren<{}>): 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) "StructuredMetadataTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const StructuredMetadataTable: ({ - metadata, - dense, - options, -}: Props_16) => JSX.Element; +export function StructuredMetadataTable(props: Props_16): JSX.Element; // Warning: (ae-forgotten-export) The symbol "SubvalueCellProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SubvalueCell" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SubvalueCell: ({ - value, - subvalue, -}: SubvalueCellProps) => JSX.Element; +export function SubvalueCell(props: SubvalueCellProps): JSX.Element; // Warning: (ae-forgotten-export) The symbol "SupportButtonProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SupportButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SupportButton: ({ - title, - children, -}: SupportButtonProps) => JSX.Element; +export function SupportButton(props: SupportButtonProps): JSX.Element; // Warning: (ae-missing-release-tag) "SupportConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2593,38 +1853,26 @@ export type Tab = { // Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const TabbedCard: ({ - slackChannel, - errorBoundaryProps, - children, - title, - deepLink, - value, - onChange, -}: PropsWithChildren) => JSX.Element; +export function TabbedCard(props: PropsWithChildren): JSX.Element; +// Warning: (ae-missing-release-tag) "TabbedLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // Warning: (ae-missing-release-tag) "TabbedLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const TabbedLayout: { - ({ children }: PropsWithChildren<{}>): JSX.Element; - Route: (props: SubRoute) => null; -}; +export function TabbedLayout(props: PropsWithChildren<{}>): JSX.Element; + +// @public (undocumented) +export namespace TabbedLayout { + var // Warning: (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts + // + // (undocumented) + Route: (props: SubRoute) => null; +} // Warning: (ae-missing-release-tag) "Table" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Table({ - columns, - options, - title, - subtitle, - filters, - initialState, - emptyContent, - onStateChange, - ...props -}: TableProps): JSX.Element; +export function Table(props: TableProps): JSX.Element; // Warning: (ae-missing-release-tag) "TableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2676,17 +1924,17 @@ export type TableState = { // Warning: (ae-missing-release-tag) "Tabs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Tabs: ({ tabs }: TabsProps) => JSX.Element; +export function Tabs(props: TabsProps): JSX.Element; // Warning: (ae-missing-release-tag) "TrendLine" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const TrendLine: ( +export function TrendLine( props: SparklinesProps & Pick & { title?: string; }, -) => JSX.Element | null; +): JSX.Element | null; // Warning: (ae-forgotten-export) The symbol "SetQueryParams" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "useQueryParamState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2700,7 +1948,7 @@ export function useQueryParamState( // Warning: (ae-missing-release-tag) "UserIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const UserIcon: IconComponent; +export function UserIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-missing-release-tag) "useSupportConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2710,7 +1958,7 @@ export function useSupportConfig(): SupportConfig; // Warning: (ae-missing-release-tag) "WarningIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const WarningIcon: IconComponent; +export function WarningIcon(props: IconComponentProps): 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-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. @@ -2728,19 +1976,11 @@ export const WarningIcon: IconComponent; // Warning: (ae-missing-release-tag) "WarningPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export const WarningPanel: ({ - severity, - title, - message, - children, - defaultExpanded, -}: WarningProps) => JSX.Element; +export function WarningPanel(props: WarningProps): JSX.Element; // Warnings were encountered during analysis: // -// src/components/CopyTextButton/CopyTextButton.d.ts:24:5 - (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // src/components/TabbedLayout/RoutedTabs.d.ts:9:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts -// src/components/TabbedLayout/TabbedLayout.d.ts:29:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts // src/components/Table/Table.d.ts:15:5 - (ae-forgotten-export) The symbol "SelectedFilters" needs to be exported by the entry point index.d.ts // src/layout/ErrorBoundary/ErrorBoundary.d.ts:7:5 - (ae-forgotten-export) The symbol "SlackChannel" needs to be exported by the entry point index.d.ts diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 720482f46b..7ac256ad30 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.4.0", + "version": "0.4.1", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.8", - "@backstage/core-plugin-api": "^0.1.6", - "@backstage/errors": "^0.1.1", + "@backstage/config": "^0.1.9", + "@backstage/core-plugin-api": "^0.1.7", + "@backstage/errors": "^0.1.2", "@backstage/theme": "^0.2.10", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", @@ -70,8 +70,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/core-app-api": "^0.1.11", - "@backstage/cli": "^0.7.10", + "@backstage/core-app-api": "^0.1.12", + "@backstage/cli": "^0.7.11", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx b/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx index 495e2ea14c..f3e102d9a0 100644 --- a/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx +++ b/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx @@ -22,7 +22,7 @@ import { AlertMessage, useApi, alertApiRef } from '@backstage/core-plugin-api'; import pluralize from 'pluralize'; // TODO: improve on this and promote to a shared component for use by all apps. -export const AlertDisplay = () => { +export function AlertDisplay(_props: {}) { const [messages, setMessages] = useState>([]); const alertApi = useApi(alertApiRef); @@ -73,4 +73,4 @@ export const AlertDisplay = () => { ); -}; +} diff --git a/packages/core-components/src/components/Avatar/Avatar.tsx b/packages/core-components/src/components/Avatar/Avatar.tsx index df3c46fba7..f694365233 100644 --- a/packages/core-components/src/components/Avatar/Avatar.tsx +++ b/packages/core-components/src/components/Avatar/Avatar.tsx @@ -41,7 +41,8 @@ export type AvatarProps = { customStyles?: CSSProperties; }; -export const Avatar = ({ displayName, picture, customStyles }: AvatarProps) => { +export function Avatar(props: AvatarProps) { + const { displayName, picture, customStyles } = props; const classes = useStyles(); return ( { {displayName && extractInitials(displayName)} ); -}; +} diff --git a/packages/core-components/src/components/Button/Button.stories.tsx b/packages/core-components/src/components/Button/Button.stories.tsx index 28deb1ad33..3451a77765 100644 --- a/packages/core-components/src/components/Button/Button.stories.tsx +++ b/packages/core-components/src/components/Button/Button.stories.tsx @@ -15,7 +15,7 @@ */ import React, { ComponentType } from 'react'; import { Button } from './Button'; -import { MemoryRouter, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { createRouteRef, useRouteRef } from '@backstage/core-plugin-api'; import { Divider, @@ -26,11 +26,7 @@ import { Typography, Button as MaterialButton, } from '@material-ui/core'; -// We don't want to export RoutingProvider from core-app-api, but it's way easier to -// use here. This hack only works in storybook stories. -// TODO: Export a nicer to user routing provider, perhaps from test-utils -// eslint-disable-next-line monorepo/no-internal-import -import { RoutingProvider } from '@backstage/core-app-api/src/routing/RoutingProvider'; +import { wrapInTestApp } from '@backstage/test-utils'; const routeRef = createRouteRef({ id: 'storybook.test-route', @@ -45,36 +41,29 @@ export default { title: 'Inputs/Button', component: Button, decorators: [ - (Story: ComponentType<{}>) => ( - <> - - A collection of buttons that should be used in the Backstage - interface. These leverage the properties inherited from{' '} - - Material-UI Button - - , but include an opinionated set that align to the Backstage design. - + (Story: ComponentType<{}>) => + wrapInTestApp( + <> + + A collection of buttons that should be used in the Backstage + interface. These leverage the properties inherited from{' '} + + Material-UI Button + + , but include an opinionated set that align to the Backstage design. + - + - - +

-
- -
- +
- - - - ), + +
+ , + { mountedRoutes: { '/hello': routeRef } }, + ), ], }; diff --git a/packages/core-components/src/components/Button/Button.tsx b/packages/core-components/src/components/Button/Button.tsx index 6dd593d776..e52285312b 100644 --- a/packages/core-components/src/components/Button/Button.tsx +++ b/packages/core-components/src/components/Button/Button.tsx @@ -23,10 +23,19 @@ import { Link, LinkProps } from '../Link'; type Props = MaterialButtonProps & Omit; +declare function ButtonType(props: Props): JSX.Element; + /** * Thin wrapper on top of material-ui's Button component * Makes the Button to utilise react-router */ -export const Button = React.forwardRef((props, ref) => ( +const ActualButton = React.forwardRef((props, ref) => ( -)); +)) as { (props: Props): JSX.Element }; + +// TODO(Rugvip): We use this as a workaround to make the exported type be a +// function, which makes our API reference docs much nicer. +// The first type to be exported gets priority, but it will +// be thrown away when compiling to JS. +// @ts-ignore +export { ButtonType as Button, ActualButton as Button }; diff --git a/packages/core-components/src/components/CheckboxTree/CheckboxTree.tsx b/packages/core-components/src/components/CheckboxTree/CheckboxTree.tsx index 7e7155143b..695da88192 100644 --- a/packages/core-components/src/components/CheckboxTree/CheckboxTree.tsx +++ b/packages/core-components/src/components/CheckboxTree/CheckboxTree.tsx @@ -225,13 +225,8 @@ const indexer = ( }; }, {}); -export const CheckboxTree = ({ - subCategories, - label, - selected, - onChange, - triggerReset, -}: CheckboxTreeProps) => { +export function CheckboxTree(props: CheckboxTreeProps) { + const { subCategories, label, selected, onChange, triggerReset } = props; const classes = useStyles(); const [state, dispatch] = useReducer(reducer, indexer(subCategories)); @@ -355,4 +350,4 @@ export const CheckboxTree = ({
); -}; +} diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx index a3badaad07..6417151ff4 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx @@ -30,14 +30,15 @@ type Props = { customStyle?: any; }; -export const CodeSnippet = ({ - text, - language, - showLineNumbers = false, - showCopyCodeButton = false, - highlightedNumbers, - customStyle, -}: Props) => { +export const CodeSnippet = (props: Props) => { + const { + text, + language, + showLineNumbers = false, + showCopyCodeButton = false, + highlightedNumbers, + customStyle, + } = props; const theme = useTheme(); const mode = theme.palette.type === 'dark' ? dark : docco; const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; diff --git a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx index cc0ecaf76f..2d3afff1dc 100644 --- a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx +++ b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx @@ -47,7 +47,7 @@ const defaultProps = { tooltipText: 'Text copied to clipboard', }; -export const CopyTextButton = (props: Props) => { +export function CopyTextButton(props: Props) { const { text, tooltipDelay, tooltipText } = { ...defaultProps, ...props, @@ -84,7 +84,7 @@ export const CopyTextButton = (props: Props) => { ); -}; +} // Type check for the JS files using this core component CopyTextButton.propTypes = { diff --git a/packages/core-components/src/components/CreateButton/CreateButton.tsx b/packages/core-components/src/components/CreateButton/CreateButton.tsx index 021e1b002d..1ca4fdaa97 100644 --- a/packages/core-components/src/components/CreateButton/CreateButton.tsx +++ b/packages/core-components/src/components/CreateButton/CreateButton.tsx @@ -24,7 +24,8 @@ type CreateButtonProps = { title: string; } & Partial>; -export const CreateButton = ({ title, to }: CreateButtonProps) => { +export function CreateButton(props: CreateButtonProps) { + const { title, to } = props; const isXSScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); @@ -48,4 +49,4 @@ export const CreateButton = ({ title, to }: CreateButtonProps) => { {title} ); -}; +} diff --git a/packages/core-components/src/components/DependencyGraph/DefaultNode.tsx b/packages/core-components/src/components/DependencyGraph/DefaultNode.tsx index 69269d687d..8feca43e9a 100644 --- a/packages/core-components/src/components/DependencyGraph/DefaultNode.tsx +++ b/packages/core-components/src/components/DependencyGraph/DefaultNode.tsx @@ -21,11 +21,11 @@ import { RenderNodeProps } from './types'; const useStyles = makeStyles((theme: BackstageTheme) => ({ node: { - fill: theme.palette.background.paper, - stroke: theme.palette.border, + fill: theme.palette.primary.light, + stroke: theme.palette.primary.light, }, text: { - fill: theme.palette.textContrast, + fill: theme.palette.primary.contrastText, }, })); diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 436eda614c..0d74965e7e 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -60,27 +60,28 @@ export type DependencyGraphProps = React.SVGProps & { const WORKSPACE_ID = 'workspace'; -export function DependencyGraph({ - edges, - nodes, - renderNode, - direction = Direction.TOP_BOTTOM, - align, - nodeMargin = 50, - edgeMargin = 10, - rankMargin = 50, - paddingX = 0, - paddingY = 0, - acyclicer, - ranker = Ranker.NETWORK_SIMPLEX, - labelPosition = LabelPosition.RIGHT, - labelOffset = 10, - edgeRanks = 1, - edgeWeight = 1, - renderLabel, - defs, - ...svgProps -}: DependencyGraphProps) { +export function DependencyGraph(props: DependencyGraphProps) { + const { + edges, + nodes, + renderNode, + direction = Direction.TOP_BOTTOM, + align, + nodeMargin = 50, + edgeMargin = 10, + rankMargin = 50, + paddingX = 0, + paddingY = 0, + acyclicer, + ranker = Ranker.NETWORK_SIMPLEX, + labelPosition = LabelPosition.RIGHT, + labelOffset = 10, + edgeRanks = 1, + edgeWeight = 1, + renderLabel, + defs, + ...svgProps + } = props; const theme: BackstageTheme = useTheme(); const [containerWidth, setContainerWidth] = React.useState(100); const [containerHeight, setContainerHeight] = React.useState(100); diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx index 6c4e43811d..cc20a49ad6 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx @@ -70,12 +70,8 @@ type Props = { fixed?: boolean; }; -export const DismissableBanner = ({ - variant, - message, - id, - fixed = false, -}: Props) => { +export const DismissableBanner = (props: Props) => { + const { variant, message, id, fixed = false } = props; const classes = useStyles(); const storageApi = useApi(storageApiRef); const notificationsStore = storageApi.forBucket('notifications'); diff --git a/packages/core-components/src/components/EmptyState/EmptyState.tsx b/packages/core-components/src/components/EmptyState/EmptyState.tsx index d916ad9a61..b2f5eca65e 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.tsx @@ -38,7 +38,8 @@ type Props = { action?: JSX.Element; }; -export const EmptyState = ({ title, description, missing, action }: Props) => { +export function EmptyState(props: Props) { + const { title, description, missing, action } = props; const classes = useStyles(); return ( { ); -}; +} diff --git a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx index d477494964..17061fa237 100644 --- a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx @@ -45,7 +45,8 @@ const useStyles = makeStyles(theme => ({ }, })); -export const MissingAnnotationEmptyState = ({ annotation }: Props) => { +export function MissingAnnotationEmptyState(props: Props) { + const { annotation } = props; const classes = useStyles(); const description = ( <> @@ -84,4 +85,4 @@ export const MissingAnnotationEmptyState = ({ annotation }: Props) => { } /> ); -}; +} diff --git a/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx b/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx index f9996b9b1f..309f2b447a 100644 --- a/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx +++ b/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx @@ -92,12 +92,8 @@ export type ErrorPanelProps = { /** * Renders a warning panel as the effect of an error. */ -export const ErrorPanel = ({ - title, - error, - defaultExpanded, - children, -}: PropsWithChildren) => { +export function ErrorPanel(props: PropsWithChildren) { + const { title, error, defaultExpanded, children } = props; return ( ); -}; +} diff --git a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx index 9af6c30c3b..9459c3cbe0 100644 --- a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx +++ b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx @@ -93,12 +93,8 @@ type Placement = { textWidth: number; }; -export const FeatureCalloutCircular = ({ - featureId, - title, - description, - children, -}: PropsWithChildren) => { +export function FeatureCalloutCircular(props: PropsWithChildren) { + const { featureId, title, description, children } = props; const { show, hide } = useShowCallout(featureId); const portalElement = usePortal('core.callout'); const wrapperRef = useRef(null); @@ -196,4 +192,4 @@ export const FeatureCalloutCircular = ({ )} ); -}; +} diff --git a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx index afceda165e..7e49537492 100644 --- a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx +++ b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx @@ -31,7 +31,8 @@ type Props = { links: IconLinkVerticalProps[]; }; -export const HeaderIconLinkRow = ({ links }: Props) => { +export function HeaderIconLinkRow(props: Props) { + const { links } = props; const classes = useStyles(); return ( ); -}; +} diff --git a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx index f5c45d3eb0..a3d16bc4ec 100644 --- a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx +++ b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx @@ -181,7 +181,7 @@ function useSmoothScroll( return setScrollTarget; } -export const HorizontalScrollGrid = (props: PropsWithChildren) => { +export function HorizontalScrollGrid(props: PropsWithChildren) { const { scrollStep = 100, scrollSpeed = 50, @@ -244,4 +244,4 @@ export const HorizontalScrollGrid = (props: PropsWithChildren) => { )}
); -}; +} diff --git a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx index c72ea46f1b..ef451ac1b0 100644 --- a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx +++ b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx @@ -38,7 +38,7 @@ const useStyles = makeStyles({ }, }); -export const Lifecycle = (props: Props) => { +export function Lifecycle(props: Props) { const classes = useStyles(props); const { shorthand, alpha } = props; return shorthand ? ( @@ -53,4 +53,4 @@ export const Lifecycle = (props: Props) => { {alpha ? 'Alpha' : 'Beta'} ); -}; +} diff --git a/packages/core-components/src/components/Link/Link.stories.tsx b/packages/core-components/src/components/Link/Link.stories.tsx index 516a5adcff..d9944c1c51 100644 --- a/packages/core-components/src/components/Link/Link.stories.tsx +++ b/packages/core-components/src/components/Link/Link.stories.tsx @@ -15,17 +15,9 @@ */ import React, { ComponentType } from 'react'; import { Link } from './Link'; -import { - MemoryRouter, - Route, - useLocation, - NavLink as RouterNavLink, -} from 'react-router-dom'; +import { Route, useLocation, NavLink as RouterNavLink } from 'react-router-dom'; import { createRouteRef, useRouteRef } from '@backstage/core-plugin-api'; -// We don't want to export RoutingProvider from core-app-api, but it's way easier to -// use here. This hack only works in storybook stories. -// eslint-disable-next-line monorepo/no-internal-import -import { RoutingProvider } from '@backstage/core-app-api/src/routing/RoutingProvider'; +import { wrapInTestApp } from '@backstage/test-utils'; const routeRef = createRouteRef({ id: 'storybook.test-route', @@ -40,23 +32,16 @@ export default { title: 'Navigation/Link', component: Link, decorators: [ - (Story: ComponentType<{}>) => ( - - + (Story: ComponentType<{}>) => + wrapInTestApp( +
-
- -
- +
- - - ), + +
, + { mountedRoutes: { '/hello': routeRef } }, + ), ], }; diff --git a/packages/core-components/src/components/Link/Link.tsx b/packages/core-components/src/components/Link/Link.tsx index 7853c4f52f..0d301dccf9 100644 --- a/packages/core-components/src/components/Link/Link.tsx +++ b/packages/core-components/src/components/Link/Link.tsx @@ -31,11 +31,13 @@ export type LinkProps = MaterialLinkProps & component?: ElementType; }; +declare function LinkType(props: LinkProps): JSX.Element; + /** * Thin wrapper on top of material-ui's Link component * Makes the Link to utilise react-router */ -export const Link = React.forwardRef((props, ref) => { +const ActualLink = React.forwardRef((props, ref) => { const to = String(props.to); const external = isExternalUri(to); const newWindow = external && !!/^https?:/.exec(to); @@ -52,3 +54,10 @@ export const Link = React.forwardRef((props, ref) => { ); }); + +// TODO(Rugvip): We use this as a workaround to make the exported type be a +// function, which makes our API reference docs much nicer. +// The first type to be exported gets priority, but it will +// be thrown away when compiling to JS. +// @ts-ignore +export { LinkType as Link, ActualLink as Link }; diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index 296df22ecd..d2930cbf5d 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -76,7 +76,8 @@ const renderers = { * Renders markdown with the default dialect [gfm - GitHub flavored Markdown](https://github.github.com/gfm/) to backstage theme styled HTML. * If you just want to render to plain [CommonMark](https://commonmark.org/), set the dialect to `'common-mark'` */ -export const MarkdownContent = ({ content, dialect = 'gfm' }: Props) => { +export function MarkdownContent(props: Props) { + const { content, dialect = 'gfm' } = props; const classes = useStyles(); return ( { renderers={renderers} /> ); -}; +} diff --git a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx index e0102564fd..f9533d9d03 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx @@ -21,6 +21,7 @@ import { makeStyles, Typography, Theme, + Button, } from '@material-ui/core'; import React, { useState } from 'react'; import { PendingAuthRequest } from '@backstage/core-plugin-api'; @@ -55,12 +56,7 @@ const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => { const IconComponent = request.provider.icon; return ( - + @@ -74,6 +70,9 @@ const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => { ) } /> + ); }; diff --git a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx index 687e4211bf..e79d28c470 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx @@ -39,9 +39,12 @@ const useStyles = makeStyles(theme => ({ contentList: { padding: 0, }, + actionButtons: { + padding: theme.spacing(2, 0), + }, })); -export const OAuthRequestDialog = () => { +export function OAuthRequestDialog(_props: {}) { const classes = useStyles(); const [busy, setBusy] = useState(false); const oauthRequestApi = useApi(oauthRequestApiRef); @@ -65,7 +68,7 @@ export const OAuthRequestDialog = () => { Login Required - + {requests.map(request => ( { - + ); -}; +} diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx index 1be7ebc7ed..fbb1537847 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx @@ -32,7 +32,7 @@ const useStyles = makeStyles({ }, }); -export const OverflowTooltip = (props: Props) => { +export function OverflowTooltip(props: Props) { const [hover, setHover] = useState(false); const classes = useStyles(); @@ -54,4 +54,4 @@ export const OverflowTooltip = (props: Props) => { /> ); -}; +} diff --git a/packages/core-components/src/components/Progress/Progress.tsx b/packages/core-components/src/components/Progress/Progress.tsx index 68dcb91785..00ad532b5e 100644 --- a/packages/core-components/src/components/Progress/Progress.tsx +++ b/packages/core-components/src/components/Progress/Progress.tsx @@ -17,7 +17,7 @@ import React, { useState, useEffect, PropsWithChildren } from 'react'; import { LinearProgress, LinearProgressProps } from '@material-ui/core'; -export const Progress = (props: PropsWithChildren) => { +export function Progress(props: PropsWithChildren) { const [isVisible, setIsVisible] = useState(false); useEffect(() => { @@ -30,4 +30,4 @@ export const Progress = (props: PropsWithChildren) => { ) : (
); -}; +} diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 46948d4af6..3b31f64fe7 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -77,7 +77,7 @@ export function getProgressColor( return palette.status.ok; } -export const Gauge = (props: Props) => { +export function Gauge(props: Props) { const classes = useStyles(props); const theme = useTheme(); const { value, fractional, inverse, unit, max } = { @@ -103,4 +103,4 @@ export const Gauge = (props: Props) => {
); -}; +} diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx index 39248b1786..0a603ea991 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx @@ -37,7 +37,7 @@ const useStyles = makeStyles({ }, }); -export const GaugeCard = (props: Props) => { +export function GaugeCard(props: Props) { const classes = useStyles(props); const { title, subheader, progress, inverse, deepLink, variant } = props; @@ -53,4 +53,4 @@ export const GaugeCard = (props: Props) => { ); -}; +} diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx index d733cb2eae..cf8c022852 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx @@ -28,7 +28,8 @@ type Props = { value: number; }; -export const LinearGauge = ({ value }: Props) => { +export function LinearGauge(props: Props) { + const { value } = props; const theme = useTheme(); if (isNaN(value)) { return null; @@ -50,4 +51,4 @@ export const LinearGauge = ({ value }: Props) => { ); -}; +} diff --git a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx index ccfb706bf9..9f25e8290f 100644 --- a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx +++ b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx @@ -39,11 +39,8 @@ const useStyles = makeStyles(theme => ({ * Has special treatment for ResponseError errors, to display rich * server-provided information about what happened. */ -export const ResponseErrorPanel = ({ - title, - error, - defaultExpanded, -}: ErrorPanelProps) => { +export function ResponseErrorPanel(props: ErrorPanelProps) { + const { title, error, defaultExpanded } = props; const classes = useStyles(); if (error.name !== 'ResponseError') { @@ -93,4 +90,4 @@ export const ResponseErrorPanel = ({ ); -}; +} diff --git a/packages/core-components/src/components/Select/Select.tsx b/packages/core-components/src/components/Select/Select.tsx index b4512b3562..ae14545b29 100644 --- a/packages/core-components/src/components/Select/Select.tsx +++ b/packages/core-components/src/components/Select/Select.tsx @@ -106,15 +106,16 @@ export type SelectProps = { triggerReset?: boolean; }; -export const SelectComponent = ({ - multiple, - items, - label, - placeholder, - selected, - onChange, - triggerReset, -}: SelectProps) => { +export function SelectComponent(props: SelectProps) { + const { + multiple, + items, + label, + placeholder, + selected, + onChange, + triggerReset, + } = props; const classes = useStyles(); const [value, setValue] = useState( selected || (multiple ? [] : ''), @@ -228,4 +229,4 @@ export const SelectComponent = ({ ); -}; +} diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx index 46b2ce551a..9ffb63c085 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx @@ -47,12 +47,8 @@ export interface StepperProps { activeStep?: number; } -export const SimpleStepper = ({ - children, - elevated, - onStepChange, - activeStep = 0, -}: PropsWithChildren) => { +export function SimpleStepper(props: PropsWithChildren) { + const { children, elevated, onStepChange, activeStep = 0 } = props; const [stepIndex, setStepIndex] = useState(activeStep); const [stepHistory, setStepHistory] = useState([0]); @@ -95,4 +91,4 @@ export const SimpleStepper = ({ {stepIndex >= Children.count(children) - 1 && endStep} ); -}; +} diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx index 51fcee1f71..39383b934b 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx @@ -30,13 +30,8 @@ const useStyles = makeStyles(theme => ({ }, })); -export const SimpleStepperStep = ({ - title, - children, - end, - actions, - ...muiProps -}: PropsWithChildren) => { +export function SimpleStepperStep(props: PropsWithChildren) { + const { title, children, end, actions, ...muiProps } = props; const classes = useStyles(); // The end step is not a part of the stepper @@ -58,4 +53,4 @@ export const SimpleStepperStep = ({ ); -}; +} diff --git a/packages/core-components/src/components/Status/Status.tsx b/packages/core-components/src/components/Status/Status.tsx index 0b370a1397..2b2d40dee3 100644 --- a/packages/core-components/src/components/Status/Status.tsx +++ b/packages/core-components/src/components/Status/Status.tsx @@ -63,7 +63,7 @@ const useStyles = makeStyles(theme => ({ }, })); -export const StatusOK = (props: PropsWithChildren<{}>) => { +export function StatusOK(props: PropsWithChildren<{}>) { const classes = useStyles(props); return ( ) => { {...props} /> ); -}; +} -export const StatusWarning = (props: PropsWithChildren<{}>) => { +export function StatusWarning(props: PropsWithChildren<{}>) { const classes = useStyles(props); return ( ) => { {...props} /> ); -}; +} -export const StatusError = (props: PropsWithChildren<{}>) => { +export function StatusError(props: PropsWithChildren<{}>) { const classes = useStyles(props); return ( ) => { {...props} /> ); -}; +} -export const StatusPending = (props: PropsWithChildren<{}>) => { +export function StatusPending(props: PropsWithChildren<{}>) { const classes = useStyles(props); return ( ) => { {...props} /> ); -}; +} -export const StatusRunning = (props: PropsWithChildren<{}>) => { +export function StatusRunning(props: PropsWithChildren<{}>) { const classes = useStyles(props); return ( ) => { {...props} /> ); -}; +} -export const StatusAborted = (props: PropsWithChildren<{}>) => { +export function StatusAborted(props: PropsWithChildren<{}>) { const classes = useStyles(props); return ( ) => { {...props} /> ); -}; +} diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index 9b08123d1b..22a1a43b69 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -153,11 +153,8 @@ type Props = { options?: any; }; -export const StructuredMetadataTable = ({ - metadata, - dense = true, - options, -}: Props) => { +export function StructuredMetadataTable(props: Props) { + const { metadata, dense = true, options } = props; const metadataItems = mapToItems(metadata, options || {}); return {metadataItems}; -}; +} diff --git a/packages/core-components/src/components/SupportButton/SupportButton.tsx b/packages/core-components/src/components/SupportButton/SupportButton.tsx index 22ec5cf47e..8076636654 100644 --- a/packages/core-components/src/components/SupportButton/SupportButton.tsx +++ b/packages/core-components/src/components/SupportButton/SupportButton.tsx @@ -80,7 +80,8 @@ const SupportListItem = ({ item }: { item: SupportItem }) => { ); }; -export const SupportButton = ({ title, children }: SupportButtonProps) => { +export function SupportButton(props: SupportButtonProps) { + const { title, children } = props; const { items } = useSupportConfig(); const [popoverOpen, setPopoverOpen] = useState(false); @@ -160,4 +161,4 @@ export const SupportButton = ({ title, children }: SupportButtonProps) => { ); -}; +} diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index 52a2c29e2d..2a65c8ffc8 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -47,7 +47,8 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): { }; } -export const RoutedTabs = ({ routes }: { routes: SubRoute[] }) => { +export function RoutedTabs(props: { routes: SubRoute[] }) { + const { routes } = props; const navigate = useNavigate(); const { index, route, element } = useSelectedSubRoute(routes); const headerTabs = useMemo( @@ -80,4 +81,4 @@ export const RoutedTabs = ({ routes }: { routes: SubRoute[] }) => { ); -}; +} diff --git a/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx b/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx index f7671928f2..03f012b78f 100644 --- a/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx +++ b/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx @@ -82,10 +82,10 @@ export function createSubRoutesFromChildren( * * ``` */ -export const TabbedLayout = ({ children }: PropsWithChildren<{}>) => { - const routes = createSubRoutesFromChildren(children); +export function TabbedLayout(props: PropsWithChildren<{}>) { + const routes = createSubRoutesFromChildren(props.children); return ; -}; +} TabbedLayout.Route = Route; diff --git a/packages/core-components/src/components/Table/SubvalueCell.tsx b/packages/core-components/src/components/Table/SubvalueCell.tsx index 1dab2e1f49..fd7d3fd9fa 100644 --- a/packages/core-components/src/components/Table/SubvalueCell.tsx +++ b/packages/core-components/src/components/Table/SubvalueCell.tsx @@ -33,7 +33,8 @@ type SubvalueCellProps = { subvalue: React.ReactNode; }; -export const SubvalueCell = ({ value, subvalue }: SubvalueCellProps) => { +export function SubvalueCell(props: SubvalueCellProps) { + const { value, subvalue } = props; const classes = useSubvalueCellStyles(); return ( @@ -42,4 +43,4 @@ export const SubvalueCell = ({ value, subvalue }: SubvalueCellProps) => {
{subvalue}
); -}; +} diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 22d8bff014..edc5fd33ce 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -263,21 +263,21 @@ export function TableToolbar(toolbarProps: { ); } -export function Table({ - columns, - options, - title, - subtitle, - filters, - initialState, - emptyContent, - onStateChange, - ...props -}: TableProps) { +export function Table(props: TableProps) { + const { + data, + columns, + options, + title, + subtitle, + filters, + initialState, + emptyContent, + onStateChange, + ...restProps + } = props; const tableClasses = useTableStyles(); - const { data, ...propsWithoutData } = props; - const theme = useTheme(); const calculatedInitialState = { ...defaultInitialState, ...initialState }; @@ -495,7 +495,7 @@ export function Table({ } data={typeof data === 'function' ? data : tableData} style={{ width: '100%' }} - {...propsWithoutData} + {...restProps} /> ); diff --git a/packages/core-components/src/components/Tabs/Tabs.tsx b/packages/core-components/src/components/Tabs/Tabs.tsx index ec9fecaca4..844f54a543 100644 --- a/packages/core-components/src/components/Tabs/Tabs.tsx +++ b/packages/core-components/src/components/Tabs/Tabs.tsx @@ -58,7 +58,8 @@ const useStyles = makeStyles(theme => ({ }, })); -export const Tabs = ({ tabs }: TabsProps) => { +export function Tabs(props: TabsProps) { + const { tabs } = props; const classes = useStyles(); const [value, setValue] = useState([0, 0]); // [selectedChunkedNavIndex, selectedIndex] const [navIndex, setNavIndex] = useState(0); @@ -160,4 +161,4 @@ export const Tabs = ({ tabs }: TabsProps) => { )} ); -}; +} diff --git a/packages/core-components/src/components/TrendLine/TrendLine.tsx b/packages/core-components/src/components/TrendLine/TrendLine.tsx index 0cbc2ca9d7..cb44d2b36b 100644 --- a/packages/core-components/src/components/TrendLine/TrendLine.tsx +++ b/packages/core-components/src/components/TrendLine/TrendLine.tsx @@ -32,10 +32,10 @@ function color(data: number[], theme: BackstageTheme): string | undefined { return theme.palette.status.error; } -export const TrendLine = ( +export function TrendLine( props: SparklinesProps & Pick & { title?: string }, -) => { +) { const theme = useTheme(); if (!props.data) return null; @@ -45,4 +45,4 @@ export const TrendLine = ( ); -}; +} diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx index e6188172b7..536ab066b3 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx @@ -137,13 +137,14 @@ const capitalize = (s: string) => { * @param {Object} [children] Objects to provide context, such as a stack trace or detailed error reporting. * Will be available inside an unfolded accordion. */ -export const WarningPanel = ({ - severity = 'warning', - title, - message, - children, - defaultExpanded, -}: WarningProps) => { +export function WarningPanel(props: WarningProps) { + const { + severity = 'warning', + title, + message, + children, + defaultExpanded, + } = props; const classes = useStyles({ severity }); // If no severity or title provided, the heading will read simply "Warning" @@ -184,4 +185,4 @@ export const WarningPanel = ({ )} ); -}; +} diff --git a/packages/core-components/src/icons/icons.tsx b/packages/core-components/src/icons/icons.tsx index 258bc9f8e1..7a9f7f9832 100644 --- a/packages/core-components/src/icons/icons.tsx +++ b/packages/core-components/src/icons/icons.tsx @@ -15,27 +15,48 @@ */ import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; -import React from 'react'; +import React, { ComponentProps } from 'react'; import { useApp, IconComponent } from '@backstage/core-plugin-api'; -const overridableSystemIcon = (key: string): IconComponent => { - const Component: IconComponent = props => { - const app = useApp(); - const Icon = app.getSystemIcon(key); - return Icon ? : ; - }; - return Component; -}; +type IconComponentProps = ComponentProps; + +function useSystemIcon(key: string, props: IconComponentProps) { + const app = useApp(); + const Icon = app.getSystemIcon(key); + return Icon ? : ; +} // Should match the list of overridable system icon keys in @backstage/core-app-api -export const BrokenImageIcon = overridableSystemIcon('brokenImage'); -export const CatalogIcon = overridableSystemIcon('catalog'); -export const ChatIcon = overridableSystemIcon('chat'); -export const DashboardIcon = overridableSystemIcon('dashboard'); -export const DocsIcon = overridableSystemIcon('docs'); -export const EmailIcon = overridableSystemIcon('email'); -export const GitHubIcon = overridableSystemIcon('github'); -export const GroupIcon = overridableSystemIcon('group'); -export const HelpIcon = overridableSystemIcon('help'); -export const UserIcon = overridableSystemIcon('user'); -export const WarningIcon = overridableSystemIcon('warning'); +export function BrokenImageIcon(props: IconComponentProps) { + return useSystemIcon('brokenImage', props); +} +export function CatalogIcon(props: IconComponentProps) { + return useSystemIcon('catalog', props); +} +export function ChatIcon(props: IconComponentProps) { + return useSystemIcon('chat', props); +} +export function DashboardIcon(props: IconComponentProps) { + return useSystemIcon('dashboard', props); +} +export function DocsIcon(props: IconComponentProps) { + return useSystemIcon('docs', props); +} +export function EmailIcon(props: IconComponentProps) { + return useSystemIcon('email', props); +} +export function GitHubIcon(props: IconComponentProps) { + return useSystemIcon('github', props); +} +export function GroupIcon(props: IconComponentProps) { + return useSystemIcon('group', props); +} +export function HelpIcon(props: IconComponentProps) { + return useSystemIcon('help', props); +} +export function UserIcon(props: IconComponentProps) { + return useSystemIcon('user', props); +} +export function WarningIcon(props: IconComponentProps) { + return useSystemIcon('warning', props); +} diff --git a/packages/core-components/src/layout/BottomLink/BottomLink.tsx b/packages/core-components/src/layout/BottomLink/BottomLink.tsx index 74912acf4c..6d6811cc09 100644 --- a/packages/core-components/src/layout/BottomLink/BottomLink.tsx +++ b/packages/core-components/src/layout/BottomLink/BottomLink.tsx @@ -41,7 +41,8 @@ export type BottomLinkProps = { onClick?: (event: React.MouseEvent) => void; }; -export const BottomLink = ({ link, title, onClick }: BottomLinkProps) => { +export function BottomLink(props: BottomLinkProps) { + const { link, title, onClick } = props; const classes = useStyles(); return ( @@ -59,4 +60,4 @@ export const BottomLink = ({ link, title, onClick }: BottomLinkProps) => { ); -}; +} diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx index 418217f3b5..28c86d32f0 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx @@ -41,7 +41,8 @@ const StyledBox = withStyles({ }, })(Box); -export const Breadcrumbs = ({ children, ...props }: Props) => { +export function Breadcrumbs(props: Props) { + const { children, ...restProps } = props; const [anchorEl, setAnchorEl] = React.useState( null, ); @@ -65,7 +66,7 @@ export const Breadcrumbs = ({ children, ...props }: Props) => { const open = Boolean(anchorEl); return ( - + {childrenArray.length > 1 && {firstPage}} {childrenArray.length > 2 && {secondPage}} {hasHiddenBreadcrumbs && ( @@ -96,4 +97,4 @@ export const Breadcrumbs = ({ children, ...props }: Props) => { ); -}; +} diff --git a/packages/core-components/src/layout/Content/Content.tsx b/packages/core-components/src/layout/Content/Content.tsx index 323565159a..a3771dff3f 100644 --- a/packages/core-components/src/layout/Content/Content.tsx +++ b/packages/core-components/src/layout/Content/Content.tsx @@ -47,17 +47,12 @@ type Props = { className?: string; }; -export const Content = ({ - className, - stretch, - noPadding, - children, - ...props -}: PropsWithChildren) => { +export function Content(props: PropsWithChildren) { + const { className, stretch, noPadding, children, ...restProps } = props; const classes = useStyles(); return (
); -}; +} diff --git a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx index 2259731148..76fec703a9 100644 --- a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx +++ b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx @@ -82,13 +82,14 @@ type ContentHeaderProps = { textAlign?: 'left' | 'right' | 'center'; }; -export const ContentHeader = ({ - description, - title, - titleComponent: TitleComponent = undefined, - children, - textAlign = 'left', -}: PropsWithChildren) => { +export function ContentHeader(props: PropsWithChildren) { + const { + description, + title, + titleComponent: TitleComponent = undefined, + children, + textAlign = 'left', + } = props; const classes = useStyles({ textAlign })(); const renderedTitle = TitleComponent ? ( @@ -112,4 +113,4 @@ export const ContentHeader = ({ ); -}; +} diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index cca88147c4..f4b2960f96 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -47,11 +47,8 @@ const useStyles = makeStyles(theme => ({ }, })); -export const ErrorPage = ({ - status, - statusMessage, - additionalInfo, -}: IErrorPageProps) => { +export function ErrorPage(props: IErrorPageProps) { + const { status, statusMessage, additionalInfo } = props; const classes = useStyles(); const navigate = useNavigate(); const support = useSupportConfig(); @@ -82,4 +79,4 @@ export const ErrorPage = ({ ); -}; +} diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 3b2771e299..3f83bcc843 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -22,13 +22,10 @@ import { Helmet } from 'react-helmet'; import { Link } from '../../components/Link'; import { Breadcrumbs } from '../Breadcrumbs'; -const minHeaderHeight = 118; - const useStyles = makeStyles(theme => ({ header: { gridArea: 'pageHeader', padding: theme.spacing(3), - minHeight: minHeaderHeight, width: '100%', boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', position: 'relative', @@ -44,7 +41,6 @@ const useStyles = makeStyles(theme => ({ leftItemsBox: { maxWidth: '100%', flexGrow: 1, - marginBottom: theme.spacing(1), }, rightItemsBox: { width: 'auto', @@ -53,11 +49,13 @@ const useStyles = makeStyles(theme => ({ color: theme.palette.bursts.fontColor, wordBreak: 'break-all', fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', - marginBottom: theme.spacing(1), + marginBottom: 0, }, subtitle: { color: 'rgba(255, 255, 255, 0.8)', lineHeight: '1.0em', + display: 'inline-block', // prevents margin collapse of adjacent siblings + marginTop: theme.spacing(1), }, type: { textTransform: 'uppercase', @@ -174,16 +172,17 @@ const SubtitleFragment = ({ classes, subtitle }: SubtitleFragmentProps) => { ); }; -export const Header = ({ - children, - pageTitleOverride, - style, - subtitle, - title, - tooltip, - type, - typeLink, -}: PropsWithChildren) => { +export function Header(props: PropsWithChildren) { + const { + children, + pageTitleOverride, + style, + subtitle, + title, + tooltip, + type, + typeLink, + } = props; const classes = useStyles(); const configApi = useApi(configApiRef); const appTitle = configApi.getOptionalString('app.title') || 'Backstage'; @@ -216,4 +215,4 @@ export const Header = ({ ); -}; +} diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx index 06a9b2b701..5243a65498 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx @@ -66,7 +66,8 @@ export type HeaderActionMenuProps = { actionItems: ActionItemProps[]; }; -export const HeaderActionMenu = ({ actionItems }: HeaderActionMenuProps) => { +export function HeaderActionMenu(props: HeaderActionMenuProps) { + const { actionItems } = props; const [open, setOpen] = React.useState(false); const anchorElRef = React.useRef(null); @@ -103,4 +104,4 @@ export const HeaderActionMenu = ({ actionItems }: HeaderActionMenuProps) => { ); -}; +} diff --git a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx index 8557ceddf7..88f6e26bf5 100644 --- a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx +++ b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx @@ -51,7 +51,8 @@ type HeaderLabelProps = { url?: string; }; -export const HeaderLabel = ({ label, value, url }: HeaderLabelProps) => { +export function HeaderLabel(props: HeaderLabelProps) { + const { label, value, url } = props; const classes = useStyles(); const content = ( { ); -}; +} diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx index 20742114c9..0fe3cd967e 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx @@ -55,11 +55,8 @@ type HeaderTabsProps = { onChange?: (index: number) => void; selectedIndex?: number; }; -export const HeaderTabs = ({ - tabs, - onChange, - selectedIndex, -}: HeaderTabsProps) => { +export function HeaderTabs(props: HeaderTabsProps) { + const { tabs, onChange, selectedIndex } = props; const [selectedTab, setSelectedTab] = useState(selectedIndex ?? 0); const styles = useStyles(); @@ -101,4 +98,4 @@ export const HeaderTabs = ({ ); -}; +} diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx index 04073cca48..a78f4af8ca 100644 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx +++ b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx @@ -67,7 +67,7 @@ function getTimes(configApi: ConfigApi) { return clocks; } -export const HomepageTimer = () => { +export function HomepageTimer(_props: {}) { const configApi = useApi(configApiRef); const defaultTimes: TimeObj[] = []; @@ -99,4 +99,4 @@ export const HomepageTimer = () => { ); } return null; -}; +} diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.tsx index 650bf78bbd..36205b0dc0 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.tsx @@ -128,26 +128,27 @@ type Props = { titleTypographyProps?: object; }; -export const InfoCard = ({ - title, - subheader, - divider = true, - deepLink, - slackChannel, - errorBoundaryProps, - variant, - children, - headerStyle, - headerProps, - action, - actionsClassName, - actions, - cardClassName, - actionsTopRight, - className, - noPadding, - titleTypographyProps, -}: Props): JSX.Element => { +export function InfoCard(props: Props): JSX.Element { + const { + title, + subheader, + divider = true, + deepLink, + slackChannel, + errorBoundaryProps, + variant, + children, + headerStyle, + headerProps, + action, + actionsClassName, + actions, + cardClassName, + actionsTopRight, + className, + noPadding, + titleTypographyProps, + } = props; const classes = useStyles(); /** * If variant is specified, we build up styles for that particular variant for both @@ -214,4 +215,4 @@ export const InfoCard = ({ ); -}; +} diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.tsx index b4273ee837..81a88d2c09 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.tsx @@ -62,16 +62,9 @@ type ItemCardProps = { * @deprecated Use plain MUI and composable helpers instead. * @see https://material-ui.com/components/cards/ */ -export const ItemCard = ({ - description, - tags, - title, - type, - subtitle, - label, - onClick, - href, -}: ItemCardProps) => { +export function ItemCard(props: ItemCardProps) { + const { description, tags, title, type, subtitle, label, onClick, href } = + props; return ( @@ -101,4 +94,4 @@ export const ItemCard = ({ ); -}; +} diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx index a5c57ba246..137c48d272 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx @@ -51,7 +51,7 @@ export type ItemCardGridProps = Partial> & { * This can be useful for e.g. overriding gridTemplateColumns to adapt the * minimum size of the cells to fit the content better. */ -export const ItemCardGrid = (props: ItemCardGridProps) => { +export function ItemCardGrid(props: ItemCardGridProps) { const { children, ...otherProps } = props; const classes = useStyles(otherProps); return ( @@ -59,4 +59,4 @@ export const ItemCardGrid = (props: ItemCardGridProps) => { {children} ); -}; +} diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx index 58c7c1501d..15a1a7d78b 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx @@ -72,7 +72,7 @@ export type ItemCardHeaderProps = Partial> & { * * */ -export const ItemCardHeader = (props: ItemCardHeaderProps) => { +export function ItemCardHeader(props: ItemCardHeaderProps) { const { title, subtitle, children } = props; const classes = useStyles(props); return ( @@ -90,4 +90,4 @@ export const ItemCardHeader = (props: ItemCardHeaderProps) => { {children} ); -}; +} diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index f78dc038f2..b838693a3c 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -34,7 +34,8 @@ type Props = { themeId: string; }; -export const Page = ({ themeId, children }: PropsWithChildren) => { +export function Page(props: PropsWithChildren) { + const { themeId, children } = props; const classes = useStyles(); return ( ) => {
{children}
); -}; +} diff --git a/packages/core-components/src/layout/Page/PageWithHeader.tsx b/packages/core-components/src/layout/Page/PageWithHeader.tsx index 70a6c1f693..541b655dff 100644 --- a/packages/core-components/src/layout/Page/PageWithHeader.tsx +++ b/packages/core-components/src/layout/Page/PageWithHeader.tsx @@ -23,13 +23,12 @@ type PageWithHeaderProps = ComponentProps & { themeId: string; }; -export const PageWithHeader = ({ - themeId, - children, - ...props -}: PropsWithChildren) => ( - -
- {children} - -); +export function PageWithHeader(props: PropsWithChildren) { + const { themeId, children, ...restProps } = props; + return ( + +
+ {children} + + ); +} diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index f928c4b7c4..8855f01104 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -74,11 +74,12 @@ type Props = { closeDelayMs?: number; }; -export const Sidebar = ({ - openDelayMs = sidebarConfig.defaultOpenDelayMs, - closeDelayMs = sidebarConfig.defaultCloseDelayMs, - children, -}: PropsWithChildren) => { +export function Sidebar(props: PropsWithChildren) { + const { + openDelayMs = sidebarConfig.defaultOpenDelayMs, + closeDelayMs = sidebarConfig.defaultCloseDelayMs, + children, + } = props; const classes = useStyles(); const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('md'), @@ -149,4 +150,4 @@ export const Sidebar = ({ ); -}; +} diff --git a/packages/core-components/src/layout/Sidebar/Intro.tsx b/packages/core-components/src/layout/Sidebar/Intro.tsx index 14c5d9e25a..45098b49e1 100644 --- a/packages/core-components/src/layout/Sidebar/Intro.tsx +++ b/packages/core-components/src/layout/Sidebar/Intro.tsx @@ -74,7 +74,7 @@ type IntroCardProps = { onClose: () => void; }; -export const IntroCard = (props: IntroCardProps) => { +export function IntroCard(props: IntroCardProps) { const classes = useStyles(); const { text, onClose } = props; const handleClose = () => onClose(); @@ -97,7 +97,7 @@ export const IntroCard = (props: IntroCardProps) => { ); -}; +} type SidebarIntroLocalStorage = { starredItemsDismissed: boolean; @@ -127,7 +127,7 @@ Keep an eye out for the little star icon (⭐) next to the plugin name and give const recentlyViewedIntroText = 'And your recently viewed plugins will pop up here!'; -export const SidebarIntro = () => { +export function SidebarIntro(_props: {}) { const { isOpen } = useContext(SidebarContext); const defaultValue = { starredItemsDismissed: false, @@ -177,4 +177,4 @@ export const SidebarIntro = () => { )} ); -}; +} diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 1068a877bd..b3acc36f98 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -282,7 +282,7 @@ type SidebarSearchFieldProps = { to?: string; }; -export const SidebarSearchField = (props: SidebarSearchFieldProps) => { +export function SidebarSearchField(props: SidebarSearchFieldProps) { const [input, setInput] = useState(''); const classes = useStyles(); @@ -334,7 +334,7 @@ export const SidebarSearchField = (props: SidebarSearchFieldProps) => { ); -}; +} export const SidebarSpace = styled('div')({ flex: 1, diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 0ace7468ef..47c5878cde 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -49,7 +49,7 @@ export const SidebarPinStateContext = createContext( }, ); -export const SidebarPage = (props: PropsWithChildren<{}>) => { +export function SidebarPage(props: PropsWithChildren<{}>) { const [isPinned, setIsPinned] = useState(() => LocalStorage.getSidebarPinState(), ); @@ -71,4 +71,4 @@ export const SidebarPage = (props: PropsWithChildren<{}>) => {
{props.children}
); -}; +} diff --git a/packages/core-components/src/layout/SignInPage/SignInPage.tsx b/packages/core-components/src/layout/SignInPage/SignInPage.tsx index dcaad83c72..e0d4f6ed0d 100644 --- a/packages/core-components/src/layout/SignInPage/SignInPage.tsx +++ b/packages/core-components/src/layout/SignInPage/SignInPage.tsx @@ -192,10 +192,10 @@ export const SingleSignInPage = ({ ); }; -export const SignInPage = (props: Props) => { +export function SignInPage(props: Props) { if ('provider' in props) { return ; } return ; -}; +} diff --git a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx index c9f2a477ef..f350b24f1d 100644 --- a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx +++ b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx @@ -62,15 +62,16 @@ type Props = { deepLink?: BottomLinkProps; }; -const TabbedCard = ({ - slackChannel, - errorBoundaryProps, - children, - title, - deepLink, - value, - onChange, -}: PropsWithChildren) => { +export function TabbedCard(props: PropsWithChildren) { + const { + slackChannel, + errorBoundaryProps, + children, + title, + deepLink, + value, + onChange, + } = props; const tabsClasses = useTabsStyles(); const [selectedIndex, selectIndex] = useState(0); @@ -111,7 +112,7 @@ const TabbedCard = ({ ); -}; +} const useCardTabStyles = makeStyles(theme => ({ root: { @@ -135,10 +136,9 @@ type CardTabProps = TabProps & { children: ReactNode; }; -const CardTab = ({ children, ...props }: PropsWithChildren) => { +export function CardTab(props: PropsWithChildren) { + const { children, ...restProps } = props; const classes = useCardTabStyles(); - return ; -}; - -export { TabbedCard, CardTab }; + return ; +} diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 2a76ab243f..4cd9b4ade7 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/core-plugin-api +## 0.1.7 + +### Patch Changes + +- 3d238b028: Migrated component data attachment method to have better compatibility with component proxies such as `react-hot-loader`. +- Updated dependencies + - @backstage/config@0.1.9 + ## 0.1.6 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index d482795c90..414ca2afe1 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "0.1.6", + "version": "0.1.7", "private": false, "publishConfig": { "access": "public", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.6", + "@backstage/config": "^0.1.9", "@backstage/theme": "^0.2.9", "@material-ui/core": "^4.12.2", "@types/react": "*", @@ -41,8 +41,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.8", - "@backstage/core-app-api": "^0.1.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", "@backstage/test-utils": "^0.1.17", "@backstage/test-utils-core": "^0.1.2", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/core-plugin-api/src/extensions/componentData.test.tsx b/packages/core-plugin-api/src/extensions/componentData.test.tsx index 49a1d59cc4..6f2694a512 100644 --- a/packages/core-plugin-api/src/extensions/componentData.test.tsx +++ b/packages/core-plugin-api/src/extensions/componentData.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { attachComponentData, getComponentData } from './componentData'; -describe('elementData', () => { +describe('componentData', () => { it('should attach a single piece of data', () => { const data = { foo: 'bar' }; const Component = () => null; @@ -59,4 +59,51 @@ describe('elementData', () => { 'Attempted to attach duplicate data "my-data" to component "MyComponent"', ); }); + + describe('works across versions', () => { + it('should should be able to get data from newer versions', () => { + const data = { foo: 'bar' }; + const Component = () => null; + attachComponentData(Component, 'my-data', data); + + const element = ; + expect((element as any).type.__backstage_data.map.get('my-data')).toBe( + data, + ); + }); + + it('should should be able to attach data for newer versions', () => { + const data = { foo: 'bar' }; + const Component = () => null; + (Component as any).__backstage_data = { + map: new Map([['my-data', data]]), + }; + + const element = ; + expect(getComponentData(element, 'my-data')).toBe(data); + }); + + it('should be able to get data from older versions', () => { + const data = { foo: 'bar' }; + const Component = () => null; + attachComponentData(Component, 'my-data', data); + + const element = ; + const container = (global as any)[ + '__@backstage/component-data-store__' + ].get(element.type); + expect(container.map.get('my-data')).toBe(data); + }); + + it('should should be able to attach data for older versions', () => { + const data = { foo: 'bar' }; + const Component = () => null; + (global as any)['__@backstage/component-data-store__'].set(Component, { + map: new Map([['my-data', data]]), + }); + + const element = ; + expect(getComponentData(element, 'my-data')).toBe(data); + }); + }); }); diff --git a/packages/core-plugin-api/src/extensions/componentData.tsx b/packages/core-plugin-api/src/extensions/componentData.tsx index 85527950ab..d28d17d8ca 100644 --- a/packages/core-plugin-api/src/extensions/componentData.tsx +++ b/packages/core-plugin-api/src/extensions/componentData.tsx @@ -21,24 +21,42 @@ type DataContainer = { map: Map; }; -type MaybeComponentNode = ReactNode & { - type?: ComponentType; -}; - -// The store is bridged across versions using the global object +// This method of storing the component data was deprecated in September 2021, it +// will be removed in the future for the reasons described below. const globalStore = getOrCreateGlobalSingleton( 'component-data-store', () => new WeakMap, DataContainer>(), ); +// This key is used to attach component data to the component type (function or class) +// itself. This method is used because it has better compatibility component wrappers +// like react-hot-loader, as opposed to the WeakMap method or using a symbol. +const componentDataKey = '__backstage_data'; + +type ComponentWithData = ComponentType & { + [componentDataKey]?: DataContainer; +}; + +type MaybeComponentNode = ReactNode & { + type?: ComponentWithData; +}; + export function attachComponentData

( component: ComponentType

, type: string, data: unknown, ) { - let container = globalStore.get(component); + const dataComponent = component as ComponentWithData; + + let container = dataComponent[componentDataKey] ?? globalStore.get(component); if (!container) { container = { map: new Map() }; + Object.defineProperty(dataComponent, componentDataKey, { + enumerable: false, + configurable: true, + writable: false, + value: container, + }); globalStore.set(component, container); } @@ -65,7 +83,7 @@ export function getComponentData( return undefined; } - const container = globalStore.get(component); + const container = component[componentDataKey] ?? globalStore.get(component); if (!container) { return undefined; } diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 670613dac9..f8d93846ba 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/create-app +## 0.3.40 + +### Patch Changes + +- a5013957e: Updated the search configuration class to use the static `fromConfig`-based constructor for the `DefaultCatalogCollator`. + + To apply this change to an existing app, replace the following line in `search.ts`: + + ```diff + -collator: new DefaultCatalogCollator({ discovery }) + +collator: DefaultCatalogCollator.fromConfig(config, { discovery }) + ``` + + The `config` parameter was not needed before, so make sure you also add that in the signature of `createPlugin` + in `search.ts`: + + ```diff + export default async function createPlugin({ + logger, + discovery, + + config, + }: PluginEnvironment) { + ``` + +- Updated dependencies + - @backstage/cli-common@0.1.3 + ## 0.3.39 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index f0939c213e..aac845093f 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.3.39", + "version": "0.3.40", "private": false, "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "start": "nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.2", + "@backstage/cli-common": "^0.1.3", "chalk": "^4.0.0", "commander": "^6.1.0", "fs-extra": "9.1.0", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index f0c4c062b4..b15b3437f2 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/dev-utils +## 0.2.9 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/catalog-model@0.9.2 + - @backstage/core-app-api@0.1.12 + - @backstage/core-plugin-api@0.1.7 + ## 0.2.8 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 9317c0a107..5d6bbba3d7 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.2.8", + "version": "0.2.9", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-app-api": "^0.1.11", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", - "@backstage/catalog-model": "^0.9.1", + "@backstage/core-app-api": "^0.1.12", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", + "@backstage/catalog-model": "^0.9.2", "@backstage/integration-react": "^0.1.8", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/test-utils": "^0.1.17", @@ -50,7 +50,7 @@ "react-router-dom": "6.0.0-beta.0" }, "devDependencies": { - "@backstage/cli": "^0.7.10", + "@backstage/cli": "^0.7.11", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/docgen/.eslintrc.js b/packages/docgen/.eslintrc.js deleted file mode 100644 index 503c048748..0000000000 --- a/packages/docgen/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: [require.resolve('@backstage/cli/config/eslint.backend')], - rules: { - 'no-console': 0, - }, -}; diff --git a/packages/docgen/README.md b/packages/docgen/README.md deleted file mode 100644 index be1d7cd521..0000000000 --- a/packages/docgen/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# DocGen - API Reference Documentation Generator - -The docgen package provides a CLI to generate markdown documentation for all exported ApiRefs in `@backstage/core`. The documentation is generated based on exported `ApiRef` instances and their type parameters. - -The CLI supports generating both TechDocs and GitHub Markdown, where the TechDocs one provides some better linking and syntax highlighting. - -## Usage - -To generate markdown documentation in the top-level `docs/` directory, run the following: - -```bash -yarn docgen -``` - -## TODO - -This package was lifted out from the Spotify internal Backstage project and could use some further work: - -- Use a higher-level TypeScript compiler library, e.g. `ts-morph`. -- Support for generating docs for any package or multiple packages. -- Better handling of self-referencing types in APIs, e.g. ConfigApi. diff --git a/packages/docgen/bin/backstage-docgen b/packages/docgen/bin/backstage-docgen deleted file mode 100755 index 71674eedd4..0000000000 --- a/packages/docgen/bin/backstage-docgen +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env node -/* - * 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. - */ - -/* eslint-disable no-restricted-syntax */ -const path = require('path'); - -// Figure out whether we're running inside the backstage repo or as an installed dependency -const isLocal = require('fs').existsSync(path.resolve(__dirname, '../src')); - -if (!isLocal || process.env.BACKSTAGE_E2E_CLI_TEST) { - require('..'); -} else { - require('ts-node').register({ - transpileOnly: true, - project: path.resolve(__dirname, '../../../tsconfig.json'), - compilerOptions: { - module: 'CommonJS', - }, - }); - - require('../src'); -} diff --git a/packages/docgen/package.json b/packages/docgen/package.json deleted file mode 100644 index ad47991159..0000000000 --- a/packages/docgen/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "docgen", - "description": "Tool for generating API Documentation for itself", - "version": "0.1.1", - "private": true, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/docgen" - }, - "keywords": [ - "backstage" - ], - "license": "Apache-2.0", - "main": "src/index.ts", - "scripts": { - "build": "backstage-cli build --outputs cjs", - "lint": "backstage-cli lint", - "test": "backstage-cli test", - "docgen": "backstage-docgen generate --output ../../docs/reference/utility-apis --format github && prettier --write ../../docs/reference/utility-apis", - "clean": "backstage-cli clean", - "start": "nodemon --" - }, - "bin": { - "backstage-docgen": "bin/backstage-docgen" - }, - "dependencies": { - "chalk": "^4.0.0", - "commander": "^6.1.0", - "fs-extra": "9.1.0", - "github-slugger": "^1.3.0", - "ts-node": "^10.0.0", - "typescript": "^4.0.3" - }, - "devDependencies": { - "@types/fs-extra": "^9.0.1", - "@types/github-slugger": "^1.3.0", - "@types/node": "^14.14.32", - "nodemon": "^2.0.2" - }, - "files": [ - "bin", - "dist" - ], - "nodemonConfig": { - "watch": "./src", - "exec": "bin/backstage-docgen", - "ext": "ts" - } -} diff --git a/packages/docgen/src/docgen/ApiDocGenerator.test.ts b/packages/docgen/src/docgen/ApiDocGenerator.test.ts deleted file mode 100644 index ea053b1ef4..0000000000 --- a/packages/docgen/src/docgen/ApiDocGenerator.test.ts +++ /dev/null @@ -1,266 +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 ts from 'typescript'; -import TypeLocator from './TypeLocator'; -import { createMemProgram } from './testUtils'; -import ApiDocGenerator from './ApiDocGenerator'; - -describe('ApiDocGenerator', () => { - it('should generate empty API doc', () => { - const program = createMemProgram( - ` - import MyApi from './type'; - - type MyApiType = {}; - - export const myApi = new MyApi({ - id: 'my-id', - description: 'my-description', - }); - `, - { - '/mem/type.ts': `export default class MyApi { - constructor(private readonly info: { id: string, description: string }) {} - }`, - }, - ); - - const typeLocator = TypeLocator.fromProgram(program, '/'); - - const { apiInstances } = typeLocator.findExportedInstances({ - apiInstances: typeLocator.getExportedType('/mem/type.ts'), - }); - - expect(apiInstances.length).toBe(1); - const [apiInstance] = apiInstances; - - const docGenerator = ApiDocGenerator.fromProgram(program, '/'); - const doc = docGenerator.toDoc(apiInstance); - - expect(doc.id).toBe('my-id'); - expect(doc.description).toBe('my-description'); - expect(doc.name).toBe('myApi'); - expect(doc.file).toBe('mem/index.ts'); - expect(doc.lineInFile).toBe(6); - expect(doc.interfaceInfos).toEqual([ - { - dependentTypes: [], - docs: [], - file: 'mem/index.ts', - lineInFile: 4, - members: [], - name: 'MyApiType', - }, - ]); - }); - - it('should generate API docs', () => { - const program = createMemProgram( - ` - import MyApi from './type'; - - /** MySubSubType Docs */ - type MySubSubType = { n: number; }; - - /** MySubType Docs */ - type MySubType = { - /** Field a docs */ - a: boolean; - // Field b docs - b: MySubSubType; - } - - /** MySecondSubType Docs */ - - /** With multiple comments */ - export type MySecondSubType = { s: string }; - - // MyThirdSubType Docs that shouldn't show up - type MyThirdSubType = { b: boolean }; - - /** MyApiType Docs */ - type MyApiType = { - /** Docs for x */ - x: string; - // Line comments shouldn't show up - y: MySubType; - /** Multiple */ - /** JsDoc */ - /** Comments */ - z(a: Promise): Array; - }; - - /** Should not show up */ - export const myApi = new MyApi({ - id: 'my-id', - description: 'my-description', - }); - `, - { - '/mem/type.ts': `export default class MyApi { - constructor(private readonly info: { id: string, description: string }) {} - }`, - }, - ); - - const source = program.getSourceFile('/mem/index.ts'); - - // Figure out type IDs so we can make sure they match later - const checker = program.getTypeChecker(); - const symbols = checker.getSymbolsInScope( - source!.getChildren().slice(-1)[0], - ts.SymbolFlags.TypeAlias, - ); - const Ids = [ - 'MySubType', - 'MySubSubType', - 'MySecondSubType', - 'MyThirdSubType', - ].reduce((ids, name) => { - const symbol = symbols.find(s => s.escapedName === name)!; - const type = checker.getTypeAtLocation(symbol.declarations[0]); - ids[name] = (type.aliasSymbol as any).id; - return ids; - }, {} as { [key in string]: number }); - - const typeLocator = TypeLocator.fromProgram(program, '/mem'); - - const { apiInstances } = typeLocator.findExportedInstances({ - apiInstances: typeLocator.getExportedType('/mem/type.ts'), - }); - - expect(apiInstances.length).toBe(1); - const [apiInstance] = apiInstances; - - const docGenerator = ApiDocGenerator.fromProgram(program, '/mem'); - const doc = docGenerator.toDoc(apiInstance); - - expect(doc.id).toBe('my-id'); - expect(doc.description).toBe('my-description'); - expect(doc.name).toBe('myApi'); - expect(doc.file).toBe('index.ts'); - expect(doc.interfaceInfos.length).toBe(1); - expect(doc.interfaceInfos[0].docs).toEqual(['MyApiType Docs']); - expect(doc.interfaceInfos[0].file).toBe('index.ts'); - expect(doc.interfaceInfos[0].lineInFile).toBe(24); - expect(doc.interfaceInfos[0].name).toBe('MyApiType'); - expect(doc.interfaceInfos[0].members).toEqual([ - { - type: 'prop', - name: 'x', - path: 'MyApiType.x', - text: 'x: string', - docs: ['Docs for x'], - links: [], - }, - { - type: 'prop', - name: 'y', - path: 'MyApiType.y', - text: 'y: MySubType', - docs: [], - links: [ - { - id: Ids.MySubType, - name: 'MySubType', - path: 'index.ts/MySubType', - location: [3, 12], - }, - ], - }, - { - type: 'method', - name: 'z', - path: 'MyApiType.z', - text: 'z(a: Promise): Array', - docs: ['Multiple', 'JsDoc', 'Comments'], - links: [ - { - id: Ids.MySecondSubType, - name: 'MySecondSubType', - path: 'index.ts/MySecondSubType', - location: [27, 42], - }, - { - id: Ids.MyThirdSubType, - name: 'MyThirdSubType', - path: 'index.ts/MyThirdSubType', - location: [56, 70], - }, - ], - }, - ]); - expect(doc.interfaceInfos[0].dependentTypes).toEqual([ - { - id: Ids.MySubType, - name: 'MySubType', - path: 'index.ts/MySubType', - file: 'index.ts', - lineInFile: 8, - text: `type MySubType = { - /** Field a docs */ - a: boolean; - // Field b docs - b: MySubSubType; - }`, - docs: ['MySubType Docs'], - links: [ - { - id: Ids.MySubSubType, - name: 'MySubSubType', - path: 'index.ts/MySubSubType', - location: [102, 114], - }, - ], - children: [], - }, - { - id: Ids.MySubSubType, - name: 'MySubSubType', - path: 'index.ts/MySubSubType', - file: 'index.ts', - lineInFile: 5, - text: 'type MySubSubType = { n: number; }', - docs: ['MySubSubType Docs'], - links: [], - children: [], - }, - { - id: Ids.MySecondSubType, - name: 'MySecondSubType', - path: 'index.ts/MySecondSubType', - file: 'index.ts', - lineInFile: 18, - text: 'export type MySecondSubType = { s: string }', - docs: ['MySecondSubType Docs', 'With multiple comments'], - links: [], - children: [], - }, - { - id: Ids.MyThirdSubType, - name: 'MyThirdSubType', - path: 'index.ts/MyThirdSubType', - file: 'index.ts', - lineInFile: 21, - text: 'type MyThirdSubType = { b: boolean }', - docs: [], - links: [], - children: [], - }, - ]); - }); -}); diff --git a/packages/docgen/src/docgen/ApiDocGenerator.ts b/packages/docgen/src/docgen/ApiDocGenerator.ts deleted file mode 100644 index 32216745a2..0000000000 --- a/packages/docgen/src/docgen/ApiDocGenerator.ts +++ /dev/null @@ -1,343 +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 ts from 'typescript'; -import { - relative as relativePath, - sep as pathSep, - posix as posixPath, -} from 'path'; -import { - ExportedInstance, - ApiDoc, - InterfaceInfo, - FieldInfo, - TypeInfo, - TypeLink, -} from './types'; - -// Always use unix path separators for the relative file -// paths, since we'll be using those for HTTP URLs. -function relativeLink(basePath: string, filePath: string) { - return relativePath(basePath, filePath).split(pathSep).join(posixPath.sep); -} - -/** - * The ApiDocGenerator uses the typescript compiler API to build the data structure that - * describes a Backstage API and all of it's related types. - * - * It receives an exported instance that of the form - * `export name = createApiRef({id: ..., description: ...})`. - * It will then traverse all fields and methods on the interface type, and also - * types and declaration of all types that are used by those, both directly and indirectly. - * While traversing, it collects information such as names, location in source, docs, etc. - */ -export default class ApiDocGenerator { - static fromProgram(program: ts.Program, sourcePath: string) { - return new ApiDocGenerator(program.getTypeChecker(), sourcePath); - } - - constructor( - private readonly checker: ts.TypeChecker, - private readonly basePath: string, - ) {} - - /** - * Generate documentation information for a given exported symbol. - */ - toDoc(apiInstance: ExportedInstance): ApiDoc { - const { name, source, args, typeArgs } = apiInstance; - - const [info] = args; - if (!ts.isObjectLiteralExpression(info)) { - throw new Error('api info is not an object literal'); - } - - const id = this.getObjectPropertyLiteral(info, 'id'); - const description = this.getObjectPropertyLiteral(info, 'description'); - const file = relativeLink(this.basePath, source.fileName); - const { line } = source.getLineAndCharacterOfPosition( - apiInstance.node.getStart(), - ); - - const rootTypeNode = typeArgs[0]; - const typeNodes = ts.isIntersectionTypeNode(rootTypeNode) - ? rootTypeNode.types.slice() - : [rootTypeNode]; - const interfaceInfos = typeNodes.map(typeNode => - this.getInterfaceInfo(typeNode), - ); - - return { - id, - name, - file, - lineInFile: line + 1, - description, - interfaceInfos, - }; - } - - /** - * Grab jsDoc and regular comments for a node - */ - private getNodeDocs(node: ts.Node): string[] { - const docNodes = ((node && (node as any).jsDoc) || []) as ts.JSDoc[]; - const docs = docNodes.map(docNode => docNode.comment || '').filter(Boolean); - return docs; - } - - /** - * Collect information about a top-level type. - */ - private getInterfaceInfo(typeNode: ts.TypeNode): InterfaceInfo { - if (ts.isTypeQueryNode(typeNode)) { - throw new Error( - 'APIs must have a proper type parameter, TypeQueries are now allowed', - ); - } - if (!ts.isTypeReferenceNode(typeNode)) { - throw new Error('Interface is not a type node'); - } - - const type = this.checker.getTypeFromTypeNode(typeNode); - const interfaceMembers = type.symbol.members as Map; - if (!interfaceMembers) { - throw new Error('Interface does not have any members'); - } - const name = (type.aliasSymbol || type.symbol).name; - const [declaration] = (type.aliasSymbol || type.symbol).declarations; - const sourceFile = declaration.getSourceFile(); - const file = relativeLink(this.basePath, sourceFile.fileName); - const { line } = sourceFile.getLineAndCharacterOfPosition( - declaration.getStart(), - ); - const docs = this.getNodeDocs(declaration); - - const membersAndTypes = Array.from(interfaceMembers.values()).flatMap( - fieldSymbol => this.getMemberInfo(name, fieldSymbol), - ); - - const members = membersAndTypes.map(t => t.member); - const dependentTypes = this.flattenTypes( - membersAndTypes.flatMap(t => t.dependentTypes), - ); - - return { name, docs, file, lineInFile: line + 1, members, dependentTypes }; - } - - /** - * Grab primitive values from an object literal expression. - */ - private getObjectPropertyLiteral( - objectLiteral: ts.ObjectLiteralExpression, - propertyName: string, - ): string { - const matchingProp = objectLiteral.properties.filter( - prop => - ts.isPropertyAssignment(prop) && - ts.isIdentifier(prop.name) && - prop.name.text === propertyName, - )[0] as ts.PropertyAssignment; - - if (!matchingProp) { - throw new Error(`no identifier found for property ${propertyName}`); - } - - const { initializer } = matchingProp; - if (!ts.isStringLiteral(initializer)) { - throw new Error(`no string literal for ${propertyName}`); - } - - return initializer.text; - } - - /** - * Get field definitions for a given symbol. - */ - private getMemberInfo( - parentName: string, - symbol: ts.Symbol, - ): { member: FieldInfo; dependentTypes: TypeInfo[] } { - const declaration = symbol.valueDeclaration; - - const { links, infos: dependentTypes } = - this.findAllTypeReferences(declaration); - - let type: FieldInfo['type'] = 'prop'; - if ( - ts.isPropertySignature(declaration) && - declaration.type && - ts.isFunctionTypeNode(declaration.type) - ) { - type = 'method'; - } else if (ts.isMethodSignature(declaration)) { - type = 'method'; - } - - return { - member: { - type, - name: symbol.name, - path: `${parentName}.${symbol.name}`, - text: declaration.getText().replace(/;$/, ''), - docs: this.getNodeDocs(declaration), - links: this.recalculateLinkOffsets(declaration, links), - }, - dependentTypes, - }; - } - - /** - * Recursively search for references to types that we care about and want to include in the documentation. - */ - private findAllTypeReferences = ( - node: ts.Node | undefined, - visited: Set = new Set(), - ): { links: TypeLink[]; infos: TypeInfo[] } => { - if (!node || visited.has(node)) { - return { links: [], infos: [] }; - } - // This makes sure we don't end up in a loop. - // It doesn't exclude repeated types, e.g. Array>, since we're exiting on duplicate nodes, not types. - visited.add(node); - - const children = node - .getChildren() - .flatMap(child => this.findAllTypeReferences(child, visited)); - - if (!ts.isTypeReferenceNode(node)) { - return { - links: children.flatMap(child => child.links), - infos: children.flatMap(child => child.infos), - }; - } - - const type = this.checker.getTypeFromTypeNode(node); - - const info = this.validateTypeDeclaration(type); - if (!info) { - return { - links: children.flatMap(child => child.links), - infos: children.flatMap(child => child.infos), - }; - } - const { symbol, declaration } = info; - - const typeRefs = this.findAllTypeReferences(declaration, visited); - - const sourceFile = declaration.getSourceFile(); - const { line } = sourceFile.getLineAndCharacterOfPosition( - declaration.getStart(), - ); - const file = relativeLink(this.basePath, sourceFile.fileName); - const typeInfo = { - id: (symbol as any).id, - name: symbol.name, - text: declaration.getText().replace(/;$/, ''), - docs: this.getNodeDocs(declaration), - path: `${file}/${symbol.name}`, - file, - lineInFile: line + 1, // TS line is 0-based - links: this.recalculateLinkOffsets(declaration, typeRefs.links), - children: typeRefs.infos, - }; - - const link = { - id: typeInfo.id, - path: typeInfo.path, - name: typeInfo.name, - location: [node.typeName.getStart(), node.typeName.getEnd()] as const, - }; - - return { - links: [link, ...children.flatMap(child => child.links)], - infos: [typeInfo, ...children.flatMap(child => child.infos)], - }; - }; - - /** - * Check if a given type declaration is one that we care about. - */ - private validateTypeDeclaration( - type: ts.Type | undefined, - ): undefined | { symbol: ts.Symbol; declaration: ts.Declaration } { - if (!type) { - return undefined; - } - const symbol = type.aliasSymbol || type.symbol; - if (!symbol) { - return undefined; - } - - const [declaration] = symbol.declarations; - - // Don't generate standalone infos for type paramters - if (ts.isTypeParameterDeclaration(declaration)) { - return undefined; - } - - if (declaration.getSourceFile().fileName.includes('node_modules')) { - return undefined; - } - - return { symbol, declaration }; - } - - /** - * Flatten a tree of TypeInfos with children into a flat - * list of TypeInfos with duplicates removed. - */ - private flattenTypes(descs: TypeInfo[]): TypeInfo[] { - function getChildren(parent: TypeInfo): TypeInfo[] { - return [ - { - ...parent, - children: [], - }, - ...parent.children.flatMap(getChildren), - ]; - } - - const flatDescs = descs.flatMap(getChildren); - const seenTypes = new Set(); - return flatDescs.filter(desc => { - if (seenTypes.has(desc.id)) { - return false; - } - seenTypes.add(desc.id); - return true; - }); - } - - /** - * Calculate link positions within a block of code, with 0 being - * the first character in the block. - */ - private recalculateLinkOffsets( - parent: ts.Node, - links: TypeLink[], - ): TypeLink[] { - const parentStart = parent.getStart(); - return links.map(link => { - const [start, end] = link.location; - return { - ...link, - location: [start - parentStart, end - parentStart], - }; - }); - } -} diff --git a/packages/docgen/src/docgen/ApiDocsPrinter.ts b/packages/docgen/src/docgen/ApiDocsPrinter.ts deleted file mode 100644 index 1fd62c29cd..0000000000 --- a/packages/docgen/src/docgen/ApiDocsPrinter.ts +++ /dev/null @@ -1,163 +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 sortSelector from './sortSelector'; -import { ApiDoc, InterfaceInfo, MarkdownPrinter } from './types'; - -/** - * The ApiDocPrinter takes a ApiDoc data structure, typically generated by an ApiDocGenerator, - * and prints it out as a markdown doc with custom code highlighting and links. - */ -export default class ApiDocPrinter { - printerFactory: () => MarkdownPrinter; - - constructor(printerFactory: () => MarkdownPrinter) { - this.printerFactory = printerFactory; - } - - /** - * Print an index file with all ApiRefs and what types they implement. - */ - printApiIndex(apiDocs: ApiDoc[]): Buffer { - const printer = this.printerFactory(); - - printer.header(1, 'Backstage Core Utility APIs'); - - printer.paragraph( - 'The following is a list of all Utility APIs defined by `@backstage/core`.', - 'They are available to use by plugins and components, and can be accessed ', - 'using the `useApi` hook, also provided by `@backstage/core`.', - 'For more information, see https://github.com/backstage/backstage/blob/master/docs/api/utility-apis.md.', - ); - - for (const api of apiDocs) { - printer.header(3, `${this.apiDisplayName(api)}`, api.id); - - printer.paragraph(api.description); - - const typeLinks = api.interfaceInfos.map( - i => `[${i.name}](${printer.pageLink(i.name)})`, - ); - printer.paragraph( - `Implemented type${typeLinks.length > 1 ? 's' : ''}: ${typeLinks.join( - ', ', - )}`, - ); - - printer.paragraph(`ApiRef: ${printer.srcLink(api, api.name)}`); - } - - return printer.toBuffer(); - } - - /** - * Print documentation page for a type implemented by an ApiRef and - */ - printInterface(apiType: InterfaceInfo, apiDocs: ApiDoc[]): Buffer { - const printer = this.printerFactory(); - - printer.header(1, apiType.name); - - printer.paragraph( - `The ${apiType.name} type is defined at ${printer.srcLink(apiType)}.`, - ); - - const apiLinks = apiDocs - .filter(ad => ad.interfaceInfos.some(i => i.name === apiType.name)) - .map(ad => { - const link = - printer.indexLink() + - printer.headerLink(this.apiDisplayName(ad), ad.id); - return `[${ad.name}](${link})`; - }); - - if (apiLinks.length === 1) { - printer.paragraph( - `The following Utility API implements this type: ${apiLinks}`, - ); - } else { - printer.paragraph(`The following Utility APIs implement this type:`); - for (const link of apiLinks) { - printer.text(` - ${link}`); - } - } - - printer.header(2, 'Members'); - - this.addInterfaceMembers(printer, apiType); - - if (apiType.dependentTypes.length) { - printer.header(2, 'Supporting types'); - printer.paragraph( - 'These types are part of the API declaration, but may not be unique to this API.', - ); - - this.addInterfaceTypes(printer, apiType); - } - - return printer.toBuffer(); - } - - private addInterfaceMembers( - printer: MarkdownPrinter, - apiType: InterfaceInfo, - ) { - for (const member of apiType.members) { - printer.header( - 3, - `${member.name}${member.type === 'method' ? '()' : ''}`, - member.path, - ); - - for (const doc of member.docs) { - printer.text(doc); - } - - printer.code(member); - } - } - - private addInterfaceTypes(printer: MarkdownPrinter, apiType: InterfaceInfo) { - for (const type of apiType.dependentTypes - .slice() - .sort(sortSelector(x => x.name))) { - printer.header(3, `${type.name}`, type.path); - - for (const doc of type.docs) { - printer.text(doc); - } - printer.code(type); - - printer.paragraph(`Defined at ${printer.srcLink(type)}.`); - - const usageLinks = [...apiType.members, ...apiType.dependentTypes] - .filter(member => { - return member.links.some(link => link.id === type.id); - }) - .map( - ({ name, path }) => `[${name}](${printer.headerLink(name, path)})`, - ); - - if (usageLinks.length) { - printer.paragraph(`Referenced by: ${usageLinks.join(', ')}.`); - } - } - } - - private apiDisplayName(api: ApiDoc): string { - return api.name.replace(/ApiRef$/, ''); - } -} diff --git a/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts b/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts deleted file mode 100644 index b36895597e..0000000000 --- a/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts +++ /dev/null @@ -1,145 +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 GithubSlugger from 'github-slugger'; -import sortSelector from './sortSelector'; -import { MarkdownPrinter, TypeLink } from './types'; -import { execSync } from 'child_process'; - -// TODO(Rugvip): provide through options? -const GH_BASE_URL = 'https://github.com/backstage/backstage'; - -const COMMIT_SHA = - process.env.COMMIT_SHA || - execSync('git rev-parse HEAD').toString('utf8').trim(); - -/** - * The GithubMarkdownPrinter is a MarkdownPrinter for printing GitHub Flavored Markdown documents. - */ -export default class GithubMarkdownPrinter implements MarkdownPrinter { - private str: string = ''; - - private line(line: string = '') { - this.str += `${line}\n`; - } - - text(text: string) { - this.line(text); - this.line(); - } - - header(level: number, text: string) { - this.line(`${'#'.repeat(level)} ${text}`); - this.line(); - } - - headerLink(heading: string): string { - const slug = GithubSlugger.slug(heading); - return `#${slug}`; - } - - indexLink() { - return './README.md'; - } - - pageLink(name: string) { - return `./${name}.md`; - } - - srcLink( - { file, lineInFile }: { file: string; lineInFile: number }, - text?: string, - ): string { - const linkText = text ?? `${file}:${lineInFile}`; - const href = `${GH_BASE_URL}/blob/${COMMIT_SHA}/${file}#L${lineInFile}`; - return `[${linkText}](${href})`; - } - - paragraph(...text: string[]) { - this.line( - text - .join('\n') - .trim() - .split('\n') - .map(line => line.trim()) - .join('\n'), - ); - this.line(); - } - - code({ text, links = [] }: { text: string; links?: TypeLink[] }) { - this.line('

');
-    this.line(this.formatWithLinks({ text, links }));
-    this.line('
'); - this.line(); - } - - private escapeText: (text: string) => string = (() => { - const escapes: { [char in string]: string } = { - '&': '&', - '<': '<', - '>': '>', - }; - - return (text: string) => text.replace(/[&<>]/g, char => escapes[char]); - })(); - - private formatWithLinks({ - text, - links = [], - }: { - text: string; - links?: TypeLink[]; - }): string { - const sortedLinks = links.slice().sort(sortSelector(x => x.location[0])); - - sortedLinks.reduce((lastEnd, link) => { - if (link.location[0] <= lastEnd) { - throw new Error( - `overlapping link detected for ${link.path}, ${link.location}`, - ); - } - return link.location[1]; - }, -1); - - const parts: Array<{ text: string; link?: boolean }> = []; - - const endLocation = sortedLinks.reduce((prev, link) => { - const [start, end] = link.location; - parts.push( - { text: text.slice(prev, start) }, - { text: text.slice(start, end), link: true }, - ); - return end; - }, 0); - - parts.push({ text: text.slice(endLocation) }); - - return parts - .map(part => { - if (part.link) { - const link = this.headerLink(part.text); - return `${this.escapeText(part.text)}`; - } - return this.escapeText(part.text); - }) - .join(''); - } - - toBuffer(): Buffer { - return Buffer.from(this.str, 'utf8'); - } -} diff --git a/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts b/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts deleted file mode 100644 index c9e3010dda..0000000000 --- a/packages/docgen/src/docgen/TechdocsMarkdownPrinter.ts +++ /dev/null @@ -1,161 +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 sortSelector from './sortSelector'; -import { Highlighter, MarkdownPrinter, TypeLink } from './types'; -import { execSync } from 'child_process'; - -// TODO(Rugvip): provide through options? -const GH_BASE_URL = 'https://github.com/backstage/backstage'; - -const COMMIT_SHA = - process.env.COMMIT_SHA || execSync('git rev-parse HEAD').toString('utf8'); - -/** - * The TechdocsMarkdownPrinter is a MarkdownPrinter for building TechDocs markdown documents. - */ -export default class TechdocsMarkdownPrinter implements MarkdownPrinter { - private str: string = ''; - private readonly highlighter: Highlighter; - - constructor(highlighter: Highlighter) { - this.highlighter = highlighter; - - // Remove line numbers from codeblocks - this.style('.linenodiv{ display: none }'); - } - - private line(line: string = '') { - this.str += `${line}\n`; - } - - text(text: string) { - this.line(text); - this.line(); - } - - style(style: string) { - this.line(''); - } - - header(level: number, text: string, id?: string) { - this.line(`${'#'.repeat(level)} ${text}${id ? ` {#${id}}` : ''}`); - this.line(); - } - - headerLink(heading: string, id?: string): string { - return `#${id ?? heading}`; - } - - indexLink() { - return '../'; - } - - pageLink(name: string) { - return `./${name}/`; - } - - srcLink( - { file, lineInFile }: { file: string; lineInFile: number }, - text?: string, - ): string { - const linkText = text ?? `${file}:${lineInFile}`; - const href = `${GH_BASE_URL}/blob/${COMMIT_SHA}/${file}#L${lineInFile}`; - return `[${linkText}](${href})`; - } - - paragraph(...text: string[]) { - this.line( - text - .join('\n') - .trim() - .split('\n') - .map(line => line.trim()) - .join('\n'), - ); - this.line(); - } - - code({ text, links = [] }: { text: string; links?: TypeLink[] }) { - this.line('
'); - this.line( - '
', - ); - this.line('
');
-    this.line(this.formatWithLinks({ text, links }));
-    this.line('
'); - this.line('
'); - this.line('
'); - } - - private escapeText: (text: string) => string = (() => { - const escapes: { [char in string]: string } = { - '&': '&', - '<': '<', - '>': '>', - }; - - return (text: string) => text.replace(/[&<>]/g, char => escapes[char]); - })(); - - private formatWithLinks({ - text, - links = [], - }: { - text: string; - links?: TypeLink[]; - }): string { - const sortedLinks = links.slice().sort(sortSelector(x => x.location[0])); - - sortedLinks.reduce((lastEnd, link) => { - if (link.location[0] <= lastEnd) { - throw new Error( - `overlapping link detected for ${link.path}, ${link.location}`, - ); - } - return link.location[1]; - }, -1); - - const parts: Array<{ text: string; path?: string }> = []; - - const endLocation = sortedLinks.reduce((prev, link) => { - const [start, end] = link.location; - parts.push( - { text: text.slice(prev, start) }, - { text: text.slice(start, end), path: link.path }, - ); - return end; - }, 0); - - parts.push({ text: text.slice(endLocation) }); - - return parts - .map(part => { - if (part.path) { - const link = this.headerLink(part.text, part.path); - return `${this.escapeText(part.text)}`; - } - return this.highlighter.highlight(this.escapeText(part.text)); - }) - .join(''); - } - - toBuffer(): Buffer { - return Buffer.from(this.str, 'utf8'); - } -} diff --git a/packages/docgen/src/docgen/TypeLocator.test.ts b/packages/docgen/src/docgen/TypeLocator.test.ts deleted file mode 100644 index 8988b25814..0000000000 --- a/packages/docgen/src/docgen/TypeLocator.test.ts +++ /dev/null @@ -1,68 +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 ts from 'typescript'; -import TypeLocator from './TypeLocator'; -import { createMemProgram } from './testUtils'; - -describe('TypeLocator', () => { - it('should find a default export', () => { - const program = createMemProgram('export default class MyApi {}'); - - const typeLocator = TypeLocator.fromProgram(program, '/mem'); - - const apiType = typeLocator.getExportedType('/mem/index.ts'); - expect(apiType.symbol.name).toBe('default'); - const [declaration] = apiType.symbol.declarations; - expect(declaration.kind).toBe(ts.SyntaxKind.ClassDeclaration); - expect((declaration as ts.ClassDeclaration).name).toBeDefined(); - expect((declaration as ts.ClassDeclaration).name!.text).toBe('MyApi'); - }, 10000); - - it('should find api instance export', () => { - const program = createMemProgram( - ` - import MyApi from './type'; - - type MyApiType = {}; - - export const myApi = new MyApi(); - `, - { - '/mem/type.ts': 'export default class MyApi {}', - }, - ); - - const typeLocator = TypeLocator.fromProgram(program, '/mem'); - - const { apiInstances } = typeLocator.findExportedInstances({ - apiInstances: typeLocator.getExportedType('/mem/type.ts'), - }); - - expect(apiInstances.length).toBe(1); - const [apiInstance] = apiInstances; - expect(apiInstance.name).toBe('myApi'); - expect(apiInstance.source.fileName).toBe('/mem/index.ts'); - expect(apiInstance.args).toEqual([]); - - expect(apiInstance.typeArgs.length).toBe(1); - const [typeArg] = apiInstance.typeArgs; - expect(typeArg.kind).toBe(ts.SyntaxKind.TypeReference); - expect((typeArg as ts.TypeReferenceNode).typeName.getText()).toBe( - 'MyApiType', - ); - }); -}); diff --git a/packages/docgen/src/docgen/TypeLocator.ts b/packages/docgen/src/docgen/TypeLocator.ts deleted file mode 100644 index 30d240da3d..0000000000 --- a/packages/docgen/src/docgen/TypeLocator.ts +++ /dev/null @@ -1,143 +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 ts from 'typescript'; -import { ExportedInstance } from './types'; - -/** - * The TypeLocator is used to extract typescrint Type structures from a compiled program, - * as well as finding locations where types are used in according to a matching pattern. - * - * This is used to e.g. find exported APIs that we should generate documentation for. - */ -export default class TypeLocator { - static fromProgram(program: ts.Program, sourcePath: string) { - return new TypeLocator(program.getTypeChecker(), program, sourcePath); - } - - constructor( - private readonly checker: ts.TypeChecker, - private readonly program: ts.Program, - private readonly sourcePath: string, - ) {} - - /** - * Get the type of a symbol by export path and name - */ - getExportedType(path: string, exportedName: string = 'default'): ts.Type { - const source = this.program.getSourceFile(path); - if (!source) { - throw new Error(`Source not found for path '${path}'`); - } - const exported = this.checker.getExportsOfModule( - this.checker.getSymbolAtLocation(source)!, - ); - const [symbol] = exported.filter(e => e.name === exportedName); - if (!symbol) { - throw new Error(`No export '${exportedName}' found in ${path}`); - } - const type = this.checker.getTypeOfSymbolAtLocation(symbol, source); - return type; - } - - /** - * Find exported instances and return values from calls using the types - * provided in the lookup table. - */ - findExportedInstances( - typeLookupTable: { [key in T]: ts.Type }, - ): { [key in T]: ExportedInstance[] } { - const docMap = new Map(); - for (const type of Object.values(typeLookupTable)) { - docMap.set(type, []); - } - - this.program.getSourceFiles().forEach(source => { - const inRoot = source.fileName.startsWith(this.sourcePath); - if (!inRoot) { - return; - } - ts.forEachChild(source, node => { - const decl = this.getExportedConstructorDeclaration(node); - if (!decl || !docMap.has(decl.constructorType)) { - return; - } - - docMap.get(decl.constructorType)!.push({ node, source, ...decl }); - }); - }); - - const result: { [key in T]?: ExportedInstance[] } = {}; - - for (const key in typeLookupTable) { - if (typeLookupTable.hasOwnProperty(key)) { - const type = typeLookupTable[key]; - result[key] = docMap.get(type)!; - } - } - - return result as { [key in T]: ExportedInstance[] }; - } - - private getExportedConstructorDeclaration(node: ts.Node): - | { - constructorType: ts.Type; - args: ts.Expression[]; - typeArgs: ts.TypeNode[]; - name: string; - } - | undefined { - if (!ts.isVariableStatement(node)) { - return undefined; - } - if ( - !node.modifiers || - !node.modifiers.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword) - ) { - return undefined; - } - const { declarations } = node.declarationList; - if (declarations.length !== 1) { - return undefined; - } - - const [declaration] = declarations; - const { initializer, name } = declaration; - - if (!initializer || !name) { - return undefined; - } - if (!ts.isCallOrNewExpression(initializer)) { - return undefined; - } - if (!ts.isIdentifier(name)) { - return undefined; - } - - const constructorType = this.checker.getTypeAtLocation( - initializer.expression, - ); - const args = Array.from(initializer.arguments ?? []); - const typeNode = declaration.type; - const typeArgs = Array.from( - (typeNode && ts.isTypeReferenceNode(typeNode) - ? typeNode.typeArguments - : initializer.typeArguments) ?? [], - ); - - return { constructorType, args, typeArgs, name: name.text }; - } -} diff --git a/packages/docgen/src/docgen/TypescriptHighlighter.ts b/packages/docgen/src/docgen/TypescriptHighlighter.ts deleted file mode 100644 index ab70ea8110..0000000000 --- a/packages/docgen/src/docgen/TypescriptHighlighter.ts +++ /dev/null @@ -1,88 +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 { Highlighter } from './types'; - -// Simple syntax highlighter that mimics hilite -export default class TypescriptHighlighter implements Highlighter { - private static readonly basicTypes = [ - 'boolean', - 'number', - 'string', - 'Array', - 'object', - 'Record', - 'Set', - 'Map', - 'true', - 'false', - 'null', - 'undefined', - 'void', - 'Promise', - 'any', - '[0-9\\.]+', - ]; - - // List of highlightings to apply, each with a match regex and the style that should be applied - private static readonly highlighters = [ - [/(\/\*\*?(?:.|\n)+?\*\/)/g, 'color: #60a0b0; font-style: italic'], // block comment - [/(\/\/.*)/gm, 'color: #60a0b0; font-style: italic'], // line comment - [/('[^']*?'|"[^"]*?")/g, 'color: #4070a0'], // string literals - [ - new RegExp( - `\\b(${TypescriptHighlighter.basicTypes.join('|')})\\b(?!:|,)`, - 'g', - ), - 'color: #902000', - ], // basic types - [ - /^((?:export\s)?(?:type\s)?(?:interface\s)?)/g, - 'color: #007020; font-weight: bold', - ], // keywords - ] as readonly [RegExp, string][]; - - highlight(fullText: string): string { - // Each part is either plain text that can be highlighted or text that is already highlighted - type HighlightPart = { text: string; highlighted?: boolean }; - - const painter = (regex: RegExp, style: string) => (part: HighlightPart) => { - if (part.highlighted) { - return [part]; - } - // Apply highlighting to all matches of the regex by splitting into parts - return part.text.split(regex).map((text, index) => { - // Odd parts are the ones that matched the regex and should be highlighted. - if (index % 2 === 1) { - return { - text: `${text}`, - highlighted: true, - }; - } - return { text }; - }); - }; - - // Order here is important, e.g. comments must be first to avoid string literals inside comments being highlighted - return TypescriptHighlighter.highlighters - .reduce( - (parts, highlighter) => parts.flatMap(painter(...highlighter)), - [{ text: fullText }], - ) - .map(({ text }) => text) - .join(''); - } -} diff --git a/packages/docgen/src/docgen/sortSelector.test.ts b/packages/docgen/src/docgen/sortSelector.test.ts deleted file mode 100644 index 277b06faa5..0000000000 --- a/packages/docgen/src/docgen/sortSelector.test.ts +++ /dev/null @@ -1,49 +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 sortSelector from './sortSelector'; - -describe('sortSelector', () => { - it('should stable sort', () => { - const arr = [ - [3, 1], - [1, 2], - [1, 1], - [1, 3], - [2, 1], - ]; - - const sortedByFirst = arr.slice().sort(sortSelector(([first]) => first)); - expect(sortedByFirst).toEqual([ - [1, 2], - [1, 1], - [1, 3], - [2, 1], - [3, 1], - ]); - - const sortedBySecond = arr - .slice() - .sort(sortSelector(([, second]) => second)); - expect(sortedBySecond).toEqual([ - [3, 1], - [1, 1], - [2, 1], - [1, 2], - [1, 3], - ]); - }); -}); diff --git a/packages/docgen/src/docgen/sortSelector.ts b/packages/docgen/src/docgen/sortSelector.ts deleted file mode 100644 index e8cb577ba5..0000000000 --- a/packages/docgen/src/docgen/sortSelector.ts +++ /dev/null @@ -1,33 +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. - */ - -/** - * The sortSelector is a utility that makes a sort function by selecting the value to sort on with a lambda. - */ -export default function sortSelector( - selector: (x: T) => any, -): (a: T, b: T) => -1 | 1 | 0 { - return (a: T, b: T) => { - const aV = selector(a); - const bV = selector(b); - if (aV < bV) { - return -1; - } else if (aV > bV) { - return 1; - } - return 0; - }; -} diff --git a/packages/docgen/src/docgen/testUtils.ts b/packages/docgen/src/docgen/testUtils.ts deleted file mode 100644 index c449dd7461..0000000000 --- a/packages/docgen/src/docgen/testUtils.ts +++ /dev/null @@ -1,67 +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 ts from 'typescript'; - -export function createMemProgram( - indexSource: string, - otherSourceFiles: { [fileName in string]: string } = {}, -): ts.Program { - const rootDir = '/mem'; - - const options = { noEmit: true }; - const baseHost = ts.createCompilerHost(options); - const files: { [fileName in string]: string } = { - [`${rootDir}/index.ts`]: indexSource, - ...otherSourceFiles, - }; - - // Custom compiler hosts that reads from a map of in-memory files, but - // falls back to reading from disc for ts libs etc. - const compilerHost: ts.CompilerHost = { - ...baseHost, - readFile(fileName): string | undefined { - if (fileName in files) { - return files[fileName]; - } - return baseHost.readFile(fileName); - }, - getCurrentDirectory(): string { - return rootDir; - }, - directoryExists(dir) { - if (dir === rootDir) { - return true; - } - if (baseHost.directoryExists) { - return baseHost.directoryExists(dir); - } - return false; - }, - fileExists(fileName: string): boolean { - return !!files[fileName] || baseHost.fileExists(fileName); - }, - getSourceFile(fileName, ...rest): ts.SourceFile | undefined { - const file = files[fileName]; - if (file) { - return ts.createSourceFile(fileName, file, ts.ScriptTarget.ES2017); - } - return baseHost.getSourceFile(fileName, ...rest); - }, - }; - - return ts.createProgram(['/mem/index.ts'], options, compilerHost); -} diff --git a/packages/docgen/src/docgen/types.ts b/packages/docgen/src/docgen/types.ts deleted file mode 100644 index 60f3d35007..0000000000 --- a/packages/docgen/src/docgen/types.ts +++ /dev/null @@ -1,119 +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 ts from 'typescript'; - -/** - * A TypeLink is a link to a different type. - */ -export type TypeLink = { - // The ID of the linked type - id: number; - // The path to the type from the project root - path: string; - // The name of the type, to display - name: string; - // The location of the type name as it appears in it's parent text. - location: readonly [number, number]; -}; - -/** - * TypeInfo describes a TypeScript Type. - */ -export type TypeInfo = { - id: number; - name: string; - path: string; - file: string; - lineInFile: number; - text: string; - docs: string[]; - links: TypeLink[]; - children: TypeInfo[]; -}; - -/** - * FieldInfo describes a property or method in a documented API. - */ -export type FieldInfo = { - type: 'prop' | 'method'; - name: string; - path: string; - text: string; - docs: string[]; - links: TypeLink[]; -}; - -/** - * InterfaceInfo describes the type of a documented API. - */ -export type InterfaceInfo = { - name: string; - docs: string[]; - file: string; - lineInFile: number; - members: Array; - dependentTypes: TypeInfo[]; -}; - -/** - * ApiDoc describes a documented API. - */ -export type ApiDoc = { - id: string; - name: string; - description: string; - file: string; - lineInFile: number; - interfaceInfos: InterfaceInfo[]; -}; - -/** - * ExportedInstance describes an expression matching `export {name} = new {Contructor}<{typeArgs}...>({args}...)` - */ -export type ExportedInstance = { - node: ts.Node; - name: string; - source: ts.SourceFile; - args: Array; - typeArgs: Array; -}; - -export type Highlighter = { - highlight(test: string): string; -}; - -/** - * Markdown printer is an abstraction for printing markdown documents of different flavors. - */ -export type MarkdownPrinter = { - text(text: string): void; - header(level: number, text: string, id?: string): void; - paragraph(...text: string[]): void; - code(options: { text: string; links?: TypeLink[] }): void; - - headerLink(header: string, id?: string): string; - /** Link from pages to index */ - indexLink(): string; - /** Link from index to pages */ - pageLink(name: string): string; - srcLink( - { file, lineInFile }: { file: string; lineInFile: number }, - text?: string, - ): string; - - toBuffer(): Buffer; -}; diff --git a/packages/docgen/src/generate.ts b/packages/docgen/src/generate.ts deleted file mode 100644 index bbc18e48ab..0000000000 --- a/packages/docgen/src/generate.ts +++ /dev/null @@ -1,127 +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 * as ts from 'typescript'; -import fs from 'fs-extra'; -import { resolve as resolvePath, join as joinPath } from 'path'; -import ApiDocGenerator from './docgen/ApiDocGenerator'; -import sortSelector from './docgen/sortSelector'; -import TypeLocator from './docgen/TypeLocator'; -import ApiDocPrinter from './docgen/ApiDocsPrinter'; -import TypescriptHighlighter from './docgen/TypescriptHighlighter'; -import GitHubMarkdownPrinter from './docgen/GitHubMarkdownPrinter'; -import TechdocsMarkdownPrinter from './docgen/TechdocsMarkdownPrinter'; - -const FORMATS = ['github', 'techdocs'] as const; - -export async function generate( - targetPath: string, - format: typeof FORMATS[number], -) { - if (!FORMATS.includes(format)) { - throw new TypeError( - `Invalid format, '${format}', must be one of ${FORMATS.join(', ')}`, - ); - } - - /* eslint-disable-next-line no-restricted-syntax */ - const rootDir = resolvePath(__dirname, '../../..'); - const srcDir = resolvePath(rootDir, 'packages', 'core-api', 'src'); - const targetDir = resolvePath(targetPath); - - const options = await fs.readJson(resolvePath('../cli/config/tsconfig.json')); - - delete options.moduleResolution; - options.noEmit = true; - - const program = ts.createProgram([resolvePath(srcDir, 'index.ts')], options); - - const typeLocator = TypeLocator.fromProgram(program, srcDir); - - const { apis } = typeLocator.findExportedInstances({ - apis: typeLocator.getExportedType( - resolvePath(srcDir, 'index.ts'), - 'createApiRef', - ), - }); - - const apiDocGenerator = ApiDocGenerator.fromProgram(program, rootDir); - const apiDocs = apis - .map(api => { - try { - return apiDocGenerator.toDoc(api); - } catch (error) { - throw new Error( - `Doc generation failed for API in ${api.source.fileName}, ${error.stack}`, - ); - } - }) - .sort(sortSelector(x => x.name)); - - const apiTypes = Object.values( - Object.fromEntries( - apiDocs.flatMap(d => d.interfaceInfos).map(i => [i.name, i]), - ), - ).sort(sortSelector(i => i.name)); - - if (format === 'techdocs') { - const docsDir = resolvePath(targetDir, 'docs'); - await fs.ensureDir(docsDir); - - const apiDocPrinter = new ApiDocPrinter( - () => new TechdocsMarkdownPrinter(new TypescriptHighlighter()), - ); - - await fs.writeFile( - joinPath(docsDir, 'README.md'), - apiDocPrinter.printApiIndex(apiDocs), - ); - - for (const apiType of Object.values(apiTypes)) { - const data = apiDocPrinter.printInterface(apiType, apiDocs); - - await fs.writeFile(joinPath(docsDir, `${apiType.name}.md`), data); - } - - await fs.writeFile( - resolvePath(targetDir, 'mkdocs.yml'), - [ - 'site_name: Backstage Core Utility API References', - 'nav:', - ` - API Index: 'README.md'`, - ...apiTypes.map(({ name }) => ` - ${name}: '${name}.md'`), - 'plugins:', - ' - techdocs-core', - ].join('\n'), - 'utf8', - ); - } else { - await fs.ensureDir(targetDir); - - const apiDocPrinter = new ApiDocPrinter(() => new GitHubMarkdownPrinter()); - - await fs.writeFile( - joinPath(targetDir, 'README.md'), - apiDocPrinter.printApiIndex(apiDocs), - ); - - for (const apiType of Object.values(apiTypes)) { - const data = apiDocPrinter.printInterface(apiType, apiDocs); - - await fs.writeFile(joinPath(targetDir, `${apiType.name}.md`), data); - } - } -} diff --git a/packages/docgen/src/index.ts b/packages/docgen/src/index.ts deleted file mode 100644 index 755bc9cb98..0000000000 --- a/packages/docgen/src/index.ts +++ /dev/null @@ -1,60 +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 program from 'commander'; -import { resolve as resolvePath } from 'path'; -import chalk from 'chalk'; -import fs from 'fs-extra'; -import { generate } from './generate'; - -const main = (argv: string[]) => { - /* eslint-disable-next-line no-restricted-syntax */ - const pkgJson = fs.readJsonSync(resolvePath(__dirname, '../package.json')); - program.name('docgen').version(pkgJson.version); - - program - .command('generate') - .description( - 'Generate documentation for the declarations in the core-api package', - ) - .option('--output ', 'Output directory [./dist]') - .option( - '--format ', - 'Output format, either techdocs or github [techdocs]', - ) - .action(async cmd => { - await generate(cmd.output ?? './dist', cmd.format ?? 'techdocs'); - }); - - program.on('command:*', () => { - console.log(); - console.log( - chalk.red(`Invalid command: ${chalk.cyan(program.args.join(' '))}`), - ); - console.log(chalk.red('See --help for a list of available commands.')); - console.log(); - process.exit(1); - }); - - program.parse(argv); -}; - -process.on('unhandledRejection', rejection => { - console.error(String(rejection)); - process.exit(1); -}); - -main(process.argv); diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md index e69de29bb2..7cadaa6e07 100644 --- a/packages/errors/CHANGELOG.md +++ b/packages/errors/CHANGELOG.md @@ -0,0 +1,9 @@ +# @backstage/errors + +## 0.1.2 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/config@0.1.9 diff --git a/packages/errors/package.json b/packages/errors/package.json index 8fa76d8bfc..dc31d8827e 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/errors", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.2", + "@backstage/config": "^0.1.9", "cross-fetch": "^3.0.6", "serialize-error": "^8.0.1" }, "devDependencies": { - "@backstage/cli": "^0.7.0", + "@backstage/cli": "^0.7.11", "@types/jest": "^26.0.7" }, "files": [ diff --git a/packages/integration/package.json b/packages/integration/package.json index 5ae86ea163..5358912852 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -31,7 +31,7 @@ "dependencies": { "@backstage/config": "^0.1.8", "cross-fetch": "^3.0.6", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "@octokit/rest": "^18.5.3", "@octokit/auth-app": "^3.4.0", "luxon": "^2.0.2" diff --git a/packages/integration/src/azure/AzureIntegration.test.ts b/packages/integration/src/azure/AzureIntegration.test.ts index 80cc68047f..c30283f432 100644 --- a/packages/integration/src/azure/AzureIntegration.test.ts +++ b/packages/integration/src/azure/AzureIntegration.test.ts @@ -60,22 +60,43 @@ describe('AzureIntegration', () => { expect( integration.resolveUrl({ url: '/a.yaml', - base: 'https://dev.azure.com/organization/project/_git/repository?path=%2Ffolder%2Fcatalog-info.yaml', + base: 'https://internal.com/organization/project/_git/repository?path=%2Ffolder%2Fcatalog-info.yaml', lineNumber: 14, }), ).toBe( - 'https://dev.azure.com/organization/project/_git/repository?path=%2Fa.yaml&line=14&lineEnd=15&lineStartColumn=1&lineEndColumn=1', + 'https://internal.com/organization/project/_git/repository?path=%2Fa.yaml&line=14&lineEnd=15&lineStartColumn=1&lineEndColumn=1', ); expect( integration.resolveUrl({ url: './a.yaml', - base: 'https://dev.azure.com/organization/project/_git/repository', + base: 'https://dev.azure.com/organization/_git/project', + }), + ).toBe('https://dev.azure.com/organization/_git/project?path=%2Fa.yaml'); + + expect( + integration.resolveUrl({ + url: 'https://dev.azure.com/organization/_git/project?path=%2Fa.yaml', + base: 'https://dev.azure.com/organization/_git/project', + }), + ).toBe('https://dev.azure.com/organization/_git/project?path=%2Fa.yaml'); + + expect( + integration.resolveUrl({ + url: 'https://dev.azure.com/other-organization/_git/other-project?path=%2Fa.yaml', + base: 'https://dev.azure.com/organization/_git/project', }), ).toBe( - 'https://dev.azure.com/organization/project/_git/repository?path=%2Fa.yaml', + 'https://dev.azure.com/other-organization/_git/other-project?path=%2Fa.yaml', ); + expect( + integration.resolveUrl({ + url: './a.yaml', + base: 'http://not-azure.com/organization/_git/project', + }), + ).toBe('http://not-azure.com/organization/_git/project?path=%2Fa.yaml'); + expect( integration.resolveUrl({ url: 'https://absolute.com/path', diff --git a/packages/integration/src/azure/AzureIntegration.ts b/packages/integration/src/azure/AzureIntegration.ts index c2a8dd8f5e..ab1b2489ae 100644 --- a/packages/integration/src/azure/AzureIntegration.ts +++ b/packages/integration/src/azure/AzureIntegration.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import parseGitUrl from 'git-url-parse'; import { basicIntegrations, isValidUrl } from '../helpers'; import { ScmIntegration, ScmIntegrationsFactory } from '../types'; +import { AzureUrl } from './AzureUrl'; import { AzureIntegrationConfig, readAzureIntegrationConfigs } from './config'; export class AzureIntegration implements ScmIntegration { @@ -61,29 +61,27 @@ export class AzureIntegration implements ScmIntegration { return url; } - const parsed = parseGitUrl(base); - const { organization, owner, name, filepath } = parsed; + try { + const azureUrl = AzureUrl.fromRepoUrl(base); + const newUrl = new URL(base); - // If not an actual file path within a repo, treat the URL as raw - if (!organization || !owner || !name) { + // We lean on the URL path resolution logic to resolve the path param + const mockBaseUrl = new URL(`https://a.com${azureUrl.getPath() ?? ''}`); + const updatedPath = new URL(url, mockBaseUrl).pathname; + newUrl.searchParams.set('path', updatedPath); + + if (options.lineNumber) { + newUrl.searchParams.set('line', String(options.lineNumber)); + newUrl.searchParams.set('lineEnd', String(options.lineNumber + 1)); + newUrl.searchParams.set('lineStartColumn', '1'); + newUrl.searchParams.set('lineEndColumn', '1'); + } + + return newUrl.toString(); + } catch { + // If not an actual file path within a repo, treat the URL as raw return new URL(url, base).toString(); } - - const path = filepath?.replace(/^\//, '') || ''; - const mockBaseUrl = new URL(`https://a.com/${path}`); - const updatedPath = new URL(url, mockBaseUrl).pathname; - - const newUrl = new URL(base); - newUrl.searchParams.set('path', updatedPath); - - if (options.lineNumber) { - newUrl.searchParams.set('line', String(options.lineNumber)); - newUrl.searchParams.set('lineEnd', String(options.lineNumber + 1)); - newUrl.searchParams.set('lineStartColumn', '1'); - newUrl.searchParams.set('lineEndColumn', '1'); - } - - return newUrl.toString(); } resolveEditUrl(url: string): string { diff --git a/packages/integration/src/azure/AzureUrl.test.ts b/packages/integration/src/azure/AzureUrl.test.ts new file mode 100644 index 0000000000..2f22dd13da --- /dev/null +++ b/packages/integration/src/azure/AzureUrl.test.ts @@ -0,0 +1,177 @@ +/* + * 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 { AzureUrl } from './AzureUrl'; + +describe('AzureUrl', () => { + it('should work with the short URL form', () => { + const url = AzureUrl.fromRepoUrl( + 'https://dev.azure.com/my-org/_git/my-project', + ); + + expect(url.getOwner()).toBe('my-org'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-project'); + expect(url.getRef()).toBeUndefined(); + expect(url.getPath()).toBeUndefined(); + + expect(url.toRepoUrl()).toBe( + 'https://dev.azure.com/my-org/_git/my-project', + ); + expect(() => url.toFileUrl()).toThrow( + 'Azure URL must point to a specific path to be able to download a file', + ); + expect(url.toArchiveUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/items?recursionLevel=full&download=true&api-version=6.0', + ); + expect(url.toCommitsUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/commits?api-version=6.0', + ); + }); + + it('should work with the short URL form with a path', () => { + const url = AzureUrl.fromRepoUrl( + 'https://dev.azure.com/my-org/_git/my-project?path=%2Ftest.yaml', + ); + + expect(url.getOwner()).toBe('my-org'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-project'); + expect(url.getRef()).toBeUndefined(); + expect(url.getPath()).toBe('/test.yaml'); + + expect(url.toRepoUrl()).toBe( + 'https://dev.azure.com/my-org/_git/my-project?path=%2Ftest.yaml', + ); + expect(url.toFileUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/items?api-version=6.0&path=%2Ftest.yaml', + ); + expect(url.toArchiveUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/items?recursionLevel=full&download=true&api-version=6.0&scopePath=%2Ftest.yaml', + ); + expect(url.toCommitsUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/commits?api-version=6.0', + ); + }); + + it('should work with the short URL form with a path and ref', () => { + const url = AzureUrl.fromRepoUrl( + 'https://dev.azure.com/my-org/_git/my-project?path=%2Ftest.yaml&version=GBtest-branch', + ); + + expect(url.getOwner()).toBe('my-org'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-project'); + expect(url.getRef()).toBe('test-branch'); + expect(url.getPath()).toBe('/test.yaml'); + + expect(url.toRepoUrl()).toBe( + 'https://dev.azure.com/my-org/_git/my-project?path=%2Ftest.yaml&version=GBtest-branch', + ); + expect(url.toFileUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/items?api-version=6.0&path=%2Ftest.yaml&version=test-branch', + ); + expect(url.toArchiveUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/items?recursionLevel=full&download=true&api-version=6.0&scopePath=%2Ftest.yaml&version=test-branch', + ); + expect(url.toCommitsUrl()).toBe( + 'https://dev.azure.com/my-org/my-project/_apis/git/repositories/my-project/commits?api-version=6.0&searchCriteria.itemVersion.version=test-branch', + ); + }); + + it('should work with the long URL', () => { + const url = AzureUrl.fromRepoUrl( + 'http://my-host/my-org/my-project/_git/my-repo', + ); + + expect(url.getOwner()).toBe('my-org'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-repo'); + expect(url.getRef()).toBeUndefined(); + expect(url.getPath()).toBeUndefined(); + + expect(url.toRepoUrl()).toBe( + 'http://my-host/my-org/my-project/_git/my-repo', + ); + expect(() => url.toFileUrl()).toThrow( + 'Azure URL must point to a specific path to be able to download a file', + ); + expect(url.toArchiveUrl()).toBe( + 'http://my-host/my-org/my-project/_apis/git/repositories/my-repo/items?recursionLevel=full&download=true&api-version=6.0', + ); + expect(url.toCommitsUrl()).toBe( + 'http://my-host/my-org/my-project/_apis/git/repositories/my-repo/commits?api-version=6.0', + ); + }); + + it('should work with the long URL form with a path and ref', () => { + const url = AzureUrl.fromRepoUrl( + 'http://my-host/my-org/my-project/_git/my-repo?path=%2Ffolder&version=GBtest-branch', + ); + + expect(url.getOwner()).toBe('my-org'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-repo'); + expect(url.getRef()).toBe('test-branch'); + expect(url.getPath()).toBe('/folder'); + + expect(url.toRepoUrl()).toBe( + 'http://my-host/my-org/my-project/_git/my-repo?path=%2Ffolder&version=GBtest-branch', + ); + expect(url.toFileUrl()).toBe( + 'http://my-host/my-org/my-project/_apis/git/repositories/my-repo/items?api-version=6.0&path=%2Ffolder&version=test-branch', + ); + expect(url.toArchiveUrl()).toBe( + 'http://my-host/my-org/my-project/_apis/git/repositories/my-repo/items?recursionLevel=full&download=true&api-version=6.0&scopePath=%2Ffolder&version=test-branch', + ); + expect(url.toCommitsUrl()).toBe( + 'http://my-host/my-org/my-project/_apis/git/repositories/my-repo/commits?api-version=6.0&searchCriteria.itemVersion.version=test-branch', + ); + }); + + it('should reject non-branch refs', () => { + expect(() => + AzureUrl.fromRepoUrl( + 'https://dev.azure.com/my-org/_git/my-project?version=GC6eead79870d998a3befd4bc7c72cc89e446f2970', + ), + ).toThrow('Azure URL version must point to a git branch'); + }); + + it('should reject non-repo URLs', () => { + expect(() => + AzureUrl.fromRepoUrl('https://dev.azure.com/my-org/_git'), + ).toThrow('Azure URL must point to a git repository'); + expect(() => + AzureUrl.fromRepoUrl('https://dev.azure.com/my-org/_git/'), + ).toThrow('Azure URL must point to a git repository'); + expect(() => + AzureUrl.fromRepoUrl('https://dev.azure.com/my-org/my-project/'), + ).toThrow('Azure URL must point to a git repository'); + expect(() => + AzureUrl.fromRepoUrl('https://dev.azure.com/my-org/my-project/_not-git'), + ).toThrow('Azure URL must point to a git repository'); + expect(() => + AzureUrl.fromRepoUrl( + 'https://dev.azure.com/my-org/my-project/_not-git/my-repo', + ), + ).toThrow('Azure URL must point to a git repository'); + expect(() => + AzureUrl.fromRepoUrl( + 'https://dev.azure.com/my-org/_workitems/recentlyupdated/', + ), + ).toThrow('Azure URL must point to a git repository'); + }); +}); diff --git a/packages/integration/src/azure/AzureUrl.ts b/packages/integration/src/azure/AzureUrl.ts new file mode 100644 index 0000000000..76de8550ed --- /dev/null +++ b/packages/integration/src/azure/AzureUrl.ts @@ -0,0 +1,231 @@ +/* + * 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. + */ + +const VERSION_PREFIX_GIT_BRANCH = 'GB'; + +export class AzureUrl { + /** + * Parses an azure URL as copied from the browser address bar. + * + * Throws an error if the URL is not a valid azure repo URL. + */ + static fromRepoUrl(repoUrl: string): AzureUrl { + const url = new URL(repoUrl); + + let owner; + let project; + let repo; + + const parts = url.pathname.split('/').map(part => decodeURIComponent(part)); + if (parts[2] === '_git') { + owner = parts[1]; + project = repo = parts[3]; + } else if (parts[3] === '_git') { + owner = parts[1]; + project = parts[2]; + repo = parts[4]; + } + + if (!owner || !project || !repo) { + throw new Error('Azure URL must point to a git repository'); + } + + const path = url.searchParams.get('path') ?? undefined; + + let ref; + const version = url.searchParams.get('version'); + if (version) { + const prefix = version.slice(0, 2); + if (prefix !== 'GB') { + throw new Error('Azure URL version must point to a git branch'); + } + ref = version.slice(2); + } + + return new AzureUrl(url.origin, owner, project, repo, path, ref); + } + + #origin: string; + #owner: string; + #project: string; + #repo: string; + #path?: string; + #ref?: string; + + private constructor( + origin: string, + owner: string, + project: string, + repo: string, + path?: string, + ref?: string, + ) { + this.#origin = origin; + this.#owner = owner; + this.#project = project; + this.#repo = repo; + this.#path = path; + this.#ref = ref; + } + + #baseUrl = (...parts: string[]): URL => { + const url = new URL(this.#origin); + url.pathname = parts.map(part => encodeURIComponent(part)).join('/'); + return url; + }; + + /** + * Returns a repo URL that can be used to navigate to the resource in azure. + * + * Throws an error if the URL is not a valid azure repo URL. + */ + toRepoUrl(): string { + let url; + if (this.#project === this.#repo) { + url = this.#baseUrl(this.#owner, '_git', this.#repo); + } else { + url = this.#baseUrl(this.#owner, this.#project, '_git', this.#repo); + } + + if (this.#path) { + url.searchParams.set('path', this.#path); + } + if (this.#ref) { + url.searchParams.set('version', VERSION_PREFIX_GIT_BRANCH + this.#ref); + } + + return url.toString(); + } + + /** + * Returns the file download URL for this azure resource. + * + * Throws an error if the URL does not point to a file. + */ + toFileUrl(): string { + if (!this.#path) { + throw new Error( + 'Azure URL must point to a specific path to be able to download a file', + ); + } + + const url = this.#baseUrl( + this.#owner, + this.#project, + '_apis', + 'git', + 'repositories', + this.#repo, + 'items', + ); + url.searchParams.set('api-version', '6.0'); + url.searchParams.set('path', this.#path); + + if (this.#ref) { + url.searchParams.set('version', this.#ref); + } + + return url.toString(); + } + + /** + * Returns the archive download URL for this azure resource. + * + * Throws an error if the URL does not point to a repo. + */ + toArchiveUrl(): string { + const url = this.#baseUrl( + this.#owner, + this.#project, + '_apis', + 'git', + 'repositories', + this.#repo, + 'items', + ); + url.searchParams.set('recursionLevel', 'full'); + url.searchParams.set('download', 'true'); + url.searchParams.set('api-version', '6.0'); + + if (this.#path) { + url.searchParams.set('scopePath', this.#path); + } + if (this.#ref) { + url.searchParams.set('version', this.#ref); + } + + return url.toString(); + } + + /** + * Returns the API url for fetching commits from a branch for this azure resource. + * + * Throws an error if the URL does not point to a commit. + */ + toCommitsUrl(): string { + const url = this.#baseUrl( + this.#owner, + this.#project, + '_apis', + 'git', + 'repositories', + this.#repo, + 'commits', + ); + url.searchParams.set('api-version', '6.0'); + + if (this.#ref) { + url.searchParams.set('searchCriteria.itemVersion.version', this.#ref); + } + + return url.toString(); + } + + /** + * Returns the name of the owner, a user or an organization. + */ + getOwner(): string { + return this.#owner; + } + + /** + * Returns the name of the project. + */ + getProject(): string { + return this.#project; + } + + /** + * Returns the name of the repo. + */ + getRepo(): string { + return this.#repo; + } + + /** + * Returns the file path within the repo if the URL contains one. + */ + getPath(): string | undefined { + return this.#path; + } + + /** + * Returns the git ref in the repo if the URL contains one. + */ + getRef(): string | undefined { + return this.#ref; + } +} diff --git a/packages/integration/src/azure/core.test.ts b/packages/integration/src/azure/core.test.ts index 37fe50f3ea..ad8f2691b5 100644 --- a/packages/integration/src/azure/core.test.ts +++ b/packages/integration/src/azure/core.test.ts @@ -45,22 +45,22 @@ describe('azure core', () => { { url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml&version=GBmaster', result: - 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml&version=master', + 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml&version=master', }, { url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml', result: - 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml', + 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml', }, { url: 'https://api.com/org-name/project-name/_git/repo-name?path=my-template.yaml', result: - 'https://api.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml', + 'https://api.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml', }, { url: 'https://api.com/org-name/project-name/_git/repo-name?path=my-template.yaml&version=GBmaster', result: - 'https://api.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml&version=master', + 'https://api.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml&version=master', }, ])('should handle happy path %#', async ({ url, result }) => { expect(getAzureFileFetchUrl(url)).toBe(result); @@ -69,13 +69,11 @@ describe('azure core', () => { it.each([ { url: 'https://api.com/a/b/blob/master/path/to/c.yaml', - error: - 'Incorrect URL: https://api.com/a/b/blob/master/path/to/c.yaml, Error: Wrong Azure Devops URL or Invalid file path', + error: 'Azure URL must point to a git repository', }, { url: 'com/a/b/blob/master/path/to/c.yaml', - error: - 'Incorrect URL: com/a/b/blob/master/path/to/c.yaml, TypeError: Invalid URL: com/a/b/blob/master/path/to/c.yaml', + error: 'Invalid URL: com/a/b/blob/master/path/to/c.yaml', }, ])('should handle error path %#', ({ url, error }) => { expect(() => getAzureFileFetchUrl(url)).toThrow(error); @@ -95,7 +93,7 @@ describe('azure core', () => { const result = getAzureDownloadUrl( 'https://dev.azure.com/organization/project/_git/repository?path=%2Fdocs', ); - expect(new URL(result).searchParams.get('scopePath')).toEqual('docs'); + expect(new URL(result).searchParams.get('scopePath')).toEqual('/docs'); }); it.each([ diff --git a/packages/integration/src/azure/core.ts b/packages/integration/src/azure/core.ts index e22764f643..30603b1f09 100644 --- a/packages/integration/src/azure/core.ts +++ b/packages/integration/src/azure/core.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import parseGitUrl from 'git-url-parse'; +import { AzureUrl } from './AzureUrl'; import { AzureIntegrationConfig } from './config'; /** @@ -28,53 +28,7 @@ import { AzureIntegrationConfig } from './config'; * @param url A URL pointing to a file */ export function getAzureFileFetchUrl(url: string): string { - try { - const parsedUrl = new URL(url); - - const [empty, userOrOrg, project, srcKeyword, repoName] = - parsedUrl.pathname.split('/'); - - const path = parsedUrl.searchParams.get('path') || ''; - const ref = parsedUrl.searchParams.get('version')?.substr(2); - - if ( - empty !== '' || - userOrOrg === '' || - project === '' || - srcKeyword !== '_git' || - repoName === '' || - path === '' || - ref === '' - ) { - throw new Error('Wrong Azure Devops URL or Invalid file path'); - } - - // transform to api - parsedUrl.pathname = [ - empty, - userOrOrg, - project, - '_apis', - 'git', - 'repositories', - repoName, - 'items', - ].join('/'); - - const queryParams = [`path=${path}`]; - - if (ref) { - queryParams.push(`version=${ref}`); - } - - parsedUrl.search = queryParams.join('&'); - - parsedUrl.protocol = 'https'; - - return parsedUrl.toString(); - } catch (e) { - throw new Error(`Incorrect URL: ${url}, ${e}`); - } + return AzureUrl.fromRepoUrl(url).toFileUrl(); } /** @@ -84,33 +38,7 @@ export function getAzureFileFetchUrl(url: string): string { * @param url A URL pointing to a path */ export function getAzureDownloadUrl(url: string): string { - const { - name: repoName, - owner: project, - organization, - protocol, - resource, - filepath, - } = parseGitUrl(url); - - // scopePath will limit the downloaded content - // /docs will only download the docs folder and everything below it - // /docs/index.md will only download index.md but put it in the root of the archive - const scopePath = filepath - ? `&scopePath=${encodeURIComponent(filepath)}` - : ''; - - if (resource === 'dev.azure.com') { - return `${protocol}://${resource}/${organization}/${project}/_apis/git/repositories/${repoName}/items?recursionLevel=full&download=true&api-version=6.0${scopePath}`; - } - - // For Azure DevOps Server `parseGitUrl` returns the same values - // for `organization` and `project` like this: `organization/project/_git` - // so we drop `project` and then strip `/_git` from `organization` - return `${protocol}://${resource}/${organization.replace( - '/_git', - '', - )}/_apis/git/repositories/${repoName}/items?recursionLevel=full&download=true&api-version=6.0${scopePath}`; + return AzureUrl.fromRepoUrl(url).toArchiveUrl(); } /** @@ -119,49 +47,7 @@ export function getAzureDownloadUrl(url: string): string { * @param url A URL pointing to a repository or a sub-path */ export function getAzureCommitsUrl(url: string): string { - try { - const parsedUrl = new URL(url); - - const [empty, userOrOrg, project, srcKeyword, repoName] = - parsedUrl.pathname.split('/'); - - // Remove the "GB" from "GBmain" for example. - const ref = parsedUrl.searchParams.get('version')?.substr(2); - - if ( - !!empty || - !userOrOrg || - !project || - srcKeyword !== '_git' || - !repoName - ) { - throw new Error('Wrong Azure Devops URL'); - } - - // transform to commits api - parsedUrl.pathname = [ - empty, - userOrOrg, - project, - '_apis', - 'git', - 'repositories', - repoName, - 'commits', - ].join('/'); - - const queryParams = []; - if (ref) { - queryParams.push(`searchCriteria.itemVersion.version=${ref}`); - } - parsedUrl.search = queryParams.join('&'); - - parsedUrl.protocol = 'https'; - - return parsedUrl.toString(); - } catch (e) { - throw new Error(`Incorrect URL: ${url}, ${e}`); - } + return AzureUrl.fromRepoUrl(url).toCommitsUrl(); } /** diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index c7ba6408d9..3156488ba7 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -110,9 +110,10 @@ class GithubAppManager { const installationClient = new Octokit({ auth: result.data.token, }); - const repos = - await installationClient.apps.listReposAccessibleToInstallation(); - const hasRepo = repos.data.repositories.some(repository => { + const repos = await installationClient.paginate( + installationClient.apps.listReposAccessibleToInstallation, + ); + const hasRepo = repos.some(repository => { return repository.name === repo; }); if (!hasRepo) { diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index a914830fd5..2316d96a55 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -50,7 +50,7 @@ "aws-sdk": "^2.840.0", "express": "^4.17.1", "fs-extra": "9.1.0", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "js-yaml": "^4.0.0", "json5": "^2.1.3", "mime-types": "^2.1.27", diff --git a/packages/test-utils/.snyk b/packages/test-utils/.snyk new file mode 100644 index 0000000000..7737037634 --- /dev/null +++ b/packages/test-utils/.snyk @@ -0,0 +1,17 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-ANSIREGEX-1583908: + - '*': + reason: Self-inflicted ReDos in tests is not an issue + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + + SNYK-JS-XMLDOM-1534562: + - 'msw@0.29.0 > @mswjs/interceptors@0.10.0 > xmldom@0.6.0': + reason: Self-inflicted XML injection attacks in tests is not an issue + expires: 2022-03-06T17:18:55.019Z + created: 2021-09-06T17:18:55.027Z + +patch: {} diff --git a/plugins/allure/package.json b/plugins/allure/package.json index bc56349a1c..efa44cb6f8 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -35,9 +35,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 083464ed9b..63871ce4e1 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -31,8 +31,8 @@ "dependencies": { "@asyncapi/react-component": "^0.23.0", "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog": "^0.6.14", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", @@ -52,9 +52,9 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index e3d626a9df..e1f927dd24 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -48,7 +48,7 @@ export interface Config { entryPoint: string; logoutUrl?: string; issuer: string; - cert?: string; + cert: string; privateKey?: string; decryptionPvk?: string; signatureAlgorithm?: 'sha256' | 'sha512'; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 3ef3f6155e..afd8006596 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -64,7 +64,7 @@ "passport-oauth2": "^1.5.0", "passport-okta-oauth": "^0.0.1", "passport-onelogin-oauth": "^0.0.1", - "passport-saml": "^2.0.0", + "passport-saml": "^3.1.2", "uuid": "^8.0.0", "winston": "^3.2.1", "yn": "^4.0.0" @@ -78,7 +78,7 @@ "@types/passport-github2": "^1.2.4", "@types/passport-google-oauth20": "^2.0.3", "@types/passport-microsoft": "^0.0.0", - "@types/passport-saml": "^1.1.2", + "@types/passport-saml": "^1.1.3", "@types/passport-strategy": "^0.2.35", "@types/xml2js": "^0.4.7", "msw": "^0.29.0" diff --git a/plugins/auth-backend/src/providers/gitlab/provider.test.ts b/plugins/auth-backend/src/providers/gitlab/provider.test.ts index f8795aec34..d1a84b43e8 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.test.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.test.ts @@ -16,6 +16,7 @@ import { GitlabAuthProvider, gitlabDefaultSignInResolver } from './provider'; import * as helpers from '../../lib/passport/PassportStrategyHelper'; +import { PassportProfile } from '../../lib/passport/types'; import { OAuthResult } from '../../lib/oauth'; import { getVoidLogger } from '@backstage/backend-common'; import { TokenIssuer } from '../../identity'; @@ -27,6 +28,33 @@ const mockFrameHandler = jest.spyOn( ) as unknown as jest.MockedFunction<() => Promise<{ result: OAuthResult }>>; describe('GitlabAuthProvider', () => { + const tokenIssuer = { + issueToken: jest.fn(), + listPublicKeys: jest.fn(), + }; + const catalogIdentityClient = { + findUser: jest.fn(), + }; + + const provider = new GitlabAuthProvider({ + clientId: 'mock', + clientSecret: 'mock', + callbackUrl: 'mock', + baseUrl: 'mock', + catalogIdentityClient: + catalogIdentityClient as unknown as CatalogIdentityClient, + tokenIssuer: tokenIssuer as unknown as TokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: { + email: fullProfile.emails![0]!.value, + displayName: fullProfile.displayName, + picture: 'http://gitlab.com/lols', + }, + }), + signInResolver: gitlabDefaultSignInResolver, + logger: getVoidLogger(), + }); + it('should transform to type OAuthResponse', async () => { const tests = [ { @@ -117,36 +145,62 @@ describe('GitlabAuthProvider', () => { }, ]; - const tokenIssuer = { - issueToken: jest.fn(), - listPublicKeys: jest.fn(), - }; - const catalogIdentityClient = { - findUser: jest.fn(), - }; - - const provider = new GitlabAuthProvider({ - clientId: 'mock', - clientSecret: 'mock', - callbackUrl: 'mock', - baseUrl: 'mock', - catalogIdentityClient: - catalogIdentityClient as unknown as CatalogIdentityClient, - tokenIssuer: tokenIssuer as unknown as TokenIssuer, - authHandler: async ({ fullProfile }) => ({ - profile: { - email: fullProfile.emails![0]!.value, - displayName: fullProfile.displayName, - picture: 'http://gitlab.com/lols', - }, - }), - signInResolver: gitlabDefaultSignInResolver, - logger: getVoidLogger(), - }); for (const test of tests) { mockFrameHandler.mockResolvedValueOnce(test.input); const { response } = await provider.handler({} as any); expect(response).toEqual(test.expect); } }); + + it('should forward a new refresh token on refresh', async () => { + const mockRefreshToken = jest.spyOn( + helpers, + 'executeRefreshTokenStrategy', + ) as unknown as jest.MockedFunction<() => Promise<{}>>; + + mockRefreshToken.mockResolvedValueOnce({ + accessToken: 'a.b.c', + refreshToken: 'dont-forget-to-send-refresh', + params: { + id_token: 'my-id', + scope: 'read_user', + }, + }); + + const mockUserProfile = jest.spyOn( + helpers, + 'executeFetchUserProfileStrategy', + ) as unknown as jest.MockedFunction<() => Promise>; + + mockUserProfile.mockResolvedValueOnce({ + id: 'uid-my-id', + username: 'mockuser', + provider: 'gitlab', + displayName: 'Mocked User', + emails: [ + { + value: 'mockuser@gmail.com', + }, + ], + }); + + const response = await provider.refresh({} as any); + + expect(response).toEqual({ + backstageIdentity: { + id: 'mockuser', + }, + profile: { + displayName: 'Mocked User', + email: 'mockuser@gmail.com', + picture: 'http://gitlab.com/lols', + }, + providerInfo: { + accessToken: 'a.b.c', + idToken: 'my-id', + refreshToken: 'dont-forget-to-send-refresh', + scope: 'read_user', + }, + }); + }); }); diff --git a/plugins/auth-backend/src/providers/gitlab/provider.ts b/plugins/auth-backend/src/providers/gitlab/provider.ts index 419f00a346..6ca405d15f 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.ts @@ -177,6 +177,7 @@ export class GitlabAuthProvider implements OAuthHandlers { providerInfo: { idToken: result.params.id_token, accessToken: result.accessToken, + refreshToken: result.refreshToken, // GitLab expires the old refresh token when used scope: result.params.scope, expiresInSeconds: result.params.expires_in, }, diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 306adc70f5..092ffa7099 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -157,6 +157,11 @@ export class OidcAuthProvider implements OAuthHandlers { userinfo: UserinfoResponse, done: PassportDoneCallback, ) => { + if (typeof done !== 'function') { + throw new Error( + 'OIDC IdP must provide a userinfo_endpoint in the metadata response', + ); + } done( undefined, { tokenset, userinfo }, diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 5b95634414..21e6c9080d 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -119,13 +119,13 @@ export type SamlProviderOptions = {}; export const createSamlProvider = ( _options?: SamlProviderOptions, ): AuthProviderFactory => { - return ({ providerId, globalConfig, config, tokenIssuer, logger }) => { + return ({ providerId, globalConfig, config, tokenIssuer }) => { const opts = { callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`, entryPoint: config.getString('entryPoint'), logoutUrl: config.getOptionalString('logoutUrl'), issuer: config.getString('issuer'), - cert: config.getOptionalString('cert'), + cert: config.getString('cert'), privateCert: config.getOptionalString('privateKey'), decryptionPvk: config.getOptionalString('decryptionPvk'), signatureAlgorithm: config.getOptionalString('signatureAlgorithm') as @@ -138,17 +138,6 @@ export const createSamlProvider = ( appUrl: globalConfig.appUrl, }; - // passport-saml will return an error if the `cert` key is set, and the value is empty. - // Since we read from config (such as environment variables) an empty string should be equal to being unset. - if (!opts.cert) { - logger.warn( - 'SamlAuthProvider was initialized without a cert configuration parameter. ' + - 'This will soon be required by the underlying passport-saml library, which may soon lead to failures to start the auth backend. ' + - 'Please add an "auth.saml.cert" config parameter.', - ); - delete opts.cert; - } - return new SamlAuthProvider(opts); }; }; diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 7a8bf9079f..8b286599e5 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", @@ -35,9 +35,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index d0b9a012f5..a476d0f4cb 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -38,9 +38,9 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 67b71f3fe8..ddf62aa655 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,48 @@ # @backstage/plugin-catalog-backend +## 0.13.7 + +### Patch Changes + +- ce17a1693: Allow the catalog search collator to filter the entities that it indexes +- dbb952787: Use `ScmIntegrationRegistry#resolveUrl` in the placeholder processors instead of a custom implementation. + + If you manually instantiate the `PlaceholderProcessor` (you most probably don't), add the new required constructor parameter: + + ```diff + + import { ScmIntegrations } from '@backstage/integration'; + // ... + + const integrations = ScmIntegrations.fromConfig(config); + // ... + new PlaceholderProcessor({ + resolvers: placeholderResolvers, + reader, + + integrations, + }); + ``` + + All custom `PlaceholderResolver` can use the new `resolveUrl` parameter to resolve relative URLs. + +- 1797c5ce5: This change drops support for deprecated location types which have all been replaced by the `url` type. + There has been a deprecation warning in place since the beginning of this year so most should already be migrated and received information at this point. + + The now removed location types are: + + ``` + github + github/api + bitbucket/api + gitlab/api + azure/api + ``` + +- Updated dependencies + - @backstage/catalog-client@0.3.19 + - @backstage/catalog-model@0.9.2 + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + - @backstage/backend-common@0.9.2 + ## 0.13.6 ### Patch Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 665c90ce4e..c0beed6b45 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -40,6 +40,36 @@ export type AddLocationResult = { entities: Entity[]; }; +// Warning: (ae-missing-release-tag) "AnalyzeLocationEntityField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AnalyzeLocationEntityField = { + field: string; + state: + | 'analysisSuggestedValue' + | 'analysisSuggestedNoValue' + | 'needsUserInput'; + value: string | null; + description: string; +}; + +// Warning: (ae-missing-release-tag) "AnalyzeLocationExistingEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AnalyzeLocationExistingEntity = { + location: LocationSpec; + isRegistered: boolean; + entity: Entity; +}; + +// Warning: (ae-missing-release-tag) "AnalyzeLocationGenerateEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AnalyzeLocationGenerateEntity = { + entity: RecursivePartial; + fields: AnalyzeLocationEntityField[]; +}; + // Warning: (ae-missing-release-tag) "AnalyzeLocationRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -111,8 +141,6 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { normalizeName(name: string): string; // (undocumented) organizations: Organizations; - // Warning: (ae-forgotten-export) The symbol "AwsOrganizationProviderConfig" needs to be exported by the entry point index.d.ts - // // (undocumented) provider: AwsOrganizationProviderConfig; // (undocumented) @@ -123,6 +151,7 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { ): Promise; } +<<<<<<< HEAD // Warning: (ae-missing-release-tag) "AwsS3DiscoveryProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -136,6 +165,14 @@ export class AwsS3DiscoveryProcessor implements CatalogProcessor { parser: CatalogProcessorParser, ): Promise; } +======= +// Warning: (ae-missing-release-tag) "AwsOrganizationProviderConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type AwsOrganizationProviderConfig = { + roleArn?: string; +}; +>>>>>>> 11da7e59e940bb91e61c3b0d2db6ec7d38ce0126 // Warning: (ae-missing-release-tag) "BitbucketDiscoveryProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -200,8 +237,6 @@ export class CatalogBuilder { higherOrderOperation: HigherOrderOperation; locationAnalyzer: LocationAnalyzer; }>; - // Warning: (ae-forgotten-export) The symbol "CatalogEnvironment" needs to be exported by the entry point index.d.ts - // // (undocumented) static create(env: CatalogEnvironment): Promise; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -220,6 +255,42 @@ export class CatalogBuilder { ): CatalogBuilder; } +// Warning: (ae-missing-release-tag) "CatalogEntityDocument" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface CatalogEntityDocument extends IndexableDocument { + // (undocumented) + componentType: string; + // (undocumented) + kind: string; + // (undocumented) + lifecycle: string; + // (undocumented) + namespace: string; + // (undocumented) + owner: string; +} + +// Warning: (ae-missing-release-tag) "CatalogEnvironment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type CatalogEnvironment = { + logger: Logger_2; + database: PluginDatabaseManager; + config: Config; + reader: UrlReader; +}; + +// Warning: (ae-missing-release-tag) "CatalogProcessingEngine" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface CatalogProcessingEngine { + // (undocumented) + start(): Promise; + // (undocumented) + stop(): Promise; +} + // Warning: (ae-missing-release-tag) "CatalogProcessingOrchestrator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -346,15 +417,11 @@ export class CommonDatabase implements Database { txOpaque: Transaction, request: DbEntityRequest[], ): Promise; - // Warning: (ae-forgotten-export) The symbol "DbLocationsRow" needs to be exported by the entry point index.d.ts - // // (undocumented) addLocation( txOpaque: Transaction, location: Location_2, ): Promise; - // Warning: (ae-forgotten-export) The symbol "DatabaseLocationUpdateLogStatus" needs to be exported by the entry point index.d.ts - // // (undocumented) addLocationUpdateLogEvent( locationId: string, @@ -362,9 +429,6 @@ export class CommonDatabase implements Database { entityName?: string | string[], message?: string, ): Promise; - // Warning: (ae-forgotten-export) The symbol "DbEntitiesRequest" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "DbEntitiesResponse" needs to be exported by the entry point index.d.ts - // // (undocumented) entities( txOpaque: Transaction, @@ -380,12 +444,8 @@ export class CommonDatabase implements Database { txOpaque: Transaction, uid: string, ): Promise; - // Warning: (ae-forgotten-export) The symbol "DbLocationsRowWithStatus" needs to be exported by the entry point index.d.ts - // // (undocumented) location(id: string): Promise; - // Warning: (ae-forgotten-export) The symbol "DatabaseLocationUpdateLogEvent" needs to be exported by the entry point index.d.ts - // // (undocumented) locationHistory(id: string): Promise; // (undocumented) @@ -411,12 +471,18 @@ export class CommonDatabase implements Database { ): Promise; } -// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "CreateDatabaseOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type CreateDatabaseOptions = { + logger: Logger_2; +}; + // Warning: (ae-missing-release-tag) "createNextRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export function createNextRouter( - options: RouterOptions_2, + options: NextRouterOptions, ): Promise; // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag @@ -485,9 +551,6 @@ export type Database = { // @public (undocumented) export class DatabaseEntitiesCatalog implements EntitiesCatalog { constructor(database: Database, logger: Logger_2); - // Warning: (ae-forgotten-export) The symbol "EntityUpsertRequest" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "EntityUpsertResponse" needs to be exported by the entry point index.d.ts - // // (undocumented) batchAddOrUpdateEntities( requests: EntityUpsertRequest[], @@ -497,9 +560,6 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { outputEntities?: boolean; }, ): Promise; - // Warning: (ae-forgotten-export) The symbol "EntitiesRequest" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "EntitiesResponse" needs to be exported by the entry point index.d.ts - // // (undocumented) entities(request?: EntitiesRequest): Promise; // (undocumented) @@ -517,8 +577,6 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { location(id: string): Promise; // (undocumented) locationHistory(id: string): Promise; - // Warning: (ae-forgotten-export) The symbol "LocationResponse" needs to be exported by the entry point index.d.ts - // // (undocumented) locations(): Promise; // (undocumented) @@ -536,12 +594,32 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { removeLocation(id: string): Promise; } +// Warning: (ae-missing-release-tag) "DatabaseLocationUpdateLogEvent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DatabaseLocationUpdateLogEvent = { + id: string; + status: DatabaseLocationUpdateLogStatus; + location_id: string; + entity_name: string; + created_at?: string; + message?: string; +}; + +// Warning: (ae-missing-release-tag) "DatabaseLocationUpdateLogStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum DatabaseLocationUpdateLogStatus { + // (undocumented) + FAIL = 'fail', + // (undocumented) + SUCCESS = 'success', +} + // Warning: (ae-missing-release-tag) "DatabaseManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export class DatabaseManager { - // Warning: (ae-forgotten-export) The symbol "CreateDatabaseOptions" needs to be exported by the entry point index.d.ts - // // (undocumented) static createDatabase( knex: Knex, @@ -557,6 +635,22 @@ export class DatabaseManager { static createTestDatabaseConnection(): Promise; } +// Warning: (ae-missing-release-tag) "DbEntitiesRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DbEntitiesRequest = { + filter?: EntityFilter; + pagination?: EntityPagination; +}; + +// Warning: (ae-missing-release-tag) "DbEntitiesResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DbEntitiesResponse = { + entities: DbEntityResponse[]; + pageInfo: DbPageInfo; +}; + // Warning: (ae-missing-release-tag) "DbEntityRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -574,6 +668,36 @@ export type DbEntityResponse = { entity: Entity; }; +// Warning: (ae-missing-release-tag) "DbLocationsRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DbLocationsRow = { + id: string; + type: string; + target: string; +}; + +// Warning: (ae-missing-release-tag) "DbLocationsRowWithStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DbLocationsRowWithStatus = DbLocationsRow & { + status: string | null; + timestamp: string | null; + message: string | null; +}; + +// Warning: (ae-missing-release-tag) "DbPageInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DbPageInfo = + | { + hasNextPage: false; + } + | { + hasNextPage: true; + endCursor: string; + }; + // Warning: (ae-missing-release-tag) "DefaultCatalogCollator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -598,8 +722,6 @@ export class DefaultCatalogCollator implements DocumentCollator { protected readonly catalogClient: CatalogApi; // (undocumented) protected discovery: PluginEndpointDiscovery; - // Warning: (ae-forgotten-export) The symbol "CatalogEntityDocument" needs to be exported by the entry point index.d.ts - // // (undocumented) execute(): Promise; // (undocumented) @@ -635,6 +757,14 @@ export class DefaultCatalogProcessingOrchestrator process(request: EntityProcessingRequest): Promise; } +// Warning: (ae-missing-release-tag) "DeferredEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type DeferredEntity = { + entity: Entity; + locationKey?: string; +}; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (ae-missing-release-tag) "durationText" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -657,6 +787,23 @@ export type EntitiesCatalog = { ): Promise; }; +// Warning: (ae-missing-release-tag) "EntitiesRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EntitiesRequest = { + filter?: EntityFilter; + fields?: (entity: Entity) => Entity; + pagination?: EntityPagination; +}; + +// Warning: (ae-missing-release-tag) "EntitiesResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EntitiesResponse = { + entities: Entity[]; + pageInfo: PageInfo; +}; + // Warning: (ae-missing-release-tag) "EntitiesSearchFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -749,6 +896,22 @@ export type EntityProviderMutation = removed: DeferredEntity[]; }; +// Warning: (ae-missing-release-tag) "EntityUpsertRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EntityUpsertRequest = { + entity: Entity; + relations: EntityRelationSpec[]; +}; + +// Warning: (ae-missing-release-tag) "EntityUpsertResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type EntityUpsertResponse = { + entityId: string; + entity?: Entity; +}; + // Warning: (ae-missing-release-tag) "FileReaderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -914,8 +1077,7 @@ export type LocationAnalyzer = { // // @public (undocumented) export class LocationEntityProcessor implements CatalogProcessor { - // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts - constructor(options: Options_3); + constructor(options: LocationEntityProcessorOptions); // (undocumented) postProcessEntity( entity: Entity, @@ -924,6 +1086,13 @@ export class LocationEntityProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "LocationEntityProcessorOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type LocationEntityProcessorOptions = { + integrations: ScmIntegrationRegistry; +}; + // Warning: (ae-missing-release-tag) "LocationReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -941,6 +1110,14 @@ export class LocationReaders implements LocationReader { read(location: LocationSpec): Promise; } +// Warning: (ae-missing-release-tag) "LocationResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type LocationResponse = { + data: Location_2; + currentStatus: LocationUpdateStatus; +}; + // Warning: (ae-missing-release-tag) "LocationsCatalog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -961,12 +1138,66 @@ export type LocationsCatalog = { ): Promise; }; +// Warning: (ae-missing-release-tag) "LocationService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface LocationService { + // (undocumented) + createLocation( + spec: LocationSpec, + dryRun: boolean, + ): Promise<{ + location: Location_2; + entities: Entity[]; + }>; + // (undocumented) + deleteLocation(id: string): Promise; + // (undocumented) + getLocation(id: string): Promise; + // (undocumented) + listLocations(): Promise; +} + +// Warning: (ae-missing-release-tag) "LocationStore" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface LocationStore { + // (undocumented) + createLocation(spec: LocationSpec): Promise; + // (undocumented) + deleteLocation(id: string): Promise; + // (undocumented) + getLocation(id: string): Promise; + // (undocumented) + listLocations(): Promise; +} + +// Warning: (ae-missing-release-tag) "LocationUpdateLogEvent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type LocationUpdateLogEvent = { + id: string; + status: 'fail' | 'success'; + location_id: string; + entity_name: string; + created_at?: string; + message?: string; +}; + +// Warning: (ae-missing-release-tag) "LocationUpdateStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type LocationUpdateStatus = { + timestamp: string | null; + status: string | null; + message: string | null; +}; + // Warning: (ae-missing-release-tag) "NextCatalogBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public export class NextCatalogBuilder { - // Warning: (ae-forgotten-export) The symbol "CatalogEnvironment" needs to be exported by the entry point index.d.ts - constructor(env: CatalogEnvironment_2); + constructor(env: CatalogEnvironment); // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen addEntityPolicy(...policies: EntityPolicy[]): NextCatalogBuilder; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -1000,6 +1231,22 @@ export class NextCatalogBuilder { setRefreshIntervalSeconds(seconds: number): NextCatalogBuilder; } +// Warning: (ae-missing-release-tag) "NextRouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface NextRouterOptions { + // (undocumented) + config: Config; + // (undocumented) + entitiesCatalog?: EntitiesCatalog; + // (undocumented) + locationAnalyzer?: LocationAnalyzer; + // (undocumented) + locationService: LocationService; + // (undocumented) + logger: Logger_2; +} + // Warning: (ae-missing-release-tag) "notFoundError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1008,6 +1255,18 @@ function notFoundError( message: string, ): CatalogProcessorResult; +// Warning: (ae-missing-release-tag) "PageInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PageInfo = + | { + hasNextPage: false; + } + | { + hasNextPage: true; + endCursor: string; + }; + // Warning: (ae-missing-release-tag) "parseEntityYaml" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1020,20 +1279,51 @@ export function parseEntityYaml( // // @public export class PlaceholderProcessor implements CatalogProcessor { - // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts - constructor(options: Options_4); + constructor(options: PlaceholderProcessorOptions); // (undocumented) preProcessEntity(entity: Entity, location: LocationSpec): Promise; } -// Warning: (ae-forgotten-export) The symbol "ResolverParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PlaceholderProcessorOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PlaceholderProcessorOptions = { + resolvers: Record; + reader: UrlReader; + integrations: ScmIntegrationRegistry; +}; + // Warning: (ae-missing-release-tag) "PlaceholderResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export type PlaceholderResolver = ( - params: ResolverParams, + params: PlaceholderResolverParams, ) => Promise; +// Warning: (ae-missing-release-tag) "PlaceholderResolverParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PlaceholderResolverParams = { + key: string; + value: JsonValue; + baseUrl: string; + read: PlaceholderResolverRead; + resolveUrl: PlaceholderResolverResolveUrl; +}; + +// Warning: (ae-missing-release-tag) "PlaceholderResolverRead" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PlaceholderResolverRead = (url: string) => Promise; + +// Warning: (ae-missing-release-tag) "PlaceholderResolverResolveUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PlaceholderResolverResolveUrl = ( + url: string, + base: string, +) => string; + // Warning: (ae-missing-release-tag) "ReadLocationEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1146,7 +1436,7 @@ export type Transaction = { // @public (undocumented) export class UrlReaderProcessor implements CatalogProcessor { // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts - constructor(options: Options_5); + constructor(options: Options_3); // (undocumented) readLocation( location: LocationSpec, @@ -1164,7 +1454,6 @@ export class UrlReaderProcessor implements CatalogProcessor { // src/catalog/types.d.ts:43:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters // src/catalog/types.d.ts:44:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters // src/catalog/types.d.ts:45:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters -// src/catalog/types.d.ts:75:5 - (ae-forgotten-export) The symbol "LocationUpdateLogEvent" needs to be exported by the entry point index.d.ts // src/database/types.d.ts:125:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/database/types.d.ts:131:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/database/types.d.ts:132:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -1193,11 +1482,6 @@ export class UrlReaderProcessor implements CatalogProcessor { // src/ingestion/processors/types.d.ts:58:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/ingestion/types.d.ts:17:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/ingestion/types.d.ts:41:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/ingestion/types.d.ts:49:5 - (ae-forgotten-export) The symbol "AnalyzeLocationExistingEntity" needs to be exported by the entry point index.d.ts -// src/ingestion/types.d.ts:50:5 - (ae-forgotten-export) The symbol "AnalyzeLocationGenerateEntity" needs to be exported by the entry point index.d.ts -// src/next/NextCatalogBuilder.d.ts:147:9 - (ae-forgotten-export) The symbol "CatalogProcessingEngine" needs to be exported by the entry point index.d.ts -// src/next/NextCatalogBuilder.d.ts:148:9 - (ae-forgotten-export) The symbol "LocationService" needs to be exported by the entry point index.d.ts -// src/next/processing/types.d.ts:11:5 - (ae-forgotten-export) The symbol "DeferredEntity" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index d853e1eb6b..478038eebd 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.13.6", + "version": "0.13.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.1", - "@backstage/catalog-client": "^0.3.18", - "@backstage/catalog-model": "^0.9.1", - "@backstage/config": "^0.1.8", - "@backstage/errors": "^0.1.1", + "@backstage/backend-common": "^0.9.2", + "@backstage/catalog-client": "^0.3.19", + "@backstage/catalog-model": "^0.9.2", + "@backstage/config": "^0.1.9", + "@backstage/errors": "^0.1.2", "@backstage/integration": "^0.6.3", "@backstage/plugin-search-backend-node": "^0.4.2", "@backstage/search-common": "^0.2.0", @@ -48,7 +48,7 @@ "express-promise-router": "^4.1.0", "fast-json-stable-stringify": "^2.1.0", "fs-extra": "9.1.0", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "glob": "^7.1.6", "knex": "^0.95.1", "lodash": "^4.17.15", @@ -64,8 +64,8 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.6", - "@backstage/cli": "^0.7.10", + "@backstage/backend-test-utils": "^0.1.7", + "@backstage/cli": "^0.7.11", "@backstage/test-utils": "^0.1.17", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/catalog-backend/src/catalog/index.ts b/plugins/catalog-backend/src/catalog/index.ts index bc33031d67..04cc358568 100644 --- a/plugins/catalog-backend/src/catalog/index.ts +++ b/plugins/catalog-backend/src/catalog/index.ts @@ -16,4 +16,15 @@ export { DatabaseEntitiesCatalog } from './DatabaseEntitiesCatalog'; export { DatabaseLocationsCatalog } from './DatabaseLocationsCatalog'; -export type { EntitiesCatalog, LocationsCatalog } from './types'; +export type { + EntitiesCatalog, + LocationsCatalog, + EntityUpsertRequest, + EntityUpsertResponse, + EntitiesRequest, + EntitiesResponse, + LocationResponse, + PageInfo, + LocationUpdateLogEvent, + LocationUpdateStatus, +} from './types'; diff --git a/plugins/catalog-backend/src/database/index.ts b/plugins/catalog-backend/src/database/index.ts index 1a07f3825f..028b0caa4b 100644 --- a/plugins/catalog-backend/src/database/index.ts +++ b/plugins/catalog-backend/src/database/index.ts @@ -16,6 +16,7 @@ export { CommonDatabase } from './CommonDatabase'; export { DatabaseManager } from './DatabaseManager'; +export type { CreateDatabaseOptions } from './DatabaseManager'; export type { Database, DbEntityRequest, @@ -24,4 +25,11 @@ export type { EntityFilter, EntityPagination, Transaction, + DbEntitiesRequest, + DbEntitiesResponse, + DbLocationsRowWithStatus, + DatabaseLocationUpdateLogEvent, + DbLocationsRow, + DatabaseLocationUpdateLogStatus, + DbPageInfo, } from './types'; diff --git a/plugins/catalog-backend/src/ingestion/index.ts b/plugins/catalog-backend/src/ingestion/index.ts index eace66b220..e27a3ef0ac 100644 --- a/plugins/catalog-backend/src/ingestion/index.ts +++ b/plugins/catalog-backend/src/ingestion/index.ts @@ -16,5 +16,18 @@ export { HigherOrderOperations } from './HigherOrderOperations'; export { LocationReaders } from './LocationReaders'; -export * from './types'; +export type { + AddLocationResult, + AnalyzeLocationRequest, + AnalyzeLocationResponse, + HigherOrderOperation, + LocationAnalyzer, + LocationReader, + ReadLocationEntity, + ReadLocationError, + ReadLocationResult, + AnalyzeLocationEntityField, + AnalyzeLocationExistingEntity, + AnalyzeLocationGenerateEntity, +} from './types'; export * from './processors'; diff --git a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts index 964a769e55..c6b7def5fc 100644 --- a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts @@ -38,12 +38,12 @@ export function toAbsoluteUrl( } } -type Options = { +export type LocationEntityProcessorOptions = { integrations: ScmIntegrationRegistry; }; export class LocationEntityProcessor implements CatalogProcessor { - constructor(private readonly options: Options) {} + constructor(private readonly options: LocationEntityProcessorOptions) {} async postProcessEntity( entity: Entity, diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts index 0254e854e5..c7a49353ae 100644 --- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.test.ts @@ -15,18 +15,22 @@ */ import { UrlReader } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; import { jsonPlaceholderResolver, PlaceholderProcessor, PlaceholderResolver, - ResolverParams, - ResolverRead, + PlaceholderResolverParams, + PlaceholderResolverRead, textPlaceholderResolver, yamlPlaceholderResolver, } from './PlaceholderProcessor'; +const integrations = ScmIntegrations.fromConfig(new ConfigReader({})); + describe('PlaceholderProcessor', () => { - const read: jest.MockedFunction = jest.fn(); + const read: jest.MockedFunction = jest.fn(); const reader: UrlReader = { read, readTree: jest.fn(), search: jest.fn() }; beforeEach(() => { @@ -44,6 +48,7 @@ describe('PlaceholderProcessor', () => { foo: async () => 'replaced', }, reader, + integrations, }); await expect( processor.preProcessEntity(input, { type: 't', target: 'l' }), @@ -59,6 +64,7 @@ describe('PlaceholderProcessor', () => { upper: upperResolver, }, reader, + integrations, }); await expect( @@ -95,6 +101,7 @@ describe('PlaceholderProcessor', () => { bar: jest.fn(), }, reader, + integrations, }); const entity: Entity = { apiVersion: 'a', @@ -115,6 +122,7 @@ describe('PlaceholderProcessor', () => { bar: jest.fn(), }, reader, + integrations, }); const entity: Entity = { apiVersion: 'a', @@ -134,6 +142,7 @@ describe('PlaceholderProcessor', () => { const processor = new PlaceholderProcessor({ resolvers: { text: textPlaceholderResolver }, reader, + integrations, }); await expect( @@ -169,6 +178,7 @@ describe('PlaceholderProcessor', () => { const processor = new PlaceholderProcessor({ resolvers: { json: jsonPlaceholderResolver }, reader, + integrations, }); await expect( @@ -202,6 +212,7 @@ describe('PlaceholderProcessor', () => { const processor = new PlaceholderProcessor({ resolvers: { yaml: yamlPlaceholderResolver }, reader, + integrations, }); await expect( @@ -235,6 +246,7 @@ describe('PlaceholderProcessor', () => { const processor = new PlaceholderProcessor({ resolvers: { text: textPlaceholderResolver }, reader, + integrations, }); await expect( @@ -272,6 +284,7 @@ describe('PlaceholderProcessor', () => { const processor = new PlaceholderProcessor({ resolvers: { text: textPlaceholderResolver }, reader, + integrations, }); await expect( @@ -311,6 +324,7 @@ describe('PlaceholderProcessor', () => { const processor = new PlaceholderProcessor({ resolvers: { text: textPlaceholderResolver }, reader, + integrations, }); await expect( @@ -331,7 +345,7 @@ describe('PlaceholderProcessor', () => { }, ), ).rejects.toThrow( - 'Placeholder $text could not form a URL out of ./a/b/catalog-info.yaml and ../c/catalog-info.yaml', + 'Placeholder $text could not form a URL out of ./a/b/catalog-info.yaml and ../c/catalog-info.yaml, TypeError: Invalid base URL: ./a/b/catalog-info.yaml', ); expect(read).not.toBeCalled(); @@ -339,12 +353,13 @@ describe('PlaceholderProcessor', () => { }); describe('yamlPlaceholderResolver', () => { - const read: jest.MockedFunction = jest.fn(); - const params: ResolverParams = { + const read: jest.MockedFunction = jest.fn(); + const params: PlaceholderResolverParams = { key: 'a', value: './file.yaml', baseUrl: 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', read, + resolveUrl: (url, base) => integrations.resolveUrl({ url, base }), }; beforeEach(() => { @@ -383,12 +398,13 @@ describe('yamlPlaceholderResolver', () => { }); describe('jsonPlaceholderResolver', () => { - const read: jest.MockedFunction = jest.fn(); - const params: ResolverParams = { + const read: jest.MockedFunction = jest.fn(); + const params: PlaceholderResolverParams = { key: 'a', value: './file.json', baseUrl: 'https://github.com/backstage/backstage/a/b/catalog-info.yaml', read, + resolveUrl: (url, base) => integrations.resolveUrl({ url, base }), }; beforeEach(() => { diff --git a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts index 1b015d7d93..9ddef1ab3b 100644 --- a/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/PlaceholderProcessor.ts @@ -17,25 +17,32 @@ import { UrlReader } from '@backstage/backend-common'; import { Entity, LocationSpec } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/config'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import yaml from 'yaml'; import { CatalogProcessor } from './types'; -export type ResolverRead = (url: string) => Promise; +export type PlaceholderResolverRead = (url: string) => Promise; +export type PlaceholderResolverResolveUrl = ( + url: string, + base: string, +) => string; -export type ResolverParams = { +export type PlaceholderResolverParams = { key: string; value: JsonValue; baseUrl: string; - read: ResolverRead; + read: PlaceholderResolverRead; + resolveUrl: PlaceholderResolverResolveUrl; }; export type PlaceholderResolver = ( - params: ResolverParams, + params: PlaceholderResolverParams, ) => Promise; -type Options = { +export type PlaceholderProcessorOptions = { resolvers: Record; reader: UrlReader; + integrations: ScmIntegrationRegistry; }; /** @@ -43,7 +50,7 @@ type Options = { * that it then fills in with actual data. */ export class PlaceholderProcessor implements CatalogProcessor { - constructor(private readonly options: Options) {} + constructor(private readonly options: PlaceholderProcessorOptions) {} async preProcessEntity( entity: Entity, @@ -103,12 +110,19 @@ export class PlaceholderProcessor implements CatalogProcessor { return this.options.reader.read(url); }; + const resolveUrl = (url: string, base: string): string => + this.options.integrations.resolveUrl({ + url, + base, + }); + return [ await resolver({ key: resolverKey, value: resolverValue, baseUrl: location.target, read, + resolveUrl, }), true, ]; @@ -124,7 +138,7 @@ export class PlaceholderProcessor implements CatalogProcessor { */ export async function yamlPlaceholderResolver( - params: ResolverParams, + params: PlaceholderResolverParams, ): Promise { const text = await readTextLocation(params); @@ -155,7 +169,7 @@ export async function yamlPlaceholderResolver( } export async function jsonPlaceholderResolver( - params: ResolverParams, + params: PlaceholderResolverParams, ): Promise { const text = await readTextLocation(params); @@ -169,7 +183,7 @@ export async function jsonPlaceholderResolver( } export async function textPlaceholderResolver( - params: ResolverParams, + params: PlaceholderResolverParams, ): Promise { return await readTextLocation(params); } @@ -178,7 +192,9 @@ export async function textPlaceholderResolver( * Helpers */ -async function readTextLocation(params: ResolverParams): Promise { +async function readTextLocation( + params: PlaceholderResolverParams, +): Promise { const newUrl = relativeUrl(params); try { @@ -191,31 +207,27 @@ async function readTextLocation(params: ResolverParams): Promise { } } -function relativeUrl({ key, value, baseUrl }: ResolverParams): string { +function relativeUrl({ + key, + value, + baseUrl, + resolveUrl, +}: PlaceholderResolverParams): string { if (typeof value !== 'string') { throw new Error( `Placeholder \$${key} expected a string value parameter, in the form of an absolute URL or a relative path`, ); } - let url: URL; try { - // The two-value form of the URL constructor handles relative paths for us - url = new URL(value, baseUrl); - } catch { - try { - // Check whether value is a valid absolute URL on it's own, if not fail. - url = new URL(value); - } catch { - // The only remaining case that isn't support is a relative file path that should be - // resolved using a relative file location. Accessing local file paths can lead to - // path traversal attacks and access to any file on the host system. Implementing this - // would require additional security measures. - throw new Error( - `Placeholder \$${key} could not form a URL out of ${baseUrl} and ${value}`, - ); - } + return resolveUrl(value, baseUrl); + } catch (e) { + // The only remaining case that isn't support is a relative file path that should be + // resolved using a relative file location. Accessing local file paths can lead to + // path traversal attacks and access to any file on the host system. Implementing this + // would require additional security measures. + throw new Error( + `Placeholder \$${key} could not form a URL out of ${baseUrl} and ${value}, ${e}`, + ); } - - return url.toString(); } diff --git a/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts index da7bee12d8..e3ec8c0480 100644 --- a/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts @@ -26,16 +26,6 @@ import { CatalogProcessorParser, } from './types'; -// TODO(Rugvip): Added for backwards compatibility when moving to UrlReader, this -// can be removed in a bit -const deprecatedTypes = [ - 'github', - 'github/api', - 'bitbucket/api', - 'gitlab/api', - 'azure/api', -]; - type Options = { reader: UrlReader; logger: Logger; @@ -50,13 +40,7 @@ export class UrlReaderProcessor implements CatalogProcessor { emit: CatalogProcessorEmit, parser: CatalogProcessorParser, ): Promise { - if (deprecatedTypes.includes(location.type)) { - // TODO(Rugvip): Remove this warning a month or two into 2021, and remove support for the deprecated types. - this.options.logger.warn( - `Location '${location.target}' uses deprecated location type '${location.type}', use 'url' instead. ` + - 'Use "scripts/migrate-location-types.js" in the Backstage repo to migrate existing locations.', - ); - } else if (location.type !== 'url') { + if (location.type !== 'url') { return false; } diff --git a/plugins/catalog-backend/src/ingestion/processors/index.ts b/plugins/catalog-backend/src/ingestion/processors/index.ts index fe4d374829..fbdadba667 100644 --- a/plugins/catalog-backend/src/ingestion/processors/index.ts +++ b/plugins/catalog-backend/src/ingestion/processors/index.ts @@ -19,6 +19,7 @@ import * as results from './results'; export { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcessor'; export { AnnotateScmSlugEntityProcessor } from './AnnotateScmSlugEntityProcessor'; export { AwsOrganizationCloudAccountProcessor } from './AwsOrganizationCloudAccountProcessor'; +export type { AwsOrganizationProviderConfig } from './awsOrganization/config'; export { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor'; export { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor'; export { CodeOwnersProcessor } from './CodeOwnersProcessor'; @@ -28,8 +29,15 @@ export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor'; export { GithubMultiOrgReaderProcessor } from './GithubMultiOrgReaderProcessor'; export { GitLabDiscoveryProcessor } from './GitLabDiscoveryProcessor'; export { LocationEntityProcessor } from './LocationEntityProcessor'; +export type { LocationEntityProcessorOptions } from './LocationEntityProcessor'; export { PlaceholderProcessor } from './PlaceholderProcessor'; -export type { PlaceholderResolver } from './PlaceholderProcessor'; +export type { + PlaceholderProcessorOptions, + PlaceholderResolver, + PlaceholderResolverParams, + PlaceholderResolverRead, + PlaceholderResolverResolveUrl, +} from './PlaceholderProcessor'; export { StaticLocationProcessor } from './StaticLocationProcessor'; export * from './types'; export { UrlReaderProcessor } from './UrlReaderProcessor'; diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index 297234b3b4..eb7b84e95f 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -99,7 +99,7 @@ export type AnalyzeLocationResponse = { // read and emitted like this so that the frontend can inform the user that it // located them and can make sure to register them as well if they weren't // already -type AnalyzeLocationExistingEntity = { +export type AnalyzeLocationExistingEntity = { location: LocationSpec; isRegistered: boolean; entity: Entity; @@ -110,7 +110,7 @@ type AnalyzeLocationExistingEntity = { // the frontend. It'll probably contain a (possibly incomplete) entity, plus // enough info for the frontend to know what form data to show to the user // for overriding/completing the info. -type AnalyzeLocationGenerateEntity = { +export type AnalyzeLocationGenerateEntity = { // Some form of partial representation of the entity entity: RecursivePartial; // Lists the suggestions that the user may want to override @@ -120,7 +120,7 @@ type AnalyzeLocationGenerateEntity = { // This is where I get really vague. Something like this perhaps? Or it could be // something like a json-schema that contains enough info for the frontend to // be able to present a form and explanations -type AnalyzeLocationEntityField = { +export type AnalyzeLocationEntityField = { // e.g. "spec.owner"? The frontend needs to know how to "inject" the field into the // entity again if the user wants to change it field: string; diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index ed690d07b6..fa43286777 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - PluginDatabaseManager, - resolvePackagePath, - UrlReader, -} from '@backstage/backend-common'; +import { resolvePackagePath } from '@backstage/backend-common'; import { DefaultNamespaceEntityPolicy, EntityPolicies, @@ -29,11 +25,9 @@ import { SchemaValidEntityPolicy, Validators, } from '@backstage/catalog-model'; -import { Config } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; import { createHash } from 'crypto'; import lodash from 'lodash'; -import { Logger } from 'winston'; import { DatabaseLocationsCatalog, EntitiesCatalog, @@ -80,13 +74,7 @@ import { createRandomRefreshInterval, RefreshIntervalFunction, } from './refresh'; - -export type CatalogEnvironment = { - logger: Logger; - database: PluginDatabaseManager; - config: Config; - reader: UrlReader; -}; +import { CatalogEnvironment } from '../service/CatalogBuilder'; /** * A builder that helps wire up all of the component parts of the catalog. @@ -385,7 +373,11 @@ export class NextCatalogBuilder { // These are always there no matter what const processors: CatalogProcessor[] = [ - new PlaceholderProcessor({ resolvers: placeholderResolvers, reader }), + new PlaceholderProcessor({ + resolvers: placeholderResolvers, + reader, + integrations, + }), new BuiltinKindsEntityProcessor(), ]; diff --git a/plugins/catalog-backend/src/next/NextRouter.ts b/plugins/catalog-backend/src/next/NextRouter.ts index 849d2ee36d..99073be421 100644 --- a/plugins/catalog-backend/src/next/NextRouter.ts +++ b/plugins/catalog-backend/src/next/NextRouter.ts @@ -36,7 +36,7 @@ import { import { disallowReadonlyMode, validateRequestBody } from '../service/util'; import { LocationService } from './types'; -export interface RouterOptions { +export interface NextRouterOptions { entitiesCatalog?: EntitiesCatalog; locationAnalyzer?: LocationAnalyzer; locationService: LocationService; @@ -45,7 +45,7 @@ export interface RouterOptions { } export async function createNextRouter( - options: RouterOptions, + options: NextRouterOptions, ): Promise { const { entitiesCatalog, locationAnalyzer, locationService, config, logger } = options; diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/next/index.ts index 2afdff63fb..cdd81d9f4d 100644 --- a/plugins/catalog-backend/src/next/index.ts +++ b/plugins/catalog-backend/src/next/index.ts @@ -16,6 +16,7 @@ export { NextCatalogBuilder } from './NextCatalogBuilder'; export { createNextRouter } from './NextRouter'; +export type { NextRouterOptions } from './NextRouter'; export * from './processing'; export { createRandomRefreshInterval } from './refresh'; export type { RefreshIntervalFunction } from './refresh'; @@ -24,4 +25,7 @@ export type { EntityProvider, EntityProviderConnection, EntityProviderMutation, + CatalogProcessingEngine, + LocationService, + LocationStore, } from './types'; diff --git a/plugins/catalog-backend/src/next/processing/index.ts b/plugins/catalog-backend/src/next/processing/index.ts index e2f5a99507..9e03f18f28 100644 --- a/plugins/catalog-backend/src/next/processing/index.ts +++ b/plugins/catalog-backend/src/next/processing/index.ts @@ -18,5 +18,6 @@ export type { CatalogProcessingOrchestrator, EntityProcessingRequest, EntityProcessingResult, + DeferredEntity, } from './types'; export { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator'; diff --git a/plugins/catalog-backend/src/search/index.ts b/plugins/catalog-backend/src/search/index.ts index dfe1cf541e..5641670333 100644 --- a/plugins/catalog-backend/src/search/index.ts +++ b/plugins/catalog-backend/src/search/index.ts @@ -15,3 +15,4 @@ */ export { DefaultCatalogCollator } from './DefaultCatalogCollator'; +export type { CatalogEntityDocument } from './DefaultCatalogCollator'; diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts index 579772dcce..87a26ed3aa 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.test.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.test.ts @@ -166,7 +166,7 @@ describe('CatalogBuilder', () => { const { entitiesCatalog, higherOrderOperation } = await builder.build(); await higherOrderOperation.addLocation({ - type: 'github', + type: 'url', target: 'https://github.com/a/b/x.yaml', }); const { entities } = await entitiesCatalog.entities(); diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index 70c1890fbf..e32e071ebc 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -306,7 +306,11 @@ export class CatalogBuilder { // These are always there no matter what const processors: CatalogProcessor[] = [ StaticLocationProcessor.fromConfig(config), - new PlaceholderProcessor({ resolvers: placeholderResolvers, reader }), + new PlaceholderProcessor({ + resolvers: placeholderResolvers, + reader, + integrations, + }), new BuiltinKindsEntityProcessor(), ]; diff --git a/plugins/catalog-backend/src/service/index.ts b/plugins/catalog-backend/src/service/index.ts index baf3f499d7..865e611de4 100644 --- a/plugins/catalog-backend/src/service/index.ts +++ b/plugins/catalog-backend/src/service/index.ts @@ -15,5 +15,6 @@ */ export { CatalogBuilder } from './CatalogBuilder'; +export type { CatalogEnvironment } from './CatalogBuilder'; export { createRouter } from './router'; export type { RouterOptions } from './router'; diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 570658f712..047a55e716 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -32,8 +32,8 @@ "dependencies": { "@backstage/catalog-client": "^0.3.18", "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/integration": "^0.6.3", "@backstage/integration-react": "^0.1.8", "@backstage/plugin-catalog-react": "^0.4.5", @@ -43,7 +43,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "@octokit/rest": "^18.5.3", "@types/react": "*", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "js-base64": "^3.6.0", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -54,9 +54,9 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx index a2da75edd4..98d741df15 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx @@ -25,7 +25,7 @@ type Props = { options: string[]; required?: boolean; - errors?: FieldErrors>; + errors?: FieldErrors; rules?: React.ComponentProps['rules']; loading?: boolean; diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 89a0ca20cb..b7a843d954 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -32,8 +32,8 @@ "dependencies": { "@backstage/catalog-client": "^0.3.18", "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.6.3", "@backstage/integration-react": "^0.1.8", @@ -44,7 +44,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "@types/react": "*", "classnames": "^2.2.6", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "lodash": "^4.17.21", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -54,9 +54,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index a828a26ba0..466171e6ac 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -50,9 +50,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index f28a42bac6..d45542a082 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -31,8 +31,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -48,9 +48,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index d69ce9c910..061e4108c1 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -22,8 +22,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.1", "@backstage/config": "^0.1.6", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", @@ -40,9 +40,9 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 8001f6b055..b6fb078a6a 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/config": "^0.1.6", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -35,9 +35,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index b48888025d..f4cad51d21 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -31,8 +31,8 @@ }, "dependencies": { "@backstage/config": "^0.1.6", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -54,9 +54,9 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/explore/api-report.md b/plugins/explore/api-report.md index 6ed2be6cde..2d04574f88 100644 --- a/plugins/explore/api-report.md +++ b/plugins/explore/api-report.md @@ -3,11 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - /// import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { default } from 'react'; +import { default as default_2 } from 'react'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; import { TabProps } from '@material-ui/core'; @@ -15,25 +14,30 @@ import { TabProps } from '@material-ui/core'; // Warning: (ae-missing-release-tag) "catalogEntityRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const catalogEntityRouteRef: ExternalRouteRef< { -name: string; -kind: string; -namespace: string; -}, false>; +export const catalogEntityRouteRef: ExternalRouteRef< + { + name: string; + kind: string; + namespace: string; + }, + false +>; // Warning: (ae-missing-release-tag) "DomainExplorerContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const DomainExplorerContent: ({ title, }: { - title?: string | undefined; +export const DomainExplorerContent: ({ + title, +}: { + title?: string | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "ExploreLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public export const ExploreLayout: { - ({ title, subtitle, children, }: ExploreLayoutProps): JSX.Element; - Route: (props: SubRoute) => null; + ({ title, subtitle, children }: ExploreLayoutProps): JSX.Element; + Route: (props: SubRoute) => null; }; // Warning: (ae-missing-release-tag) "ExplorePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -44,17 +48,23 @@ export const ExplorePage: () => JSX.Element; // Warning: (ae-missing-release-tag) "explorePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const explorePlugin: BackstagePlugin< { -explore: RouteRef; -}, { -catalogEntity: ExternalRouteRef< { -name: string; -kind: string; -namespace: string; -}, false>; -}>; -export { explorePlugin } -export { explorePlugin as plugin } +const explorePlugin: BackstagePlugin< + { + explore: RouteRef; + }, + { + catalogEntity: ExternalRouteRef< + { + name: string; + kind: string; + namespace: string; + }, + false + >; + } +>; +export { explorePlugin }; +export { explorePlugin as plugin }; // Warning: (ae-missing-release-tag) "exploreRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -64,15 +74,19 @@ export const exploreRouteRef: RouteRef; // Warning: (ae-missing-release-tag) "GroupsExplorerContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const GroupsExplorerContent: ({ title, }: { - title?: string | undefined; +export const GroupsExplorerContent: ({ + title, +}: { + title?: string | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "ToolExplorerContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ToolExplorerContent: ({ title }: { - title?: string | undefined; +export const ToolExplorerContent: ({ + title, +}: { + title?: string | undefined; }) => JSX.Element; // Warnings were encountered during analysis: @@ -81,5 +95,4 @@ export const ToolExplorerContent: ({ title }: { // src/components/ExploreLayout/ExploreLayout.d.ts:30:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 204fa7f1ae..85e9e32669 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -31,8 +31,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/plugin-explore-react": "^0.0.6", "@backstage/theme": "^0.2.10", @@ -48,9 +48,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index c865920e7d..49a9481b15 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -15,49 +15,59 @@ */ import { + GroupEntity, + parseEntityRef, RELATION_CHILD_OF, stringifyEntityRef, - parseEntityRef, - GroupEntity, } from '@backstage/catalog-model'; -import { - catalogApiRef, - entityRouteRef, - getEntityRelations, - formatEntityRefTitle, -} from '@backstage/plugin-catalog-react'; -import { makeStyles, Typography } from '@material-ui/core'; -import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; -import React from 'react'; -import { useAsync } from 'react-use'; -import { BackstageTheme } from '@backstage/theme'; - import { DependencyGraph, DependencyGraphTypes, + Link, Progress, ResponseErrorPanel, - Link, } from '@backstage/core-components'; -import { useApi, useRouteRef, configApiRef } from '@backstage/core-plugin-api'; +import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { + catalogApiRef, + entityRouteRef, + formatEntityRefTitle, + getEntityRelations, +} from '@backstage/plugin-catalog-react'; +import { BackstageTheme } from '@backstage/theme'; +import { makeStyles, Typography, useTheme } from '@material-ui/core'; +import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; +import classNames from 'classnames'; +import React from 'react'; +import { useAsync } from 'react-use'; const useStyles = makeStyles((theme: BackstageTheme) => ({ + graph: { + flex: 1, + minHeight: 0, + }, organizationNode: { - fill: 'coral', - stroke: theme.palette.border, + fill: theme.palette.secondary.light, + stroke: theme.palette.secondary.light, }, groupNode: { - fill: 'yellowgreen', - stroke: theme.palette.border, + fill: theme.palette.primary.light, + stroke: theme.palette.primary.light, }, centeredContent: { - padding: '10px', + padding: theme.spacing(1), height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'black', }, + textOrganization: { + color: theme.palette.secondary.contrastText, + }, + textGroup: { + color: theme.palette.primary.contrastText, + }, textWrapper: { display: '-webkit-box', WebkitBoxOrient: 'vertical', @@ -70,14 +80,10 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ }, })); -const dimensions = { - nodeWidth: 180, - nodeHeight: 90, - nodeCornerRadius: 20, - nodeAligmentShift: 5, -}; - function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { + const nodeWidth = 180; + const nodeHeight = 60; + const theme = useTheme(); const classes = useStyles(); const catalogEntityRoute = useRouteRef(entityRouteRef); @@ -85,18 +91,22 @@ function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { return ( {props.node.name} - +
-
{props.node.name}
+
+ {props.node.name} +
@@ -108,9 +118,9 @@ function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { return ( {props.node.name} @@ -122,12 +132,11 @@ function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { name: ref.name, })} > - +
-
{props.node.name}
+
+ {props.node.name} +
@@ -146,6 +155,7 @@ export function GroupsDiagram() { }>(); const edges = new Array<{ from: string; to: string; label: string }>(); + const classes = useStyles(); const configApi = useApi(configApiRef); const catalogApi = useApi(catalogApiRef); const organizationName = @@ -218,6 +228,7 @@ export function GroupsDiagram() { nodeMargin={10} direction={DependencyGraphTypes.Direction.RIGHT_LEFT} renderNode={RenderNode} + className={classes.graph} /> { + const classes = useStyles(); + return ( - + Explore your groups. diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index be6abb11ff..551c6af021 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -34,9 +34,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 1fbcdb1a90..7fb897095b 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", @@ -48,9 +48,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 9c81c58f3c..0e8028d0b8 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,9 +42,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 0427f4d7fd..8a6cd56e37 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/integration": "^0.6.3", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -38,9 +38,9 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 0d2aba6af4..97bbee8913 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/integration": "^0.6.3", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", @@ -51,9 +51,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 420a948ec3..f5439d9bc6 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.6.3", "@backstage/integration-react": "^0.1.8", @@ -38,9 +38,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 95300a86af..cec185dadc 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,9 +43,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index ec74cedd37..302c0361a7 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,9 +44,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index c573ebb32b..0092442f02 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-home +## 0.3.0 + +### Minor Changes + +- 7f00902d9: Rename RandomJokeHomePageComponent to HomePageRandomJoke to fit convention, and update example app accordingly. + **NOTE**: If you're using the RandomJoke component in your instance, it now has to be renamed to `HomePageRandomJoke` + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/core-plugin-api@0.1.7 + ## 0.2.1 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 8ae92e8ace..aaf8993d23 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.2.1", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,22 +20,22 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "@types/react": "^16.9", + "@types/react": "*", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-router": "^6.0.0-beta.0", + "react-router": "6.0.0-beta.0", "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/home/src/components/HomepageCompositionRoot.tsx b/plugins/home/src/components/HomepageCompositionRoot.tsx index 92232fb387..f2c5ad0e8b 100644 --- a/plugins/home/src/components/HomepageCompositionRoot.tsx +++ b/plugins/home/src/components/HomepageCompositionRoot.tsx @@ -16,7 +16,6 @@ import React, { ReactNode } from 'react'; import { useOutlet } from 'react-router'; -import { Content, Header, Page } from '@backstage/core-components'; export const HomepageCompositionRoot = (props: { title?: string; @@ -24,10 +23,5 @@ export const HomepageCompositionRoot = (props: { }) => { const outlet = useOutlet(); const children = props.children ?? outlet; - return ( - -
- {children} - - ); + return <>{children}; }; diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index b3591fb903..689a4ec957 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", @@ -38,9 +38,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index c9e0b5a36d..b25cffef61 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -47,9 +47,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 3024d88311..06fb56a17e 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -34,9 +34,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 7d64315e0c..fe4391e1f7 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -32,8 +32,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.1", "@backstage/config": "^0.1.6", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/plugin-kubernetes-common": "^0.1.3", "@backstage/theme": "^0.2.10", @@ -50,9 +50,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 256767ede6..8702dc8f99 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -33,8 +33,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.1", "@backstage/config": "^0.1.6", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -47,9 +47,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index dfb245b65f..16a03ec473 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,9 +42,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/newrelic/src/components/NewRelicFetchComponent/NewRelicFetchComponent.tsx b/plugins/newrelic/src/components/NewRelicFetchComponent/NewRelicFetchComponent.tsx index 72945155fc..40f82d4beb 100644 --- a/plugins/newrelic/src/components/NewRelicFetchComponent/NewRelicFetchComponent.tsx +++ b/plugins/newrelic/src/components/NewRelicFetchComponent/NewRelicFetchComponent.tsx @@ -54,7 +54,7 @@ export const NewRelicAPMTable = ({ applications }: NewRelicApplications) => { return ( diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index b6b94201ce..ff6ed74c6b 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org +## 0.3.22 + +### Patch Changes + +- 6ad8fe1a0: Make ownership card style customizable via custom `theme.getPageTheme()`. +- 70718686f: Use correct `Link` in ownership card to avoid a full reload of the app while navigating. +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/catalog-model@0.9.2 + - @backstage/core-plugin-api@0.1.7 + ## 0.3.21 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 22df2e6606..60abb61e74 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.3.21", + "version": "0.3.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/catalog-model": "^0.9.2", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -36,9 +36,9 @@ "qs": "^6.10.1" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx index da7f8381e3..96e7d08c31 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx @@ -18,6 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { InfoCard, InfoCardVariants, + Link, Progress, ResponseErrorPanel, } from '@backstage/core-components'; @@ -29,18 +30,16 @@ import { isOwnerOf, useEntity, } from '@backstage/plugin-catalog-react'; -import { BackstageTheme, genPageTheme } from '@backstage/theme'; +import { BackstageTheme } from '@backstage/theme'; import { Box, createStyles, Grid, - Link, makeStyles, Typography, } from '@material-ui/core'; import qs from 'qs'; import React from 'react'; -import { generatePath } from 'react-router'; import { useAsync } from 'react-use'; type EntityTypeProps = { @@ -49,16 +48,6 @@ type EntityTypeProps = { count: number; }; -const createPageTheme = ( - theme: BackstageTheme, - shapeKey: string, - colorsKey: string, -) => { - const { colors } = theme.getPageTheme({ themeId: colorsKey }); - const { shape } = theme.getPageTheme({ themeId: shapeKey }); - return genPageTheme(colors, shape).backgroundImage; -}; - const useStyles = makeStyles((theme: BackstageTheme) => createStyles({ card: { @@ -77,7 +66,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => }, entityTypeBox: { background: (props: { type: string }) => - createPageTheme(theme, props.type, props.type), + theme.getPageTheme({ themeId: props.type }).backgroundImage, }, }), ); @@ -96,7 +85,7 @@ const EntityCountTile = ({ const classes = useStyles({ type }); return ( - + ))} diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index b8066e9080..ddeaecb462 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -31,8 +31,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -47,9 +47,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 79cfab6f43..e2384b6b1b 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -48,9 +48,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 5827b82466..1024798587 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend +## 0.15.4 + +### Patch Changes + +- 04aad2dab: Fix issue #7021 scaffolder action fetch:template preserves templates file permissions +- 21ccd4997: GitHub Webhook action in Scaffolder Backend has been improved to validate event names against Octokit Webhook event names list. +- Updated dependencies + - @backstage/catalog-client@0.3.19 + - @backstage/catalog-model@0.9.2 + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + - @backstage/backend-common@0.9.2 + ## 0.15.3 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 902860679d..6be563abaa 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.15.3", + "version": "0.15.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.1", - "@backstage/catalog-client": "^0.3.17", - "@backstage/catalog-model": "^0.9.1", - "@backstage/config": "^0.1.8", - "@backstage/errors": "^0.1.1", + "@backstage/backend-common": "^0.9.2", + "@backstage/catalog-client": "^0.3.19", + "@backstage/catalog-model": "^0.9.2", + "@backstage/config": "^0.1.9", + "@backstage/errors": "^0.1.2", "@backstage/integration": "^0.6.3", "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.2", "@gitbeaker/core": "^30.2.0", @@ -49,7 +49,7 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "10.0.0", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "globby": "^11.0.0", "handlebars": "^4.7.6", "helmet": "^4.0.0", @@ -67,7 +67,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.7.10", + "@backstage/cli": "^0.7.11", "@backstage/test-utils": "^0.1.17", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore deleted file mode 100644 index 4d29575de8..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md deleted file mode 100644 index 305d413b8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `yarn start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.\ -You will also see any lint errors in the console. - -### `yarn test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `yarn build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `yarn eject` - -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** - -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (`webpack`, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml deleted file mode 100644 index df23b90b97..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.component_id | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: website - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/docs/index.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml deleted file mode 100644 index 64f1e9ee8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.component_id | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json deleted file mode 100644 index 2ce9bf6266..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "{{cookiecutter.component_id}}", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.1.0", - "@testing-library/user-event": "^12.1.10", - "@types/jest": "^26.0.15", - "@types/node": "^12.0.0", - "@types/react": "^17.0.0", - "@types/react-dom": "^17.0.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "typescript": "^4.1.2", - "web-vitals": "^1.0.1" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/favicon.ico b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/favicon.ico deleted file mode 100644 index a11777cc47..0000000000 Binary files a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/favicon.ico and /dev/null differ diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html deleted file mode 100644 index aa069f27cb..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - React App - - - -
- - - diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo192.png b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo192.png deleted file mode 100644 index fc44b0a379..0000000000 Binary files a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo192.png and /dev/null differ diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo512.png b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo512.png deleted file mode 100644 index a4e47a6545..0000000000 Binary files a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo512.png and /dev/null differ diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json deleted file mode 100644 index 080d6c77ac..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt deleted file mode 100644 index e9e57dc4d4..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css deleted file mode 100644 index 74b5e05345..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx deleted file mode 100644 index 2a68616d98..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx deleted file mode 100644 index a53698aab3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
- ); -} - -export default App; diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css deleted file mode 100644 index ec2585e8c0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx deleted file mode 100644 index ef2edf8ea3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -ReactDOM.render( - - - , - document.getElementById('root') -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg deleted file mode 100644 index 9dfc1c058c..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5fc6..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16e0f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts deleted file mode 100644 index 8f2609b7b3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json deleted file mode 100644 index a273b0cfc0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": [ - "src" - ] -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml deleted file mode 100644 index 664b405f29..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: create-react-app-template - title: Create React App Template - description: Create a new CRA website project - tags: - - react - - cra -spec: - owner: web@example.com - type: website - parameters: - - title: Provide some simple information - required: - - component_id - - owner - properties: - component_id: - title: Name - type: string - description: Unique name of the component - description: - title: Description - type: string - description: Help others understand what this website is for. - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - steps: - - id: template - name: Fetch Skeleton + Template - action: fetch:cookiecutter - input: - url: ./skeleton - copyWithoutRender: - - .github/workflows/* - values: - component_id: '{{ parameters.component_id }}' - description: '{{ parameters.description }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - owner: '{{ parameters.owner }}' - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml deleted file mode 100644 index 8f18e06d7e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.name | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: documentation - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md deleted file mode 100644 index b6f7344334..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md +++ /dev/null @@ -1,28 +0,0 @@ -## {{ cookiecutter.name }} - -{{ cookiecutter.description }} - -## Getting started - -Start write your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file. - -## Table of Contents - -The Table of Contents on the right is generated automatically based on the hierarchy -of headings. Only use one H1 (`#` in Markdown) per file. - -## Site navigation - -For new pages to appear in the left hand navigation you need edit the `mkdocs.yml` -file in root of your repo. The navigation can also link out to other sites. - -Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section -will be created for you. However, you will not be able to use alternate titles for -pages, or include links to other sites. - -Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work. -See also . - -## Support - -That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord. diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/mkdocs.yml deleted file mode 100644 index 7e1c015f28..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.name | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml deleted file mode 100644 index 1e81242e7a..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml +++ /dev/null @@ -1,86 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: docs-template - title: Documentation Template - description: Create a new standalone documentation project - tags: - - recommended - - techdocs - - mkdocs -spec: - owner: backstage/techdocs-core - type: documentation - - parameters: - - title: Fill in some steps - required: - - name - - description - properties: - name: - title: Name - type: string - description: Unique name of the component - ui:autofocus: true - ui:options: - rows: 5 - description: - title: Description - type: string - description: A description for the component - ui:options: - rows: 5 - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - - # This template is meant to be used on top of an existing tmeplate. - # By adding the following and fetching from an absolute URL you can - # add in the docs template - steps: - - id: fetch - name: Template Docs Skeleton - action: fetch:cookiecutter - input: - url: ./skeleton - values: - name: '{{ parameters.name }}' - description: '{{ parameters.description }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - owner: '{{ parameters.owner }}' - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml b/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml deleted file mode 100644 index b6de1c29ca..0000000000 --- a/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: pull-request - title: Pull Request Action template - description: scaffolder v1beta2 template demo publishing to PR on existing git repository -spec: - owner: backstage/techdocs-core - type: service - - parameters: - - title: Fill in some steps - required: - - name - properties: - name: - title: Name - type: string - description: Unique name of the component - ui:autofocus: true - ui:options: - rows: 5 - description: - title: Description - type: string - description: Description of the component - targetPath: - title: Target Path in repo - type: string - description: Name of the directory to create in the repository - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - - steps: - - id: fetch-base - name: Fetch Base - action: fetch:cookiecutter - input: - url: ./template - values: - name: '{{parameters.name}}' - - - id: fetch-docs - name: Fetch Docs - action: fetch:plain - input: - targetPath: ./community - url: https://github.com/backstage/community/tree/main/backstage-community-sessions - - - id: publish - name: Publish - action: publish:github:pull-request - input: - repoUrl: '{{ parameters.repoUrl }}' - title: 'Create new project: {{parameters.name}}' - branchName: 'create-{{parameters.name}}' - description: | - # New project: {{parameters.name}} - - {{#if parameters.description}} - {{parameters.description}} - {{/if}} - targetPath: '{{#if parameters.targetPath}}{{parameters.targetPath}}{{else}}{{parameters.name}}{{/if}}' - - output: - links: - - url: '{{steps.publish.output.remoteUrl}}' - text: 'Go to PR' diff --git a/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml deleted file mode 100644 index 04f055bd46..0000000000 --- a/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.name | jsonify}} -spec: - type: website - lifecycle: experimental - owner: user:guest diff --git a/plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml b/plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml deleted file mode 100644 index 174c58741b..0000000000 --- a/plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml +++ /dev/null @@ -1,174 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: rails-demo - title: Rails template - description: scaffolder Rails app -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 - railsArguments: - title: arguments to run the rails new command - type: object - properties: - minimal: - title: minimal - description: Preconfigure a minimal rails app - type: boolean - skipBundle: - title: skipBundle - description: Don't run bundle install - type: boolean - skipWebpackInstall: - title: skipWebpackInstall - description: Don't run Webpack install - type: boolean - api: - title: api - description: Preconfigure smaller stack for API only apps - type: boolean - template: - title: template - description: Path to some application template (can be a filesystem path or URL) - type: string - default: './rails-template-file.rb' - webpacker: - title: webpacker - description: 'Preconfigure Webpack with a particular framework (options: react, - vue, angular, elm, stimulus)' - type: string - enum: - - react - - vue - - angular - - elm - - stimulus - database: - title: database - description: 'Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)' - type: string - enum: - - mysql - - postgresql - - sqlite3 - - oracle - - sqlserver - - jdbcmysql - - jdbcsqlite3 - - jdbcpostgresql - - jdbc - railsVersion: - title: Rails version in Gemfile - description: 'Set up the application with Gemfile pointing to a specific version - (options: dev, edge, master)' - type: string - enum: - - dev - - edge - - master - - steps: - - id: fetch-base - name: Fetch Base - action: fetch:rails - input: - url: ./template - values: - name: '{{ parameters.name }}' - owner: '{{ parameters.owner }}' - system: '{{ parameters.system }}' - railsArguments: '{{ json parameters.railsArguments }}' - - - name: Write Catalog information - action: catalog:write - input: - component: - apiVersion: 'backstage.io/v1alpha1' - kind: Component - metadata: - name: '{{ parameters.name }}' - annotations: - github.com/project-slug: '{{ projectSlug parameters.repoUrl }}' - spec: - type: service - lifecycle: production - owner: '{{ 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 }}' diff --git a/plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb b/plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb deleted file mode 100644 index 90c0d53ddc..0000000000 --- a/plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb +++ /dev/null @@ -1,14 +0,0 @@ -gem_group :development, :test do - gem "rspec" - gem "rspec-rails" -end - -rakefile("example.rake") do - <<-TASK - namespace :example do - task :backstage do - puts "i like backstage!" - end - end - TASK -end diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig deleted file mode 100644 index 23c3268a0f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -# editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -insert_final_newline = false \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore deleted file mode 100644 index 8b13789179..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js deleted file mode 100644 index 8623614899..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@spotify/web-scripts/config/eslintrc.js'); diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml deleted file mode 100644 index 907da7e820..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Frontend CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x] - - steps: - - name: checkout code - uses: actions/checkout@v1 - - name: get yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: yarn install, build, and test - working-directory: . - run: | - yarn install - yarn build - yarn test - env: - CI: true diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore deleted file mode 100644 index 4d1e0d9635..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -/node_modules - -# testing -/coverage -junit.xml - -# build -/build - -# misc -.DS_Store - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md deleted file mode 100644 index 415065179b..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# {{ cookiecutter.component_id }} - -## Description - -{{ cookiecutter.description }} - -## Develop - -```bash -# install dependencies -$ yarn -# start development server -$ yarn dev -``` \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js deleted file mode 100644 index db2d280677..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - plugins: ['babel-plugin-styled-components'], - presets: ['next/babel', '@zeit/next-typescript/babel'], -}; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml deleted file mode 100644 index df23b90b97..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.component_id | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: website - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md deleted file mode 100644 index 2a0f572ff2..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md +++ /dev/null @@ -1,28 +0,0 @@ -# {{ cookiecutter.component_id }} - -{{ cookiecutter.description }} - -## Getting started - -Start writing your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file. - -## Table of Contents - -The Table of Contents on the right is generated automatically based on the hierarchy -of headings. Only use one H1 (`#` in Markdown) per file. - -## Site navigation - -For new pages to appear in the left hand navigation you need edit the `mkdocs.yml` -file in root of your repo. The navigation can also link out to other sites. - -Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section -will be created for you. However, you will not be able to use alternate titles for -pages, or include links to other sites. - -Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work. -See also . - -## Support - -That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord. diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/jest.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/jest.config.js deleted file mode 100644 index 0dc2a9c0ca..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - ...require('@spotify/web-scripts/config/jest.config.js'), - testEnvironment: 'jsdom', - testPathIgnorePatterns: ['/node_modules/', '/build/'], - transform: { - '^.+\\.tsx?$': 'babel-jest', - }, -}; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml deleted file mode 100644 index 64f1e9ee8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.component_id | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts deleted file mode 100644 index fd10310f68..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -// read more about this file here ---> https://github.com/zeit/next.js/blob/canary/docs/basic-features/typescript.md -/* eslint spaced-comment: ["error", "always", { "markers": ["/"] }] */ - -/// -/// diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js deleted file mode 100644 index be8b159307..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - distDir: 'build', -}; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json deleted file mode 100644 index 75925bf567..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "{{ cookiecutter.component_id }}", - "version": "0.0.0", - "description": "{{ cookiecutter.description }}", - "license": "UNLICENSED", - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start", - "lint": "web-scripts lint --ignore-path=.gitignore", - "test": "web-scripts test --config jest.config.js", - "pretest:ci": "yarn lint", - "test:ci": "yarn test --ci --coverage --reporters=default --reporters=jest-junit" - }, - "dependencies": { - "@zeit/next-typescript": "^1.1.1", - "babel-plugin-styled-components": "^1.10.6", - "next": "^9.1.1", - "react": "^16.8.5", - "react-dom": "^16.8.5", - "styled-components": "^4.3.2" - }, - "devDependencies": { - "@spotify/tsconfig": "^5.0.0", - "@spotify/web-scripts": "^5.0.0", - "@testing-library/react": "^8.0.1", - "@types/node": "^14.14.32", - "@types/react": "^16.8.7", - "@types/react-dom": "^16.8.2", - "@types/styled-components": "^4.1.18", - "husky": "^2.7.0", - "jest-junit": "^8.0.0", - "typescript": "^3.9.2" - }, - "husky": { - "hooks": { - "pre-commit": "web-scripts precommit" - } - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js deleted file mode 100644 index 93df970dd6..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@spotify/web-scripts/config/prettier.config.js'); diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/public/static/fonts.css b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/public/static/fonts.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx deleted file mode 100644 index 8612ead08f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { cleanup, render } from '@testing-library/react'; - -import Index from '../pages/index'; - -afterEach(cleanup); - -describe('Index', () => { - it('Says hello', () => { - const { queryByText } = render(); - expect(queryByText('Hello!')).toBeTruthy(); - }); -}); diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx deleted file mode 100644 index f8ba94310a..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react'; - -export const Header = () =>

Header

; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx deleted file mode 100644 index a3853e47c9..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import App from 'next/app'; -import Head from 'next/head'; -import styled from 'styled-components'; -import { Header } from '../components/Header'; - -const StyledApp = styled.div` - > * { - padding-left: 16px; - padding-right: 16px; - } -`; - -const Main = styled.div` - margin: 2em auto; - height: 85vh; -`; - -class CustomApp extends App { - render() { - const { Component, pageProps } = this.props; - - return ( - - - - -
-
- -
- - ); - } -} - -export default CustomApp; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx deleted file mode 100644 index 9b01a6a74c..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This file extends the and injects the server side rendered styles into the - * By server-side rendering CSS we avoid visual changes in the layout while loading the JS. - * - * Taken from this example: - * https://github.com/zeit/next.js/tree/master/examples/with-styled-components - */ - -import React from 'react'; -import Document, { DocumentContext, DocumentInitialProps } from 'next/document'; -import { ServerStyleSheet } from 'styled-components'; - -export default class MyDocument extends Document { - static async getInitialProps( - ctx: DocumentContext, - ): Promise { - const sheet = new ServerStyleSheet(); - const originalRenderPage = ctx.renderPage; - - try { - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App: any) => (props) => - sheet.collectStyles(), - }); - - const initialProps = await Document.getInitialProps(ctx); - return { - ...initialProps, - styles: ( - <> - {initialProps.styles} - {sheet.getStyleElement()} - - ), - }; - } finally { - sheet.seal(); - } - } -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts deleted file mode 100644 index 8bc772c182..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; - -export default function handle(_: NextApiRequest, res: NextApiResponse) { - res.status(200).send('ok'); -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx deleted file mode 100644 index c01d844f9f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -const Index = () =>

Hello!

; - -export default Index; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json deleted file mode 100644 index 03508ff043..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "@spotify/tsconfig", - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve" - }, - "exclude": ["node_modules", "output/node_modules"], - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml deleted file mode 100644 index 49802a9d62..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: react-ssr-template - title: React SSR Template - description: Create a website powered with Next.js - tags: - - recommended - - react -spec: - owner: web@example.com - type: website - parameters: - - title: Provide some simple information - required: - - component_id - - owner - properties: - component_id: - title: Name - type: string - description: Unique name of the component - description: - title: Description - type: string - description: Help others understand what this website is for. - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - steps: - - id: template - name: Fetch Skeleton + Template - action: fetch:cookiecutter - input: - url: ./skeleton - copyWithoutRender: - - .github/workflows/* - values: - component_id: '{{ parameters.component_id }}' - description: '{{ parameters.description }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - owner: '{{ parameters.owner }}' - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig deleted file mode 100644 index 03eb226ef0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -continuation_indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml deleted file mode 100644 index 04e1d10b47..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Java CI with Maven - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Build with Maven - run: mvn -B package --file pom.xml diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore deleted file mode 100644 index 1504daa184..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -target/ -## IntelliJ -.idea -*.iml - -## Temp -dependency-reduced-pom.xml diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile deleted file mode 100644 index 1c8a969c8c..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM openjdk:11-alpine -ENTRYPOINT ["/usr/bin/{{cookiecutter.artifact_id}}.sh"] - -COPY {{cookiecutter.artifact_id}}.sh /usr/bin/{{cookiecutter.artifact_id}}.sh -COPY target/{{cookiecutter.artifact_id}}.jar /usr/share/{{cookiecutter.artifact_id}}/{{cookiecutter.artifact_id}}.jar diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md deleted file mode 100644 index 710b6b557e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# {{cookiecutter.component_id}} - -{{cookiecutter.description}} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker deleted file mode 100644 index 1dcf39a3b3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker +++ /dev/null @@ -1 +0,0 @@ -(This marker file causes Maven to build this artifact as a service) diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml deleted file mode 100644 index 4ddec9c032..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.component_id | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: service - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md deleted file mode 100644 index 5352ef7801..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md +++ /dev/null @@ -1,28 +0,0 @@ -## {{ cookiecutter.component_id }} - -{{ cookiecutter.description }} - -## Getting started - -Start write your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file. - -## Table of Contents - -The Table of Contents on the right is generated automatically based on the hierarchy -of headings. Only use one H1 (`#` in Markdown) per file. - -## Site navigation - -For new pages to appear in the left hand navigation you need edit the `mkdocs.yml` -file in root of your repo. The navigation can also link out to other sites. - -Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section -will be created for you. However, you will not be able to use alternate titles for -pages, or include links to other sites. - -Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work. -See also . - -## Support - -That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord. diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/mkdocs.yml deleted file mode 100644 index 64f1e9ee8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.component_id | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml deleted file mode 100644 index d5e81cf5e0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml +++ /dev/null @@ -1,221 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-starter-parent - 2.2.4.RELEASE - - - com.example - {{cookiecutter.artifact_id}} - 0.0.1-SNAPSHOT - jar - - {{cookiecutter.artifact_id}} - {{cookiecutter.description}} - - - 11 - ${java.version} - ${java.version} - ${java.version} - UTF-8 - UTF-8 - - - - jcenter - https://jcenter.bintray.com/ - - - - - io.github.lognet - grpc-spring-boot-starter - 3.5.1 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.apache.tomcat - annotations-api - 6.0.53 - provided - - - - - - ${project.artifactId} - - - kr.motd.maven - os-maven-plugin - 1.4.0.Final - - - - - - maven-compiler-plugin - 3.8.1 - - true - true - - -Xlint:all - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - - ${project.basedir}/src/main/resources - - com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier} - - grpc-java - - io.grpc:protoc-gen-grpc-java:1.13.1:exe:${os.detected.classifier} - - - - - - compile - compile-custom - - - - - - - - maven-failsafe-plugin - 2.22.2 - - - **/*IT.java - **/integrationtests/*.java - **/it/**/*.java - - - - - - - com.coveo - fmt-maven-plugin - 2.8 - - - - format - - - - - - org.jacoco - jacoco-maven-plugin - 0.8.5 - - - - prepare-agent - report - - - - - - maven-enforcer-plugin - - - enforce - - enforce - - - - - - - - - - - - maven-jar-plugin - - - true - - ${start-class} - true - true - - - - ${project.version}-${buildTimestamp}-${buildNumber} - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - build-info - - build-info - - - - - - maven-source-plugin - - - attach-sources - - jar - - - - - - maven-javadoc-plugin - - private - - - - attach-javadocs - - jar - - - - - - - diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java deleted file mode 100644 index 7ead2562d0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.example.{{cookiecutter.java_package_name}}; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java deleted file mode 100644 index 90b8412a04..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.example.{{cookiecutter.java_package_name}}; - -import com.example.{{cookiecutter.java_package_name}}.proto.GreeterGrpc; -import com.example.{{cookiecutter.java_package_name}}.proto.GreeterOuterClass; -import io.grpc.stub.StreamObserver; -import org.lognet.springboot.grpc.GRpcService; - -@GRpcService -public class GreeterService extends GreeterGrpc.GreeterImplBase { - - @Override - public void sayHello(final GreeterOuterClass.HelloRequest request, - final StreamObserver responseObserver) { - final GreeterOuterClass.HelloReply.Builder replyBuilder = - GreeterOuterClass.HelloReply.newBuilder().setMessage("Hello " + request.getName()); - responseObserver.onNext(replyBuilder.build()); - responseObserver.onCompleted(); - } -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties deleted file mode 100644 index 03be3b6ebf..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties +++ /dev/null @@ -1,3 +0,0 @@ -spring.application.name={{cookiecutter.component_id}} -server.port={{cookiecutter.http_port}} -grpc.enableReflection=true diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt deleted file mode 100644 index 46846ba35e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt +++ /dev/null @@ -1,22 +0,0 @@ -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto deleted file mode 100644 index 8015d914ee..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; - -option java_package = "com.example.{{cookiecutter.java_package_name}}.proto"; - -service Greeter { - rpc SayHello ( HelloRequest) returns ( HelloReply) {} -} - -// The request message containing the user's name. -message HelloRequest { - string name = 1; -} - -// The response message containing the greetings -message HelloReply { - string message = 1; -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java deleted file mode 100644 index 1d7298172a..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.{{cookiecutter.java_package_name}}; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class ApplicationTest { - - @Test - public void contextLoads() { - } -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh deleted file mode 100755 index c69b980dc8..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec /usr/bin/java $JVM_DEFAULT_ARGS $JVM_ARGS -jar /usr/share/{{cookiecutter.artifact_id}}/{{cookiecutter.artifact_id}}.jar "$@" diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml deleted file mode 100644 index c306187581..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml +++ /dev/null @@ -1,88 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: springboot-template - title: Spring Boot gRPC Service - description: Create a simple microservice using gRPC and Spring Boot Java - tags: - - recommended - - java - - grpc -spec: - owner: service@example.com - type: service - - parameters: - - title: Provide some simple information - required: - - component_id - - owner - - java_package_name - properties: - component_id: - title: Name - type: string - description: Unique name of the component - java_package_name: - title: Java Package Name - type: string - description: Name for the java package. eg (io.backstage.blah) - description: - title: Description - type: string - description: Help others understand what this website is for. - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - - steps: - - id: template - name: Fetch Skeleton + Template - action: fetch:cookiecutter - input: - url: ./skeleton - copyWithoutRender: - - .github/workflows/* - values: - component_id: '{{ parameters.component_id }}' - description: '{{ parameters.description }}' - artifact_id: '{{ parameters.component_id }}' - java_package_name: '{{ parameters.java_package_name }}' - owner: '{{ parameters.owner }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - http_port: 8080 - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml b/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml deleted file mode 100644 index b3228a20d0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: v1beta2-demo - title: Test Action template - description: scaffolder v1beta2 template demo publishing to github -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: fetch-base - name: Fetch Base - action: fetch:cookiecutter - input: - url: ./template - values: - name: '{{ parameters.name }}' - owner: '{{ parameters.owner }}' - system: '{{ parameters.system }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - - - id: fetch-docs - name: Fetch Docs - action: fetch:plain - input: - targetPath: ./community - url: https://github.com/backstage/community/tree/main/backstage-community-sessions - - - 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 }}' diff --git a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml deleted file mode 100644 index 02da577b39..0000000000 --- a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.name | jsonify}} - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} -spec: - type: website - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} -{%- if 'system' in cookiecutter %} - system: {{ cookiecutter.system | jsonify }} -{%- endif %} diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index f180e67011..922d583a57 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder +## 0.11.3 + +### Patch Changes + +- c299e90a2: Disable all buttons in the final step when 'Create' button is clicked in template. +- 3acf5988f: Fix display error when it fails to load a template (/create) page +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/catalog-client@0.3.19 + - @backstage/catalog-model@0.9.2 + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + - @backstage/core-plugin-api@0.1.7 + ## 0.11.2 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index c457da8b15..55f9d3b934 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.11.2", + "version": "0.11.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.18", - "@backstage/catalog-model": "^0.9.1", - "@backstage/config": "^0.1.7", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", - "@backstage/errors": "^0.1.1", + "@backstage/catalog-client": "^0.3.19", + "@backstage/catalog-model": "^0.9.2", + "@backstage/config": "^0.1.9", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", + "@backstage/errors": "^0.1.2", "@backstage/integration": "^0.6.3", "@backstage/integration-react": "^0.1.8", "@backstage/plugin-catalog-react": "^0.4.5", @@ -47,7 +47,7 @@ "@rjsf/material-ui": "^3.0.0", "@types/react": "*", "classnames": "^2.2.6", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "humanize-duration": "^3.25.1", "immer": "^9.0.1", "json-schema": "^0.3.0", @@ -63,9 +63,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index cdcc22da29..62c2881ab0 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -112,6 +112,7 @@ export const MultistepJsonForm = ({ widgets, }: Props) => { const [activeStep, setActiveStep] = useState(0); + const [disableButtons, setDisableButtons] = useState(false); const handleReset = () => { setActiveStep(0); @@ -121,6 +122,10 @@ export const MultistepJsonForm = ({ setActiveStep(Math.min(activeStep + 1, steps.length)); }; const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0)); + const handleCreate = () => { + setDisableButtons(true); + onFinish(); + }; return ( <> @@ -172,9 +177,18 @@ export const MultistepJsonForm = ({ metadata={getReviewData(formData, steps)} /> - - - + + diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx index 534b3b6ce9..3d0267ff1f 100644 --- a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx +++ b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx @@ -94,6 +94,7 @@ describe('', () => { , ); expect(rendered.getByText('Categories')).toBeInTheDocument(); + fireEvent.click(rendered.getByTestId('categories-picker-expand')); entities.forEach(entity => { expect( @@ -116,22 +117,27 @@ describe('', () => { , ); + fireEvent.click(rendered.getByTestId('categories-picker-expand')); expect(rendered.getByLabelText('Service')).not.toBeChecked(); expect(rendered.getByLabelText('Website')).not.toBeChecked(); fireEvent.click(rendered.getByLabelText('Service')); + fireEvent.click(rendered.getByTestId('categories-picker-expand')); expect(rendered.getByLabelText('Service')).toBeChecked(); expect(rendered.getByLabelText('Website')).not.toBeChecked(); fireEvent.click(rendered.getByLabelText('Website')); + fireEvent.click(rendered.getByTestId('categories-picker-expand')); expect(rendered.getByLabelText('Service')).toBeChecked(); expect(rendered.getByLabelText('Website')).toBeChecked(); fireEvent.click(rendered.getByLabelText('Service')); + fireEvent.click(rendered.getByTestId('categories-picker-expand')); expect(rendered.getByLabelText('Service')).not.toBeChecked(); expect(rendered.getByLabelText('Website')).toBeChecked(); fireEvent.click(rendered.getByLabelText('Website')); + fireEvent.click(rendered.getByTestId('categories-picker-expand')); expect(rendered.getByLabelText('Service')).not.toBeChecked(); expect(rendered.getByLabelText('Website')).not.toBeChecked(); }); diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx index 7151446f4f..a8418cfded 100644 --- a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx +++ b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx @@ -21,22 +21,20 @@ import { Box, Checkbox, FormControlLabel, - FormGroup, - makeStyles, - Theme, + TextField, Typography, } from '@material-ui/core'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { Autocomplete } from '@material-ui/lab'; import { useEntityTypeFilter } from '@backstage/plugin-catalog-react'; import { alertApiRef, useApi } from '@backstage/core-plugin-api'; -const useStyles = makeStyles(theme => ({ - checkbox: { - padding: theme.spacing(1, 1, 1, 2), - }, -})); +const icon = ; +const checkedIcon = ; export const TemplateTypePicker = () => { - const classes = useStyles(); const alertApi = useApi(alertApiRef); const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter(); @@ -53,32 +51,31 @@ export const TemplateTypePicker = () => { return null; } - function toggleSelection(type: string) { - setSelectedTypes( - selectedTypes.includes(type) - ? selectedTypes.filter(t => t !== type) - : [...selectedTypes, type], - ); - } - return ( Categories - - {availableTypes.map(type => ( + + multiple + aria-label="Categories" + options={availableTypes} + value={selectedTypes} + onChange={(_: object, value: string[]) => setSelectedTypes(value)} + renderOption={(option, { selected }) => ( toggleSelection(type)} - className={classes.checkbox} + icon={icon} + checkedIcon={checkedIcon} + checked={selected} /> } - label={capitalize(type)} - key={type} + label={capitalize(option)} /> - ))} - + )} + size="small" + popupIcon={} + renderInput={params => } + /> ); }; diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index de2af423dd..cb869810dc 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search +## 0.4.10 + +### Patch Changes + +- 7f00902d9: Add Home Page Search Bar Component, to be included in composable Home Page. +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/catalog-model@0.9.2 + - @backstage/errors@0.1.2 + - @backstage/config@0.1.9 + - @backstage/core-plugin-api@0.1.7 + ## 0.4.9 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 900ea3c11d..c60ae7c5d8 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "0.4.9", + "version": "0.4.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.1", - "@backstage/config": "^0.1.8", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", - "@backstage/errors": "^0.1.1", + "@backstage/catalog-model": "^0.9.2", + "@backstage/config": "^0.1.9", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", + "@backstage/errors": "^0.1.2", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/search-common": "^0.2.0", "@backstage/theme": "^0.2.10", @@ -49,9 +49,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/search/src/components/SearchType/SearchType.stories.tsx b/plugins/search/src/components/SearchType/SearchType.stories.tsx new file mode 100644 index 0000000000..da662d8fef --- /dev/null +++ b/plugins/search/src/components/SearchType/SearchType.stories.tsx @@ -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 React, { useState } from 'react'; + +import { SearchType } from '../index'; +import { SearchContext } from '../SearchContext'; + +export default { + title: 'Plugins/Search/SearchType', + component: SearchType, +}; + +const values = ['value-1', 'value-2', 'value-3']; + +export const Default = () => { + const [types, setTypes] = useState([]); + + return ( + + + + ); +}; diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 5b7f174583..7241858e78 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -47,9 +47,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 1df0981b18..a5e03e97e4 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -36,9 +36,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 3fce16b5fe..e821dd0bf5 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -48,9 +48,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 5d821dd44b..b3704d7551 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -31,8 +31,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -46,9 +46,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index cede08d9f4..46b279c3fd 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -30,8 +30,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,9 +44,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index a8ca36e2dd..2628ac925c 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -33,14 +33,14 @@ "dependencies": { "@backstage/catalog-model": "^0.9.1", "@backstage/config": "^0.1.8", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", - "@backstage/plugin-search": "^0.4.9", "@backstage/integration": "^0.6.3", "@backstage/integration-react": "^0.1.8", "@backstage/plugin-catalog": "^0.6.14", "@backstage/plugin-catalog-react": "^0.4.5", + "@backstage/plugin-search": "^0.4.9", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,7 +49,7 @@ "@types/react": "*", "dompurify": "^2.2.9", "event-source-polyfill": "^1.0.25", - "git-url-parse": "~11.4.4", + "git-url-parse": "^11.6.0", "lodash": "^4.17.21", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -60,9 +60,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index d867c6fe77..bbab997e8f 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-todo +## 0.1.9 + +### Patch Changes + +- a3734ba27: Fix TodoList table column widths +- b07378742: All types are now properly documented and exported. +- Updated dependencies + - @backstage/core-components@0.4.1 + - @backstage/catalog-model@0.9.2 + - @backstage/errors@0.1.2 + - @backstage/core-plugin-api@0.1.7 + ## 0.1.8 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index eb02b12a20..73b29f14e1 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-todo", - "version": "0.1.8", + "version": "0.1.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -26,10 +26,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.1", - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", - "@backstage/errors": "^0.1.1", + "@backstage/catalog-model": "^0.9.2", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", + "@backstage/errors": "^0.1.2", "@backstage/plugin-catalog-react": "^0.4.5", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -40,9 +40,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index cef50d1f2b..73208f3ec6 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", "@backstage/core-plugin-api": "^0.1.5", - "@backstage/dev-utils": "^0.2.8", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/user-settings/src/components/FeatureFlags/EmptyFlags.tsx b/plugins/user-settings/src/components/FeatureFlags/EmptyFlags.tsx index 878141f7dc..785f398d6f 100644 --- a/plugins/user-settings/src/components/FeatureFlags/EmptyFlags.tsx +++ b/plugins/user-settings/src/components/FeatureFlags/EmptyFlags.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { Button, Typography } from '@material-ui/core'; import { CodeSnippet, EmptyState } from '@backstage/core-components'; -const EXAMPLE = `import { createPlugin } from '@backstage/core'; +const EXAMPLE = `import { createPlugin } from '@backstage/core-plugin-api'; export default createPlugin({ id: 'plugin-name', diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 9dbf2a3a04..07989801c8 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -30,8 +30,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,9 +42,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index dc72855765..a5cadb03ac 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.4.0", - "@backstage/core-plugin-api": "^0.1.6", + "@backstage/core-components": "^0.4.1", + "@backstage/core-plugin-api": "^0.1.7", "@backstage/errors": "^0.1.1", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -34,9 +34,9 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.10", - "@backstage/core-app-api": "^0.1.11", - "@backstage/dev-utils": "^0.2.8", + "@backstage/cli": "^0.7.11", + "@backstage/core-app-api": "^0.1.12", + "@backstage/dev-utils": "^0.2.9", "@backstage/test-utils": "^0.1.17", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index ab5f063fb4..6b34b4a82b 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -31,8 +31,14 @@ import { CompilerState, ExtractorLogLevel, } from '@microsoft/api-extractor'; +import { DocNode, IDocNodeContainerParameters } from '@microsoft/tsdoc'; import { ApiPackage, ApiModel } from '@microsoft/api-extractor-model'; -import { MarkdownDocumenter } from '@microsoft/api-documenter/lib/documenters/MarkdownDocumenter'; +import { + IMarkdownDocumenterOptions, + MarkdownDocumenter, +} from '@microsoft/api-documenter/lib/documenters/MarkdownDocumenter'; +import { CustomMarkdownEmitter } from '@microsoft/api-documenter/lib/markdown/CustomMarkdownEmitter'; +import { IMarkdownEmitterContext } from '@microsoft/api-documenter/lib/markdown/MarkdownEmitter'; const tmpDir = resolvePath(__dirname, '../node_modules/.cache/api-extractor'); @@ -89,7 +95,6 @@ const SKIPPED_PACKAGES = [ 'packages/cli', 'packages/codemods', 'packages/create-app', - 'packages/docgen', 'packages/e2e-test', 'packages/storybook', 'packages/techdocs-cli', @@ -298,10 +303,13 @@ async function runApiExtraction({ } } -function isComponentMember(member: any) { - // React components are annotated with @component, and we want to skip those - return Boolean(member.docComment.match(/\n\s*\**\s*@component/m)); -} +/* +WARNING: Bring a blanket if you're gonna read the code below + +There's some weird shit going on here, and it's because we cba +forking rushstash to modify the api-documenter markdown generation, +which otherwise is the recommended way to do customizations. +*/ async function buildDocs({ inputDir, @@ -310,6 +318,8 @@ async function buildDocs({ inputDir: string; outputDir: string; }) { + // We start by constructing our own model from the files so that + // we get a change to modify them, as the model is otherwise read-only. const parseFile = async (filename: string): Promise => { console.log(`Reading ${filename}`); return fs.readJson(resolvePath(inputDir, filename)); @@ -324,9 +334,7 @@ async function buildDocs({ const newModel = new ApiModel(); for (const serialized of serializedPackages) { - serialized.members[0].members = serialized.members[0].members.filter( - member => !isComponentMember(member), - ); + // Add any docs filtering logic here const pkg = ApiPackage.deserialize( serialized, @@ -335,10 +343,132 @@ async function buildDocs({ newModel.addMember(pkg); } - await fs.remove(outputDir); - await fs.ensureDir(outputDir); + // The doc AST need to be extended with custom nodes if we want to + // add any extra content. + // This one is for the YAML front matter that we need for the microsite. + class DocFrontMatter extends DocNode { + static kind = 'DocFrontMatter'; - const documenter = new MarkdownDocumenter({ + public readonly id: string; + public readonly title: string; + public readonly description: string; + + public constructor( + parameters: IDocNodeContainerParameters & { + id: string; + title: string; + description: string; + }, + ) { + super(parameters); + this.id = parameters.id; + this.title = parameters.title; + this.description = parameters.description; + } + + /** @override */ + public get kind(): string { + return DocFrontMatter.kind; + } + } + + // This is where we actually write the markdown and where we can hook + // in the rendering of our own nodes. + class CustomCustomMarkdownEmitter extends CustomMarkdownEmitter { + /** @override */ + protected writeNode( + docNode: DocNode, + context: IMarkdownEmitterContext, + docNodeSiblings: boolean, + ): void { + switch (docNode.kind) { + case DocFrontMatter.kind: { + const node = docNode as DocFrontMatter; + context.writer.writeLine('---'); + context.writer.writeLine(`id: ${node.id}`); + context.writer.writeLine(`title: ${node.title}`); + context.writer.writeLine(`description: ${node.description}`); + context.writer.writeLine('---'); + context.writer.writeLine(); + break; + } + default: + super.writeNode(docNode, context, docNodeSiblings); + } + } + + /** @override */ + emit(stringBuilder, docNode, options) { + // Hack to get rid of the leading comment of each file, since + // we want the front matter to come first + stringBuilder._chunks.length = 0; + return super.emit(stringBuilder, docNode, options); + } + } + + class CustomMarkdownDocumenter extends (MarkdownDocumenter as any) { + constructor(options: IMarkdownDocumenterOptions) { + super(options); + + // It's a strict model, we gotta register the allowed usage of our new node + this._tsdocConfiguration.docNodeManager.registerDocNodes( + '@backstage/docs', + [{ docNodeKind: DocFrontMatter.kind, constructor: DocFrontMatter }], + ); + this._tsdocConfiguration.docNodeManager.registerAllowableChildren( + 'Paragraph', + [DocFrontMatter.kind], + ); + + this._markdownEmitter = new CustomCustomMarkdownEmitter(newModel); + } + + // We don't really get many chances to modify the generated AST + // so we hook in wherever we can. In this case we add the front matter + // just before writing the breadcrumbs at the top. + /** @override */ + _writeBreadcrumb(output, apiItem) { + let title; + let description; + + const name = apiItem.getScopedNameWithinPackage(); + if (name) { + title = name; + description = `API reference for ${apiItem.getScopedNameWithinPackage()}`; + } else if (apiItem.kind === 'Model') { + title = 'Package Index'; + description = 'Index of all Backstage Packages'; + } else { + title = apiItem.name; + description = `API Reference for ${apiItem.name}`; + } + + // Add our front matter + output.appendNodeInParagraph( + new DocFrontMatter({ + configuration: this._tsdocConfiguration, + id: this._getFilenameForApiItem(apiItem).slice(0, -3), + title, + description, + }), + ); + + // Now write the actual breadcrumbs + super._writeBreadcrumb(output, apiItem); + + // We wanna ignore the header that always gets written after the breadcrumb + // This otherwise becomes more or less a duplicate of the title in the front matter + const oldAppendNode = output.appendNode; + output.appendNode = () => { + output.appendNode = oldAppendNode; + }; + } + } + + // This is root of the documentation generation, but it's not directly + // responsible for generating markdown, it just constructs an AST that + // is the consumed by an emitter to actually write the files. + const documenter = new CustomMarkdownDocumenter({ apiModel: newModel, documenterConfig: { outputTarget: 'markdown', @@ -350,6 +480,9 @@ async function buildDocs({ outputFolder: outputDir, }); + // Clean up existing stuff and write ALL the docs! + await fs.remove(outputDir); + await fs.ensureDir(outputDir); documenter.generateFiles(); } diff --git a/yarn.lock b/yarn.lock index c7c260e1bf..e7a87bd787 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4540,45 +4540,45 @@ resolved "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== -"@microsoft/api-documenter@^7.13.30": - version "7.13.30" - resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.13.30.tgz#f4832b8747ad9f61b3a0d87eb61b6b1aca2aeb60" - integrity sha512-n91XihJptwcHp1g5FUIcrjDXhg/g2q6+Rj+nuPBkvsCAKQP/OwCLNVO3tYNpz+qa+lWrHPWL3Urc8G3th5cn7w== +"@microsoft/api-documenter@^7.13.47": + version "7.13.47" + resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.13.47.tgz#0b7726634232b37f76c0e5e8353cdbb5b52d4ece" + integrity sha512-jk78Pf8cKL2WZf6CkKUUtwegdsTA1Jf0MfIzD50qpG7T257HLrqCi1t70ZA85VpRLR8oSeNHMayqNTWkdku9iA== dependencies: - "@microsoft/api-extractor-model" "7.13.3" + "@microsoft/api-extractor-model" "7.13.5" "@microsoft/tsdoc" "0.13.2" - "@rushstack/node-core-library" "3.39.0" - "@rushstack/ts-command-line" "4.8.0" + "@rushstack/node-core-library" "3.40.0" + "@rushstack/ts-command-line" "4.9.0" colors "~1.2.1" js-yaml "~3.13.1" resolve "~1.17.0" -"@microsoft/api-extractor-model@7.13.3", "@microsoft/api-extractor-model@^7.13.3": - version "7.13.3" - resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.3.tgz#ac01c064c5af520d3661c85d7e5ef95e1ca8ab92" - integrity sha512-uXilAhu2GcvyY/0NwVRk3AN7TFYjkPnjHLV2UywTTz9uglS+Af0YjNrCy+aaK8qXtfbFWdBzkH9N2XU8/YBeRQ== +"@microsoft/api-extractor-model@7.13.5", "@microsoft/api-extractor-model@^7.13.5": + version "7.13.5" + resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.5.tgz#7836a81ba47b9a654062ed0361e4eee69afae51e" + integrity sha512-il6AebNltYo5hEtqXZw4DMvrwBPn6+F58TxwqmsLY+U+sSJNxaYn2jYksArrjErXVPR3gUgRMqD6zsdIkg+WEQ== dependencies: "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.39.0" + "@rushstack/node-core-library" "3.40.0" -"@microsoft/api-extractor@^7.18.1": - version "7.18.1" - resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.18.1.tgz#61b39f972b646261dd49f2de9f5d448aa6497e7a" - integrity sha512-qljUF2Q0zAx1vJrjKkJVGN7OVbsXki+Pji99jywyl6L/FK3YZ7PpstUJYE6uBcLPy6rhNPWPAsHNTMpG/kHIsg== +"@microsoft/api-extractor@^7.18.7": + version "7.18.7" + resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.18.7.tgz#851d2413a3c5d696f7cc914eb59de7a7882b2e8b" + integrity sha512-JhtV8LoyLuIecbgCPyZQg08G1kngIRWpai2UzwNil9mGVGYiDZVeeKx8c2phmlPcogmMDm4oQROxyuiYt5sJiw== dependencies: - "@microsoft/api-extractor-model" "7.13.3" + "@microsoft/api-extractor-model" "7.13.5" "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.39.0" - "@rushstack/rig-package" "0.2.12" - "@rushstack/ts-command-line" "4.8.0" + "@rushstack/node-core-library" "3.40.0" + "@rushstack/rig-package" "0.3.0" + "@rushstack/ts-command-line" "4.9.0" colors "~1.2.1" lodash "~4.17.15" resolve "~1.17.0" semver "~7.3.0" source-map "~0.6.1" - typescript "~4.3.2" + typescript "~4.3.5" "@microsoft/fetch-event-source@2.0.1": version "2.0.1" @@ -4600,7 +4600,7 @@ jju "~1.4.0" resolve "~1.19.0" -"@microsoft/tsdoc@0.13.2": +"@microsoft/tsdoc@0.13.2", "@microsoft/tsdoc@^0.13.2": version "0.13.2" resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz#3b0efb6d3903bd49edb073696f60e90df08efb26" integrity sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg== @@ -5217,10 +5217,10 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@rushstack/node-core-library@3.39.0": - version "3.39.0" - resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.39.0.tgz#38928946d15ae89b773386cf97433d0d1ec83b93" - integrity sha512-kgu3+7/zOBkZU0+NdJb1rcHcpk3/oTjn5c8cg5nUTn+JDjEw58yG83SoeJEcRNNdl11dGX0lKG2PxPsjCokZOQ== +"@rushstack/node-core-library@3.40.0": + version "3.40.0" + resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.40.0.tgz#2551915ea34e34ec2abb7172b9d7f4546144d9d4" + integrity sha512-P6uMPI7cqTdawLSPAG5BQrBu1MHlGRPqecp7ruIRgyukIEzkmh0QAnje4jAL/l1r3hw0qe4e+Dz5ZSnukT/Egg== dependencies: "@types/node" "10.17.13" colors "~1.2.1" @@ -5232,18 +5232,18 @@ timsort "~0.3.0" z-schema "~3.18.3" -"@rushstack/rig-package@0.2.12": - version "0.2.12" - resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.12.tgz#c434d62b28e0418a040938226f8913971d0424c7" - integrity sha512-nbePcvF8hQwv0ql9aeQxcaMPK/h1OLAC00W7fWCRWIvD2MchZOE8jumIIr66HGrfG2X1sw++m/ZYI4D+BM5ovQ== +"@rushstack/rig-package@0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.0.tgz#334ad2846797861361b3445d4cc9ae9164b1885c" + integrity sha512-Lj6noF7Q4BBm1hKiBDw94e6uZvq1xlBwM/d2cBFaPqXeGdV+G6r3qaCWfRiSXK0pcHpGGpV5Tb2MdfhVcO6G/g== dependencies: resolve "~1.17.0" strip-json-comments "~3.1.1" -"@rushstack/ts-command-line@4.8.0": - version "4.8.0" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.8.0.tgz#611accb931b9ac62ff4d078f68f95c47f6606724" - integrity sha512-nZ8cbzVF1VmFPfSJfy8vEohdiFAH/59Y/Y+B4nsJbn4SkifLJ8LqNZ5+LxCC2UR242EXFumxlsY1d6fPBxck5Q== +"@rushstack/ts-command-line@4.9.0": + version "4.9.0" + resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.9.0.tgz#781ba42cff73cae097b6d5241b6441e7cc2fe6e0" + integrity sha512-kmT8t+JfnvphISF1C5WwY56RefjwgajhSjs9J4ckvAFXZDXR6F5cvF5/RTh7fGCzIomg8esy2PHO/b52zFoZvA== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" @@ -6353,9 +6353,9 @@ integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== "@types/color@^3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@types/color/-/color-3.0.1.tgz#2900490ed04da8116c5058cd5dba3572d5a25071" - integrity sha512-oeUWVaAwI+xINDUx+3F2vJkl/vVB03VChFF/Gl3iQCdbcakjuoJyMOba+3BXRtnBhxZ7uBYqQBi9EpLnvSoztA== + version "3.0.2" + resolved "https://registry.npmjs.org/@types/color/-/color-3.0.2.tgz#3779043e782f562aa9157b5fc6bd07e14fd8e7f3" + integrity sha512-INiJl6sfNn8iyC5paxVzqiVUEj2boIlFki02uRTAkKwAj++7aAF+ZfEv/XrIeBa0XI/fTZuDHW8rEEcEVnON+Q== dependencies: "@types/color-convert" "*" @@ -6703,9 +6703,9 @@ "@types/node" "*" "@types/http-proxy@*", "@types/http-proxy@^1.17.4", "@types/http-proxy@^1.17.5": - version "1.17.5" - resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz#c203c5e6e9dc6820d27a40eb1e511c70a220423d" - integrity sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q== + version "1.17.7" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f" + integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w== dependencies: "@types/node" "*" @@ -7039,9 +7039,9 @@ integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== "@types/passport-github2@^1.2.4": - version "1.2.4" - resolved "https://registry.npmjs.org/@types/passport-github2/-/passport-github2-1.2.4.tgz#f56c386d1fe6435e359430e57adc1747a627bd86" - integrity sha512-dtGtA0Uyzk6ne3SrgQi/I1ClClLE3i7JmSiMaJgkGH8v1nbE9JdBpG7QWJ1XPlLdcf7EvoPdHmkWN2+Kln9y8g== + version "1.2.5" + resolved "https://registry.npmjs.org/@types/passport-github2/-/passport-github2-1.2.5.tgz#79064e213ff33ca0a9d21af3da09a3230fac8099" + integrity sha512-+WLyrd8JPsCxroK34EjegR0j3FMxp6wqB9cw/sRCFkWT9qic1dymAn021gr336EpyjzdhjUd2KKrqyxvdFSvOA== dependencies: "@types/express" "*" "@types/passport" "*" @@ -7072,10 +7072,10 @@ "@types/oauth" "*" "@types/passport" "*" -"@types/passport-saml@^1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@types/passport-saml/-/passport-saml-1.1.2.tgz#f32ac2321eb25ec7bdbb1f3a5313b596bb0887e6" - integrity sha512-vpSdcb7V/bFxrvZJwSqnBr0qEqIhtOnwRBxw+Dvq4UkVbEgcCOkxF4tERCCFfA+FP3lp63VCCAifZLQrF5JkXA== +"@types/passport-saml@^1.1.3": + version "1.1.3" + resolved "https://registry.npmjs.org/@types/passport-saml/-/passport-saml-1.1.3.tgz#efc57902a07ebe1ec114d00acd8d990e873813a1" + integrity sha512-4kqfyTN6Ud8xTPIdmsYDOBVs4vQFQRcLWhMcGLYiMBc0sFeC44k0Q8Z8v1TIYTmZUrqaO6rbf78GZXl4xAvCRA== dependencies: "@types/express" "*" "@types/passport" "*" @@ -7369,9 +7369,9 @@ integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== "@types/stoppable@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.0.tgz#a5fa6a48120b109ca9233eed05c67c50bc4f3b91" - integrity sha512-BRR23Q9CJduH7AM6mk4JRttd8XyFkb4qIPZu4mdLF+VoP+wcjIxIWIKiBbN78NBbEuynrAyMPtzOHnIp2B/JPQ== + version "1.1.1" + resolved "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.1.tgz#a6f1f280e29f8f3c743277534425e0a75041d2f9" + integrity sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw== dependencies: "@types/node" "*" @@ -7417,9 +7417,9 @@ integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ== "@types/tar@^4.0.3": - version "4.0.4" - resolved "https://registry.npmjs.org/@types/tar/-/tar-4.0.4.tgz#d680de60855e7778a51c672b755869a3b8d2889f" - integrity sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A== + version "4.0.5" + resolved "https://registry.npmjs.org/@types/tar/-/tar-4.0.5.tgz#5f953f183e36a15c6ce3f336568f6051b7b183f3" + integrity sha512-cgwPhNEabHaZcYIy5xeMtux2EmYBitfqEceBUi2t5+ETy4dW6kswt6WX4+HqLeiiKOo42EXbGiDmVJ2x+vi37Q== dependencies: "@types/minipass" "*" "@types/node" "*" @@ -7995,6 +7995,11 @@ dependencies: tslib "^1.9.3" +"@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.2": + version "0.7.3" + resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.3.tgz#55de695f77afd3cc0e5bee0aa900040bc63c0f63" + integrity sha512-8XmJdPut2XGtfFcsNsqEsvMUmAwk7xLq7m+E/GcsU9b5qyFFIsiX4Fvnb5UoQ4wo12Wlm07YFJERoyWUYdbIpw== + "@xobotyi/scrollbar-width@^1.9.5": version "1.9.5" resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" @@ -8292,10 +8297,10 @@ ansi-regex@^5.0.0: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-regex@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.0.tgz#ecc7f5933cbe5ac7b33e209a5ff409ab1669c6b2" - integrity sha512-tAaOSrWCHF+1Ear1Z4wnJCXA9GGox4K6Ic85a5qalES2aeEwQGr7UC93mwef49536PkCYjzkp0zIxfFvexJ6zQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== ansi-styles@^2.2.1: version "2.2.1" @@ -11040,9 +11045,9 @@ core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: integrity sha512-GUbtPllXMYRzIgHNZ4dTYTcUemls2cni83Q4Q/TrFONHfhcg9oEGOtaGHfb0cpzec60P96UKPvMkjX1jET8rUw== core-js@^3.6.0: - version "3.17.2" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.17.2.tgz#f960eae710dc62c29cca93d5332e3660e289db10" - integrity sha512-XkbXqhcXeMHPRk2ItS+zQYliAMilea2euoMsnpRRdDad6b2VY6CQQcwz1K8AnWesfw4p165RzY0bTnr3UrbYiA== + version "3.17.3" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.17.3.tgz#8e8bd20e91df9951e903cabe91f9af4a0895bc1e" + integrity sha512-lyvajs+wd8N1hXfzob1LdOCCHFU4bGMbqqmLn1Q4QlCpDqWPpGf+p0nj+LNrvDDG33j0hZXw2nsvvVpHysxyNw== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -14466,13 +14471,20 @@ git-up@^4.0.0: is-ssh "^1.3.0" parse-url "^5.0.0" -git-url-parse@^11.4.4, git-url-parse@~11.4.4: +git-url-parse@^11.4.4: version "11.4.4" resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== dependencies: git-up "^4.0.0" +git-url-parse@^11.6.0: + version "11.6.0" + resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" + integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== + dependencies: + git-up "^4.0.0" + gitconfiglocal@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" @@ -20907,20 +20919,20 @@ passport-onelogin-oauth@^0.0.1: pkginfo "0.2.x" uid2 "0.0.3" -passport-saml@^2.0.0: - version "2.0.5" - resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-2.0.5.tgz#d822225875d0ec640236bf27ad8d5d9436396dea" - integrity sha512-D9OkTZ2hgRHZZFU3BUPKz7PC/khu9jmJtGCoJOQcn8JKR+AW9H/aj1E32VPu/iQbYMXXxEEt4qMhkCZzK9trzw== +passport-saml@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-3.1.2.tgz#34a0c2c423d729ce102e69fea9c22040910e6d43" + integrity sha512-EhD3/ofiz1vu7R72i4RskXk/dQG9GyDmXPdHJf5LYB+93B5kvKv5p+5lpZgO3z+Wf3eN0h/tGdGd6noyYdjY6g== dependencies: + "@xmldom/xmldom" "^0.7.2" debug "^4.3.1" - passport-strategy "*" - xml-crypto "^2.0.0" - xml-encryption "1.2.1" + passport-strategy "^1.0.0" + xml-crypto "^2.1.3" + xml-encryption "^1.3.0" xml2js "^0.4.23" xmlbuilder "^15.1.1" - xmldom "0.4.x" -passport-strategy@*, passport-strategy@1.x.x: +passport-strategy@1.x.x, passport-strategy@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= @@ -21844,9 +21856,9 @@ prettier@^1.19.1: integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== prettier@^2.2.1: - version "2.3.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" - integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== + version "2.4.0" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba" + integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ== prettier@~2.2.1: version "2.2.1" @@ -22484,9 +22496,9 @@ react-hook-form@^6.15.4: integrity sha512-prq82ofMbnRyj5wqDe8hsTRcdR25jQ+B8KtCS7BLCzjFHAwNuCjRwzPuP4eYLsEBjEIeYd6try+pdLdw0kPkpg== react-hook-form@^7.12.2: - version "7.12.2" - resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.12.2.tgz#2660afbf03c4ef360a9314ebf46ce3d972296c77" - integrity sha512-cpxocjrgpMAJCMJQR51BQhMoEx80/EQqePNihMTgoTYTqCRbd2GExi+N4GJIr+cFqrmbwNj9wxk5oLWYQsUefg== + version "7.15.3" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.15.3.tgz#7d222dcfd43137be127dca2843149f09bd25d4b1" + integrity sha512-z30aZoEHkWE8oZvad4OcYSBI0kQua/T5sFGH9tB2HfeykFnP/pGXNap8lDio4/U1yPj2ffpbvRIvqKd/6jjBVA== react-hot-loader@^4.12.21: version "4.13.0" @@ -25078,11 +25090,11 @@ strip-ansi@^4.0.0: ansi-regex "^3.0.0" strip-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.0.tgz#1dc49b980c3a4100366617adac59327eefdefcb0" - integrity sha512-UhDTSnGF1dc0DRbUqr1aXwNoY3RgVkSWG8BrpnuFIxhP57IqbS7IRta2Gfiavds4yCxc5+fEAVVOgBZWnYkvzg== + version "7.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== dependencies: - ansi-regex "^6.0.0" + ansi-regex "^6.0.1" strip-bom@^2.0.0: version "2.0.0" @@ -25738,9 +25750,9 @@ thenify-all@^1.0.0: thenify ">= 3.1.0 < 4" "thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= + version "3.3.1" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== dependencies: any-promise "^1.0.0" @@ -26322,7 +26334,7 @@ typescript@^4.0.3, typescript@~4.2.3: resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== -typescript@~4.3.2: +typescript@~4.3.5: version "4.3.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== @@ -27589,9 +27601,9 @@ write-pkg@^4.0.0: write-json-file "^3.2.0" ws@7.4.5, ws@^7.4.6: - version "7.5.4" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.4.tgz#56bfa20b167427e138a7795de68d134fe92e21f9" - integrity sha512-zP9z6GXm6zC27YtspwH99T3qTG7bBFv2VIkeHstMLrLlDJuzA7tQ5ls3OJ1hOGGCzTQPniNJoHXIAOS0Jljohg== + version "7.5.5" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" + integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== ws@^5.2.0: version "5.2.3" @@ -27639,23 +27651,23 @@ xml-but-prettier@^1.0.1: dependencies: repeat-string "^1.5.2" -xml-crypto@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-2.0.0.tgz#54cd268ad9d31930afcf7092cbb664258ca9e826" - integrity sha512-/a04qr7RpONRZHOxROZ6iIHItdsQQjN3sj8lJkYDDss8tAkEaAs0VrFjb3tlhmS5snQru5lTs9/5ISSMdPDHlg== +xml-crypto@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-2.1.3.tgz#6a7272b610ea3e4ea7f13e9e4876f1b20cbc32c8" + integrity sha512-MpXZwnn9JK0mNPZ5mnFIbNnQa+8lMGK4NtnX2FlJMfMWR60sJdFO9X72yO6ji068pxixzk53O7x0/iSKh6IhyQ== dependencies: - xmldom "0.1.27" - xpath "0.0.27" + "@xmldom/xmldom" "^0.7.0" + xpath "0.0.32" -xml-encryption@1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-1.2.1.tgz#e6d18817c4309fd07ca7793cca93c3fd06745baa" - integrity sha512-hn5w3l5p2+nGjlmM0CAhMChDzVGhW+M37jH35Z+GJIipXbn9PUlAIRZ6I5Wm7ynlqZjFrMAr83d/CIp9VZJMTA== +xml-encryption@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-1.3.0.tgz#4cad44a59bf8bdec76d7865ce0b89e13c09962f4" + integrity sha512-3P8C4egMMxSR1BmsRM+fG16a3WzOuUEQKS2U4c3AZ5v7OseIfdUeVkD8dwxIhuLryFZSRWUL5OP6oqkgU7hguA== dependencies: + "@xmldom/xmldom" "^0.7.0" escape-html "^1.0.3" node-forge "^0.10.0" - xmldom "~0.1.15" - xpath "0.0.27" + xpath "0.0.32" xml-name-validator@^3.0.0: version "3.0.0" @@ -27698,30 +27710,15 @@ xmlchars@^2.2.0: resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmldom@0.1.27: - version "0.1.27" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" - integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= - -xmldom@0.4.x: - version "0.4.0" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz#8771e482a333af44587e30ce026f0998c23f3830" - integrity sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA== - xmldom@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz#43a96ecb8beece991cef382c08397d82d4d0c46f" integrity sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg== -xmldom@~0.1.15: - version "0.1.31" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" - integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== - -xpath@0.0.27: - version "0.0.27" - resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" - integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ== +xpath@0.0.32: + version "0.0.32" + resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz#1b73d3351af736e17ec078d6da4b8175405c48af" + integrity sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw== xss@^1.0.8: version "1.0.9" @@ -27742,14 +27739,14 @@ y18n@^3.2.1: integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + version "4.0.3" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: - version "5.0.5" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" - integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^2.1.2: version "2.1.2"