Merge pull request #19486 from backstage/mob/di-experiments
Initial experimental implementation of declarative integration for frontend
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,3 @@
|
||||
# example-app-next
|
||||
|
||||
This package is an example of a Backstage application.
|
||||
@@ -0,0 +1,108 @@
|
||||
app:
|
||||
packages: 'all' # ✨
|
||||
extensions:
|
||||
- core.router
|
||||
- graphiql.page:
|
||||
config:
|
||||
path: /
|
||||
|
||||
# - core.signInPage:
|
||||
# props:
|
||||
# provider:
|
||||
# id: google
|
||||
# title: Google
|
||||
# message: Sign In using Google
|
||||
# apiRef: googleAuthApiRef # ???
|
||||
|
||||
# - core.nav:
|
||||
# config:
|
||||
# logo: assets/logo.png
|
||||
# layout:
|
||||
# - label: Search
|
||||
# icon: search
|
||||
# to: /search
|
||||
# items:
|
||||
# - point: search
|
||||
# - type: divider
|
||||
# - label: Menu
|
||||
# icon: menu
|
||||
# items:
|
||||
# - label: Home
|
||||
# icon: home
|
||||
# to: /catalog
|
||||
# - label: Create...
|
||||
# icon: create
|
||||
# to: /create
|
||||
# - type: divider
|
||||
# - type: scroll-wrapper
|
||||
# items:
|
||||
# - label: Tech Radar
|
||||
# icon: map
|
||||
# to: /tech-radar
|
||||
# - label: GraphiQL
|
||||
# icon: graphiql
|
||||
# to: /graphiql
|
||||
# - type: divider
|
||||
# - point: shortcuts
|
||||
# - type: space
|
||||
# - type: divider
|
||||
# - label: Settings
|
||||
# icon: avatar
|
||||
# to: /settings
|
||||
# items:
|
||||
# - point: settings
|
||||
# - core.nav/search: '@backstage/plugin-search#SidebarSearchModal'
|
||||
# - core.nav/shortcuts:
|
||||
# use: '@backstage/plugin-shortcuts#Shortcuts'
|
||||
# config:
|
||||
# allowExternalLinks: true
|
||||
# - core.nav/settings: '@backstage/plugin-user-settings#SidebarSettings'
|
||||
|
||||
# - core.pages.index:
|
||||
# at:
|
||||
# point: core.routes
|
||||
# config:
|
||||
# path: /
|
||||
# use: 'react-router-dom#Navigate'
|
||||
# config:
|
||||
# to: /catalog
|
||||
|
||||
# - scaffolder.page:
|
||||
# config:
|
||||
# groups:
|
||||
# - title: Recommended
|
||||
# filter:
|
||||
# entity.metadata.tags: recommended
|
||||
# - scaffolder.page/fields: '@backstage/plugin-scaffolder#LowerCaseValuePickerFieldExtension'
|
||||
# - scaffolder.page/layout: '@backstage/plugin-scaffolder#TwoColumnLayout'
|
||||
|
||||
# - search.page/content: 'app#customSearchPage' # custom search page from somewhere
|
||||
|
||||
# - user-settings.page.routes.advanced:
|
||||
# at:
|
||||
# point: user-settings.page/routes
|
||||
# config:
|
||||
# title: Advanced
|
||||
# path: /advanced
|
||||
# use: '@backstage/plugin-user-settings#AdvancedSettings'
|
||||
|
||||
# - entity.card.orphanWarning
|
||||
# - entity.card.processingErrors
|
||||
# - entity.card.about
|
||||
# - entity.card.catalogGraph
|
||||
# - entity.card.pagerDuty
|
||||
# - entity.card.links
|
||||
# - entity.card.labels
|
||||
# - entity.card.githubInsightsLanguages
|
||||
# - entity.card.githubInsightsReleases
|
||||
# - entity.card.githubInsightsReadme:
|
||||
# config:
|
||||
# maxHeight: 350 # Throwing this config in to have an example, but in practice rely on default
|
||||
# - entity.card.subcomponentsCard
|
||||
# - entity.card.userProfile
|
||||
# - entity.card.ownership
|
||||
# - entity.card.likeDislikeRatings
|
||||
# - entity.content.dependsOnComponents
|
||||
# - entity.content.codeInsights
|
||||
# - entity.content.todo
|
||||
# - entity.content.feedbackResponse
|
||||
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"name": "example-app-next",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"backstage": {
|
||||
"role": "frontend"
|
||||
},
|
||||
"bundled": true,
|
||||
"dependencies": {
|
||||
"@backstage/app-defaults": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/frontend-app-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/integration-react": "workspace:^",
|
||||
"@backstage/plugin-adr": "workspace:^",
|
||||
"@backstage/plugin-airbrake": "workspace:^",
|
||||
"@backstage/plugin-apache-airflow": "workspace:^",
|
||||
"@backstage/plugin-api-docs": "workspace:^",
|
||||
"@backstage/plugin-azure-devops": "workspace:^",
|
||||
"@backstage/plugin-azure-sites": "workspace:^",
|
||||
"@backstage/plugin-badges": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-graph": "workspace:^",
|
||||
"@backstage/plugin-catalog-import": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/plugin-catalog-unprocessed-entities": "workspace:^",
|
||||
"@backstage/plugin-circleci": "workspace:^",
|
||||
"@backstage/plugin-cloudbuild": "workspace:^",
|
||||
"@backstage/plugin-code-coverage": "workspace:^",
|
||||
"@backstage/plugin-cost-insights": "workspace:^",
|
||||
"@backstage/plugin-devtools": "workspace:^",
|
||||
"@backstage/plugin-dynatrace": "workspace:^",
|
||||
"@backstage/plugin-entity-feedback": "workspace:^",
|
||||
"@backstage/plugin-explore": "workspace:^",
|
||||
"@backstage/plugin-gcalendar": "workspace:^",
|
||||
"@backstage/plugin-gcp-projects": "workspace:^",
|
||||
"@backstage/plugin-github-actions": "workspace:^",
|
||||
"@backstage/plugin-gocd": "workspace:^",
|
||||
"@backstage/plugin-graphiql": "workspace:^",
|
||||
"@backstage/plugin-home": "workspace:^",
|
||||
"@backstage/plugin-jenkins": "workspace:^",
|
||||
"@backstage/plugin-kafka": "workspace:^",
|
||||
"@backstage/plugin-kubernetes": "workspace:^",
|
||||
"@backstage/plugin-lighthouse": "workspace:^",
|
||||
"@backstage/plugin-linguist": "workspace:^",
|
||||
"@backstage/plugin-linguist-common": "workspace:^",
|
||||
"@backstage/plugin-microsoft-calendar": "workspace:^",
|
||||
"@backstage/plugin-newrelic": "workspace:^",
|
||||
"@backstage/plugin-newrelic-dashboard": "workspace:^",
|
||||
"@backstage/plugin-octopus-deploy": "workspace:^",
|
||||
"@backstage/plugin-org": "workspace:^",
|
||||
"@backstage/plugin-pagerduty": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
"@backstage/plugin-playlist": "workspace:^",
|
||||
"@backstage/plugin-puppetdb": "workspace:^",
|
||||
"@backstage/plugin-rollbar": "workspace:^",
|
||||
"@backstage/plugin-scaffolder": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-react": "workspace:^",
|
||||
"@backstage/plugin-search": "workspace:^",
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"@backstage/plugin-search-react": "workspace:^",
|
||||
"@backstage/plugin-sentry": "workspace:^",
|
||||
"@backstage/plugin-shortcuts": "workspace:^",
|
||||
"@backstage/plugin-stack-overflow": "workspace:^",
|
||||
"@backstage/plugin-stackstorm": "workspace:^",
|
||||
"@backstage/plugin-tech-insights": "workspace:^",
|
||||
"@backstage/plugin-tech-radar": "workspace:^",
|
||||
"@backstage/plugin-techdocs": "workspace:^",
|
||||
"@backstage/plugin-techdocs-module-addons-contrib": "workspace:^",
|
||||
"@backstage/plugin-techdocs-react": "workspace:^",
|
||||
"@backstage/plugin-todo": "workspace:^",
|
||||
"@backstage/plugin-user-settings": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@internal/plugin-catalog-customized": "workspace:^",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.61",
|
||||
"@octokit/rest": "^19.0.3",
|
||||
"@oriflame/backstage-plugin-score-card": "^0.7.0",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^2.0.8",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^2.0.5",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^2.2.7",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^2.0.5",
|
||||
"history": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router": "^6.3.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-use": "^17.2.4",
|
||||
"zen-observable": "^0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/cypress": "^9.0.0",
|
||||
"@testing-library/dom": "^8.0.0",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/jquery": "^3.3.34",
|
||||
"@types/node": "^16.11.26",
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"@types/zen-observable": "^0.8.0",
|
||||
"cross-env": "^7.0.0",
|
||||
"cypress": "^10.0.0",
|
||||
"eslint-plugin-cypress": "^2.10.3",
|
||||
"start-server-and-test": "^1.10.11"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start --config ../../app-config.yaml --config app-config.yaml",
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"test": "backstage-cli package test",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test:e2e": "start-server-and-test start http://localhost:3000 cy:dev",
|
||||
"test:e2e:ci": "start-server-and-test start http://localhost:3000 cy:run",
|
||||
"cy:dev": "cypress open",
|
||||
"cy:run": "cypress run"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 883 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="favicon" viewBox="0 0 337.46 428.5"><title>Backstage favicon</title><style>#favicon path{fill:#121212}@media (prefers-color-scheme:dark){#favicon path{fill:#7df3e1}}</style><path d="M303,166.05a80.69,80.69,0,0,0,13.45-10.37c.79-.77,1.55-1.53,2.3-2.3a83.12,83.12,0,0,0,7.93-9.38A63.69,63.69,0,0,0,333,133.23a48.58,48.58,0,0,0,4.35-16.4c1.49-19.39-10-38.67-35.62-54.22L198.56,0,78.3,115.23,0,190.25l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.69,19.16-18.36,25.52-42.12,13.7-61.87a49.22,49.22,0,0,0-6.8-8.87A89.17,89.17,0,0,0,259,178.29h.15a85.08,85.08,0,0,0,31-5.79A80.88,80.88,0,0,0,303,166.05ZM202.45,225.86c-19.32,18.51-50.4,21.23-75.7,5.9L51.61,186.15l67.45-64.64,76.41,46.38C223,184.58,221.49,207.61,202.45,225.86Zm8.93-82.22-70.65-42.89L205.14,39,274.51,81.1c25.94,15.72,29.31,37,10.55,55A60.69,60.69,0,0,1,211.38,143.64Zm29.86,190c-19.57,18.75-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,282.52v24.67L108.6,373.1a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A87.27,87.27,0,0,1,241.24,333.68Zm0-39c-19.57,18.75-46.17,29.08-74.88,29.08a123.81,123.81,0,0,1-64.1-18.19L0,243.53v24.68l108.6,65.91a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.5v-1.78A87.27,87.27,0,0,1,241.24,294.7Zm0-39c-19.57,18.76-46.17,29.09-74.88,29.09a123.81,123.81,0,0,1-64.1-18.19L0,204.55v24.68l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.68,12.88-12.35,20-27.13,19.68-41.5v-1.82A86.09,86.09,0,0,1,241.24,255.71Zm83.7,25.74a94.15,94.15,0,0,1-60.2,25.86h0V334a81.6,81.6,0,0,0,51.74-22.37c14-13.38,21.14-28.11,21-42.64v-2.19A94.92,94.92,0,0,1,324.94,281.45Zm-83.7,91.21c-19.57,18.76-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,321.5v24.68l108.6,65.9a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.8,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A86.29,86.29,0,0,1,241.24,372.66ZM327,162.45c-.68.69-1.35,1.38-2.05,2.06a94.37,94.37,0,0,1-10.64,8.65,91.35,91.35,0,0,1-11.6,7,94.53,94.53,0,0,1-26.24,8.71,97.69,97.69,0,0,1-14.16,1.57c.5,1.61.9,3.25,1.25,4.9a53.27,53.27,0,0,1,1.14,12V217h.05a84.41,84.41,0,0,0,25.35-5.55,81,81,0,0,0,26.39-16.82c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,172.17a48.55,48.55,0,0,0,4.32-16.45c.09-1.23.2-2.47.19-3.7V150q-1.08,1.54-2.25,3.09A96.73,96.73,0,0,1,327,162.45Zm0,77.92c-.69.7-1.31,1.41-2,2.1a94.2,94.2,0,0,1-60.2,25.86h0l0,26.67h0a81.6,81.6,0,0,0,51.74-22.37A73.51,73.51,0,0,0,333,250.13a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.47.19-3.71v-2.19c-.74,1.07-1.46,2.15-2.27,3.21A95.68,95.68,0,0,1,327,240.37Zm0-39c-.69.7-1.31,1.41-2,2.1a93.18,93.18,0,0,1-10.63,8.65,91.63,91.63,0,0,1-11.63,7,95.47,95.47,0,0,1-37.94,10.18h0V256h0a81.65,81.65,0,0,0,51.74-22.37c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,211.15a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.48.19-3.71v-2.2c-.74,1.08-1.46,2.16-2.27,3.22A95.68,95.68,0,0,1,327,201.39Z"/></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Backstage is an open platform for building developer portals"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="<%= publicPath %>/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link
|
||||
rel="manifest"
|
||||
href="<%= publicPath %>/manifest.json"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
<link rel="icon" href="<%= publicPath %>/favicon.ico" />
|
||||
<link rel="shortcut icon" href="<%= publicPath %>/favicon.ico" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="<%= publicPath %>/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="<%= publicPath %>/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="<%= publicPath %>/favicon-16x16.png"
|
||||
/>
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="<%= publicPath %>/safari-pinned-tab.svg"
|
||||
color="#5bbad5"
|
||||
/>
|
||||
<title><%= config.getString('app.title') %></title>
|
||||
|
||||
<% if (config.has('app.datadogRum')) { %>
|
||||
<script>
|
||||
(function (h, o, u, n, d) {
|
||||
h = h[d] = h[d] || {
|
||||
q: [],
|
||||
onReady: function (c) {
|
||||
h.q.push(c);
|
||||
},
|
||||
};
|
||||
d = o.createElement(u);
|
||||
d.async = 1;
|
||||
d.src = n;
|
||||
n = o.getElementsByTagName(u)[0];
|
||||
n.parentNode.insertBefore(d, n);
|
||||
})(
|
||||
window,
|
||||
document,
|
||||
'script',
|
||||
'https://www.datadoghq-browser-agent.com/datadog-rum-v3.js',
|
||||
'DD_RUM',
|
||||
);
|
||||
DD_RUM.onReady(function () {
|
||||
DD_RUM.init({
|
||||
clientToken: '<%= config.getString("app.datadogRum.clientToken") %>',
|
||||
applicationId:
|
||||
'<%= config.getString("app.datadogRum.applicationId") %>',
|
||||
site: '<%= config.getOptionalString("app.datadogRum.site") || "datadoghq.com" %>',
|
||||
service: 'backstage',
|
||||
env: '<%= config.getString("app.datadogRum.env") %>',
|
||||
sampleRate: 100,
|
||||
trackInteractions: true,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<% } %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `yarn start`.
|
||||
To create a production bundle, use `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "Backstage",
|
||||
"name": "Backstage",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="682.667" height="682.667" preserveAspectRatio="xMidYMid meet" version="1.0" viewBox="0 0 512 512"><metadata>Created by potrace 1.11, written by Peter Selinger 2001-2013</metadata><g fill="#000" stroke="none"><path d="M492 4610 c-4 -3 -8 -882 -7 -1953 l0 -1948 850 2 c898 1 945 3 1118 49 505 134 823 531 829 1037 2 136 -9 212 -44 323 -40 125 -89 218 -163 310 -35 43 -126 128 -169 157 -22 15 -43 30 -46 33 -12 13 -131 70 -188 91 l-64 22 60 28 c171 77 317 224 403 404 64 136 92 266 91 425 -5 424 -245 770 -642 923 -79 30 -105 39 -155 50 -11 3 -38 10 -60 15 -22 6 -60 13 -85 17 -25 3 -58 9 -75 12 -36 8 -1643 11 -1653 3z m1497 -743 c236 -68 352 -254 305 -486 -26 -124 -110 -224 -232 -277 -92 -40 -151 -46 -439 -49 l-283 -3 -1 27 c-1 36 -1 760 0 790 l1 23 298 -5 c226 -4 310 -9 351 -20z m-82 -1538 c98 -3 174 -19 247 -52 169 -78 257 -212 258 -395 0 -116 -36 -221 -100 -293 -64 -72 -192 -135 -314 -155 -23 -3 -181 -7 -350 -8 l-308 -2 -1 26 c-6 210 1 874 9 879 9 5 366 6 559 0z" transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"/><path d="M4160 1789 c-275 -24 -499 -263 -503 -536 -1 -115 21 -212 66 -292 210 -369 697 -402 950 -65 77 103 110 199 111 329 0 50 -6 113 -13 140 -16 58 -62 155 -91 193 -33 43 -122 132 -132 132 -5 0 -26 11 -46 25 -85 56 -219 85 -342 74z" transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 { renderWithEffects } from '@backstage/test-utils';
|
||||
|
||||
describe('App', () => {
|
||||
it('should render', async () => {
|
||||
process.env = {
|
||||
NODE_ENV: 'test',
|
||||
APP_CONFIG: [
|
||||
{
|
||||
data: {
|
||||
app: {
|
||||
title: 'Test',
|
||||
support: { url: 'http://localhost:7007/support' },
|
||||
},
|
||||
backend: { baseUrl: 'http://localhost:7007' },
|
||||
lighthouse: {
|
||||
baseUrl: 'http://localhost:3003',
|
||||
},
|
||||
techdocs: {
|
||||
storageUrl: 'http://localhost:7007/api/techdocs/static/docs',
|
||||
},
|
||||
},
|
||||
context: 'test',
|
||||
},
|
||||
] as any,
|
||||
};
|
||||
|
||||
const { default: App } = await import('./App');
|
||||
const rendered = await renderWithEffects(<App />);
|
||||
expect(rendered.baseElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2023 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 { graphiqlPlugin as legacyGraphiqlPlugin } from '@backstage/plugin-graphiql';
|
||||
import { createApp as createLegacyApp } from '@backstage/app-defaults';
|
||||
import { createApp } from '@backstage/frontend-app-api';
|
||||
import { graphiqlPlugin } from './examples/graphiqlPlugin';
|
||||
|
||||
/*
|
||||
|
||||
# Notes
|
||||
|
||||
TODO:
|
||||
- proper createApp
|
||||
- connect extensions and plugins, provide method?
|
||||
- higher level API for creating standard extensions + higher order framework API for creating those?
|
||||
- extension config schema + validation
|
||||
- figure out how to resolve configured extension ref to runtime value, e.g. '@backstage/plugin-graphiql#GraphiqlPage'
|
||||
- make sure all shorthands work + tests
|
||||
- figure out package structure / how to ship, frontend-plugin-api/frontend-app-api
|
||||
- figure out routing, useRouteRef in the new system
|
||||
- Legacy plugins / interop
|
||||
- dynamic updates, runtime API
|
||||
|
||||
*/
|
||||
|
||||
/* core */
|
||||
|
||||
// const discoverPackages = async () => {
|
||||
// // stub for now, deferring package discovery til later
|
||||
// return ['@backstage/plugin-graphiql'];
|
||||
// };
|
||||
|
||||
/* graphiql package */
|
||||
|
||||
/* app.tsx */
|
||||
|
||||
const app = createApp({
|
||||
plugins: [graphiqlPlugin],
|
||||
// bindRoutes({ bind }) {
|
||||
// bind(catalogPlugin.externalRoutes, {
|
||||
// createComponent: scaffolderPlugin.routes.root,
|
||||
// });
|
||||
// bind(scaffolderPlugin.externalRoutes, {
|
||||
// registerComponent: catalogImportPlugin.routes.importPage,
|
||||
// });
|
||||
// },
|
||||
});
|
||||
|
||||
const legacyApp = createLegacyApp({ plugins: [legacyGraphiqlPlugin] });
|
||||
|
||||
export default legacyApp.createRoot(app.createRoot());
|
||||
|
||||
// const routes = (
|
||||
// <FlatRoutes>
|
||||
// {/* <Route path="/" element={<Navigate to="catalog" />} />
|
||||
// <Route path="/catalog" element={<CatalogIndexPage />} />
|
||||
// <Route
|
||||
// path="/catalog/:namespace/:kind/:name"
|
||||
// element={<CatalogEntityPage />}
|
||||
// >
|
||||
// <EntityLayout>
|
||||
// <EntityLayout.Route path="/" title="Overview">
|
||||
// <Grid container spacing={3} alignItems="stretch">
|
||||
// <Grid item md={6} xs={12}>
|
||||
// <EntityAboutCard variant="gridItem" />
|
||||
// </Grid>
|
||||
|
||||
// <Grid item md={4} xs={12}>
|
||||
// <EntityLinksCard />
|
||||
// </Grid>
|
||||
// </Grid>
|
||||
// </EntityLayout.Route>
|
||||
|
||||
// <EntityLayout.Route path="/todos" title="TODOs">
|
||||
// <EntityTodoContent />
|
||||
// </EntityLayout.Route>
|
||||
// </EntityLayout>
|
||||
// </Route>
|
||||
// <Route
|
||||
// path="/catalog-import"
|
||||
// element={
|
||||
// <CatalogImportPage />
|
||||
// }
|
||||
// /> */}
|
||||
// {/* <Route
|
||||
// path="/tech-radar"
|
||||
// element={<TechRadarPage width={1500} height={800} />}
|
||||
// /> */}
|
||||
// <Route path="/graphiql" element={<GraphiQLPage />} />
|
||||
// </FlatRoutes>
|
||||
// );
|
||||
|
||||
// export default app.createRoot(
|
||||
// <>
|
||||
// {/* <AlertDisplay transientTimeoutMs={2500} />
|
||||
// <OAuthRequestDialog /> */}
|
||||
// <AppRouter>{routes}</AppRouter>
|
||||
// </>,
|
||||
// );
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
createExtension,
|
||||
createPlugin,
|
||||
coreExtensionData,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { Router as GraphiQLPage } from '@backstage/plugin-graphiql';
|
||||
|
||||
export const GraphiqlPageExtension = createExtension({
|
||||
output: {
|
||||
component: coreExtensionData.reactComponent,
|
||||
path: coreExtensionData.routePath,
|
||||
},
|
||||
factory({ bind, config }) {
|
||||
bind.component(() => {
|
||||
return <GraphiQLPage />;
|
||||
});
|
||||
// TODO: In need of schemas and type safety
|
||||
bind.path((config as { path: string }).path);
|
||||
},
|
||||
});
|
||||
|
||||
export const graphiqlPlugin = createPlugin({
|
||||
id: 'graphiql',
|
||||
defaultExtensionInstances: [
|
||||
{
|
||||
id: 'graphiql.page',
|
||||
at: 'core.router/routes',
|
||||
extension: GraphiqlPageExtension,
|
||||
config: { path: '/graphiql' },
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 '@backstage/cli/asset-types';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// 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';
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,12 @@
|
||||
# @backstage/frontend-app-api
|
||||
|
||||
_This package was created through the Backstage CLI_.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package via Yarn:
|
||||
|
||||
```sh
|
||||
cd <package-dir> # if within a monorepo
|
||||
yarn add @backstage/frontend-app-api
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
## API Report File for "@backstage/frontend-app-api"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { BackstagePlugin } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// @public (undocumented)
|
||||
export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
createRoot(): JSX.Element;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2023 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 interface Config {
|
||||
app?: {
|
||||
/**
|
||||
* @deepVisibility frontend
|
||||
*/
|
||||
extensions?:
|
||||
| string
|
||||
| {
|
||||
[extensionId: string]:
|
||||
| boolean
|
||||
| string
|
||||
| {
|
||||
at?: string;
|
||||
extension?: string;
|
||||
config?: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@backstage/frontend-app-api",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "web-library"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@testing-library/jest-dom": "^5.10.1"
|
||||
},
|
||||
"configSchema": "config.d.ts",
|
||||
"files": [
|
||||
"config.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-graphiql": "workspace:^",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0",
|
||||
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2023 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 { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
ExtensionInstanceConfig,
|
||||
BackstagePlugin,
|
||||
coreExtensionData,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { RouteExtension } from './extensions/RouteExtension';
|
||||
import {
|
||||
createExtensionInstance,
|
||||
ExtensionInstance,
|
||||
} from './createExtensionInstance';
|
||||
|
||||
// Since we'll never merge arrays in config the config reader context
|
||||
// isn't too much of a help. Fall back to manual config reading logic
|
||||
// as the Config interface makes it quite hard for us otherwise.
|
||||
function readAppExtensionConfigs(
|
||||
rootConfig: Config,
|
||||
): Partial<ExtensionInstanceConfig>[] {
|
||||
const arr = rootConfig.getOptional('app.extensions');
|
||||
if (!Array.isArray(arr)) {
|
||||
if (arr === undefined) {
|
||||
return [];
|
||||
}
|
||||
// This will throw, and show which part of config had the wrong type
|
||||
rootConfig.getConfigArray('app.extensions');
|
||||
return [];
|
||||
}
|
||||
|
||||
return arr.map((value, index) => {
|
||||
function errorMsg(msg: string, key?: string, prop?: string) {
|
||||
return `Invalid extension configuration at app.extensions[${index}]${
|
||||
key ? `[${key}]` : ''
|
||||
}${prop ? `.${prop}` : ''}, ${msg}`;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return { id: value };
|
||||
} else if (
|
||||
typeof value !== 'object' ||
|
||||
value === null ||
|
||||
Array.isArray(value)
|
||||
) {
|
||||
throw new Error(errorMsg('must be a string or an object'));
|
||||
}
|
||||
|
||||
const keys = Object.keys(value);
|
||||
if (keys.length !== 1) {
|
||||
const joinedKeys = `"${keys.join('", "')}"`;
|
||||
throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`));
|
||||
}
|
||||
|
||||
const key = keys[0];
|
||||
const obj = value[key];
|
||||
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
|
||||
throw new Error(errorMsg('must be an object', key));
|
||||
}
|
||||
const at = obj.at;
|
||||
if (at !== undefined && typeof at !== 'string') {
|
||||
throw new Error(errorMsg('must be a string', key, 'at'));
|
||||
}
|
||||
const extension = obj.extension;
|
||||
if (extension !== undefined && typeof extension !== 'string') {
|
||||
throw new Error(errorMsg('must be a string', key, 'extension'));
|
||||
}
|
||||
if (extension) {
|
||||
throw new Error('TODO: implement extension resolution');
|
||||
}
|
||||
return { id: key, at, config: obj.config /* validate later */ };
|
||||
});
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
createRoot(): JSX.Element;
|
||||
} {
|
||||
const appConfig = ConfigReader.fromConfigs(process.env.APP_CONFIG as any);
|
||||
|
||||
// pull in default extension instance from discovered packages
|
||||
// apply config to adjust default extension instances and add more
|
||||
const extensionInstanceConfigs = [
|
||||
...options.plugins.flatMap(plugin => plugin.defaultExtensionInstances),
|
||||
{
|
||||
id: 'core.router',
|
||||
at: 'root/default',
|
||||
extension: RouteExtension,
|
||||
config: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
const appExtensionConfigs = readAppExtensionConfigs(appConfig);
|
||||
for (const appExtensionConfig of appExtensionConfigs) {
|
||||
const existingConfig = extensionInstanceConfigs.find(
|
||||
e => e.id === appExtensionConfig.id,
|
||||
);
|
||||
if (existingConfig) {
|
||||
if (appExtensionConfig.at) {
|
||||
existingConfig.at = appExtensionConfig.at;
|
||||
}
|
||||
if (appExtensionConfig.extension) {
|
||||
// TODO: do we want to reset config here? it might be completely
|
||||
// unrelated to the previous one
|
||||
existingConfig.extension = appExtensionConfig.extension;
|
||||
}
|
||||
if (appExtensionConfig.config) {
|
||||
// TODO: merge config?
|
||||
existingConfig.config = appExtensionConfig.config;
|
||||
}
|
||||
} else if (appExtensionConfig.id) {
|
||||
const { id, at, extension, config } = appExtensionConfig;
|
||||
if (!at || !extension) {
|
||||
throw new Error(`Extension ${appExtensionConfig.id} is incomplete`);
|
||||
}
|
||||
extensionInstanceConfigs.push({ id, at, extension, config });
|
||||
}
|
||||
}
|
||||
|
||||
// Create attachment map so that we can look attachments up during instance creation
|
||||
const attachmentMap = new Map<
|
||||
string,
|
||||
Map<string, ExtensionInstanceConfig[]>
|
||||
>();
|
||||
for (const instanceConfig of extensionInstanceConfigs) {
|
||||
const [extensionId, pointId = 'default'] = instanceConfig.at.split('/');
|
||||
|
||||
let pointMap = attachmentMap.get(extensionId);
|
||||
if (!pointMap) {
|
||||
pointMap = new Map();
|
||||
attachmentMap.set(extensionId, pointMap);
|
||||
}
|
||||
|
||||
let instances = pointMap.get(pointId);
|
||||
if (!instances) {
|
||||
instances = [];
|
||||
pointMap.set(pointId, instances);
|
||||
}
|
||||
|
||||
instances.push(instanceConfig);
|
||||
}
|
||||
|
||||
const instances = new Map<string, ExtensionInstance>();
|
||||
|
||||
function createInstance(
|
||||
instanceConfig: ExtensionInstanceConfig,
|
||||
): ExtensionInstance {
|
||||
const existingInstance = instances.get(instanceConfig.id);
|
||||
if (existingInstance) {
|
||||
return existingInstance;
|
||||
}
|
||||
|
||||
const attachments = Object.fromEntries(
|
||||
Array.from(attachmentMap.get(instanceConfig.id)?.entries() ?? []).map(
|
||||
([inputName, attachmentConfigs]) => [
|
||||
inputName,
|
||||
attachmentConfigs.map(createInstance),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return createExtensionInstance({
|
||||
id: instanceConfig.id,
|
||||
config: instanceConfig.config,
|
||||
extension: instanceConfig.extension,
|
||||
attachments,
|
||||
});
|
||||
}
|
||||
|
||||
const rootConfigs = attachmentMap.get('root')?.get('default') ?? [];
|
||||
const rootInstances = rootConfigs.map(instanceConfig =>
|
||||
createInstance(instanceConfig),
|
||||
);
|
||||
|
||||
return {
|
||||
createRoot() {
|
||||
const rootComponents = rootInstances.map(
|
||||
e =>
|
||||
e.data.get(
|
||||
coreExtensionData.reactComponent.id,
|
||||
) as typeof coreExtensionData.reactComponent.T,
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{rootComponents.map(Component => (
|
||||
<Component />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2023 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 } from '@backstage/frontend-plugin-api';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
|
||||
/** @internal */
|
||||
export interface ExtensionInstance {
|
||||
id: string;
|
||||
data: Map<string, unknown>;
|
||||
$$type: 'extension-instance';
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function createExtensionInstance(options: {
|
||||
id: string;
|
||||
extension: Extension;
|
||||
config: unknown;
|
||||
attachments: Record<string, ExtensionInstance[]>;
|
||||
}): ExtensionInstance {
|
||||
const { extension, config, attachments } = options;
|
||||
const extensionData = new Map<string, unknown>();
|
||||
extension.factory({
|
||||
config,
|
||||
bind: mapValues(extension.output, ref => {
|
||||
return (value: unknown) => extensionData.set(ref.id, value);
|
||||
}),
|
||||
inputs: mapValues(
|
||||
extension.inputs,
|
||||
({ extensionData: pointData }, inputName) => {
|
||||
// TODO: validation
|
||||
return (attachments[inputName] ?? []).map(attachment =>
|
||||
mapValues(pointData, ref => attachment.data.get(ref.id)),
|
||||
);
|
||||
},
|
||||
),
|
||||
});
|
||||
return { id: options.id, data: extensionData, $$type: 'extension-instance' };
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
createExtension,
|
||||
coreExtensionData,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { BrowserRouter, useRoutes } from 'react-router-dom';
|
||||
|
||||
export const RouteExtension = createExtension({
|
||||
inputs: {
|
||||
routes: {
|
||||
extensionData: {
|
||||
path: coreExtensionData.routePath,
|
||||
component: coreExtensionData.reactComponent,
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
component: coreExtensionData.reactComponent,
|
||||
},
|
||||
factory({ bind, inputs }) {
|
||||
const Routes = () => {
|
||||
const element = useRoutes(
|
||||
inputs.routes.map(route => ({
|
||||
path: route.path,
|
||||
element: <route.component />,
|
||||
})),
|
||||
);
|
||||
|
||||
return element;
|
||||
};
|
||||
bind.component(() => (
|
||||
<BrowserRouter>
|
||||
<Routes />
|
||||
</BrowserRouter>
|
||||
));
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export { createApp } from './createApp';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 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 '@testing-library/jest-dom';
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,12 @@
|
||||
# @backstage/frontend-plugin-api
|
||||
|
||||
_This package was created through the Backstage CLI_.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package via Yarn:
|
||||
|
||||
```sh
|
||||
cd <package-dir> # if within a monorepo
|
||||
yarn add @backstage/frontend-plugin-api
|
||||
```
|
||||
@@ -0,0 +1,124 @@
|
||||
## API Report File for "@backstage/frontend-plugin-api"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AnyExtensionDataMap = Record<string, ExtensionDataRef<any>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BackstagePlugin {
|
||||
// (undocumented)
|
||||
$$type: 'backstage-plugin';
|
||||
// (undocumented)
|
||||
defaultExtensionInstances: ExtensionInstanceConfig[];
|
||||
// (undocumented)
|
||||
id: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BackstagePluginOptions {
|
||||
// (undocumented)
|
||||
defaultExtensionInstances?: ExtensionInstanceConfig[];
|
||||
// (undocumented)
|
||||
id: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const coreExtensionData: {
|
||||
reactComponent: ExtensionDataRef<ComponentType<{}>>;
|
||||
routePath: ExtensionDataRef<string>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export function createExtension<
|
||||
TData extends AnyExtensionDataMap,
|
||||
TPoint extends Record<
|
||||
string,
|
||||
{
|
||||
extensionData: AnyExtensionDataMap;
|
||||
}
|
||||
>,
|
||||
>(options: CreateExtensionOptions<TData, TPoint>): Extension;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CreateExtensionOptions<
|
||||
TData extends AnyExtensionDataMap,
|
||||
TPoint extends Record<
|
||||
string,
|
||||
{
|
||||
extensionData: AnyExtensionDataMap;
|
||||
}
|
||||
>,
|
||||
> {
|
||||
// (undocumented)
|
||||
factory(options: {
|
||||
bind: ExtensionDataBind<TData>;
|
||||
config?: unknown;
|
||||
inputs: {
|
||||
[pointName in keyof TPoint]: ExtensionDataValue<
|
||||
TPoint[pointName]['extensionData']
|
||||
>[];
|
||||
};
|
||||
}): void;
|
||||
// (undocumented)
|
||||
inputs?: TPoint;
|
||||
// (undocumented)
|
||||
output: TData;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function createPlugin(options: BackstagePluginOptions): BackstagePlugin;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Extension {
|
||||
// (undocumented)
|
||||
$$type: 'extension';
|
||||
// (undocumented)
|
||||
factory(options: {
|
||||
bind: ExtensionDataBind<AnyExtensionDataMap>;
|
||||
config?: unknown;
|
||||
inputs: Record<string, Array<Record<string, unknown>>>;
|
||||
}): void;
|
||||
// (undocumented)
|
||||
inputs: Record<
|
||||
string,
|
||||
{
|
||||
extensionData: AnyExtensionDataMap;
|
||||
}
|
||||
>;
|
||||
// (undocumented)
|
||||
output: AnyExtensionDataMap;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ExtensionDataBind<TData extends AnyExtensionDataMap> = {
|
||||
[K in keyof TData]: (value: TData[K]['T']) => void;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ExtensionDataRef<T> = {
|
||||
id: string;
|
||||
T: T;
|
||||
$$type: 'extension-data';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ExtensionDataValue<TData extends AnyExtensionDataMap> = {
|
||||
[K in keyof TData]: TData[K]['T'];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionInstanceConfig {
|
||||
// (undocumented)
|
||||
at: string;
|
||||
// (undocumented)
|
||||
config: unknown;
|
||||
// (undocumented)
|
||||
extension: Extension;
|
||||
// (undocumented)
|
||||
id: string;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@backstage/frontend-plugin-api",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "web-library"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@testing-library/jest-dom": "^5.10.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export {
|
||||
createExtension,
|
||||
coreExtensionData,
|
||||
createPlugin,
|
||||
type ExtensionInstanceConfig,
|
||||
type BackstagePlugin,
|
||||
type Extension,
|
||||
type AnyExtensionDataMap,
|
||||
type BackstagePluginOptions,
|
||||
type CreateExtensionOptions,
|
||||
type ExtensionDataBind,
|
||||
type ExtensionDataRef,
|
||||
type ExtensionDataValue,
|
||||
} from './types';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 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 '@testing-library/jest-dom';
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2023 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 } from 'react';
|
||||
|
||||
/** @public */
|
||||
export type ExtensionDataRef<T> = {
|
||||
id: string;
|
||||
T: T;
|
||||
$$type: 'extension-data';
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export function createExtensionDataRef<T>(id: string): ExtensionDataRef<T> {
|
||||
return { id, $$type: 'extension-data' } as ExtensionDataRef<T>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const coreExtensionData = {
|
||||
reactComponent: createExtensionDataRef<ComponentType>('core.reactComponent'),
|
||||
routePath: createExtensionDataRef<string>('core.routing.path'),
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type AnyExtensionDataMap = Record<string, ExtensionDataRef<any>>;
|
||||
|
||||
/** @public */
|
||||
export type ExtensionDataBind<TData extends AnyExtensionDataMap> = {
|
||||
[K in keyof TData]: (value: TData[K]['T']) => void;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type ExtensionDataValue<TData extends AnyExtensionDataMap> = {
|
||||
[K in keyof TData]: TData[K]['T'];
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface CreateExtensionOptions<
|
||||
TData extends AnyExtensionDataMap,
|
||||
TPoint extends Record<string, { extensionData: AnyExtensionDataMap }>,
|
||||
> {
|
||||
inputs?: TPoint;
|
||||
output: TData;
|
||||
factory(options: {
|
||||
bind: ExtensionDataBind<TData>;
|
||||
config?: unknown;
|
||||
inputs: {
|
||||
[pointName in keyof TPoint]: ExtensionDataValue<
|
||||
TPoint[pointName]['extensionData']
|
||||
>[];
|
||||
};
|
||||
}): void;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface Extension {
|
||||
$$type: 'extension';
|
||||
inputs: Record<string, { extensionData: AnyExtensionDataMap }>;
|
||||
output: AnyExtensionDataMap;
|
||||
factory(options: {
|
||||
bind: ExtensionDataBind<AnyExtensionDataMap>;
|
||||
config?: unknown;
|
||||
inputs: Record<string, Array<Record<string, unknown>>>;
|
||||
}): void;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createExtension<
|
||||
TData extends AnyExtensionDataMap,
|
||||
TPoint extends Record<string, { extensionData: AnyExtensionDataMap }>,
|
||||
>(options: CreateExtensionOptions<TData, TPoint>): Extension {
|
||||
return { ...options, $$type: 'extension', inputs: options.inputs ?? {} };
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionInstanceConfig {
|
||||
id: string;
|
||||
at: string;
|
||||
extension: Extension;
|
||||
config: unknown;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface BackstagePluginOptions {
|
||||
id: string;
|
||||
defaultExtensionInstances?: ExtensionInstanceConfig[];
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface BackstagePlugin {
|
||||
$$type: 'backstage-plugin';
|
||||
id: string;
|
||||
defaultExtensionInstances: ExtensionInstanceConfig[];
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createPlugin(options: BackstagePluginOptions): BackstagePlugin {
|
||||
return {
|
||||
...options,
|
||||
$$type: 'backstage-plugin',
|
||||
defaultExtensionInstances: options.defaultExtensionInstances ?? [],
|
||||
};
|
||||
}
|
||||
@@ -4132,6 +4132,34 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/frontend-app-api@workspace:^, @backstage/frontend-app-api@workspace:packages/frontend-app-api":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/frontend-app-api@workspace:packages/frontend-app-api"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/plugin-graphiql": "workspace:^"
|
||||
"@testing-library/jest-dom": ^5.10.1
|
||||
lodash: ^4.17.21
|
||||
peerDependencies:
|
||||
react: ^16.13.1 || ^17.0.0
|
||||
react-router-dom: 6.0.0-beta.0 || ^6.3.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/frontend-plugin-api@workspace:^, @backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@testing-library/jest-dom": ^5.10.1
|
||||
lodash: ^4.17.21
|
||||
peerDependencies:
|
||||
react: ^16.13.1 || ^17.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/integration-aws-node@workspace:^, @backstage/integration-aws-node@workspace:packages/integration-aws-node":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/integration-aws-node@workspace:packages/integration-aws-node"
|
||||
@@ -25316,6 +25344,116 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"example-app-next@workspace:packages/app-next":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-app-next@workspace:packages/app-next"
|
||||
dependencies:
|
||||
"@backstage/app-defaults": "workspace:^"
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/core-app-api": "workspace:^"
|
||||
"@backstage/core-components": "workspace:^"
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-app-api": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/integration-react": "workspace:^"
|
||||
"@backstage/plugin-adr": "workspace:^"
|
||||
"@backstage/plugin-airbrake": "workspace:^"
|
||||
"@backstage/plugin-apache-airflow": "workspace:^"
|
||||
"@backstage/plugin-api-docs": "workspace:^"
|
||||
"@backstage/plugin-azure-devops": "workspace:^"
|
||||
"@backstage/plugin-azure-sites": "workspace:^"
|
||||
"@backstage/plugin-badges": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-graph": "workspace:^"
|
||||
"@backstage/plugin-catalog-import": "workspace:^"
|
||||
"@backstage/plugin-catalog-react": "workspace:^"
|
||||
"@backstage/plugin-catalog-unprocessed-entities": "workspace:^"
|
||||
"@backstage/plugin-circleci": "workspace:^"
|
||||
"@backstage/plugin-cloudbuild": "workspace:^"
|
||||
"@backstage/plugin-code-coverage": "workspace:^"
|
||||
"@backstage/plugin-cost-insights": "workspace:^"
|
||||
"@backstage/plugin-devtools": "workspace:^"
|
||||
"@backstage/plugin-dynatrace": "workspace:^"
|
||||
"@backstage/plugin-entity-feedback": "workspace:^"
|
||||
"@backstage/plugin-explore": "workspace:^"
|
||||
"@backstage/plugin-gcalendar": "workspace:^"
|
||||
"@backstage/plugin-gcp-projects": "workspace:^"
|
||||
"@backstage/plugin-github-actions": "workspace:^"
|
||||
"@backstage/plugin-gocd": "workspace:^"
|
||||
"@backstage/plugin-graphiql": "workspace:^"
|
||||
"@backstage/plugin-home": "workspace:^"
|
||||
"@backstage/plugin-jenkins": "workspace:^"
|
||||
"@backstage/plugin-kafka": "workspace:^"
|
||||
"@backstage/plugin-kubernetes": "workspace:^"
|
||||
"@backstage/plugin-lighthouse": "workspace:^"
|
||||
"@backstage/plugin-linguist": "workspace:^"
|
||||
"@backstage/plugin-linguist-common": "workspace:^"
|
||||
"@backstage/plugin-microsoft-calendar": "workspace:^"
|
||||
"@backstage/plugin-newrelic": "workspace:^"
|
||||
"@backstage/plugin-newrelic-dashboard": "workspace:^"
|
||||
"@backstage/plugin-octopus-deploy": "workspace:^"
|
||||
"@backstage/plugin-org": "workspace:^"
|
||||
"@backstage/plugin-pagerduty": "workspace:^"
|
||||
"@backstage/plugin-permission-react": "workspace:^"
|
||||
"@backstage/plugin-playlist": "workspace:^"
|
||||
"@backstage/plugin-puppetdb": "workspace:^"
|
||||
"@backstage/plugin-rollbar": "workspace:^"
|
||||
"@backstage/plugin-scaffolder": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-react": "workspace:^"
|
||||
"@backstage/plugin-search": "workspace:^"
|
||||
"@backstage/plugin-search-common": "workspace:^"
|
||||
"@backstage/plugin-search-react": "workspace:^"
|
||||
"@backstage/plugin-sentry": "workspace:^"
|
||||
"@backstage/plugin-shortcuts": "workspace:^"
|
||||
"@backstage/plugin-stack-overflow": "workspace:^"
|
||||
"@backstage/plugin-stackstorm": "workspace:^"
|
||||
"@backstage/plugin-tech-insights": "workspace:^"
|
||||
"@backstage/plugin-tech-radar": "workspace:^"
|
||||
"@backstage/plugin-techdocs": "workspace:^"
|
||||
"@backstage/plugin-techdocs-module-addons-contrib": "workspace:^"
|
||||
"@backstage/plugin-techdocs-react": "workspace:^"
|
||||
"@backstage/plugin-todo": "workspace:^"
|
||||
"@backstage/plugin-user-settings": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/theme": "workspace:^"
|
||||
"@internal/plugin-catalog-customized": "workspace:^"
|
||||
"@material-ui/core": ^4.12.2
|
||||
"@material-ui/icons": ^4.9.1
|
||||
"@material-ui/lab": 4.0.0-alpha.61
|
||||
"@octokit/rest": ^19.0.3
|
||||
"@oriflame/backstage-plugin-score-card": ^0.7.0
|
||||
"@roadiehq/backstage-plugin-buildkite": ^2.0.8
|
||||
"@roadiehq/backstage-plugin-github-insights": ^2.0.5
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": ^2.2.7
|
||||
"@roadiehq/backstage-plugin-travis-ci": ^2.0.5
|
||||
"@testing-library/cypress": ^9.0.0
|
||||
"@testing-library/dom": ^8.0.0
|
||||
"@testing-library/jest-dom": ^5.10.1
|
||||
"@testing-library/react": ^12.1.3
|
||||
"@testing-library/user-event": ^14.0.0
|
||||
"@types/jquery": ^3.3.34
|
||||
"@types/node": ^16.11.26
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
"@types/zen-observable": ^0.8.0
|
||||
cross-env: ^7.0.0
|
||||
cypress: ^10.0.0
|
||||
eslint-plugin-cypress: ^2.10.3
|
||||
history: ^5.0.0
|
||||
lodash: ^4.17.21
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
react-router: ^6.3.0
|
||||
react-router-dom: ^6.3.0
|
||||
react-use: ^17.2.4
|
||||
start-server-and-test: ^1.10.11
|
||||
zen-observable: ^0.10.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"example-app@link:../app::locator=example-backend%40workspace%3Apackages%2Fbackend":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-app@link:../app::locator=example-backend%40workspace%3Apackages%2Fbackend"
|
||||
|
||||