@backstage/plugin-techdocs 0.0.0-nightly-20250218023004 → 0.0.0-nightly-20250220023208
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -11
- package/dist/alpha.d.ts +5 -0
- package/dist/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.esm.js +15 -4
- package/dist/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.esm.js.map +1 -1
- package/dist/reader/transformers/addBaseUrl.esm.js +2 -1
- package/dist/reader/transformers/addBaseUrl.esm.js.map +1 -1
- package/dist/reader/transformers/html/transformer.esm.js +1 -1
- package/dist/reader/transformers/html/transformer.esm.js.map +1 -1
- package/package.json +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# @backstage/plugin-techdocs
|
|
2
2
|
|
|
3
|
-
## 0.0.0-nightly-
|
|
3
|
+
## 0.0.0-nightly-20250220023208
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 065e6b9: Excludes svg styling from sanitization
|
|
8
|
+
- ed1cb3e: Adds the page name of techdoc to the document's title.
|
|
9
|
+
- fe4abb8: Updates logic to check for SVG sources when inlining svgs.
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
- @backstage/plugin-catalog-react@0.0.0-nightly-20250220023208
|
|
12
|
+
- @backstage/core-compat-api@0.3.6
|
|
13
|
+
- @backstage/integration-react@1.2.4
|
|
14
|
+
|
|
15
|
+
## 1.12.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
6
18
|
|
|
@@ -120,16 +132,16 @@
|
|
|
120
132
|
|
|
121
133
|
- 58ec9e7: Removed older versions of React packages as a preparatory step for upgrading to React 19. This commit does not introduce any functional changes, but removes dependencies on previous React versions, allowing for a cleaner upgrade path in subsequent commits.
|
|
122
134
|
- Updated dependencies
|
|
123
|
-
- @backstage/plugin-search-react@
|
|
124
|
-
- @backstage/core-components@0.
|
|
125
|
-
- @backstage/plugin-catalog-react@
|
|
126
|
-
- @backstage/frontend-plugin-api@0.
|
|
127
|
-
- @backstage/integration-react@
|
|
128
|
-
- @backstage/core-compat-api@0.
|
|
129
|
-
- @backstage/core-plugin-api@
|
|
130
|
-
- @backstage/plugin-techdocs-react@
|
|
131
|
-
- @backstage/plugin-auth-react@0.
|
|
132
|
-
- @backstage/theme@0.
|
|
135
|
+
- @backstage/plugin-search-react@1.8.6
|
|
136
|
+
- @backstage/core-components@0.16.4
|
|
137
|
+
- @backstage/plugin-catalog-react@1.15.2
|
|
138
|
+
- @backstage/frontend-plugin-api@0.9.5
|
|
139
|
+
- @backstage/integration-react@1.2.4
|
|
140
|
+
- @backstage/core-compat-api@0.3.6
|
|
141
|
+
- @backstage/core-plugin-api@1.10.4
|
|
142
|
+
- @backstage/plugin-techdocs-react@1.2.14
|
|
143
|
+
- @backstage/plugin-auth-react@0.1.12
|
|
144
|
+
- @backstage/theme@0.6.4
|
|
133
145
|
- @backstage/catalog-client@1.9.1
|
|
134
146
|
- @backstage/catalog-model@1.7.3
|
|
135
147
|
- @backstage/config@1.3.2
|
package/dist/alpha.d.ts
CHANGED
|
@@ -159,11 +159,13 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
|
|
|
159
159
|
path: string | undefined;
|
|
160
160
|
title: string | undefined;
|
|
161
161
|
filter: string | undefined;
|
|
162
|
+
group: string | false | undefined;
|
|
162
163
|
};
|
|
163
164
|
configInput: {
|
|
164
165
|
filter?: string | undefined;
|
|
165
166
|
title?: string | undefined;
|
|
166
167
|
path?: string | undefined;
|
|
168
|
+
group?: string | false | undefined;
|
|
167
169
|
};
|
|
168
170
|
output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
169
171
|
optional: true;
|
|
@@ -171,6 +173,8 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
|
|
|
171
173
|
optional: true;
|
|
172
174
|
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
|
|
173
175
|
optional: true;
|
|
176
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string | false, "catalog.entity-content-group", {
|
|
177
|
+
optional: true;
|
|
174
178
|
}>;
|
|
175
179
|
inputs: {
|
|
176
180
|
emptyState: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {
|
|
@@ -186,6 +190,7 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
|
|
|
186
190
|
loader: () => Promise<JSX.Element>;
|
|
187
191
|
defaultPath: string;
|
|
188
192
|
defaultTitle: string;
|
|
193
|
+
defaultGroup?: "documentation" | "development" | "deployment" | "observability" | undefined;
|
|
189
194
|
routeRef?: _backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams> | undefined;
|
|
190
195
|
filter?: string | ((entity: _backstage_catalog_model.Entity) => boolean) | undefined;
|
|
191
196
|
};
|
|
@@ -4,18 +4,21 @@ import Grid from '@material-ui/core/Grid';
|
|
|
4
4
|
import Skeleton from '@material-ui/lab/Skeleton';
|
|
5
5
|
import CodeIcon from '@material-ui/icons/Code';
|
|
6
6
|
import { useTechDocsAddons, useTechDocsReaderPage, TechDocsAddonLocations } from '@backstage/plugin-techdocs-react';
|
|
7
|
-
import { getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
|
|
8
|
-
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
|
|
7
|
+
import { entityPresentationApiRef, getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
|
|
8
|
+
import { RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
|
|
9
9
|
import { HeaderLabel, Header } from '@backstage/core-components';
|
|
10
10
|
import { useApi, configApiRef, useRouteRef } from '@backstage/core-plugin-api';
|
|
11
|
-
import
|
|
11
|
+
import capitalize from 'lodash/capitalize';
|
|
12
12
|
import { rootRouteRef } from '../../../routes.esm.js';
|
|
13
|
+
import { useParams } from 'react-router-dom';
|
|
13
14
|
|
|
14
15
|
const skeleton = /* @__PURE__ */ React.createElement(Skeleton, { animation: "wave", variant: "text", height: 40 });
|
|
15
16
|
const TechDocsReaderPageHeader = (props) => {
|
|
16
17
|
const { children } = props;
|
|
17
18
|
const addons = useTechDocsAddons();
|
|
18
19
|
const configApi = useApi(configApiRef);
|
|
20
|
+
const entityPresentationApi = useApi(entityPresentationApiRef);
|
|
21
|
+
const { "*": path = "" } = useParams();
|
|
19
22
|
const {
|
|
20
23
|
title,
|
|
21
24
|
setTitle,
|
|
@@ -37,7 +40,6 @@ const TechDocsReaderPageHeader = (props) => {
|
|
|
37
40
|
});
|
|
38
41
|
}, [metadata, setTitle, setSubtitle]);
|
|
39
42
|
const appTitle = configApi.getOptional("app.title") || "Backstage";
|
|
40
|
-
const tabTitle = [title, subtitle, appTitle].filter(Boolean).join(" | ");
|
|
41
43
|
const { locationMetadata, spec } = entityMetadata || {};
|
|
42
44
|
const lifecycle = spec?.lifecycle;
|
|
43
45
|
const ownedByRelations = entityMetadata ? getEntityRelations(entityMetadata, RELATION_OWNED_BY) : [];
|
|
@@ -80,6 +82,15 @@ const TechDocsReaderPageHeader = (props) => {
|
|
|
80
82
|
const noEntMetadata = !entityMetadataLoading && entityMetadata === void 0;
|
|
81
83
|
const noTdMetadata = !metadataLoading && metadata === void 0;
|
|
82
84
|
if (noEntMetadata || noTdMetadata) return null;
|
|
85
|
+
const stringEntityRef = stringifyEntityRef(entityRef);
|
|
86
|
+
const entityDisplayName = entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;
|
|
87
|
+
const removeTrailingSlash = (str) => str.replace(/\/$/, "");
|
|
88
|
+
const normalizeAndSpace = (str) => str.replace(/-/g, " ").split(" ").map(capitalize).join(" ");
|
|
89
|
+
let techdocsTabTitleItems = [];
|
|
90
|
+
if (path !== "")
|
|
91
|
+
techdocsTabTitleItems = removeTrailingSlash(path).split("/").slice(0, 3).map(normalizeAndSpace);
|
|
92
|
+
const tabTitleItems = [appTitle, entityDisplayName, ...techdocsTabTitleItems];
|
|
93
|
+
const tabTitle = tabTitleItems.join(" | ");
|
|
83
94
|
return /* @__PURE__ */ React.createElement(
|
|
84
95
|
Header,
|
|
85
96
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TechDocsReaderPageHeader.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useEffect } from 'react';\nimport Helmet from 'react-helmet';\n\nimport Grid from '@material-ui/core/Grid';\nimport Skeleton from '@material-ui/lab/Skeleton';\nimport CodeIcon from '@material-ui/icons/Code';\n\nimport {\n TechDocsAddonLocations as locations,\n useTechDocsAddons,\n useTechDocsReaderPage,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n} from '@backstage/plugin-techdocs-react';\nimport {\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { RELATION_OWNED_BY, CompoundEntityRef } from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { capitalize } from 'lodash';\n\nimport { rootRouteRef } from '../../../routes';\n\nconst skeleton = <Skeleton animation=\"wave\" variant=\"text\" height={40} />;\n\n/**\n * Props for {@link TechDocsReaderPageHeader}\n *\n * @public\n * @deprecated No need to pass down properties anymore. The component consumes data from `TechDocsReaderPageContext` instead. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom header.\n */\nexport type TechDocsReaderPageHeaderProps = PropsWithChildren<{\n entityRef?: CompoundEntityRef;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n}>;\n\n/**\n * Renders the reader page header.\n * This component does not accept props, please use\n * the Tech Docs add-ons to customize it\n * @public\n */\nexport const TechDocsReaderPageHeader = (\n props: TechDocsReaderPageHeaderProps,\n) => {\n const { children } = props;\n const addons = useTechDocsAddons();\n const configApi = useApi(configApiRef);\n\n const {\n title,\n setTitle,\n subtitle,\n setSubtitle,\n entityRef,\n metadata: { value: metadata, loading: metadataLoading },\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n } = useTechDocsReaderPage();\n\n useEffect(() => {\n if (!metadata) return;\n setTitle(metadata.site_name);\n setSubtitle(() => {\n let { site_description } = metadata;\n if (!site_description || site_description === 'None') {\n site_description = '';\n }\n return site_description;\n });\n }, [metadata, setTitle, setSubtitle]);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n const tabTitle = [title, subtitle, appTitle].filter(Boolean).join(' | ');\n\n const { locationMetadata, spec } = entityMetadata || {};\n const lifecycle = spec?.lifecycle;\n\n const ownedByRelations = entityMetadata\n ? getEntityRelations(entityMetadata, RELATION_OWNED_BY)\n : [];\n\n const docsRootLink = useRouteRef(rootRouteRef)();\n\n const labels = (\n <>\n <HeaderLabel\n label={capitalize(entityMetadata?.kind || 'entity')}\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\n title={entityMetadata?.metadata.title}\n defaultKind=\"Component\"\n />\n }\n />\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label=\"Owner\"\n value={\n <EntityRefLinks\n color=\"inherit\"\n entityRefs={ownedByRelations}\n defaultKind=\"group\"\n />\n }\n />\n )}\n {lifecycle ? (\n <HeaderLabel label=\"Lifecycle\" value={String(lifecycle)} />\n ) : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <Grid container direction=\"column\" alignItems=\"center\">\n <Grid style={{ padding: 0 }} item>\n <CodeIcon style={{ marginTop: '-25px' }} />\n </Grid>\n <Grid style={{ padding: 0 }} item>\n Source\n </Grid>\n </Grid>\n }\n url={locationMetadata.target}\n />\n ) : null}\n </>\n );\n\n // If there is no entity or techdocs metadata, there's no reason to show the\n // header (hides the header on 404 error pages).\n const noEntMetadata = !entityMetadataLoading && entityMetadata === undefined;\n const noTdMetadata = !metadataLoading && metadata === undefined;\n if (noEntMetadata || noTdMetadata) return null;\n\n return (\n <Header\n type=\"Documentation\"\n typeLink={docsRootLink}\n title={title || skeleton}\n subtitle={subtitle === '' ? undefined : subtitle || skeleton}\n >\n <Helmet titleTemplate=\"%s\">\n <title>{tabTitle}</title>\n </Helmet>\n {labels}\n {children}\n {addons.renderComponentsByLocation(locations.Header)}\n </Header>\n );\n};\n"],"names":["locations"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TechDocsReaderPageHeader.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useEffect } from 'react';\nimport Helmet from 'react-helmet';\n\nimport Grid from '@material-ui/core/Grid';\nimport Skeleton from '@material-ui/lab/Skeleton';\nimport CodeIcon from '@material-ui/icons/Code';\n\nimport {\n TechDocsAddonLocations as locations,\n useTechDocsAddons,\n useTechDocsReaderPage,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n} from '@backstage/plugin-techdocs-react';\nimport {\n entityPresentationApiRef,\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport {\n RELATION_OWNED_BY,\n CompoundEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport capitalize from 'lodash/capitalize';\n\nimport { rootRouteRef } from '../../../routes';\nimport { useParams } from 'react-router-dom';\n\nconst skeleton = <Skeleton animation=\"wave\" variant=\"text\" height={40} />;\n\n/**\n * Props for {@link TechDocsReaderPageHeader}\n *\n * @public\n * @deprecated No need to pass down properties anymore. The component consumes data from `TechDocsReaderPageContext` instead. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom header.\n */\nexport type TechDocsReaderPageHeaderProps = PropsWithChildren<{\n entityRef?: CompoundEntityRef;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n}>;\n\n/**\n * Renders the reader page header.\n * This component does not accept props, please use\n * the Tech Docs add-ons to customize it\n * @public\n */\nexport const TechDocsReaderPageHeader = (\n props: TechDocsReaderPageHeaderProps,\n) => {\n const { children } = props;\n const addons = useTechDocsAddons();\n const configApi = useApi(configApiRef);\n\n const entityPresentationApi = useApi(entityPresentationApiRef);\n const { '*': path = '' } = useParams();\n\n const {\n title,\n setTitle,\n subtitle,\n setSubtitle,\n entityRef,\n metadata: { value: metadata, loading: metadataLoading },\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n } = useTechDocsReaderPage();\n\n useEffect(() => {\n if (!metadata) return;\n setTitle(metadata.site_name);\n setSubtitle(() => {\n let { site_description } = metadata;\n if (!site_description || site_description === 'None') {\n site_description = '';\n }\n return site_description;\n });\n }, [metadata, setTitle, setSubtitle]);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n const { locationMetadata, spec } = entityMetadata || {};\n const lifecycle = spec?.lifecycle;\n\n const ownedByRelations = entityMetadata\n ? getEntityRelations(entityMetadata, RELATION_OWNED_BY)\n : [];\n\n const docsRootLink = useRouteRef(rootRouteRef)();\n\n const labels = (\n <>\n <HeaderLabel\n label={capitalize(entityMetadata?.kind || 'entity')}\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\n title={entityMetadata?.metadata.title}\n defaultKind=\"Component\"\n />\n }\n />\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label=\"Owner\"\n value={\n <EntityRefLinks\n color=\"inherit\"\n entityRefs={ownedByRelations}\n defaultKind=\"group\"\n />\n }\n />\n )}\n {lifecycle ? (\n <HeaderLabel label=\"Lifecycle\" value={String(lifecycle)} />\n ) : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <Grid container direction=\"column\" alignItems=\"center\">\n <Grid style={{ padding: 0 }} item>\n <CodeIcon style={{ marginTop: '-25px' }} />\n </Grid>\n <Grid style={{ padding: 0 }} item>\n Source\n </Grid>\n </Grid>\n }\n url={locationMetadata.target}\n />\n ) : null}\n </>\n );\n\n // If there is no entity or techdocs metadata, there's no reason to show the\n // header (hides the header on 404 error pages).\n const noEntMetadata = !entityMetadataLoading && entityMetadata === undefined;\n const noTdMetadata = !metadataLoading && metadata === undefined;\n if (noEntMetadata || noTdMetadata) return null;\n\n const stringEntityRef = stringifyEntityRef(entityRef);\n\n const entityDisplayName =\n entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;\n\n const removeTrailingSlash = (str: string) => str.replace(/\\/$/, '');\n const normalizeAndSpace = (str: string) =>\n str.replace(/-/g, ' ').split(' ').map(capitalize).join(' ');\n\n let techdocsTabTitleItems: string[] = [];\n\n if (path !== '')\n techdocsTabTitleItems = removeTrailingSlash(path)\n .split('/')\n .slice(0, 3)\n .map(normalizeAndSpace);\n\n const tabTitleItems = [appTitle, entityDisplayName, ...techdocsTabTitleItems];\n const tabTitle = tabTitleItems.join(' | ');\n\n return (\n <Header\n type=\"Documentation\"\n typeLink={docsRootLink}\n title={title || skeleton}\n subtitle={subtitle === '' ? undefined : subtitle || skeleton}\n >\n <Helmet titleTemplate=\"%s\">\n <title>{tabTitle}</title>\n </Helmet>\n {labels}\n {children}\n {addons.renderComponentsByLocation(locations.Header)}\n </Header>\n );\n};\n"],"names":["locations"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const isSvgNeedingInlining = (attrName, attrVal, apiOrigin) => {
|
|
2
|
-
const
|
|
2
|
+
const pathname = new URL(attrVal, "https://ignored.com").pathname;
|
|
3
|
+
const isSrcToSvg = attrName === "src" && pathname.endsWith(".svg");
|
|
3
4
|
const isRelativeUrl = !attrVal.match(/^([a-z]*:)?\/\//i);
|
|
4
5
|
const pointsToOurBackend = attrVal.startsWith(apiOrigin);
|
|
5
6
|
return isSrcToSvg && (isRelativeUrl || pointsToOurBackend);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"addBaseUrl.esm.js","sources":["../../../src/reader/transformers/addBaseUrl.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { TechDocsStorageApi } from '../../api';\nimport type { Transformer } from './transformer';\n\ntype AddBaseUrlOptions = {\n techdocsStorageApi: TechDocsStorageApi;\n entityId: CompoundEntityRef;\n path: string;\n};\n\n/**\n * TechDocs backend serves SVGs with text/plain content-type for security. This\n * helper determines if an SVG is being loaded from the backend, and thus needs\n * inlining to be displayed properly.\n */\nconst isSvgNeedingInlining = (\n attrName: string,\n attrVal: string,\n apiOrigin: string,\n) => {\n const isSrcToSvg = attrName === 'src' &&
|
|
1
|
+
{"version":3,"file":"addBaseUrl.esm.js","sources":["../../../src/reader/transformers/addBaseUrl.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { TechDocsStorageApi } from '../../api';\nimport type { Transformer } from './transformer';\n\ntype AddBaseUrlOptions = {\n techdocsStorageApi: TechDocsStorageApi;\n entityId: CompoundEntityRef;\n path: string;\n};\n\n/**\n * TechDocs backend serves SVGs with text/plain content-type for security. This\n * helper determines if an SVG is being loaded from the backend, and thus needs\n * inlining to be displayed properly.\n */\nconst isSvgNeedingInlining = (\n attrName: string,\n attrVal: string,\n apiOrigin: string,\n) => {\n // Let's let the URL object do automatic parsing of the pathname for us\n const pathname = new URL(attrVal, 'https://ignored.com').pathname;\n const isSrcToSvg = attrName === 'src' && pathname.endsWith('.svg');\n\n const isRelativeUrl = !attrVal.match(/^([a-z]*:)?\\/\\//i);\n const pointsToOurBackend = attrVal.startsWith(apiOrigin);\n return isSrcToSvg && (isRelativeUrl || pointsToOurBackend);\n};\n\nexport const addBaseUrl = ({\n techdocsStorageApi,\n entityId,\n path,\n}: AddBaseUrlOptions): Transformer => {\n return async dom => {\n const apiOrigin = await techdocsStorageApi.getApiOrigin();\n\n const updateDom = async <T extends Element>(\n list: HTMLCollectionOf<T> | NodeListOf<T>,\n attributeName: string,\n ) => {\n for (const elem of list) {\n if (elem.hasAttribute(attributeName)) {\n const elemAttribute = elem.getAttribute(attributeName);\n if (!elemAttribute) return;\n\n // Special handling for SVG images.\n const newValue = await techdocsStorageApi.getBaseUrl(\n elemAttribute,\n entityId,\n path,\n );\n\n if (isSvgNeedingInlining(attributeName, elemAttribute, apiOrigin)) {\n try {\n const svg = await fetch(newValue, { credentials: 'include' });\n const svgContent = await svg.text();\n elem.setAttribute(\n attributeName,\n `data:image/svg+xml;base64,${btoa(\n unescape(encodeURIComponent(svgContent)),\n )}`,\n );\n } catch (e) {\n elem.setAttribute('alt', `Error: ${elemAttribute}`);\n }\n } else {\n elem.setAttribute(attributeName, newValue);\n }\n }\n }\n };\n\n await Promise.all([\n updateDom<HTMLImageElement>(dom.querySelectorAll('img'), 'src'),\n updateDom<HTMLScriptElement>(dom.querySelectorAll('script'), 'src'),\n updateDom<HTMLSourceElement>(dom.querySelectorAll('source'), 'src'),\n updateDom<HTMLLinkElement>(dom.querySelectorAll('link'), 'href'),\n updateDom<HTMLAnchorElement>(dom.querySelectorAll('a[download]'), 'href'),\n ]);\n\n return dom;\n };\n};\n"],"names":[],"mappings":"AA8BA,MAAM,oBAAuB,GAAA,CAC3B,QACA,EAAA,OAAA,EACA,SACG,KAAA;AAEH,EAAA,MAAM,QAAW,GAAA,IAAI,GAAI,CAAA,OAAA,EAAS,qBAAqB,CAAE,CAAA,QAAA;AACzD,EAAA,MAAM,UAAa,GAAA,QAAA,KAAa,KAAS,IAAA,QAAA,CAAS,SAAS,MAAM,CAAA;AAEjE,EAAA,MAAM,aAAgB,GAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,kBAAkB,CAAA;AACvD,EAAM,MAAA,kBAAA,GAAqB,OAAQ,CAAA,UAAA,CAAW,SAAS,CAAA;AACvD,EAAA,OAAO,eAAe,aAAiB,IAAA,kBAAA,CAAA;AACzC,CAAA;AAEO,MAAM,aAAa,CAAC;AAAA,EACzB,kBAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAsC,KAAA;AACpC,EAAA,OAAO,OAAM,GAAO,KAAA;AAClB,IAAM,MAAA,SAAA,GAAY,MAAM,kBAAA,CAAmB,YAAa,EAAA;AAExD,IAAM,MAAA,SAAA,GAAY,OAChB,IAAA,EACA,aACG,KAAA;AACH,MAAA,KAAA,MAAW,QAAQ,IAAM,EAAA;AACvB,QAAI,IAAA,IAAA,CAAK,YAAa,CAAA,aAAa,CAAG,EAAA;AACpC,UAAM,MAAA,aAAA,GAAgB,IAAK,CAAA,YAAA,CAAa,aAAa,CAAA;AACrD,UAAA,IAAI,CAAC,aAAe,EAAA;AAGpB,UAAM,MAAA,QAAA,GAAW,MAAM,kBAAmB,CAAA,UAAA;AAAA,YACxC,aAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACF;AAEA,UAAA,IAAI,oBAAqB,CAAA,aAAA,EAAe,aAAe,EAAA,SAAS,CAAG,EAAA;AACjE,YAAI,IAAA;AACF,cAAA,MAAM,MAAM,MAAM,KAAA,CAAM,UAAU,EAAE,WAAA,EAAa,WAAW,CAAA;AAC5D,cAAM,MAAA,UAAA,GAAa,MAAM,GAAA,CAAI,IAAK,EAAA;AAClC,cAAK,IAAA,CAAA,YAAA;AAAA,gBACH,aAAA;AAAA,gBACA,CAA6B,0BAAA,EAAA,IAAA;AAAA,kBAC3B,QAAA,CAAS,kBAAmB,CAAA,UAAU,CAAC;AAAA,iBACxC,CAAA;AAAA,eACH;AAAA,qBACO,CAAG,EAAA;AACV,cAAA,IAAA,CAAK,YAAa,CAAA,KAAA,EAAO,CAAU,OAAA,EAAA,aAAa,CAAE,CAAA,CAAA;AAAA;AACpD,WACK,MAAA;AACL,YAAK,IAAA,CAAA,YAAA,CAAa,eAAe,QAAQ,CAAA;AAAA;AAC3C;AACF;AACF,KACF;AAEA,IAAA,MAAM,QAAQ,GAAI,CAAA;AAAA,MAChB,SAA4B,CAAA,GAAA,CAAI,gBAAiB,CAAA,KAAK,GAAG,KAAK,CAAA;AAAA,MAC9D,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,QAAQ,GAAG,KAAK,CAAA;AAAA,MAClE,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,QAAQ,GAAG,KAAK,CAAA;AAAA,MAClE,SAA2B,CAAA,GAAA,CAAI,gBAAiB,CAAA,MAAM,GAAG,MAAM,CAAA;AAAA,MAC/D,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,aAAa,GAAG,MAAM;AAAA,KACzE,CAAA;AAED,IAAO,OAAA,GAAA;AAAA,GACT;AACF;;;;"}
|
|
@@ -45,7 +45,7 @@ const useSanitizerTransformer = () => {
|
|
|
45
45
|
return DOMPurify.sanitize(dom.outerHTML, {
|
|
46
46
|
ADD_TAGS: tags,
|
|
47
47
|
FORBID_TAGS: ["style"],
|
|
48
|
-
ADD_ATTR: ["http-equiv", "content"],
|
|
48
|
+
ADD_ATTR: ["http-equiv", "content", "dominant-baseline"],
|
|
49
49
|
WHOLE_DOCUMENT: true,
|
|
50
50
|
RETURN_DOM: true,
|
|
51
51
|
CUSTOM_ELEMENT_HANDLING: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformer.esm.js","sources":["../../../../src/reader/transformers/html/transformer.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport DOMPurify from 'dompurify';\nimport { useCallback, useMemo } from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { Transformer } from '../transformer';\nimport { removeUnsafeIframes, removeUnsafeLinks } from './hooks';\n\n/**\n * Returns html sanitizer configuration\n */\nconst useSanitizerConfig = () => {\n const configApi = useApi(configApiRef);\n\n return useMemo(() => {\n return configApi.getOptionalConfig('techdocs.sanitizer');\n }, [configApi]);\n};\n\n/**\n * Returns a transformer that sanitizes the dom\n */\nexport const useSanitizerTransformer = (): Transformer => {\n const config = useSanitizerConfig();\n\n return useCallback(\n async (dom: Element) => {\n const hosts = config?.getOptionalStringArray('allowedIframeHosts');\n\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeLinks);\n const tags = ['link', 'meta'];\n\n if (hosts) {\n tags.push('iframe');\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts));\n }\n\n // Only allow meta tags if they are used for refreshing the page. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeElement', (currNode, data) => {\n if (data.tagName === 'meta') {\n const isMetaRefreshTag =\n currNode.getAttribute('http-equiv') === 'refresh' &&\n currNode.getAttribute('content')?.includes('url=');\n if (!isMetaRefreshTag) {\n currNode.parentNode?.removeChild(currNode);\n }\n }\n });\n\n // Only allow http-equiv and content attributes on meta tags. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeAttribute', (currNode, data) => {\n if (currNode.tagName !== 'meta') {\n if (data.attrName === 'http-equiv' || data.attrName === 'content') {\n currNode.removeAttribute(data.attrName);\n }\n }\n });\n\n const tagNameCheck = config?.getOptionalString(\n 'allowedCustomElementTagNameRegExp',\n );\n const attributeNameCheck = config?.getOptionalString(\n 'allowedCustomElementAttributeNameRegExp',\n );\n\n // using outerHTML as we want to preserve the html tag attributes (lang)\n return DOMPurify.sanitize(dom.outerHTML, {\n ADD_TAGS: tags,\n FORBID_TAGS: ['style'],\n ADD_ATTR: ['http-equiv', 'content'],\n WHOLE_DOCUMENT: true,\n RETURN_DOM: true,\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: tagNameCheck ? new RegExp(tagNameCheck) : undefined,\n attributeNameCheck: attributeNameCheck\n ? new RegExp(attributeNameCheck)\n : undefined,\n },\n });\n },\n [config],\n );\n};\n"],"names":[],"mappings":";;;;;;AA2BA,MAAM,qBAAqB,MAAM;AAC/B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAO,OAAA,SAAA,CAAU,kBAAkB,oBAAoB,CAAA;AAAA,GACzD,EAAG,CAAC,SAAS,CAAC,CAAA;AAChB,CAAA;AAKO,MAAM,0BAA0B,MAAmB;AACxD,EAAA,MAAM,SAAS,kBAAmB,EAAA;AAElC,EAAO,OAAA,WAAA;AAAA,IACL,OAAO,GAAiB,KAAA;AACtB,MAAM,MAAA,KAAA,GAAQ,MAAQ,EAAA,sBAAA,CAAuB,oBAAoB,CAAA;AAEjE,MAAU,SAAA,CAAA,OAAA,CAAQ,0BAA0B,iBAAiB,CAAA;AAC7D,MAAM,MAAA,IAAA,GAAO,CAAC,MAAA,EAAQ,MAAM,CAAA;AAE5B,MAAA,IAAI,KAAO,EAAA;AACT,QAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,QAAA,SAAA,CAAU,OAAQ,CAAA,wBAAA,EAA0B,mBAAoB,CAAA,KAAK,CAAC,CAAA;AAAA;AAIxE,MAAA,SAAA,CAAU,OAAQ,CAAA,qBAAA,EAAuB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC3D,QAAI,IAAA,IAAA,CAAK,YAAY,MAAQ,EAAA;AAC3B,UAAM,MAAA,gBAAA,GACJ,QAAS,CAAA,YAAA,CAAa,YAAY,CAAA,KAAM,SACxC,IAAA,QAAA,CAAS,YAAa,CAAA,SAAS,CAAG,EAAA,QAAA,CAAS,MAAM,CAAA;AACnD,UAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,YAAS,QAAA,CAAA,UAAA,EAAY,YAAY,QAAQ,CAAA;AAAA;AAC3C;AACF,OACD,CAAA;AAGD,MAAA,SAAA,CAAU,OAAQ,CAAA,uBAAA,EAAyB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC7D,QAAI,IAAA,QAAA,CAAS,YAAY,MAAQ,EAAA;AAC/B,UAAA,IAAI,IAAK,CAAA,QAAA,KAAa,YAAgB,IAAA,IAAA,CAAK,aAAa,SAAW,EAAA;AACjE,YAAS,QAAA,CAAA,eAAA,CAAgB,KAAK,QAAQ,CAAA;AAAA;AACxC;AACF,OACD,CAAA;AAED,MAAA,MAAM,eAAe,MAAQ,EAAA,iBAAA;AAAA,QAC3B;AAAA,OACF;AACA,MAAA,MAAM,qBAAqB,MAAQ,EAAA,iBAAA;AAAA,QACjC;AAAA,OACF;AAGA,MAAO,OAAA,SAAA,CAAU,QAAS,CAAA,GAAA,CAAI,SAAW,EAAA;AAAA,QACvC,QAAU,EAAA,IAAA;AAAA,QACV,WAAA,EAAa,CAAC,OAAO,CAAA;AAAA,QACrB,
|
|
1
|
+
{"version":3,"file":"transformer.esm.js","sources":["../../../../src/reader/transformers/html/transformer.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport DOMPurify from 'dompurify';\nimport { useCallback, useMemo } from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { Transformer } from '../transformer';\nimport { removeUnsafeIframes, removeUnsafeLinks } from './hooks';\n\n/**\n * Returns html sanitizer configuration\n */\nconst useSanitizerConfig = () => {\n const configApi = useApi(configApiRef);\n\n return useMemo(() => {\n return configApi.getOptionalConfig('techdocs.sanitizer');\n }, [configApi]);\n};\n\n/**\n * Returns a transformer that sanitizes the dom\n */\nexport const useSanitizerTransformer = (): Transformer => {\n const config = useSanitizerConfig();\n\n return useCallback(\n async (dom: Element) => {\n const hosts = config?.getOptionalStringArray('allowedIframeHosts');\n\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeLinks);\n const tags = ['link', 'meta'];\n\n if (hosts) {\n tags.push('iframe');\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts));\n }\n\n // Only allow meta tags if they are used for refreshing the page. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeElement', (currNode, data) => {\n if (data.tagName === 'meta') {\n const isMetaRefreshTag =\n currNode.getAttribute('http-equiv') === 'refresh' &&\n currNode.getAttribute('content')?.includes('url=');\n if (!isMetaRefreshTag) {\n currNode.parentNode?.removeChild(currNode);\n }\n }\n });\n\n // Only allow http-equiv and content attributes on meta tags. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeAttribute', (currNode, data) => {\n if (currNode.tagName !== 'meta') {\n if (data.attrName === 'http-equiv' || data.attrName === 'content') {\n currNode.removeAttribute(data.attrName);\n }\n }\n });\n\n const tagNameCheck = config?.getOptionalString(\n 'allowedCustomElementTagNameRegExp',\n );\n const attributeNameCheck = config?.getOptionalString(\n 'allowedCustomElementAttributeNameRegExp',\n );\n\n // using outerHTML as we want to preserve the html tag attributes (lang)\n return DOMPurify.sanitize(dom.outerHTML, {\n ADD_TAGS: tags,\n FORBID_TAGS: ['style'],\n ADD_ATTR: ['http-equiv', 'content', 'dominant-baseline'],\n WHOLE_DOCUMENT: true,\n RETURN_DOM: true,\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: tagNameCheck ? new RegExp(tagNameCheck) : undefined,\n attributeNameCheck: attributeNameCheck\n ? new RegExp(attributeNameCheck)\n : undefined,\n },\n });\n },\n [config],\n );\n};\n"],"names":[],"mappings":";;;;;;AA2BA,MAAM,qBAAqB,MAAM;AAC/B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAO,OAAA,SAAA,CAAU,kBAAkB,oBAAoB,CAAA;AAAA,GACzD,EAAG,CAAC,SAAS,CAAC,CAAA;AAChB,CAAA;AAKO,MAAM,0BAA0B,MAAmB;AACxD,EAAA,MAAM,SAAS,kBAAmB,EAAA;AAElC,EAAO,OAAA,WAAA;AAAA,IACL,OAAO,GAAiB,KAAA;AACtB,MAAM,MAAA,KAAA,GAAQ,MAAQ,EAAA,sBAAA,CAAuB,oBAAoB,CAAA;AAEjE,MAAU,SAAA,CAAA,OAAA,CAAQ,0BAA0B,iBAAiB,CAAA;AAC7D,MAAM,MAAA,IAAA,GAAO,CAAC,MAAA,EAAQ,MAAM,CAAA;AAE5B,MAAA,IAAI,KAAO,EAAA;AACT,QAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,QAAA,SAAA,CAAU,OAAQ,CAAA,wBAAA,EAA0B,mBAAoB,CAAA,KAAK,CAAC,CAAA;AAAA;AAIxE,MAAA,SAAA,CAAU,OAAQ,CAAA,qBAAA,EAAuB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC3D,QAAI,IAAA,IAAA,CAAK,YAAY,MAAQ,EAAA;AAC3B,UAAM,MAAA,gBAAA,GACJ,QAAS,CAAA,YAAA,CAAa,YAAY,CAAA,KAAM,SACxC,IAAA,QAAA,CAAS,YAAa,CAAA,SAAS,CAAG,EAAA,QAAA,CAAS,MAAM,CAAA;AACnD,UAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,YAAS,QAAA,CAAA,UAAA,EAAY,YAAY,QAAQ,CAAA;AAAA;AAC3C;AACF,OACD,CAAA;AAGD,MAAA,SAAA,CAAU,OAAQ,CAAA,uBAAA,EAAyB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC7D,QAAI,IAAA,QAAA,CAAS,YAAY,MAAQ,EAAA;AAC/B,UAAA,IAAI,IAAK,CAAA,QAAA,KAAa,YAAgB,IAAA,IAAA,CAAK,aAAa,SAAW,EAAA;AACjE,YAAS,QAAA,CAAA,eAAA,CAAgB,KAAK,QAAQ,CAAA;AAAA;AACxC;AACF,OACD,CAAA;AAED,MAAA,MAAM,eAAe,MAAQ,EAAA,iBAAA;AAAA,QAC3B;AAAA,OACF;AACA,MAAA,MAAM,qBAAqB,MAAQ,EAAA,iBAAA;AAAA,QACjC;AAAA,OACF;AAGA,MAAO,OAAA,SAAA,CAAU,QAAS,CAAA,GAAA,CAAI,SAAW,EAAA;AAAA,QACvC,QAAU,EAAA,IAAA;AAAA,QACV,WAAA,EAAa,CAAC,OAAO,CAAA;AAAA,QACrB,QAAU,EAAA,CAAC,YAAc,EAAA,SAAA,EAAW,mBAAmB,CAAA;AAAA,QACvD,cAAgB,EAAA,IAAA;AAAA,QAChB,UAAY,EAAA,IAAA;AAAA,QACZ,uBAAyB,EAAA;AAAA,UACvB,YAAc,EAAA,YAAA,GAAe,IAAI,MAAA,CAAO,YAAY,CAAI,GAAA,KAAA,CAAA;AAAA,UACxD,kBAAoB,EAAA,kBAAA,GAChB,IAAI,MAAA,CAAO,kBAAkB,CAC7B,GAAA,KAAA;AAAA;AACN,OACD,CAAA;AAAA,KACH;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AACF;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-techdocs",
|
|
3
|
-
"version": "0.0.0-nightly-
|
|
3
|
+
"version": "0.0.0-nightly-20250220023208",
|
|
4
4
|
"description": "The Backstage plugin that renders technical documentation for your components",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "frontend-plugin",
|
|
@@ -74,20 +74,20 @@
|
|
|
74
74
|
"@backstage/catalog-client": "1.9.1",
|
|
75
75
|
"@backstage/catalog-model": "1.7.3",
|
|
76
76
|
"@backstage/config": "1.3.2",
|
|
77
|
-
"@backstage/core-compat-api": "0.
|
|
78
|
-
"@backstage/core-components": "0.
|
|
79
|
-
"@backstage/core-plugin-api": "
|
|
77
|
+
"@backstage/core-compat-api": "0.3.6",
|
|
78
|
+
"@backstage/core-components": "0.16.4",
|
|
79
|
+
"@backstage/core-plugin-api": "1.10.4",
|
|
80
80
|
"@backstage/errors": "1.2.7",
|
|
81
|
-
"@backstage/frontend-plugin-api": "0.
|
|
81
|
+
"@backstage/frontend-plugin-api": "0.9.5",
|
|
82
82
|
"@backstage/integration": "1.16.1",
|
|
83
|
-
"@backstage/integration-react": "
|
|
84
|
-
"@backstage/plugin-auth-react": "0.
|
|
85
|
-
"@backstage/plugin-catalog-react": "0.0.0-nightly-
|
|
83
|
+
"@backstage/integration-react": "1.2.4",
|
|
84
|
+
"@backstage/plugin-auth-react": "0.1.12",
|
|
85
|
+
"@backstage/plugin-catalog-react": "0.0.0-nightly-20250220023208",
|
|
86
86
|
"@backstage/plugin-search-common": "1.2.17",
|
|
87
|
-
"@backstage/plugin-search-react": "
|
|
87
|
+
"@backstage/plugin-search-react": "1.8.6",
|
|
88
88
|
"@backstage/plugin-techdocs-common": "0.1.0",
|
|
89
|
-
"@backstage/plugin-techdocs-react": "
|
|
90
|
-
"@backstage/theme": "0.
|
|
89
|
+
"@backstage/plugin-techdocs-react": "1.2.14",
|
|
90
|
+
"@backstage/theme": "0.6.4",
|
|
91
91
|
"@material-ui/core": "^4.12.2",
|
|
92
92
|
"@material-ui/icons": "^4.9.1",
|
|
93
93
|
"@material-ui/lab": "4.0.0-alpha.61",
|
|
@@ -101,12 +101,12 @@
|
|
|
101
101
|
"react-use": "^17.2.4"
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
|
-
"@backstage/cli": "0.
|
|
105
|
-
"@backstage/core-app-api": "
|
|
106
|
-
"@backstage/dev-utils": "0.0.0-nightly-
|
|
107
|
-
"@backstage/plugin-catalog": "0.0.0-nightly-
|
|
108
|
-
"@backstage/plugin-techdocs-module-addons-contrib": "
|
|
109
|
-
"@backstage/test-utils": "
|
|
104
|
+
"@backstage/cli": "0.30.0",
|
|
105
|
+
"@backstage/core-app-api": "1.15.5",
|
|
106
|
+
"@backstage/dev-utils": "0.0.0-nightly-20250220023208",
|
|
107
|
+
"@backstage/plugin-catalog": "0.0.0-nightly-20250220023208",
|
|
108
|
+
"@backstage/plugin-techdocs-module-addons-contrib": "1.1.21",
|
|
109
|
+
"@backstage/test-utils": "1.7.5",
|
|
110
110
|
"@testing-library/dom": "^10.0.0",
|
|
111
111
|
"@testing-library/jest-dom": "^6.0.0",
|
|
112
112
|
"@testing-library/react": "^16.0.0",
|