@backstage/plugin-techdocs 0.13.2-next.1 → 0.13.4
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 +57 -0
- package/dist/index.esm.js +34 -11
- package/dist/index.esm.js.map +1 -1
- package/package.json +28 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @backstage/plugin-techdocs
|
|
2
2
|
|
|
3
|
+
## 0.13.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1ed305728b: Bump `node-fetch` to version 2.6.7 and `cross-fetch` to version 3.1.5
|
|
8
|
+
- c77c5c7eb6: Added `backstage.role` to `package.json`
|
|
9
|
+
- 6553985cd4: Match text size of admonitions to main content text size.
|
|
10
|
+
- 9df7b43e1a: Improve overall appearance of highlighted code in docs.
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @backstage/core-components@0.8.9
|
|
13
|
+
- @backstage/core-plugin-api@0.6.1
|
|
14
|
+
- @backstage/errors@0.2.1
|
|
15
|
+
- @backstage/integration@0.7.3
|
|
16
|
+
- @backstage/integration-react@0.1.22
|
|
17
|
+
- @backstage/plugin-catalog@0.8.0
|
|
18
|
+
- @backstage/plugin-catalog-react@0.6.15
|
|
19
|
+
- @backstage/plugin-search@0.7.0
|
|
20
|
+
- @backstage/catalog-model@0.10.0
|
|
21
|
+
- @backstage/config@0.1.14
|
|
22
|
+
- @backstage/theme@0.2.15
|
|
23
|
+
|
|
24
|
+
## 0.13.3
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
- @backstage/core-components@0.8.8
|
|
30
|
+
- @backstage/plugin-search@0.6.2
|
|
31
|
+
- @backstage/plugin-catalog-react@0.6.14
|
|
32
|
+
- @backstage/plugin-catalog@0.7.12
|
|
33
|
+
- @backstage/integration-react@0.1.21
|
|
34
|
+
|
|
35
|
+
## 0.13.3-next.0
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- Updated dependencies
|
|
40
|
+
- @backstage/core-components@0.8.8-next.0
|
|
41
|
+
- @backstage/plugin-search@0.6.2-next.0
|
|
42
|
+
- @backstage/plugin-catalog-react@0.6.14-next.0
|
|
43
|
+
- @backstage/integration-react@0.1.21-next.0
|
|
44
|
+
- @backstage/plugin-catalog@0.7.12-next.0
|
|
45
|
+
|
|
46
|
+
## 0.13.2
|
|
47
|
+
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
- 742434a6ba: Fixed a bug where links to files within a TechDocs site that use the `download` attribute would result in a 404 in cases where the TechDocs backend and Backstage frontend application are on the same host.
|
|
51
|
+
- 359c31e31d: Added support for documentation using the raw `<source>` tag to point to relative resources like audio or video files.
|
|
52
|
+
- 18317a08db: Fixed a bug where copy-to-clipboard buttons were appended to unintended elements.
|
|
53
|
+
- Updated dependencies
|
|
54
|
+
- @backstage/core-components@0.8.7
|
|
55
|
+
- @backstage/plugin-catalog-react@0.6.13
|
|
56
|
+
- @backstage/integration-react@0.1.20
|
|
57
|
+
- @backstage/plugin-catalog@0.7.11
|
|
58
|
+
- @backstage/plugin-search@0.6.1
|
|
59
|
+
|
|
3
60
|
## 0.13.2-next.1
|
|
4
61
|
|
|
5
62
|
### Patch Changes
|
package/dist/index.esm.js
CHANGED
|
@@ -685,7 +685,7 @@ const addLinkClickListener = ({
|
|
|
685
685
|
|
|
686
686
|
const copyToClipboard = () => {
|
|
687
687
|
return (dom) => {
|
|
688
|
-
Array.from(dom.querySelectorAll("code")).forEach((codeElem) => {
|
|
688
|
+
Array.from(dom.querySelectorAll("pre > code")).forEach((codeElem) => {
|
|
689
689
|
var _a;
|
|
690
690
|
const button = document.createElement("button");
|
|
691
691
|
const toBeCopied = codeElem.textContent || "";
|
|
@@ -1273,6 +1273,7 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1273
1273
|
const techdocsSanitizer = useApi(configApiRef);
|
|
1274
1274
|
const { namespace = "", kind = "", name = "" } = entityRef;
|
|
1275
1275
|
const { state, path, content: rawPage } = useTechDocsReader();
|
|
1276
|
+
const isDarkTheme = theme.palette.type === "dark";
|
|
1276
1277
|
const [sidebars, setSidebars] = useState();
|
|
1277
1278
|
const [dom, setDom] = useState(null);
|
|
1278
1279
|
const { isPinned } = useContext(SidebarPinStateContext);
|
|
@@ -1336,6 +1337,27 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1336
1337
|
--md-accent-fg-color: ${theme.palette.primary.main};
|
|
1337
1338
|
--md-default-fg-color--lightest: ${theme.palette.textVerySubtle};
|
|
1338
1339
|
}
|
|
1340
|
+
.codehilite .gd, .highlight .gd {
|
|
1341
|
+
background-color: ${isDarkTheme ? "rgba(248,81,73,0.65)" : "#fdd"};
|
|
1342
|
+
}
|
|
1343
|
+
.codehilite .gi, .highlight .gi {
|
|
1344
|
+
background-color: ${isDarkTheme ? "rgba(46,160,67,0.65)" : "#dfd"};
|
|
1345
|
+
}
|
|
1346
|
+
.highlight .kd {
|
|
1347
|
+
color: ${isDarkTheme ? "#4aaaf7" : "#3b78e7"};
|
|
1348
|
+
}
|
|
1349
|
+
.highlight .k {
|
|
1350
|
+
color: ${isDarkTheme ? "#4aaaf7" : "#3b78e7"};
|
|
1351
|
+
}
|
|
1352
|
+
.highlight .nx {
|
|
1353
|
+
color: ${isDarkTheme ? "#ff53a3" : "#ec407a"};
|
|
1354
|
+
}
|
|
1355
|
+
.highlight .s1 {
|
|
1356
|
+
color: ${isDarkTheme ? "#1cad46" : "rgb(13, 144, 79)"};
|
|
1357
|
+
}
|
|
1358
|
+
.highlight .kt {
|
|
1359
|
+
color: ${isDarkTheme ? "#4aaaf7" : "#3e61a2"};
|
|
1360
|
+
}
|
|
1339
1361
|
.md-main__inner { margin-top: 0; }
|
|
1340
1362
|
.md-sidebar { position: fixed; bottom: 100px; width: 20rem; }
|
|
1341
1363
|
.md-sidebar--secondary { right: 2rem; }
|
|
@@ -1365,7 +1387,7 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1365
1387
|
}
|
|
1366
1388
|
.md-typeset table:not([class]) th { font-weight: bold; }
|
|
1367
1389
|
.md-typeset .admonition, .md-typeset details {
|
|
1368
|
-
font-size:
|
|
1390
|
+
font-size: inherit;
|
|
1369
1391
|
}
|
|
1370
1392
|
|
|
1371
1393
|
/* style the checkmarks of the task list */
|
|
@@ -1452,21 +1474,22 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1452
1474
|
`
|
|
1453
1475
|
})
|
|
1454
1476
|
]), [
|
|
1477
|
+
techdocsSanitizer,
|
|
1478
|
+
techdocsStorageApi,
|
|
1455
1479
|
kind,
|
|
1456
1480
|
name,
|
|
1457
1481
|
namespace,
|
|
1458
1482
|
scmIntegrationsApi,
|
|
1459
|
-
|
|
1460
|
-
techdocsSanitizer,
|
|
1461
|
-
theme.palette.action.disabledBackground,
|
|
1462
|
-
theme.palette.background.default,
|
|
1463
|
-
theme.palette.background.paper,
|
|
1464
|
-
theme.palette.primary.main,
|
|
1465
|
-
theme.palette.success.main,
|
|
1483
|
+
theme.typography.fontFamily,
|
|
1466
1484
|
theme.palette.text.primary,
|
|
1467
|
-
theme.palette.
|
|
1485
|
+
theme.palette.primary.main,
|
|
1486
|
+
theme.palette.background.paper,
|
|
1487
|
+
theme.palette.background.default,
|
|
1468
1488
|
theme.palette.textVerySubtle,
|
|
1469
|
-
theme.
|
|
1489
|
+
theme.palette.textSubtle,
|
|
1490
|
+
theme.palette.action.disabledBackground,
|
|
1491
|
+
theme.palette.success.main,
|
|
1492
|
+
isDarkTheme,
|
|
1470
1493
|
isPinned
|
|
1471
1494
|
]);
|
|
1472
1495
|
const postRender = useCallback(async (transformedElement) => transform(transformedElement, [
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/api.ts","../src/client.ts","../src/components/DocsResultListItem/DocsResultListItem.tsx","../src/routes.ts","../src/home/components/actions.tsx","../src/home/components/columns.tsx","../src/helpers.ts","../src/home/components/DocsTable.tsx","../src/home/components/EntityListDocsTable.tsx","../src/home/components/TechDocsPageWrapper.tsx","../src/home/components/TechDocsPicker.tsx","../src/home/components/DefaultTechDocsHome.tsx","../src/home/components/DocsCardGrid.tsx","../src/home/components/EntityListDocsGrid.tsx","../src/plugin.ts","../src/reader/transformers/addBaseUrl.ts","../src/reader/transformers/addGitFeedbackLink.ts","../src/reader/transformers/rewriteDocLinks.ts","../src/reader/transformers/addLinkClickListener.ts","../src/reader/transformers/copyToClipboard.ts","../src/reader/transformers/removeMkdocsHeader.ts","../src/reader/transformers/simplifyMkdocsFooter.ts","../src/reader/transformers/onCssReady.ts","../src/reader/transformers/sanitizeDOM.ts","../src/reader/transformers/injectCss.ts","../src/reader/transformers/scrollIntoAnchor.ts","../src/reader/transformers/transformer.ts","../src/reader/components/TechDocsSearch.tsx","../src/reader/components/TechDocsBuildLogs.tsx","../src/reader/components/TechDocsNotFound.tsx","../src/reader/components/TechDocsStateIndicator.tsx","../src/reader/components/useReaderState.ts","../src/reader/components/Reader.tsx","../src/reader/components/TechDocsPageHeader.tsx","../src/reader/components/LegacyTechDocsPage.tsx","../src/reader/components/TechDocsPage.tsx","../src/home/components/TechDocsCustomHome.tsx","../src/home/components/LegacyTechDocsHome.tsx","../src/home/components/TechDocsIndexPage.tsx","../src/EntityPageDocs.tsx","../src/Router.tsx"],"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 */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from './types';\nimport { createApiRef } from '@backstage/core-plugin-api';\n\n/**\n * Utility API reference for the {@link TechDocsStorageApi}.\n *\n * @public\n */\nexport const techdocsStorageApiRef = createApiRef<TechDocsStorageApi>({\n id: 'plugin.techdocs.storageservice',\n});\n\n/**\n * Utility API reference for the {@link TechDocsApi}.\n *\n * @public\n */\nexport const techdocsApiRef = createApiRef<TechDocsApi>({\n id: 'plugin.techdocs.service',\n});\n\n/**\n * The outcome of a docs sync operation.\n *\n * @public\n */\nexport type SyncResult = 'cached' | 'updated';\n\n/**\n * API which talks to TechDocs storage to fetch files to render.\n *\n * @public\n */\nexport interface TechDocsStorageApi {\n /**\n * Set to techdocs.requestUrl as the URL for techdocs-backend API.\n */\n getApiOrigin(): Promise<string>;\n getStorageUrl(): Promise<string>;\n getBuilder(): Promise<string>;\n getEntityDocs(entityId: EntityName, path: string): Promise<string>;\n syncEntityDocs(\n entityId: EntityName,\n logHandler?: (line: string) => void,\n ): Promise<SyncResult>;\n getBaseUrl(\n oldBaseUrl: string,\n entityId: EntityName,\n path: string,\n ): Promise<string>;\n}\n\n/**\n * API to talk to techdocs-backend.\n *\n * @public\n */\nexport interface TechDocsApi {\n /**\n * Set to techdocs.requestUrl as the URL for techdocs-backend API.\n */\n getApiOrigin(): Promise<string>;\n getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata>;\n getEntityMetadata(entityId: EntityName): Promise<TechDocsEntityMetadata>;\n}\n","/*\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 */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n DiscoveryApi,\n FetchApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport { NotFoundError, ResponseError } from '@backstage/errors';\nimport { EventSourcePolyfill } from 'event-source-polyfill';\nimport { SyncResult, TechDocsApi, TechDocsStorageApi } from './api';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from './types';\n\n/**\n * API to talk to `techdocs-backend`.\n *\n * @public\n */\nexport class TechDocsClient implements TechDocsApi {\n public configApi: Config;\n public discoveryApi: DiscoveryApi;\n private fetchApi: FetchApi;\n\n constructor(options: {\n configApi: Config;\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n }) {\n this.configApi = options.configApi;\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getApiOrigin(): Promise<string> {\n return (\n this.configApi.getOptionalString('techdocs.requestUrl') ??\n (await this.discoveryApi.getBaseUrl('techdocs'))\n );\n }\n\n /**\n * Retrieve TechDocs metadata.\n *\n * When docs are built, we generate a techdocs_metadata.json and store it along with the generated\n * static files. It includes necessary data about the docs site. This method requests techdocs-backend\n * which retrieves the TechDocs metadata.\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n */\n async getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;\n const request = await this.fetchApi.fetch(`${requestUrl}`);\n if (!request.ok) {\n throw await ResponseError.fromResponse(request);\n }\n\n return await request.json();\n }\n\n /**\n * Retrieve metadata about an entity.\n *\n * This method requests techdocs-backend which uses the catalog APIs to respond with filtered\n * information required here.\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n */\n async getEntityMetadata(\n entityId: EntityName,\n ): Promise<TechDocsEntityMetadata> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;\n\n const request = await this.fetchApi.fetch(`${requestUrl}`);\n if (!request.ok) {\n throw await ResponseError.fromResponse(request);\n }\n\n return await request.json();\n }\n}\n\n/**\n * API which talks to TechDocs storage to fetch files to render.\n *\n * @public\n */\nexport class TechDocsStorageClient implements TechDocsStorageApi {\n public configApi: Config;\n public discoveryApi: DiscoveryApi;\n public identityApi: IdentityApi;\n private fetchApi: FetchApi;\n\n constructor(options: {\n configApi: Config;\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n fetchApi: FetchApi;\n }) {\n this.configApi = options.configApi;\n this.discoveryApi = options.discoveryApi;\n this.identityApi = options.identityApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getApiOrigin(): Promise<string> {\n return (\n this.configApi.getOptionalString('techdocs.requestUrl') ??\n (await this.discoveryApi.getBaseUrl('techdocs'))\n );\n }\n\n async getStorageUrl(): Promise<string> {\n return (\n this.configApi.getOptionalString('techdocs.storageUrl') ??\n `${await this.discoveryApi.getBaseUrl('techdocs')}/static/docs`\n );\n }\n\n async getBuilder(): Promise<string> {\n return this.configApi.getString('techdocs.builder');\n }\n\n /**\n * Fetch HTML content as text for an individual docs page in an entity's docs site.\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n * @param path - The unique path to an individual docs page e.g. overview/what-is-new\n * @returns HTML content of the docs page as string\n * @throws Throws error when the page is not found.\n */\n async getEntityDocs(entityId: EntityName, path: string): Promise<string> {\n const { kind, namespace, name } = entityId;\n\n const storageUrl = await this.getStorageUrl();\n const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;\n\n const request = await this.fetchApi.fetch(\n `${url.endsWith('/') ? url : `${url}/`}index.html`,\n );\n\n let errorMessage = '';\n switch (request.status) {\n case 404:\n errorMessage = 'Page not found. ';\n // path is empty for the home page of an entity's docs site\n if (!path) {\n errorMessage +=\n 'This could be because there is no index.md file in the root of the docs directory of this repository.';\n }\n throw new NotFoundError(errorMessage);\n case 500:\n errorMessage =\n 'Could not generate documentation or an error in the TechDocs backend. ';\n throw new Error(errorMessage);\n default:\n // Do nothing\n break;\n }\n\n return request.text();\n }\n\n /**\n * Check if docs are on the latest version and trigger rebuild if not\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n * @param logHandler - Callback to receive log messages from the build process\n * @returns Whether documents are currently synchronized to newest version\n * @throws Throws error on error from sync endpoint in Techdocs Backend\n */\n async syncEntityDocs(\n entityId: EntityName,\n logHandler: (line: string) => void = () => {},\n ): Promise<SyncResult> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;\n const { token } = await this.identityApi.getCredentials();\n\n return new Promise((resolve, reject) => {\n // Polyfill is used to add support for custom headers and auth\n const source = new EventSourcePolyfill(url, {\n withCredentials: true,\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n source.addEventListener('log', (e: any) => {\n if (e.data) {\n logHandler(JSON.parse(e.data));\n }\n });\n\n source.addEventListener('finish', (e: any) => {\n let updated: boolean = false;\n\n if (e.data) {\n ({ updated } = JSON.parse(e.data));\n }\n\n resolve(updated ? 'updated' : 'cached');\n });\n\n source.onerror = (e: any) => {\n source.close();\n\n switch (e.status) {\n // the endpoint returned a 404 status\n case 404:\n reject(new NotFoundError(e.message));\n return;\n\n // also handles the event-stream close. the reject is ignored if the Promise was already\n // resolved by a finish event.\n default:\n reject(new Error(e.data));\n return;\n }\n };\n });\n }\n\n async getBaseUrl(\n oldBaseUrl: string,\n entityId: EntityName,\n path: string,\n ): Promise<string> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`;\n\n return new URL(\n oldBaseUrl,\n newBaseUrl.endsWith('/') ? newBaseUrl : `${newBaseUrl}/`,\n ).toString();\n }\n}\n","/*\n * Copyright 2021 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 } from 'react';\nimport { Divider, ListItem, ListItemText, makeStyles } from '@material-ui/core';\nimport { Link } from '@backstage/core-components';\nimport TextTruncate from 'react-text-truncate';\n\nconst useStyles = makeStyles({\n flexContainer: {\n flexWrap: 'wrap',\n },\n itemText: {\n width: '100%',\n marginBottom: '1rem',\n },\n});\n\nexport const DocsResultListItem = ({\n result,\n lineClamp = 5,\n asListItem = true,\n asLink = true,\n title,\n}: {\n result: any;\n lineClamp?: number;\n asListItem?: boolean;\n asLink?: boolean;\n title?: string;\n}) => {\n const classes = useStyles();\n const TextItem = () => (\n <ListItemText\n className={classes.itemText}\n primaryTypographyProps={{ variant: 'h6' }}\n primary={\n title\n ? title\n : `${result.title} | ${result.entityTitle ?? result.name} docs`\n }\n secondary={\n <TextTruncate\n line={lineClamp}\n truncateText=\"…\"\n text={result.text}\n element=\"span\"\n />\n }\n />\n );\n\n const LinkWrapper = ({ children }: PropsWithChildren<{}>) =>\n asLink ? <Link to={result.location}>{children}</Link> : <>{children}</>;\n\n const ListItemWrapper = ({ children }: PropsWithChildren<{}>) =>\n asListItem ? (\n <>\n <ListItem alignItems=\"flex-start\" className={classes.flexContainer}>\n {children}\n </ListItem>\n <Divider component=\"li\" />\n </>\n ) : (\n <>{children}</>\n );\n\n return (\n <LinkWrapper>\n <ListItemWrapper>\n <TextItem />\n </ListItemWrapper>\n </LinkWrapper>\n );\n};\n","/*\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 */\n\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'techdocs:index-page',\n});\n\nexport const rootDocsRouteRef = createRouteRef({\n id: 'techdocs:reader-page',\n params: ['namespace', 'kind', 'name'],\n});\n\nexport const rootCatalogDocsRouteRef = createRouteRef({\n id: 'techdocs:catalog-reader-view',\n});\n","/*\n * Copyright 2021 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 from 'react';\nimport ShareIcon from '@material-ui/icons/Share';\nimport {\n favoriteEntityIcon,\n favoriteEntityTooltip,\n} from '@backstage/plugin-catalog-react';\nimport { DocsTableRow } from './types';\n\nexport function createCopyDocsUrlAction(copyToClipboard: Function) {\n return (row: DocsTableRow) => {\n return {\n icon: () => <ShareIcon fontSize=\"small\" />,\n tooltip: 'Click to copy documentation link to clipboard',\n onClick: () =>\n copyToClipboard(`${window.location.origin}${row.resolved.docsUrl}`),\n };\n };\n}\n\nexport function createStarEntityAction(\n isStarredEntity: Function,\n toggleStarredEntity: Function,\n) {\n return ({ entity }: DocsTableRow) => {\n const isStarred = isStarredEntity(entity);\n return {\n cellStyle: { paddingLeft: '1em' },\n icon: () => favoriteEntityIcon(isStarred),\n tooltip: favoriteEntityTooltip(isStarred),\n onClick: () => toggleStarredEntity(entity),\n };\n };\n}\n","/*\n * Copyright 2021 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 from 'react';\nimport { Link, SubvalueCell, TableColumn } from '@backstage/core-components';\nimport { EntityRefLinks } from '@backstage/plugin-catalog-react';\nimport { Entity } from '@backstage/catalog-model';\nimport { DocsTableRow } from './types';\n\nfunction customTitle(entity: Entity): string {\n return entity.metadata.title || entity.metadata.name;\n}\n\nexport function createNameColumn(): TableColumn<DocsTableRow> {\n return {\n title: 'Document',\n field: 'entity.metadata.name',\n highlight: true,\n render: (row: DocsTableRow) => (\n <SubvalueCell\n value={<Link to={row.resolved.docsUrl}>{customTitle(row.entity)}</Link>}\n subvalue={row.entity.metadata.description}\n />\n ),\n };\n}\n\nexport function createOwnerColumn(): TableColumn<DocsTableRow> {\n return {\n title: 'Owner',\n field: 'resolved.ownedByRelationsTitle',\n render: ({ resolved }) => (\n <EntityRefLinks\n entityRefs={resolved.ownedByRelations}\n defaultKind=\"group\"\n />\n ),\n };\n}\n\nexport function createTypeColumn(): TableColumn<DocsTableRow> {\n return {\n title: 'Type',\n field: 'entity.spec.type',\n };\n}\n","/*\n * Copyright 2021 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 { Config } from '@backstage/config';\n\n// Lower-case entity triplets by default, but allow override.\nexport function toLowerMaybe(str: string, config: Config) {\n return config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n )\n ? str\n : str.toLocaleLowerCase('en-US');\n}\n","/*\n * Copyright 2021 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 from 'react';\nimport useCopyToClipboard from 'react-use/lib/useCopyToClipboard';\n\nimport { useRouteRef, useApi, configApiRef } from '@backstage/core-plugin-api';\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n formatEntityRefTitle,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { rootDocsRouteRef } from '../../routes';\nimport {\n Button,\n EmptyState,\n Table,\n TableColumn,\n TableProps,\n} from '@backstage/core-components';\nimport * as actionFactories from './actions';\nimport * as columnFactories from './columns';\nimport { DocsTableRow } from './types';\nimport { toLowerMaybe } from '../../helpers';\n\nexport const DocsTable = ({\n entities,\n title,\n loading,\n columns,\n actions,\n}: {\n entities: Entity[] | undefined;\n title?: string | undefined;\n loading?: boolean | undefined;\n columns?: TableColumn<DocsTableRow>[];\n actions?: TableProps<DocsTableRow>['actions'];\n}) => {\n const [, copyToClipboard] = useCopyToClipboard();\n const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);\n const config = useApi(configApiRef);\n if (!entities) return null;\n\n const documents = entities.map(entity => {\n const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);\n return {\n entity,\n resolved: {\n docsUrl: getRouteToReaderPageFor({\n namespace: toLowerMaybe(\n entity.metadata.namespace ?? 'default',\n config,\n ),\n kind: toLowerMaybe(entity.kind, config),\n name: toLowerMaybe(entity.metadata.name, config),\n }),\n ownedByRelations,\n ownedByRelationsTitle: ownedByRelations\n .map(r => formatEntityRefTitle(r, { defaultKind: 'group' }))\n .join(', '),\n },\n };\n });\n\n const defaultColumns: TableColumn<DocsTableRow>[] = [\n columnFactories.createNameColumn(),\n columnFactories.createOwnerColumn(),\n columnFactories.createTypeColumn(),\n ];\n\n const defaultActions: TableProps<DocsTableRow>['actions'] = [\n actionFactories.createCopyDocsUrlAction(copyToClipboard),\n ];\n\n return (\n <>\n {loading || (documents && documents.length > 0) ? (\n <Table<DocsTableRow>\n isLoading={loading}\n options={{\n paging: true,\n pageSize: 20,\n search: true,\n actionsColumnIndex: -1,\n }}\n data={documents}\n columns={columns || defaultColumns}\n actions={actions || defaultActions}\n title={\n title\n ? `${title} (${documents.length})`\n : `All (${documents.length})`\n }\n />\n ) : (\n <EmptyState\n missing=\"data\"\n title=\"No documents to show\"\n description=\"Create your own document. Check out our Getting Started Information\"\n action={\n <Button\n color=\"primary\"\n to=\"https://backstage.io/docs/features/techdocs/getting-started\"\n variant=\"contained\"\n >\n DOCS\n </Button>\n }\n />\n )}\n </>\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport useCopyToClipboard from 'react-use/lib/useCopyToClipboard';\nimport { capitalize } from 'lodash';\nimport {\n CodeSnippet,\n TableColumn,\n TableProps,\n WarningPanel,\n} from '@backstage/core-components';\nimport {\n useEntityListProvider,\n useStarredEntities,\n} from '@backstage/plugin-catalog-react';\nimport { DocsTable } from './DocsTable';\nimport * as actionFactories from './actions';\nimport * as columnFactories from './columns';\nimport { DocsTableRow } from './types';\n\nexport const EntityListDocsTable = ({\n columns,\n actions,\n}: {\n columns?: TableColumn<DocsTableRow>[];\n actions?: TableProps<DocsTableRow>['actions'];\n}) => {\n const { loading, error, entities, filters } = useEntityListProvider();\n const { isStarredEntity, toggleStarredEntity } = useStarredEntities();\n const [, copyToClipboard] = useCopyToClipboard();\n\n const title = capitalize(filters.user?.value ?? 'all');\n\n const defaultActions = [\n actionFactories.createCopyDocsUrlAction(copyToClipboard),\n actionFactories.createStarEntityAction(\n isStarredEntity,\n toggleStarredEntity,\n ),\n ];\n\n if (error) {\n return (\n <WarningPanel\n severity=\"error\"\n title=\"Could not load available documentation.\"\n >\n <CodeSnippet language=\"text\" text={error.toString()} />\n </WarningPanel>\n );\n }\n\n return (\n <DocsTable\n title={title}\n entities={entities}\n loading={loading}\n actions={actions || defaultActions}\n columns={columns}\n />\n );\n};\n\nEntityListDocsTable.columns = columnFactories;\nEntityListDocsTable.actions = actionFactories;\n","/*\n * Copyright 2021 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 from 'react';\n\nimport { PageWithHeader } from '@backstage/core-components';\nimport { useApi, configApiRef } from '@backstage/core-plugin-api';\n\ntype Props = {\n children?: React.ReactNode;\n};\n\nexport const TechDocsPageWrapper = ({ children }: Props) => {\n const configApi = useApi(configApiRef);\n const generatedSubtitle = `Documentation available in ${\n configApi.getOptionalString('organization.name') ?? 'Backstage'\n }`;\n\n return (\n <PageWithHeader\n title=\"Documentation\"\n subtitle={generatedSubtitle}\n themeId=\"documentation\"\n >\n {children}\n </PageWithHeader>\n );\n};\n","/*\n * Copyright 2021 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 { useEffect } from 'react';\nimport {\n CATALOG_FILTER_EXISTS,\n DefaultEntityFilters,\n EntityFilter,\n useEntityListProvider,\n} from '@backstage/plugin-catalog-react';\n\nclass TechDocsFilter implements EntityFilter {\n getCatalogFilters(): Record<string, string | symbol | (string | symbol)[]> {\n return {\n 'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS,\n };\n }\n}\n\ntype CustomFilters = DefaultEntityFilters & {\n techdocs?: TechDocsFilter;\n};\n\nexport const TechDocsPicker = () => {\n const { updateFilters } = useEntityListProvider<CustomFilters>();\n\n useEffect(() => {\n updateFilters({\n techdocs: new TechDocsFilter(),\n });\n }, [updateFilters]);\n\n return null;\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport {\n Content,\n ContentHeader,\n SupportButton,\n TableColumn,\n TableProps,\n} from '@backstage/core-components';\nimport {\n EntityListContainer,\n FilterContainer,\n FilteredEntityLayout,\n} from '@backstage/plugin-catalog';\nimport {\n EntityListProvider,\n EntityOwnerPicker,\n EntityTagPicker,\n UserListFilterKind,\n UserListPicker,\n} from '@backstage/plugin-catalog-react';\nimport { EntityListDocsTable } from './EntityListDocsTable';\nimport { TechDocsPageWrapper } from './TechDocsPageWrapper';\nimport { TechDocsPicker } from './TechDocsPicker';\nimport { DocsTableRow } from './types';\n\nexport const DefaultTechDocsHome = ({\n initialFilter = 'all',\n columns,\n actions,\n}: {\n initialFilter?: UserListFilterKind;\n columns?: TableColumn<DocsTableRow>[];\n actions?: TableProps<DocsTableRow>['actions'];\n}) => {\n return (\n <TechDocsPageWrapper>\n <Content>\n <ContentHeader title=\"\">\n <SupportButton>\n Discover documentation in your ecosystem.\n </SupportButton>\n </ContentHeader>\n <EntityListProvider>\n <FilteredEntityLayout>\n <FilterContainer>\n <TechDocsPicker />\n <UserListPicker initialFilter={initialFilter} />\n <EntityOwnerPicker />\n <EntityTagPicker />\n </FilterContainer>\n <EntityListContainer>\n <EntityListDocsTable actions={actions} columns={columns} />\n </EntityListContainer>\n </FilteredEntityLayout>\n </EntityListProvider>\n </Content>\n </TechDocsPageWrapper>\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\n\nimport { Entity } from '@backstage/catalog-model';\nimport { useApi, useRouteRef, configApiRef } from '@backstage/core-plugin-api';\nimport { Card, CardActions, CardContent, CardMedia } from '@material-ui/core';\nimport { rootDocsRouteRef } from '../../routes';\n\nimport {\n Button,\n ItemCardGrid,\n ItemCardHeader,\n} from '@backstage/core-components';\nimport { toLowerMaybe } from '../../helpers';\n\nexport const DocsCardGrid = ({\n entities,\n}: {\n entities: Entity[] | undefined;\n}) => {\n const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);\n const config = useApi(configApiRef);\n if (!entities) return null;\n return (\n <ItemCardGrid data-testid=\"docs-explore\">\n {!entities?.length\n ? null\n : entities.map((entity, index: number) => (\n <Card key={index}>\n <CardMedia>\n <ItemCardHeader\n title={entity.metadata.title ?? entity.metadata.name}\n />\n </CardMedia>\n <CardContent>{entity.metadata.description}</CardContent>\n <CardActions>\n <Button\n to={getRouteToReaderPageFor({\n namespace: toLowerMaybe(\n entity.metadata.namespace ?? 'default',\n config,\n ),\n kind: toLowerMaybe(entity.kind, config),\n name: toLowerMaybe(entity.metadata.name, config),\n })}\n color=\"primary\"\n data-testid=\"read_docs\"\n >\n Read Docs\n </Button>\n </CardActions>\n </Card>\n ))}\n </ItemCardGrid>\n );\n};\n","/*\n * Copyright 2021 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 {\n CodeSnippet,\n Progress,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useEntityListProvider } from '@backstage/plugin-catalog-react';\nimport React from 'react';\nimport { DocsCardGrid } from './DocsCardGrid';\n\nexport const EntityListDocsGrid = () => {\n const { loading, error, entities } = useEntityListProvider();\n\n if (error) {\n return (\n <WarningPanel\n severity=\"error\"\n title=\"Could not load available documentation.\"\n >\n <CodeSnippet language=\"text\" text={error.toString()} />\n </WarningPanel>\n );\n }\n\n if (loading || !entities) {\n return <Progress />;\n }\n\n entities.sort((a, b) =>\n (a.metadata.title ?? a.metadata.name).localeCompare(\n b.metadata.title ?? b.metadata.name,\n ),\n );\n\n return <DocsCardGrid entities={entities} />;\n};\n","/*\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 */\n\nimport { techdocsApiRef, techdocsStorageApiRef } from './api';\nimport { TechDocsClient, TechDocsStorageClient } from './client';\nimport {\n rootDocsRouteRef,\n rootRouteRef,\n rootCatalogDocsRouteRef,\n} from './routes';\nimport {\n configApiRef,\n createApiFactory,\n createComponentExtension,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nexport const techdocsPlugin = createPlugin({\n id: 'techdocs',\n apis: [\n createApiFactory({\n api: techdocsStorageApiRef,\n deps: {\n configApi: configApiRef,\n discoveryApi: discoveryApiRef,\n identityApi: identityApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, discoveryApi, identityApi, fetchApi }) =>\n new TechDocsStorageClient({\n configApi,\n discoveryApi,\n identityApi,\n fetchApi,\n }),\n }),\n createApiFactory({\n api: techdocsApiRef,\n deps: {\n configApi: configApiRef,\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, discoveryApi, fetchApi }) =>\n new TechDocsClient({\n configApi,\n discoveryApi,\n fetchApi,\n }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n docRoot: rootDocsRouteRef,\n entityContent: rootCatalogDocsRouteRef,\n },\n});\n\nexport const TechdocsPage = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechdocsPage',\n component: () => import('./Router').then(m => m.Router),\n mountPoint: rootRouteRef,\n }),\n);\n\nexport const EntityTechdocsContent = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'EntityTechdocsContent',\n component: () => import('./Router').then(m => m.EmbeddedDocsRouter),\n mountPoint: rootCatalogDocsRouteRef,\n }),\n);\n\n// takes a list of entities and renders documentation cards\nexport const DocsCardGrid = techdocsPlugin.provide(\n createComponentExtension({\n name: 'DocsCardGrid',\n component: {\n lazy: () =>\n import('./home/components/DocsCardGrid').then(m => m.DocsCardGrid),\n },\n }),\n);\n\n// takes a list of entities and renders table listing documentation\nexport const DocsTable = techdocsPlugin.provide(\n createComponentExtension({\n name: 'DocsTable',\n component: {\n lazy: () => import('./home/components/DocsTable').then(m => m.DocsTable),\n },\n }),\n);\n\n// takes a custom tabs config object and renders a documentation landing page\nexport const TechDocsCustomHome = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechDocsCustomHome',\n component: () =>\n import('./home/components/TechDocsCustomHome').then(\n m => m.TechDocsCustomHome,\n ),\n mountPoint: rootRouteRef,\n }),\n);\n\nexport const TechDocsIndexPage = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechDocsIndexPage',\n component: () =>\n import('./home/components/TechDocsIndexPage').then(\n m => m.TechDocsIndexPage,\n ),\n mountPoint: rootRouteRef,\n }),\n);\n\nexport const TechDocsReaderPage = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechDocsReaderPage',\n component: () =>\n import('./reader/components/TechDocsPage').then(m => m.TechDocsPage),\n mountPoint: rootDocsRouteRef,\n }),\n);\n","/*\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 { EntityName } from '@backstage/catalog-model';\nimport { TechDocsStorageApi } from '../../api';\nimport type { Transformer } from './transformer';\n\ntype AddBaseUrlOptions = {\n techdocsStorageApi: TechDocsStorageApi;\n entityId: EntityName;\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' && attrVal.endsWith('.svg');\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(svgContent)}`,\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","/*\n * Copyright 2021 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 type { Transformer } from './index';\nimport {\n replaceGitHubUrlType,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport parseGitUrl from 'git-url-parse';\n\n// requires repo\nexport const addGitFeedbackLink = (\n scmIntegrationsApi: ScmIntegrationRegistry,\n): Transformer => {\n return dom => {\n // attempting to use selectors that are more likely to be static as MkDocs updates over time\n const sourceAnchor = dom.querySelector(\n '[title=\"Edit this page\"]',\n ) as HTMLAnchorElement;\n\n // don't show if edit link not available in raw page\n if (!sourceAnchor || !sourceAnchor.href) {\n return dom;\n }\n\n const sourceURL = new URL(sourceAnchor.href);\n const integration = scmIntegrationsApi.byUrl(sourceURL);\n\n // don't show if can't identify edit link hostname as a gitlab/github hosting\n if (integration?.type !== 'github' && integration?.type !== 'gitlab') {\n return dom;\n }\n\n // topmost h1 only contains title for whole page\n const title = (dom.querySelector('article>h1') as HTMLElement).childNodes[0]\n .textContent;\n const issueTitle = encodeURIComponent(`Documentation Feedback: ${title}`);\n const issueDesc = encodeURIComponent(\n `Page source:\\n${sourceAnchor.href}\\n\\nFeedback:`,\n );\n\n // Convert GitHub edit url to blob type so it can be parsed by git-url-parse correctly\n const gitUrl =\n integration?.type === 'github'\n ? replaceGitHubUrlType(sourceURL.href, 'blob')\n : sourceURL.href;\n const gitInfo = parseGitUrl(gitUrl);\n const repoPath = `/${gitInfo.organization}/${gitInfo.name}`;\n\n const feedbackLink = sourceAnchor.cloneNode() as HTMLAnchorElement;\n switch (integration?.type) {\n case 'gitlab':\n feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?issue[title]=${issueTitle}&issue[description]=${issueDesc}`;\n break;\n case 'github':\n feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?title=${issueTitle}&body=${issueDesc}`;\n break;\n default:\n return dom;\n }\n ReactDOM.render(React.createElement(FeedbackOutlinedIcon), feedbackLink);\n feedbackLink.style.paddingLeft = '5px';\n feedbackLink.title = 'Leave feedback for this page';\n feedbackLink.id = 'git-feedback-link';\n sourceAnchor?.insertAdjacentElement('beforebegin', feedbackLink);\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\nexport const rewriteDocLinks = (): Transformer => {\n return dom => {\n const updateDom = <T extends Element>(\n list: Array<T>,\n attributeName: string,\n ): void => {\n Array.from(list)\n .filter(elem => elem.hasAttribute(attributeName))\n .forEach((elem: T) => {\n const elemAttribute = elem.getAttribute(attributeName);\n if (elemAttribute) {\n // if link is external, add target to open in a new window or tab\n if (elemAttribute.match(/^https?:\\/\\//i)) {\n elem.setAttribute('target', '_blank');\n }\n\n try {\n const normalizedWindowLocation = normalizeUrl(\n window.location.href,\n );\n elem.setAttribute(\n attributeName,\n new URL(elemAttribute, normalizedWindowLocation).toString(),\n );\n } catch (_e) {\n // Non-parseable links should be re-written as plain text.\n elem.replaceWith(elem.textContent || elemAttribute);\n }\n }\n });\n };\n\n updateDom(Array.from(dom.getElementsByTagName('a')), 'href');\n\n return dom;\n };\n};\n\n/** Make sure that the input url always ends with a '/' */\nexport function normalizeUrl(input: string): string {\n const url = new URL(input);\n\n if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {\n url.pathname += '/';\n }\n\n return url.toString();\n}\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\ntype AddLinkClickListenerOptions = {\n baseUrl: string;\n onClick: (e: MouseEvent, newUrl: string) => void;\n};\n\nexport const addLinkClickListener = ({\n baseUrl,\n onClick,\n}: AddLinkClickListenerOptions): Transformer => {\n return dom => {\n Array.from(dom.getElementsByTagName('a')).forEach(elem => {\n elem.addEventListener('click', (e: MouseEvent) => {\n const target = elem as HTMLAnchorElement;\n const href = target.getAttribute('href');\n\n if (!href) return;\n if (href.startsWith(baseUrl) && !elem.hasAttribute('download')) {\n e.preventDefault();\n onClick(e, href);\n }\n });\n });\n\n return dom;\n };\n};\n","/*\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 type { Transformer } from './transformer';\n\n/**\n * Recreates copy-to-clipboard functionality attached to <code> snippets that\n * is native to mkdocs-material theme.\n */\nexport const copyToClipboard = (): Transformer => {\n return dom => {\n Array.from(dom.querySelectorAll('code')).forEach(codeElem => {\n const button = document.createElement('button');\n const toBeCopied = codeElem.textContent || '';\n button.className = 'md-clipboard md-icon';\n button.title = 'Copy to clipboard';\n button.innerHTML =\n '<svg viewBox=\"0 0 24 24\"><path d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg>';\n button.addEventListener('click', () =>\n navigator.clipboard.writeText(toBeCopied),\n );\n codeElem?.parentElement?.prepend(button);\n });\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\nexport const removeMkdocsHeader = (): Transformer => {\n return dom => {\n // Remove the header\n dom.querySelector('.md-header')?.remove();\n\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\nexport const simplifyMkdocsFooter = (): Transformer => {\n return dom => {\n // Remove mkdocs copyright\n dom.querySelector('.md-footer-copyright')?.remove();\n\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\ntype OnCssReadyOptions = {\n docStorageUrl: string;\n onLoading: (dom: Element) => void;\n onLoaded: (dom: Element) => void;\n};\n\nexport const onCssReady = ({\n docStorageUrl,\n onLoading,\n onLoaded,\n}: OnCssReadyOptions): Transformer => {\n return dom => {\n const cssPages = Array.from(\n dom.querySelectorAll('head > link[rel=\"stylesheet\"]'),\n ).filter(elem => elem.getAttribute('href')?.startsWith(docStorageUrl));\n\n let count = cssPages.length;\n\n if (count > 0) {\n onLoading(dom);\n }\n\n cssPages.forEach(cssPage =>\n cssPage.addEventListener('load', () => {\n count -= 1;\n\n if (count === 0) {\n onLoaded(dom);\n }\n }),\n );\n\n return dom;\n };\n};\n","/*\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 */\n\nconst TECHDOCS_CSS = /main\\.[A-Fa-f0-9]{8}\\.min\\.css$/;\nconst GOOGLE_FONTS = /^https:\\/\\/fonts\\.googleapis\\.com/;\nconst GSTATIC_FONTS = /^https:\\/\\/fonts\\.gstatic\\.com/;\n\nexport const safeLinksHook = (node: Element) => {\n if (node.nodeName && node.nodeName === 'LINK') {\n const href = node.getAttribute('href') || '';\n if (href.match(TECHDOCS_CSS)) {\n node.setAttribute('rel', 'stylesheet');\n }\n if (href.match(GOOGLE_FONTS)) {\n node.setAttribute('rel', 'stylesheet');\n }\n if (href.match(GSTATIC_FONTS)) {\n node.setAttribute('rel', 'preconnect');\n }\n }\n return node;\n};\n\nconst filterIframeHook = (allowedIframeHosts: string[]) => (node: Element) => {\n if (node.nodeName === 'IFRAME') {\n const src = node.getAttribute('src');\n if (!src) {\n node.remove();\n return node;\n }\n\n try {\n const srcUrl = new URL(src);\n const isMatch = allowedIframeHosts.some(host => srcUrl.host === host);\n if (!isMatch) {\n node.remove();\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn(`Invalid iframe src, ${error}`);\n node.remove();\n }\n }\n return node;\n};\n\nimport { Config } from '@backstage/config';\nimport DOMPurify from 'dompurify';\nimport type { Transformer } from './transformer';\n\nexport const sanitizeDOM = (config?: Config): Transformer => {\n const allowedIframeHosts =\n config?.getOptionalStringArray('allowedIframeHosts') || [];\n\n return dom => {\n DOMPurify.addHook('afterSanitizeAttributes', safeLinksHook);\n const addTags = ['link'];\n\n if (allowedIframeHosts.length > 0) {\n DOMPurify.addHook(\n 'beforeSanitizeElements',\n filterIframeHook(allowedIframeHosts),\n );\n addTags.push('iframe');\n }\n\n return DOMPurify.sanitize(dom.innerHTML, {\n ADD_TAGS: addTags,\n FORBID_TAGS: ['style'],\n WHOLE_DOCUMENT: true,\n RETURN_DOM: true,\n });\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\ntype InjectCssOptions = {\n css: string;\n};\n\nexport const injectCss = ({ css }: InjectCssOptions): Transformer => {\n return dom => {\n dom\n .getElementsByTagName('head')[0]\n .insertAdjacentHTML('beforeend', `<style>${css}</style>`);\n\n return dom;\n };\n};\n","/*\n * Copyright 2021 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 type { Transformer } from './transformer';\n\nexport const scrollIntoAnchor = (): Transformer => {\n return dom => {\n setTimeout(() => {\n // Scroll to the desired anchor on initial navigation\n if (window.location.hash) {\n const hash = window.location.hash.slice(1);\n dom?.querySelector(`#${hash}`)?.scrollIntoView();\n }\n }, 200);\n return dom;\n };\n};\n","/*\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 */\n\nexport type Transformer = (dom: Element) => Element | Promise<Element>;\n\nexport const transform = async (\n html: string | Element,\n transformers: Transformer[],\n): Promise<Element> => {\n let dom: Element;\n\n if (typeof html === 'string') {\n dom = new DOMParser().parseFromString(html, 'text/html').documentElement;\n } else if (html instanceof Element) {\n dom = html;\n } else {\n throw new Error('dom is not a recognized type');\n }\n\n for (const transformer of transformers) {\n dom = await transformer(dom);\n }\n\n return dom;\n};\n","/*\n * Copyright 2021 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 { EntityName } from '@backstage/catalog-model';\nimport { SearchContextProvider, useSearch } from '@backstage/plugin-search';\nimport {\n CircularProgress,\n Grid,\n IconButton,\n InputAdornment,\n TextField,\n} from '@material-ui/core';\nimport SearchIcon from '@material-ui/icons/Search';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport React, { ChangeEvent, useEffect, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport useDebounce from 'react-use/lib/useDebounce';\nimport { DocsResultListItem } from '../../components/DocsResultListItem';\n\ntype TechDocsSearchProps = {\n entityId: EntityName;\n debounceTime?: number;\n};\n\ntype TechDocsDoc = {\n namespace: string;\n kind: string;\n name: string;\n path: string;\n location: string;\n title: string;\n};\n\ntype TechDocsSearchResult = {\n type: string;\n document: TechDocsDoc;\n};\n\nconst TechDocsSearchBar = ({\n entityId,\n debounceTime = 150,\n}: TechDocsSearchProps) => {\n const [open, setOpen] = useState(false);\n const navigate = useNavigate();\n const {\n term,\n setTerm,\n result: { loading, value: searchVal },\n } = useSearch();\n const [options, setOptions] = useState<any[]>([]);\n useEffect(() => {\n let mounted = true;\n\n if (mounted && searchVal) {\n // TODO: Change this into getting only subset of search results from the BE in the first place\n // once pagination is implemented for search engines\n // See: https://github.com/backstage/backstage/issues/6062\n const searchResults = searchVal.results.slice(0, 10);\n setOptions(searchResults);\n }\n return () => {\n mounted = false;\n };\n }, [loading, searchVal]);\n\n const [value, setValue] = useState<string>(term);\n\n useDebounce(() => setTerm(value), debounceTime, [value]);\n\n const handleQuery = (e: ChangeEvent<HTMLInputElement>) => {\n if (!open) {\n setOpen(true);\n }\n setValue(e.target.value);\n };\n\n const handleSelection = (_: any, selection: TechDocsSearchResult | null) => {\n if (selection?.document) {\n const { location } = selection.document;\n navigate(location);\n }\n };\n\n return (\n <Grid item xs={12}>\n <Autocomplete\n data-testid=\"techdocs-search-bar\"\n size=\"small\"\n open={open}\n getOptionLabel={() => ''}\n filterOptions={x => {\n return x; // This is needed to get renderOption to be called after options change. Bug in material-ui?\n }}\n onClose={() => {\n setOpen(false);\n }}\n onFocus={() => {\n setOpen(true);\n }}\n onChange={handleSelection}\n blurOnSelect\n noOptionsText=\"No results found\"\n value={null}\n options={options}\n renderOption={({ document }) => (\n <DocsResultListItem\n result={document}\n lineClamp={3}\n asListItem={false}\n asLink={false}\n title={document.title}\n />\n )}\n loading={loading}\n renderInput={params => (\n <TextField\n {...params}\n data-testid=\"techdocs-search-bar-input\"\n variant=\"outlined\"\n fullWidth\n placeholder={`Search ${entityId.name} docs`}\n value={value}\n onChange={handleQuery}\n InputProps={{\n ...params.InputProps,\n startAdornment: (\n <InputAdornment position=\"start\">\n <IconButton aria-label=\"Query\" disabled>\n <SearchIcon />\n </IconButton>\n </InputAdornment>\n ),\n endAdornment: (\n <React.Fragment>\n {loading ? (\n <CircularProgress color=\"inherit\" size={20} />\n ) : null}\n {params.InputProps.endAdornment}\n </React.Fragment>\n ),\n }}\n />\n )}\n />\n </Grid>\n );\n};\n\nexport const TechDocsSearch = (props: TechDocsSearchProps) => {\n const initialState = {\n term: '',\n types: ['techdocs'],\n pageCursor: '',\n filters: props.entityId,\n };\n return (\n <SearchContextProvider initialState={initialState}>\n <TechDocsSearchBar {...props} />\n </SearchContextProvider>\n );\n};\n","/*\n * Copyright 2021 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 { LogViewer } from '@backstage/core-components';\nimport {\n Button,\n createStyles,\n Drawer,\n Grid,\n IconButton,\n makeStyles,\n Theme,\n Typography,\n} from '@material-ui/core';\nimport Close from '@material-ui/icons/Close';\nimport React, { useState } from 'react';\n\nconst useDrawerStyles = makeStyles((theme: Theme) =>\n createStyles({\n paper: {\n width: '100%',\n [theme.breakpoints.up('sm')]: {\n width: '75%',\n },\n [theme.breakpoints.up('md')]: {\n width: '50%',\n },\n padding: theme.spacing(2.5),\n },\n root: {\n height: '100%',\n overflow: 'hidden',\n },\n logs: {\n background: theme.palette.background.default,\n },\n }),\n);\n\nexport const TechDocsBuildLogsDrawerContent = ({\n buildLog,\n onClose,\n}: {\n buildLog: string[];\n onClose: () => void;\n}) => {\n const classes = useDrawerStyles();\n const logText =\n buildLog.length === 0 ? 'Waiting for logs...' : buildLog.join('\\n');\n return (\n <Grid\n container\n direction=\"column\"\n className={classes.root}\n spacing={0}\n wrap=\"nowrap\"\n >\n <Grid\n item\n container\n justifyContent=\"space-between\"\n alignItems=\"center\"\n spacing={0}\n wrap=\"nowrap\"\n >\n <Typography variant=\"h5\">Build Details</Typography>\n <IconButton\n key=\"dismiss\"\n title=\"Close the drawer\"\n onClick={onClose}\n color=\"inherit\"\n >\n <Close />\n </IconButton>\n </Grid>\n <LogViewer text={logText} classes={{ root: classes.logs }} />\n </Grid>\n );\n};\n\nexport const TechDocsBuildLogs = ({ buildLog }: { buildLog: string[] }) => {\n const classes = useDrawerStyles();\n const [open, setOpen] = useState(false);\n\n return (\n <>\n <Button color=\"inherit\" onClick={() => setOpen(true)}>\n Show Build Logs\n </Button>\n <Drawer\n classes={{ paper: classes.paper }}\n anchor=\"right\"\n open={open}\n onClose={() => setOpen(false)}\n >\n <TechDocsBuildLogsDrawerContent\n buildLog={buildLog}\n onClose={() => setOpen(false)}\n />\n </Drawer>\n </>\n );\n};\n","/*\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 */\n\nimport React from 'react';\nimport { useApi, configApiRef } from '@backstage/core-plugin-api';\nimport { ErrorPage } from '@backstage/core-components';\n\ntype Props = {\n errorMessage?: string;\n};\n\nexport const TechDocsNotFound = ({ errorMessage }: Props) => {\n const techdocsBuilder =\n useApi(configApiRef).getOptionalString('techdocs.builder');\n\n let additionalInfo = '';\n if (techdocsBuilder !== 'local') {\n additionalInfo =\n \"Note that techdocs.builder is not set to 'local' in your config, which means this Backstage app will not \" +\n \"generate docs if they are not found. Make sure the project's docs are generated and published by some external \" +\n \"process (e.g. CI/CD pipeline). Or change techdocs.builder to 'local' to generate docs from this Backstage \" +\n 'instance.';\n }\n\n return (\n <ErrorPage\n status=\"404\"\n statusMessage={errorMessage || 'Documentation not found'}\n additionalInfo={additionalInfo}\n />\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport { Progress } from '@backstage/core-components';\nimport { CircularProgress, Button, makeStyles } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\n\nimport { TechDocsBuildLogs } from './TechDocsBuildLogs';\nimport { TechDocsNotFound } from './TechDocsNotFound';\nimport { useTechDocsReader } from './Reader';\n\nconst useStyles = makeStyles(() => ({\n message: {\n // `word-break: break-word` is deprecated, but gives legacy support to browsers not supporting `overflow-wrap` yet\n // https://developer.mozilla.org/en-US/docs/Web/CSS/word-break\n wordBreak: 'break-word',\n overflowWrap: 'anywhere',\n },\n}));\n\n/**\n * Note: this component is currently being exported so that we can rapidly\n * iterate on alternative <Reader /> implementations that extend core\n * functionality. There is no guarantee that this component will continue to be\n * exported by the package in the future!\n *\n * todo: Make public or stop exporting (ctrl+f \"altReaderExperiments\")\n * @internal\n */\nexport const TechDocsStateIndicator = () => {\n let StateAlert: JSX.Element | null = null;\n const classes = useStyles();\n\n const {\n state,\n contentReload,\n contentErrorMessage,\n syncErrorMessage,\n buildLog,\n } = useTechDocsReader();\n\n const ReaderProgress = state === 'CHECKING' ? <Progress /> : null;\n\n if (state === 'INITIAL_BUILD') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"info\"\n icon={<CircularProgress size=\"24px\" />}\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n >\n Documentation is accessed for the first time and is being prepared. The\n subsequent loads are much faster.\n </Alert>\n );\n }\n\n if (state === 'CONTENT_STALE_REFRESHING') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"info\"\n icon={<CircularProgress size=\"24px\" />}\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n >\n A newer version of this documentation is being prepared and will be\n available shortly.\n </Alert>\n );\n }\n\n if (state === 'CONTENT_STALE_READY') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"success\"\n action={\n <Button color=\"inherit\" onClick={() => contentReload()}>\n Refresh\n </Button>\n }\n >\n A newer version of this documentation is now available, please refresh\n to view.\n </Alert>\n );\n }\n\n if (state === 'CONTENT_STALE_ERROR') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"error\"\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n classes={{ message: classes.message }}\n >\n Building a newer version of this documentation failed.{' '}\n {syncErrorMessage}\n </Alert>\n );\n }\n\n if (state === 'CONTENT_NOT_FOUND') {\n StateAlert = (\n <>\n {syncErrorMessage && (\n <Alert\n variant=\"outlined\"\n severity=\"error\"\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n classes={{ message: classes.message }}\n >\n Building a newer version of this documentation failed.{' '}\n {syncErrorMessage}\n </Alert>\n )}\n <TechDocsNotFound errorMessage={contentErrorMessage} />\n </>\n );\n }\n\n return (\n <>\n {ReaderProgress}\n {StateAlert}\n </>\n );\n};\n","/*\n * Copyright 2021 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 { useApi } from '@backstage/core-plugin-api';\nimport { useMemo, useReducer, useRef } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport useAsyncRetry from 'react-use/lib/useAsyncRetry';\nimport { techdocsStorageApiRef } from '../../api';\n\n/**\n * A state representation that is used to configure the UI of <Reader />\n */\ntype ContentStateTypes =\n /** There is nothing to display but a loading indicator */\n | 'CHECKING'\n\n /** There is no content yet -> present a full screen loading page */\n | 'INITIAL_BUILD'\n\n /** There is content, but the backend is about to update it */\n | 'CONTENT_STALE_REFRESHING'\n\n /** There is content, but after a reload, the content will be different */\n | 'CONTENT_STALE_READY'\n\n /** There is content, the backend tried to update it, but failed */\n | 'CONTENT_STALE_ERROR'\n\n /** There is nothing to see but a \"not found\" page. Is also shown on page load errors */\n | 'CONTENT_NOT_FOUND'\n\n /** There is only the latest and greatest content */\n | 'CONTENT_FRESH';\n\n/**\n * Calculate the state that should be reported to the display component.\n */\nexport function calculateDisplayState({\n contentLoading,\n content,\n activeSyncState,\n}: Pick<\n ReducerState,\n 'contentLoading' | 'content' | 'activeSyncState'\n>): ContentStateTypes {\n // we have nothing to display yet\n if (contentLoading) {\n return 'CHECKING';\n }\n\n // the build is ready, but it triggered a content reload and the content variable is not trusted\n if (activeSyncState === 'BUILD_READY_RELOAD') {\n return 'CHECKING';\n }\n\n // there is no content, but the sync process is still evaluating\n if (!content && activeSyncState === 'CHECKING') {\n return 'CHECKING';\n }\n\n // there is no content yet so we assume that we are building it for the first time\n if (!content && activeSyncState === 'BUILDING') {\n return 'INITIAL_BUILD';\n }\n\n // if there is still no content after building, it might just not exist\n if (!content) {\n return 'CONTENT_NOT_FOUND';\n }\n\n // we are still building, but we already show stale content\n if (activeSyncState === 'BUILDING') {\n return 'CONTENT_STALE_REFRESHING';\n }\n\n // the build is ready, but the content is still stale\n if (activeSyncState === 'BUILD_READY') {\n return 'CONTENT_STALE_READY';\n }\n\n // the build failed, but the content is still stale\n if (activeSyncState === 'ERROR') {\n return 'CONTENT_STALE_ERROR';\n }\n\n // seems like the content is up-to-date (or we don't know yet and the sync process is still evaluating in the background)\n return 'CONTENT_FRESH';\n}\n\n/**\n * The state of the synchronization task. It checks whether the docs are\n * up-to-date. If they aren't, it triggers a build.\n */\ntype SyncStates =\n /** Checking if it should be synced */\n | 'CHECKING'\n\n /** Building the documentation */\n | 'BUILDING'\n\n /** Finished building the documentation */\n | 'BUILD_READY'\n\n /**\n * Finished building the documentation and triggered a content reload.\n * This state is left toward UP_TO_DATE when the content loading has finished.\n */\n | 'BUILD_READY_RELOAD'\n\n /** No need for a sync. The content was already up-to-date. */\n | 'UP_TO_DATE'\n\n /** An error occurred */\n | 'ERROR';\n\ntype ReducerActions =\n | {\n type: 'sync';\n state: SyncStates;\n syncError?: Error;\n }\n | { type: 'contentLoading' }\n | {\n type: 'content';\n path?: string;\n content?: string;\n contentError?: Error;\n }\n | { type: 'buildLog'; log: string };\n\ntype ReducerState = {\n /**\n * The path of the current page\n */\n path: string;\n\n /**\n * The current sync state\n */\n activeSyncState: SyncStates;\n\n /**\n * If true, the content is downloading from the storage.\n */\n contentLoading: boolean;\n /**\n * The content that has been downloaded and should be displayed.\n */\n content?: string;\n\n contentError?: Error;\n syncError?: Error;\n\n /**\n * A list of log messages that were emitted by the build process.\n */\n buildLog: string[];\n};\n\nexport function reducer(\n oldState: ReducerState,\n action: ReducerActions,\n): ReducerState {\n const newState = { ...oldState };\n\n switch (action.type) {\n case 'sync':\n // reset the build log when a new check starts\n if (action.state === 'CHECKING') {\n newState.buildLog = [];\n }\n\n newState.activeSyncState = action.state;\n newState.syncError = action.syncError;\n break;\n\n case 'contentLoading':\n newState.contentLoading = true;\n\n // only reset errors but keep the old content until it is replaced by the 'content' action\n newState.contentError = undefined;\n break;\n\n case 'content':\n // only override the path if it is part of the action\n if (typeof action.path === 'string') {\n newState.path = action.path;\n }\n\n newState.contentLoading = false;\n newState.content = action.content;\n newState.contentError = action.contentError;\n break;\n\n case 'buildLog':\n newState.buildLog = newState.buildLog.concat(action.log);\n break;\n\n default:\n throw new Error();\n }\n\n // a content update loads fresh content so the build is updated to being up-to-date\n if (\n ['BUILD_READY', 'BUILD_READY_RELOAD'].includes(newState.activeSyncState) &&\n ['contentLoading', 'content'].includes(action.type)\n ) {\n newState.activeSyncState = 'UP_TO_DATE';\n newState.buildLog = [];\n }\n\n return newState;\n}\n\nexport function useReaderState(\n kind: string,\n namespace: string,\n name: string,\n path: string,\n): {\n state: ContentStateTypes;\n path: string;\n contentReload: () => void;\n content?: string;\n contentErrorMessage?: string;\n syncErrorMessage?: string;\n buildLog: string[];\n} {\n const [state, dispatch] = useReducer(reducer, {\n activeSyncState: 'CHECKING',\n path,\n contentLoading: true,\n buildLog: [],\n });\n\n const techdocsStorageApi = useApi(techdocsStorageApiRef);\n\n // try to load the content. the function will fire events and we don't care for the return values\n const { retry: contentReload } = useAsyncRetry(async () => {\n dispatch({ type: 'contentLoading' });\n\n try {\n const entityDocs = await techdocsStorageApi.getEntityDocs(\n { kind, namespace, name },\n path,\n );\n\n // update content and path at the same time\n dispatch({ type: 'content', content: entityDocs, path });\n\n return entityDocs;\n } catch (e) {\n dispatch({ type: 'content', contentError: e, path });\n }\n\n return undefined;\n }, [techdocsStorageApi, kind, namespace, name, path]);\n\n // create a ref that holds the latest content. This provides a useAsync hook\n // with the latest content without restarting the useAsync hook.\n const contentRef = useRef<{ content?: string; reload: () => void }>({\n content: undefined,\n reload: () => {},\n });\n contentRef.current = { content: state.content, reload: contentReload };\n\n // try to derive the state. the function will fire events and we don't care for the return values\n useAsync(async () => {\n dispatch({ type: 'sync', state: 'CHECKING' });\n\n // should only switch to BUILDING if the request takes more than 1 seconds\n const buildingTimeout = setTimeout(() => {\n dispatch({ type: 'sync', state: 'BUILDING' });\n }, 1000);\n\n try {\n const result = await techdocsStorageApi.syncEntityDocs(\n {\n kind,\n namespace,\n name,\n },\n log => {\n dispatch({ type: 'buildLog', log });\n },\n );\n\n switch (result) {\n case 'updated':\n // if there was no content prior to building, retry the loading\n if (!contentRef.current.content) {\n contentRef.current.reload();\n dispatch({ type: 'sync', state: 'BUILD_READY_RELOAD' });\n } else {\n dispatch({ type: 'sync', state: 'BUILD_READY' });\n }\n break;\n case 'cached':\n dispatch({ type: 'sync', state: 'UP_TO_DATE' });\n break;\n\n default:\n dispatch({\n type: 'sync',\n state: 'ERROR',\n syncError: new Error('Unexpected return state'),\n });\n break;\n }\n } catch (e) {\n dispatch({ type: 'sync', state: 'ERROR', syncError: e });\n } finally {\n // Cancel the timer that sets the state \"BUILDING\"\n clearTimeout(buildingTimeout);\n }\n }, [kind, name, namespace, techdocsStorageApi, dispatch, contentRef]);\n\n const displayState = useMemo(\n () =>\n calculateDisplayState({\n activeSyncState: state.activeSyncState,\n contentLoading: state.contentLoading,\n content: state.content,\n }),\n [state.activeSyncState, state.content, state.contentLoading],\n );\n\n return {\n state: displayState,\n contentReload,\n path: state.path,\n content: state.content,\n contentErrorMessage: state.contentError?.toString(),\n syncErrorMessage: state.syncError?.toString(),\n buildLog: state.buildLog,\n };\n}\n","/*\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 */\n\nimport React, {\n PropsWithChildren,\n ComponentType,\n createContext,\n useContext,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { Grid, makeStyles, useTheme } from '@material-ui/core';\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { useApi, configApiRef } from '@backstage/core-plugin-api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { BackstageTheme } from '@backstage/theme';\nimport { SidebarPinStateContext } from '@backstage/core-components';\n\nimport { techdocsStorageApiRef } from '../../api';\n\nimport {\n addBaseUrl,\n addGitFeedbackLink,\n addLinkClickListener,\n injectCss,\n onCssReady,\n removeMkdocsHeader,\n rewriteDocLinks,\n sanitizeDOM,\n simplifyMkdocsFooter,\n scrollIntoAnchor,\n transform as transformer,\n copyToClipboard,\n} from '../transformers';\n\nimport { TechDocsSearch } from './TechDocsSearch';\nimport { TechDocsStateIndicator } from './TechDocsStateIndicator';\nimport { useReaderState } from './useReaderState';\n\ntype Props = {\n entityRef: EntityName;\n withSearch?: boolean;\n onReady?: () => void;\n};\n\nconst useStyles = makeStyles<BackstageTheme>(theme => ({\n searchBar: {\n marginLeft: '20rem',\n maxWidth: 'calc(100% - 20rem * 2 - 3rem)',\n marginTop: theme.spacing(1),\n '@media screen and (max-width: 76.1875em)': {\n marginLeft: '10rem',\n maxWidth: 'calc(100% - 10rem)',\n },\n },\n}));\n\ntype TechDocsReaderValue = ReturnType<typeof useReaderState>;\n\nconst TechDocsReaderContext = createContext<TechDocsReaderValue>(\n {} as TechDocsReaderValue,\n);\n\nconst TechDocsReaderProvider = ({\n children,\n entityRef,\n}: PropsWithChildren<{ entityRef: EntityName }>) => {\n const { '*': path } = useParams();\n const { kind, namespace, name } = entityRef;\n const value = useReaderState(kind, namespace, name, path);\n return (\n <TechDocsReaderContext.Provider value={value}>\n {children}\n </TechDocsReaderContext.Provider>\n );\n};\n\n/**\n * Note: this HOC is currently being exported so that we can rapidly\n * iterate on alternative <Reader /> implementations that extend core\n * functionality. There is no guarantee that this HOC will continue to be\n * exported by the package in the future!\n *\n * todo: Make public or stop exporting (ctrl+f \"altReaderExperiments\")\n * @internal\n */\nexport const withTechDocsReaderProvider =\n <T extends {}>(Component: ComponentType<T>, entityRef: EntityName) =>\n (props: T) =>\n (\n <TechDocsReaderProvider entityRef={entityRef}>\n <Component {...props} />\n </TechDocsReaderProvider>\n );\n\n/**\n * Note: this hook is currently being exported so that we can rapidly\n * iterate on alternative <Reader /> implementations that extend core\n * functionality. There is no guarantee that this hook will continue to be\n * exported by the package in the future!\n *\n * todo: Make public or stop exporting (ctrl+f \"altReaderExperiments\")\n * @internal\n */\nexport const useTechDocsReader = () => useContext(TechDocsReaderContext);\n\n/**\n * Hook that encapsulates the behavior of getting raw HTML and applying\n * transforms to it in order to make it function at a basic level in the\n * Backstage UI.\n *\n * Note: this hook is currently being exported so that we can rapidly iterate\n * on alternative <Reader /> implementations that extend core functionality.\n * There is no guarantee that this hook will continue to be exported by the\n * package in the future!\n *\n * todo: Make public or stop exporting (see others: \"altReaderExperiments\")\n * @internal\n */\nexport const useTechDocsReaderDom = (entityRef: EntityName): Element | null => {\n const navigate = useNavigate();\n const theme = useTheme<BackstageTheme>();\n const techdocsStorageApi = useApi(techdocsStorageApiRef);\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n const techdocsSanitizer = useApi(configApiRef);\n const { namespace = '', kind = '', name = '' } = entityRef;\n const { state, path, content: rawPage } = useTechDocsReader();\n\n const [sidebars, setSidebars] = useState<HTMLElement[]>();\n const [dom, setDom] = useState<HTMLElement | null>(null);\n\n // sidebar pinned status to be used in computing CSS style injections\n const { isPinned } = useContext(SidebarPinStateContext);\n\n const updateSidebarPosition = useCallback(() => {\n if (!dom || !sidebars) return;\n // set sidebar height so they don't initially render in wrong position\n const mdTabs = dom.querySelector('.md-container > .md-tabs');\n sidebars.forEach(sidebar => {\n const newTop = Math.max(dom.getBoundingClientRect().top, 0);\n sidebar.style.top = mdTabs\n ? `${newTop + mdTabs.getBoundingClientRect().height}px`\n : `${newTop}px`;\n });\n }, [dom, sidebars]);\n\n useEffect(() => {\n updateSidebarPosition();\n window.addEventListener('scroll', updateSidebarPosition, true);\n window.addEventListener('resize', updateSidebarPosition);\n return () => {\n window.removeEventListener('scroll', updateSidebarPosition, true);\n window.removeEventListener('resize', updateSidebarPosition);\n };\n // an update to \"state\" might lead to an updated UI so we include it as a trigger\n }, [updateSidebarPosition, state]);\n\n // dynamically set width of footer to accommodate for pinning of the sidebar\n const updateFooterWidth = useCallback(() => {\n if (!dom) return;\n const footer = dom.querySelector('.md-footer') as HTMLElement;\n if (footer) {\n footer.style.width = `${dom.getBoundingClientRect().width}px`;\n }\n }, [dom]);\n\n useEffect(() => {\n updateFooterWidth();\n window.addEventListener('resize', updateFooterWidth);\n return () => {\n window.removeEventListener('resize', updateFooterWidth);\n };\n });\n\n // a function that performs transformations that are executed prior to adding it to the DOM\n const preRender = useCallback(\n (rawContent: string, contentPath: string) =>\n transformer(rawContent, [\n sanitizeDOM(techdocsSanitizer.getOptionalConfig('techdocs.sanitizer')),\n addBaseUrl({\n techdocsStorageApi,\n entityId: {\n kind,\n name,\n namespace,\n },\n path: contentPath,\n }),\n rewriteDocLinks(),\n removeMkdocsHeader(),\n simplifyMkdocsFooter(),\n addGitFeedbackLink(scmIntegrationsApi),\n injectCss({\n css: `\n body {\n font-family: ${theme.typography.fontFamily};\n --md-text-color: ${theme.palette.text.primary};\n --md-text-link-color: ${theme.palette.primary.main};\n\n --md-code-fg-color: ${theme.palette.text.primary};\n --md-code-bg-color: ${theme.palette.background.paper};\n --md-accent-fg-color: ${theme.palette.primary.main};\n --md-default-fg-color--lightest: ${theme.palette.textVerySubtle};\n }\n .md-main__inner { margin-top: 0; }\n .md-sidebar { position: fixed; bottom: 100px; width: 20rem; }\n .md-sidebar--secondary { right: 2rem; }\n .md-content { margin-bottom: 50px }\n .md-footer { position: fixed; bottom: 0px; }\n .md-footer-nav__link { width: 20rem;}\n .md-content { margin-left: 20rem; max-width: calc(100% - 20rem * 2 - 3rem); }\n .md-typeset { font-size: 1rem; }\n .md-typeset h1, .md-typeset h2, .md-typeset h3 { font-weight: bold; }\n .md-nav { font-size: 1rem; }\n .md-grid { max-width: 90vw; margin: 0 }\n .md-typeset blockquote {\n color: ${theme.palette.textSubtle};\n border-left: 0.2rem solid ${theme.palette.textVerySubtle};\n }\n .md-typeset hr {\n border-bottom: 0.05rem dotted ${theme.palette.textVerySubtle};\n }\n .md-typeset table:not([class]) {\n font-size: 1rem;\n border: 1px solid ${theme.palette.text.primary};\n border-bottom: none;\n border-collapse: collapse;\n }\n .md-typeset table:not([class]) td, .md-typeset table:not([class]) th {\n border-bottom: 1px solid ${theme.palette.text.primary};\n }\n .md-typeset table:not([class]) th { font-weight: bold; }\n .md-typeset .admonition, .md-typeset details {\n font-size: 1rem;\n }\n \n /* style the checkmarks of the task list */\n .md-typeset .task-list-control .task-list-indicator::before {\n background-color: ${theme.palette.action.disabledBackground};\n }\n .md-typeset .task-list-control [type=\"checkbox\"]:checked + .task-list-indicator:before {\n background-color: ${theme.palette.success.main};\n }\n /**/\n\n @media screen and (max-width: 76.1875em) {\n .md-nav {\n background-color: ${theme.palette.background.default};\n transition: none !important\n }\n .md-sidebar--secondary { display: none; }\n .md-sidebar--primary { left: ${\n isPinned ? '242px' : '72px'\n }; width: 10rem }\n .md-content { margin-left: 10rem; max-width: calc(100% - 10rem); }\n .md-content__inner { font-size: 0.9rem }\n .md-footer {\n position: static;\n padding-left: 10rem;\n }\n .md-footer-nav__link {\n /* footer links begin to overlap at small sizes without setting width */\n width: 50%;\n }\n .md-nav--primary .md-nav__title {\n white-space: normal;\n height: auto;\n line-height: 1rem;\n cursor: auto;\n }\n .md-nav--primary > .md-nav__title [for=\"none\"] {\n padding-top: 0;\n }\n }\n `,\n }),\n injectCss({\n // Disable CSS animations on link colors as they lead to issues in dark\n // mode. The dark mode color theme is applied later and theirfore there\n // is always an animation from light to dark mode when navigation\n // between pages.\n css: `\n .md-nav__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink {\n transition: none;\n }\n `,\n }),\n injectCss({\n // Properly style code blocks.\n css: `\n .md-typeset pre > code::-webkit-scrollbar-thumb {\n background-color: hsla(0, 0%, 0%, 0.32);\n }\n .md-typeset pre > code::-webkit-scrollbar-thumb:hover {\n background-color: hsla(0, 0%, 0%, 0.87);\n }\n `,\n }),\n injectCss({\n // Admonitions and others are using SVG masks to define icons. These\n // masks are defined as CSS variables.\n // As the MkDocs output is rendered in shadow DOM, the CSS variable\n // definitions on the root selector are not applied. Instead, the have\n // to be applied on :host.\n // As there is no way to transform the served main*.css yet (for\n // example in the backend), we have to copy from main*.css and modify\n // them.\n css: `\n :host {\n --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z\"/></svg>');\n --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M4 5h16v2H4V5m0 4h16v2H4V9m0 4h16v2H4v-2m0 4h10v2H4v-2z\"/></svg>');\n --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10A10 10 0 0012 2z\"/></svg>');\n --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M17.55 11.2c-.23-.3-.5-.56-.76-.82-.65-.6-1.4-1.03-2.03-1.66C13.3 7.26 13 4.85 13.91 3c-.91.23-1.75.75-2.45 1.32-2.54 2.08-3.54 5.75-2.34 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.22.1-.46.04-.64-.12a.83.83 0 01-.15-.17c-1.1-1.43-1.28-3.48-.53-5.12C5.89 10 5 12.3 5.14 14.47c.04.5.1 1 .27 1.5.14.6.4 1.2.72 1.73 1.04 1.73 2.87 2.97 4.84 3.22 2.1.27 4.35-.12 5.96-1.6 1.8-1.66 2.45-4.32 1.5-6.6l-.13-.26c-.2-.46-.47-.87-.8-1.25l.05-.01m-3.1 6.3c-.28.24-.73.5-1.08.6-1.1.4-2.2-.16-2.87-.82 1.19-.28 1.89-1.16 2.09-2.05.17-.8-.14-1.46-.27-2.23-.12-.74-.1-1.37.18-2.06.17.38.37.76.6 1.06.76 1 1.95 1.44 2.2 2.8.04.14.06.28.06.43.03.82-.32 1.72-.92 2.27h.01z\"/></svg>');\n --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>');\n --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M15.07 11.25l-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 00-2-2 2 2 0 00-2 2H8a4 4 0 014-4 4 4 0 014 4 3.2 3.2 0 01-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10c0-5.53-4.5-10-10-10z\"/></svg>');\n --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M13 14h-2v-4h2m0 8h-2v-2h2M1 21h22L12 2 1 21z\"/></svg>');\n --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 2c5.53 0 10 4.47 10 10s-4.47 10-10 10S2 17.53 2 12 6.47 2 12 2m3.59 5L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41 15.59 7z\"/></svg>');\n --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M11.5 20l4.86-9.73H13V4l-5 9.73h3.5V20M12 2c2.75 0 5.1 1 7.05 2.95C21 6.9 22 9.25 22 12s-1 5.1-2.95 7.05C17.1 21 14.75 22 12 22s-5.1-1-7.05-2.95C3 17.1 2 14.75 2 12s1-5.1 2.95-7.05C6.9 3 9.25 2 12 2z\"/></svg>');\n --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 00-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 00-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z\"/></svg>');\n --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 01.75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z\"/></svg>');\n --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z\"/></svg>');\n }\n :host {\n --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.42L5.83 13H21V7h-2z\"/></svg>');\n }\n :host {\n --md-details-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M8.59 16.58L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42z\"/></svg>');\n }\n :host {\n --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>');\n --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>');\n }\n `,\n }),\n ]),\n [\n kind,\n name,\n namespace,\n scmIntegrationsApi,\n techdocsStorageApi,\n techdocsSanitizer,\n theme.palette.action.disabledBackground,\n theme.palette.background.default,\n theme.palette.background.paper,\n theme.palette.primary.main,\n theme.palette.success.main,\n theme.palette.text.primary,\n theme.palette.textSubtle,\n theme.palette.textVerySubtle,\n theme.typography.fontFamily,\n isPinned,\n ],\n );\n\n // a function that performs transformations that are executed after adding it to the DOM\n const postRender = useCallback(\n async (transformedElement: Element) =>\n transformer(transformedElement, [\n scrollIntoAnchor(),\n copyToClipboard(),\n addLinkClickListener({\n baseUrl: window.location.origin,\n onClick: (event: MouseEvent, url: string) => {\n // detect if CTRL or META keys are pressed so that links can be opened in a new tab with `window.open`\n const modifierActive = event.ctrlKey || event.metaKey;\n const parsedUrl = new URL(url);\n\n // hash exists when anchor is clicked on secondary sidebar\n if (parsedUrl.hash) {\n if (modifierActive) {\n window.open(`${parsedUrl.pathname}${parsedUrl.hash}`, '_blank');\n } else {\n navigate(`${parsedUrl.pathname}${parsedUrl.hash}`);\n // Scroll to hash if it's on the current page\n transformedElement\n ?.querySelector(`#${parsedUrl.hash.slice(1)}`)\n ?.scrollIntoView();\n }\n } else {\n if (modifierActive) {\n window.open(parsedUrl.pathname, '_blank');\n } else {\n navigate(parsedUrl.pathname);\n // Scroll to top of reader if primary sidebar link is clicked\n transformedElement\n ?.querySelector('.md-content__inner')\n ?.scrollIntoView();\n }\n }\n },\n }),\n onCssReady({\n docStorageUrl: await techdocsStorageApi.getApiOrigin(),\n onLoading: (renderedElement: Element) => {\n (renderedElement as HTMLElement).style.setProperty('opacity', '0');\n },\n onLoaded: (renderedElement: Element) => {\n (renderedElement as HTMLElement).style.removeProperty('opacity');\n // disable MkDocs drawer toggling ('for' attribute => checkbox mechanism)\n renderedElement\n .querySelector('.md-nav__title')\n ?.removeAttribute('for');\n setSidebars(\n Array.from(renderedElement.querySelectorAll('.md-sidebar')),\n );\n },\n }),\n ]),\n [navigate, techdocsStorageApi],\n );\n\n useEffect(() => {\n if (!rawPage) return () => {};\n\n // if false, there is already a newer execution of this effect\n let shouldReplaceContent = true;\n\n // Pre-render\n preRender(rawPage, path).then(async preTransformedDomElement => {\n if (!preTransformedDomElement?.innerHTML) {\n return; // An unexpected error occurred\n }\n\n // don't manipulate the shadow dom if this isn't the latest effect execution\n if (!shouldReplaceContent) {\n return;\n }\n\n // Scroll to top after render\n window.scroll({ top: 0 });\n\n // Post-render\n const postTransformedDomElement = await postRender(\n preTransformedDomElement,\n );\n setDom(postTransformedDomElement as HTMLElement);\n });\n\n // cancel this execution\n return () => {\n shouldReplaceContent = false;\n };\n }, [rawPage, path, preRender, postRender]);\n\n return dom;\n};\n\nconst TheReader = ({\n entityRef,\n onReady = () => {},\n withSearch = true,\n}: Props) => {\n const classes = useStyles();\n const dom = useTechDocsReaderDom(entityRef);\n const shadowDomRef = useRef<HTMLDivElement>(null);\n\n const onReadyRef = useRef<() => void>(onReady);\n useEffect(() => {\n onReadyRef.current = onReady;\n }, [onReady]);\n\n useEffect(() => {\n if (!dom || !shadowDomRef.current) return;\n const shadowDiv = shadowDomRef.current;\n const shadowRoot =\n shadowDiv.shadowRoot || shadowDiv.attachShadow({ mode: 'open' });\n Array.from(shadowRoot.children).forEach(child =>\n shadowRoot.removeChild(child),\n );\n shadowRoot.appendChild(dom);\n onReadyRef.current();\n\n // this hook must ONLY be triggered by a changed dom\n }, [dom]);\n\n return (\n <>\n <TechDocsStateIndicator />\n {withSearch && shadowDomRef?.current?.shadowRoot?.innerHTML && (\n <Grid container className={classes.searchBar}>\n <TechDocsSearch entityId={entityRef} />\n </Grid>\n )}\n <div data-testid=\"techdocs-content-shadowroot\" ref={shadowDomRef} />\n </>\n );\n};\n\nexport const Reader = ({\n entityRef,\n onReady = () => {},\n withSearch = true,\n}: Props) => (\n <TechDocsReaderProvider entityRef={entityRef}>\n <TheReader\n entityRef={entityRef}\n onReady={onReady}\n withSearch={withSearch}\n />\n </TechDocsReaderProvider>\n);\n","/*\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 */\n\nimport { EntityName, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport {\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport CodeIcon from '@material-ui/icons/Code';\nimport React from 'react';\nimport { rootRouteRef } from '../../routes';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';\n\nexport type TechDocsPageHeaderProps = {\n entityRef: EntityName;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n};\n\nexport const TechDocsPageHeader = ({\n entityRef,\n entityMetadata,\n techDocsMetadata,\n}: TechDocsPageHeaderProps) => {\n const { name } = entityRef;\n\n const { site_name: siteName, site_description: siteDescription } =\n techDocsMetadata || {};\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=\"Component\"\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\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 ? <HeaderLabel label=\"Lifecycle\" value={lifecycle} /> : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <a\n href={locationMetadata.target}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <CodeIcon style={{ marginTop: '-25px', fill: '#fff' }} />\n </a>\n }\n />\n ) : null}\n </>\n );\n\n return (\n <Header\n title={siteName ? siteName : '.'}\n pageTitleOverride={siteName || name}\n subtitle={\n siteDescription && siteDescription !== 'None' ? siteDescription : ''\n }\n type=\"Docs\"\n typeLink={docsRootLink}\n >\n {labels}\n </Header>\n );\n};\n","/*\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 */\n\nimport React, { useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport useAsync from 'react-use/lib/useAsync';\nimport { techdocsApiRef } from '../../api';\nimport { TechDocsNotFound } from './TechDocsNotFound';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Page, Content } from '@backstage/core-components';\nimport { Reader } from './Reader';\nimport { TechDocsPageHeader } from './TechDocsPageHeader';\n\nexport const LegacyTechDocsPage = () => {\n const [documentReady, setDocumentReady] = useState<boolean>(false);\n const { namespace, kind, name } = useParams();\n\n const techdocsApi = useApi(techdocsApiRef);\n\n const { value: techdocsMetadataValue } = useAsync(() => {\n if (documentReady) {\n return techdocsApi.getTechDocsMetadata({ kind, namespace, name });\n }\n\n return Promise.resolve(undefined);\n }, [kind, namespace, name, techdocsApi, documentReady]);\n\n const { value: entityMetadataValue, error: entityMetadataError } =\n useAsync(() => {\n return techdocsApi.getEntityMetadata({ kind, namespace, name });\n }, [kind, namespace, name, techdocsApi]);\n\n const onReady = useCallback(() => {\n setDocumentReady(true);\n }, [setDocumentReady]);\n\n if (entityMetadataError) {\n return <TechDocsNotFound errorMessage={entityMetadataError.message} />;\n }\n\n return (\n <Page themeId=\"documentation\">\n <TechDocsPageHeader\n techDocsMetadata={techdocsMetadataValue}\n entityMetadata={entityMetadataValue}\n entityRef={{\n kind,\n namespace,\n name,\n }}\n />\n <Content data-testid=\"techdocs-content\">\n <Reader\n onReady={onReady}\n entityRef={{\n kind,\n namespace,\n name,\n }}\n />\n </Content>\n </Page>\n );\n};\n","/*\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 */\n\nimport React, { useCallback, useState } from 'react';\nimport { useOutlet } from 'react-router';\nimport { useParams } from 'react-router-dom';\nimport useAsync from 'react-use/lib/useAsync';\nimport { techdocsApiRef } from '../../api';\nimport { TechDocsNotFound } from './TechDocsNotFound';\nimport { LegacyTechDocsPage } from './LegacyTechDocsPage';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';\nimport { EntityName } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Page } from '@backstage/core-components';\n\nexport type TechDocsPageRenderFunction = ({\n techdocsMetadataValue,\n entityMetadataValue,\n entityRef,\n}: {\n techdocsMetadataValue?: TechDocsMetadata | undefined;\n entityMetadataValue?: TechDocsEntityMetadata | undefined;\n entityRef: EntityName;\n onReady: () => void;\n}) => JSX.Element;\n\nexport type TechDocsPageProps = {\n children?: TechDocsPageRenderFunction | React.ReactNode;\n};\n\nexport const TechDocsPage = ({ children }: TechDocsPageProps) => {\n const outlet = useOutlet();\n\n const [documentReady, setDocumentReady] = useState<boolean>(false);\n const { namespace, kind, name } = useParams();\n\n const techdocsApi = useApi(techdocsApiRef);\n\n const { value: techdocsMetadataValue } = useAsync(() => {\n if (documentReady) {\n return techdocsApi.getTechDocsMetadata({ kind, namespace, name });\n }\n\n return Promise.resolve(undefined);\n }, [kind, namespace, name, techdocsApi, documentReady]);\n\n const { value: entityMetadataValue, error: entityMetadataError } =\n useAsync(() => {\n return techdocsApi.getEntityMetadata({ kind, namespace, name });\n }, [kind, namespace, name, techdocsApi]);\n\n const onReady = useCallback(() => {\n setDocumentReady(true);\n }, [setDocumentReady]);\n\n if (entityMetadataError) {\n return <TechDocsNotFound errorMessage={entityMetadataError.message} />;\n }\n\n if (!children) return outlet || <LegacyTechDocsPage />;\n\n return (\n <Page themeId=\"documentation\">\n {children instanceof Function\n ? children({\n techdocsMetadataValue,\n entityMetadataValue,\n entityRef: { kind, namespace, name },\n onReady,\n })\n : children}\n </Page>\n );\n};\n","/*\n * Copyright 2021 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, { useState } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport { makeStyles } from '@material-ui/core';\nimport { CSSProperties } from '@material-ui/styles';\nimport {\n CATALOG_FILTER_EXISTS,\n catalogApiRef,\n CatalogApi,\n isOwnerOf,\n useOwnUser,\n} from '@backstage/plugin-catalog-react';\nimport { Entity } from '@backstage/catalog-model';\nimport { DocsTable } from './DocsTable';\nimport { DocsCardGrid } from './DocsCardGrid';\nimport { TechDocsPageWrapper } from './TechDocsPageWrapper';\n\nimport {\n CodeSnippet,\n Content,\n HeaderTabs,\n Progress,\n WarningPanel,\n SupportButton,\n ContentHeader,\n} from '@backstage/core-components';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nconst panels = {\n DocsTable: DocsTable,\n DocsCardGrid: DocsCardGrid,\n};\n\nexport type PanelType = 'DocsCardGrid' | 'DocsTable';\n\nexport interface PanelConfig {\n title: string;\n description: string;\n panelType: PanelType;\n panelCSS?: CSSProperties;\n filterPredicate: ((entity: Entity) => boolean) | string;\n}\n\nexport interface TabConfig {\n label: string;\n panels: PanelConfig[];\n}\n\nexport type TabsConfig = TabConfig[];\n\nconst CustomPanel = ({\n config,\n entities,\n index,\n}: {\n config: PanelConfig;\n entities: Entity[];\n index: number;\n}) => {\n const useStyles = makeStyles({\n panelContainer: {\n marginBottom: '2rem',\n ...(config.panelCSS ? config.panelCSS : {}),\n },\n });\n const classes = useStyles();\n const { value: user } = useOwnUser();\n\n const Panel = panels[config.panelType];\n\n const shownEntities = entities.filter(entity => {\n if (config.filterPredicate === 'ownedByUser') {\n if (!user) {\n return false;\n }\n return isOwnerOf(user, entity);\n }\n\n return (\n typeof config.filterPredicate === 'function' &&\n config.filterPredicate(entity)\n );\n });\n\n return (\n <>\n <ContentHeader title={config.title} description={config.description}>\n {index === 0 ? (\n <SupportButton>\n Discover documentation in your ecosystem.\n </SupportButton>\n ) : null}\n </ContentHeader>\n <div className={classes.panelContainer}>\n <Panel data-testid=\"techdocs-custom-panel\" entities={shownEntities} />\n </div>\n </>\n );\n};\n\nexport const TechDocsCustomHome = ({\n tabsConfig,\n}: {\n tabsConfig: TabsConfig;\n}) => {\n const [selectedTab, setSelectedTab] = useState<number>(0);\n const catalogApi: CatalogApi = useApi(catalogApiRef);\n\n const {\n value: entities,\n loading,\n error,\n } = useAsync(async () => {\n const response = await catalogApi.getEntities({\n filter: {\n 'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'apiVersion',\n 'kind',\n 'metadata',\n 'relations',\n 'spec.owner',\n 'spec.type',\n ],\n });\n return response.items.filter((entity: Entity) => {\n return !!entity.metadata.annotations?.['backstage.io/techdocs-ref'];\n });\n });\n\n const currentTabConfig = tabsConfig[selectedTab];\n\n if (loading) {\n return (\n <TechDocsPageWrapper>\n <Content>\n <Progress />\n </Content>\n </TechDocsPageWrapper>\n );\n }\n\n if (error) {\n return (\n <TechDocsPageWrapper>\n <Content>\n <WarningPanel\n severity=\"error\"\n title=\"Could not load available documentation.\"\n >\n <CodeSnippet language=\"text\" text={error.toString()} />\n </WarningPanel>\n </Content>\n </TechDocsPageWrapper>\n );\n }\n\n return (\n <TechDocsPageWrapper>\n <HeaderTabs\n selectedIndex={selectedTab}\n onChange={index => setSelectedTab(index)}\n tabs={tabsConfig.map(({ label }, index) => ({\n id: index.toString(),\n label,\n }))}\n />\n <Content data-testid=\"techdocs-content\">\n {currentTabConfig.panels.map((config, index) => (\n <CustomPanel\n key={index}\n config={config}\n entities={!!entities ? entities : []}\n index={index}\n />\n ))}\n </Content>\n </TechDocsPageWrapper>\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport { PanelType, TechDocsCustomHome } from './TechDocsCustomHome';\n\nexport const LegacyTechDocsHome = () => {\n const tabsConfig = [\n {\n label: 'Overview',\n panels: [\n {\n title: 'Overview',\n description:\n 'Explore your internal technical ecosystem through documentation.',\n panelType: 'DocsCardGrid' as PanelType,\n filterPredicate: () => true,\n },\n // uncomment this if you would like to have a secondary panel with owned documents\n // {\n // title: 'Owned',\n // description: 'Explore your owned internal documentation.',\n // panelType: 'DocsCardGrid' as PanelType,\n // filterPredicate: 'ownedByUser',\n // },\n ],\n },\n {\n label: 'Owned Documents',\n panels: [\n {\n title: 'Owned documents',\n description: 'Access your documentation.',\n panelType: 'DocsTable' as PanelType,\n // ownedByUser filters out entities owned by signed in user\n filterPredicate: 'ownedByUser',\n },\n ],\n },\n ];\n return <TechDocsCustomHome tabsConfig={tabsConfig} />;\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport { useOutlet } from 'react-router';\nimport { LegacyTechDocsHome } from './LegacyTechDocsHome';\n\nexport const TechDocsIndexPage = () => {\n const outlet = useOutlet();\n\n return outlet || <LegacyTechDocsHome />;\n};\n","/*\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 */\n\nimport React from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport { Reader } from './reader';\nimport { toLowerMaybe } from './helpers';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nexport const EntityPageDocs = ({ entity }: { entity: Entity }) => {\n const config = useApi(configApiRef);\n return (\n <Reader\n withSearch={false}\n entityRef={{\n namespace: toLowerMaybe(entity.metadata.namespace ?? 'default', config),\n kind: toLowerMaybe(entity.kind, config),\n name: toLowerMaybe(entity.metadata.name, config),\n }}\n />\n );\n};\n","/*\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 */\n\nimport React from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { Route, Routes } from 'react-router-dom';\nimport { TechDocsIndexPage } from './home/components/TechDocsIndexPage';\nimport { TechDocsPage as TechDocsReaderPage } from './reader/components/TechDocsPage';\nimport { EntityPageDocs } from './EntityPageDocs';\nimport { MissingAnnotationEmptyState } from '@backstage/core-components';\n\nconst TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';\n\nexport const isTechDocsAvailable = (entity: Entity) =>\n Boolean(entity?.metadata?.annotations?.[TECHDOCS_ANNOTATION]);\n\nexport const Router = () => {\n return (\n <Routes>\n <Route path=\"/\" element={<TechDocsIndexPage />} />\n <Route\n path=\"/:namespace/:kind/:name/*\"\n element={<TechDocsReaderPage />}\n />\n </Routes>\n );\n};\n\ntype Props = {\n /** @deprecated The entity is now grabbed from context instead */\n entity?: Entity;\n};\n\nexport const EmbeddedDocsRouter = (_props: Props) => {\n const { entity } = useEntity();\n\n const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION];\n\n if (!projectId) {\n return <MissingAnnotationEmptyState annotation={TECHDOCS_ANNOTATION} />;\n }\n\n return (\n <Routes>\n <Route path=\"/*\" element={<EntityPageDocs entity={entity} />} />\n </Routes>\n );\n};\n"],"names":["useStyles","DocsTable","columnFactories.createNameColumn","columnFactories.createOwnerColumn","columnFactories.createTypeColumn","actionFactories.createCopyDocsUrlAction","actionFactories.createStarEntityAction","DocsCardGrid","TechDocsCustomHome","TechDocsIndexPage","Button","useNavigate","transformer","TechDocsReaderPage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/api.ts","../src/client.ts","../src/components/DocsResultListItem/DocsResultListItem.tsx","../src/routes.ts","../src/home/components/actions.tsx","../src/home/components/columns.tsx","../src/helpers.ts","../src/home/components/DocsTable.tsx","../src/home/components/EntityListDocsTable.tsx","../src/home/components/TechDocsPageWrapper.tsx","../src/home/components/TechDocsPicker.tsx","../src/home/components/DefaultTechDocsHome.tsx","../src/home/components/DocsCardGrid.tsx","../src/home/components/EntityListDocsGrid.tsx","../src/plugin.ts","../src/reader/transformers/addBaseUrl.ts","../src/reader/transformers/addGitFeedbackLink.ts","../src/reader/transformers/rewriteDocLinks.ts","../src/reader/transformers/addLinkClickListener.ts","../src/reader/transformers/copyToClipboard.ts","../src/reader/transformers/removeMkdocsHeader.ts","../src/reader/transformers/simplifyMkdocsFooter.ts","../src/reader/transformers/onCssReady.ts","../src/reader/transformers/sanitizeDOM.ts","../src/reader/transformers/injectCss.ts","../src/reader/transformers/scrollIntoAnchor.ts","../src/reader/transformers/transformer.ts","../src/reader/components/TechDocsSearch.tsx","../src/reader/components/TechDocsBuildLogs.tsx","../src/reader/components/TechDocsNotFound.tsx","../src/reader/components/TechDocsStateIndicator.tsx","../src/reader/components/useReaderState.ts","../src/reader/components/Reader.tsx","../src/reader/components/TechDocsPageHeader.tsx","../src/reader/components/LegacyTechDocsPage.tsx","../src/reader/components/TechDocsPage.tsx","../src/home/components/TechDocsCustomHome.tsx","../src/home/components/LegacyTechDocsHome.tsx","../src/home/components/TechDocsIndexPage.tsx","../src/EntityPageDocs.tsx","../src/Router.tsx"],"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 */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from './types';\nimport { createApiRef } from '@backstage/core-plugin-api';\n\n/**\n * Utility API reference for the {@link TechDocsStorageApi}.\n *\n * @public\n */\nexport const techdocsStorageApiRef = createApiRef<TechDocsStorageApi>({\n id: 'plugin.techdocs.storageservice',\n});\n\n/**\n * Utility API reference for the {@link TechDocsApi}.\n *\n * @public\n */\nexport const techdocsApiRef = createApiRef<TechDocsApi>({\n id: 'plugin.techdocs.service',\n});\n\n/**\n * The outcome of a docs sync operation.\n *\n * @public\n */\nexport type SyncResult = 'cached' | 'updated';\n\n/**\n * API which talks to TechDocs storage to fetch files to render.\n *\n * @public\n */\nexport interface TechDocsStorageApi {\n /**\n * Set to techdocs.requestUrl as the URL for techdocs-backend API.\n */\n getApiOrigin(): Promise<string>;\n getStorageUrl(): Promise<string>;\n getBuilder(): Promise<string>;\n getEntityDocs(entityId: EntityName, path: string): Promise<string>;\n syncEntityDocs(\n entityId: EntityName,\n logHandler?: (line: string) => void,\n ): Promise<SyncResult>;\n getBaseUrl(\n oldBaseUrl: string,\n entityId: EntityName,\n path: string,\n ): Promise<string>;\n}\n\n/**\n * API to talk to techdocs-backend.\n *\n * @public\n */\nexport interface TechDocsApi {\n /**\n * Set to techdocs.requestUrl as the URL for techdocs-backend API.\n */\n getApiOrigin(): Promise<string>;\n getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata>;\n getEntityMetadata(entityId: EntityName): Promise<TechDocsEntityMetadata>;\n}\n","/*\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 */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n DiscoveryApi,\n FetchApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport { NotFoundError, ResponseError } from '@backstage/errors';\nimport { EventSourcePolyfill } from 'event-source-polyfill';\nimport { SyncResult, TechDocsApi, TechDocsStorageApi } from './api';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from './types';\n\n/**\n * API to talk to `techdocs-backend`.\n *\n * @public\n */\nexport class TechDocsClient implements TechDocsApi {\n public configApi: Config;\n public discoveryApi: DiscoveryApi;\n private fetchApi: FetchApi;\n\n constructor(options: {\n configApi: Config;\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n }) {\n this.configApi = options.configApi;\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getApiOrigin(): Promise<string> {\n return (\n this.configApi.getOptionalString('techdocs.requestUrl') ??\n (await this.discoveryApi.getBaseUrl('techdocs'))\n );\n }\n\n /**\n * Retrieve TechDocs metadata.\n *\n * When docs are built, we generate a techdocs_metadata.json and store it along with the generated\n * static files. It includes necessary data about the docs site. This method requests techdocs-backend\n * which retrieves the TechDocs metadata.\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n */\n async getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;\n const request = await this.fetchApi.fetch(`${requestUrl}`);\n if (!request.ok) {\n throw await ResponseError.fromResponse(request);\n }\n\n return await request.json();\n }\n\n /**\n * Retrieve metadata about an entity.\n *\n * This method requests techdocs-backend which uses the catalog APIs to respond with filtered\n * information required here.\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n */\n async getEntityMetadata(\n entityId: EntityName,\n ): Promise<TechDocsEntityMetadata> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;\n\n const request = await this.fetchApi.fetch(`${requestUrl}`);\n if (!request.ok) {\n throw await ResponseError.fromResponse(request);\n }\n\n return await request.json();\n }\n}\n\n/**\n * API which talks to TechDocs storage to fetch files to render.\n *\n * @public\n */\nexport class TechDocsStorageClient implements TechDocsStorageApi {\n public configApi: Config;\n public discoveryApi: DiscoveryApi;\n public identityApi: IdentityApi;\n private fetchApi: FetchApi;\n\n constructor(options: {\n configApi: Config;\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n fetchApi: FetchApi;\n }) {\n this.configApi = options.configApi;\n this.discoveryApi = options.discoveryApi;\n this.identityApi = options.identityApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getApiOrigin(): Promise<string> {\n return (\n this.configApi.getOptionalString('techdocs.requestUrl') ??\n (await this.discoveryApi.getBaseUrl('techdocs'))\n );\n }\n\n async getStorageUrl(): Promise<string> {\n return (\n this.configApi.getOptionalString('techdocs.storageUrl') ??\n `${await this.discoveryApi.getBaseUrl('techdocs')}/static/docs`\n );\n }\n\n async getBuilder(): Promise<string> {\n return this.configApi.getString('techdocs.builder');\n }\n\n /**\n * Fetch HTML content as text for an individual docs page in an entity's docs site.\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n * @param path - The unique path to an individual docs page e.g. overview/what-is-new\n * @returns HTML content of the docs page as string\n * @throws Throws error when the page is not found.\n */\n async getEntityDocs(entityId: EntityName, path: string): Promise<string> {\n const { kind, namespace, name } = entityId;\n\n const storageUrl = await this.getStorageUrl();\n const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;\n\n const request = await this.fetchApi.fetch(\n `${url.endsWith('/') ? url : `${url}/`}index.html`,\n );\n\n let errorMessage = '';\n switch (request.status) {\n case 404:\n errorMessage = 'Page not found. ';\n // path is empty for the home page of an entity's docs site\n if (!path) {\n errorMessage +=\n 'This could be because there is no index.md file in the root of the docs directory of this repository.';\n }\n throw new NotFoundError(errorMessage);\n case 500:\n errorMessage =\n 'Could not generate documentation or an error in the TechDocs backend. ';\n throw new Error(errorMessage);\n default:\n // Do nothing\n break;\n }\n\n return request.text();\n }\n\n /**\n * Check if docs are on the latest version and trigger rebuild if not\n *\n * @param entityId - Object containing entity data like name, namespace, etc.\n * @param logHandler - Callback to receive log messages from the build process\n * @returns Whether documents are currently synchronized to newest version\n * @throws Throws error on error from sync endpoint in Techdocs Backend\n */\n async syncEntityDocs(\n entityId: EntityName,\n logHandler: (line: string) => void = () => {},\n ): Promise<SyncResult> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;\n const { token } = await this.identityApi.getCredentials();\n\n return new Promise((resolve, reject) => {\n // Polyfill is used to add support for custom headers and auth\n const source = new EventSourcePolyfill(url, {\n withCredentials: true,\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n });\n\n source.addEventListener('log', (e: any) => {\n if (e.data) {\n logHandler(JSON.parse(e.data));\n }\n });\n\n source.addEventListener('finish', (e: any) => {\n let updated: boolean = false;\n\n if (e.data) {\n ({ updated } = JSON.parse(e.data));\n }\n\n resolve(updated ? 'updated' : 'cached');\n });\n\n source.onerror = (e: any) => {\n source.close();\n\n switch (e.status) {\n // the endpoint returned a 404 status\n case 404:\n reject(new NotFoundError(e.message));\n return;\n\n // also handles the event-stream close. the reject is ignored if the Promise was already\n // resolved by a finish event.\n default:\n reject(new Error(e.data));\n return;\n }\n };\n });\n }\n\n async getBaseUrl(\n oldBaseUrl: string,\n entityId: EntityName,\n path: string,\n ): Promise<string> {\n const { kind, namespace, name } = entityId;\n\n const apiOrigin = await this.getApiOrigin();\n const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`;\n\n return new URL(\n oldBaseUrl,\n newBaseUrl.endsWith('/') ? newBaseUrl : `${newBaseUrl}/`,\n ).toString();\n }\n}\n","/*\n * Copyright 2021 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 } from 'react';\nimport { Divider, ListItem, ListItemText, makeStyles } from '@material-ui/core';\nimport { Link } from '@backstage/core-components';\nimport TextTruncate from 'react-text-truncate';\n\nconst useStyles = makeStyles({\n flexContainer: {\n flexWrap: 'wrap',\n },\n itemText: {\n width: '100%',\n marginBottom: '1rem',\n },\n});\n\nexport const DocsResultListItem = ({\n result,\n lineClamp = 5,\n asListItem = true,\n asLink = true,\n title,\n}: {\n result: any;\n lineClamp?: number;\n asListItem?: boolean;\n asLink?: boolean;\n title?: string;\n}) => {\n const classes = useStyles();\n const TextItem = () => (\n <ListItemText\n className={classes.itemText}\n primaryTypographyProps={{ variant: 'h6' }}\n primary={\n title\n ? title\n : `${result.title} | ${result.entityTitle ?? result.name} docs`\n }\n secondary={\n <TextTruncate\n line={lineClamp}\n truncateText=\"…\"\n text={result.text}\n element=\"span\"\n />\n }\n />\n );\n\n const LinkWrapper = ({ children }: PropsWithChildren<{}>) =>\n asLink ? <Link to={result.location}>{children}</Link> : <>{children}</>;\n\n const ListItemWrapper = ({ children }: PropsWithChildren<{}>) =>\n asListItem ? (\n <>\n <ListItem alignItems=\"flex-start\" className={classes.flexContainer}>\n {children}\n </ListItem>\n <Divider component=\"li\" />\n </>\n ) : (\n <>{children}</>\n );\n\n return (\n <LinkWrapper>\n <ListItemWrapper>\n <TextItem />\n </ListItemWrapper>\n </LinkWrapper>\n );\n};\n","/*\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 */\n\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'techdocs:index-page',\n});\n\nexport const rootDocsRouteRef = createRouteRef({\n id: 'techdocs:reader-page',\n params: ['namespace', 'kind', 'name'],\n});\n\nexport const rootCatalogDocsRouteRef = createRouteRef({\n id: 'techdocs:catalog-reader-view',\n});\n","/*\n * Copyright 2021 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 from 'react';\nimport ShareIcon from '@material-ui/icons/Share';\nimport {\n favoriteEntityIcon,\n favoriteEntityTooltip,\n} from '@backstage/plugin-catalog-react';\nimport { DocsTableRow } from './types';\n\nexport function createCopyDocsUrlAction(copyToClipboard: Function) {\n return (row: DocsTableRow) => {\n return {\n icon: () => <ShareIcon fontSize=\"small\" />,\n tooltip: 'Click to copy documentation link to clipboard',\n onClick: () =>\n copyToClipboard(`${window.location.origin}${row.resolved.docsUrl}`),\n };\n };\n}\n\nexport function createStarEntityAction(\n isStarredEntity: Function,\n toggleStarredEntity: Function,\n) {\n return ({ entity }: DocsTableRow) => {\n const isStarred = isStarredEntity(entity);\n return {\n cellStyle: { paddingLeft: '1em' },\n icon: () => favoriteEntityIcon(isStarred),\n tooltip: favoriteEntityTooltip(isStarred),\n onClick: () => toggleStarredEntity(entity),\n };\n };\n}\n","/*\n * Copyright 2021 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 from 'react';\nimport { Link, SubvalueCell, TableColumn } from '@backstage/core-components';\nimport { EntityRefLinks } from '@backstage/plugin-catalog-react';\nimport { Entity } from '@backstage/catalog-model';\nimport { DocsTableRow } from './types';\n\nfunction customTitle(entity: Entity): string {\n return entity.metadata.title || entity.metadata.name;\n}\n\nexport function createNameColumn(): TableColumn<DocsTableRow> {\n return {\n title: 'Document',\n field: 'entity.metadata.name',\n highlight: true,\n render: (row: DocsTableRow) => (\n <SubvalueCell\n value={<Link to={row.resolved.docsUrl}>{customTitle(row.entity)}</Link>}\n subvalue={row.entity.metadata.description}\n />\n ),\n };\n}\n\nexport function createOwnerColumn(): TableColumn<DocsTableRow> {\n return {\n title: 'Owner',\n field: 'resolved.ownedByRelationsTitle',\n render: ({ resolved }) => (\n <EntityRefLinks\n entityRefs={resolved.ownedByRelations}\n defaultKind=\"group\"\n />\n ),\n };\n}\n\nexport function createTypeColumn(): TableColumn<DocsTableRow> {\n return {\n title: 'Type',\n field: 'entity.spec.type',\n };\n}\n","/*\n * Copyright 2021 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 { Config } from '@backstage/config';\n\n// Lower-case entity triplets by default, but allow override.\nexport function toLowerMaybe(str: string, config: Config) {\n return config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n )\n ? str\n : str.toLocaleLowerCase('en-US');\n}\n","/*\n * Copyright 2021 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 from 'react';\nimport useCopyToClipboard from 'react-use/lib/useCopyToClipboard';\n\nimport { useRouteRef, useApi, configApiRef } from '@backstage/core-plugin-api';\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n formatEntityRefTitle,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { rootDocsRouteRef } from '../../routes';\nimport {\n Button,\n EmptyState,\n Table,\n TableColumn,\n TableProps,\n} from '@backstage/core-components';\nimport * as actionFactories from './actions';\nimport * as columnFactories from './columns';\nimport { DocsTableRow } from './types';\nimport { toLowerMaybe } from '../../helpers';\n\nexport const DocsTable = ({\n entities,\n title,\n loading,\n columns,\n actions,\n}: {\n entities: Entity[] | undefined;\n title?: string | undefined;\n loading?: boolean | undefined;\n columns?: TableColumn<DocsTableRow>[];\n actions?: TableProps<DocsTableRow>['actions'];\n}) => {\n const [, copyToClipboard] = useCopyToClipboard();\n const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);\n const config = useApi(configApiRef);\n if (!entities) return null;\n\n const documents = entities.map(entity => {\n const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);\n return {\n entity,\n resolved: {\n docsUrl: getRouteToReaderPageFor({\n namespace: toLowerMaybe(\n entity.metadata.namespace ?? 'default',\n config,\n ),\n kind: toLowerMaybe(entity.kind, config),\n name: toLowerMaybe(entity.metadata.name, config),\n }),\n ownedByRelations,\n ownedByRelationsTitle: ownedByRelations\n .map(r => formatEntityRefTitle(r, { defaultKind: 'group' }))\n .join(', '),\n },\n };\n });\n\n const defaultColumns: TableColumn<DocsTableRow>[] = [\n columnFactories.createNameColumn(),\n columnFactories.createOwnerColumn(),\n columnFactories.createTypeColumn(),\n ];\n\n const defaultActions: TableProps<DocsTableRow>['actions'] = [\n actionFactories.createCopyDocsUrlAction(copyToClipboard),\n ];\n\n return (\n <>\n {loading || (documents && documents.length > 0) ? (\n <Table<DocsTableRow>\n isLoading={loading}\n options={{\n paging: true,\n pageSize: 20,\n search: true,\n actionsColumnIndex: -1,\n }}\n data={documents}\n columns={columns || defaultColumns}\n actions={actions || defaultActions}\n title={\n title\n ? `${title} (${documents.length})`\n : `All (${documents.length})`\n }\n />\n ) : (\n <EmptyState\n missing=\"data\"\n title=\"No documents to show\"\n description=\"Create your own document. Check out our Getting Started Information\"\n action={\n <Button\n color=\"primary\"\n to=\"https://backstage.io/docs/features/techdocs/getting-started\"\n variant=\"contained\"\n >\n DOCS\n </Button>\n }\n />\n )}\n </>\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport useCopyToClipboard from 'react-use/lib/useCopyToClipboard';\nimport { capitalize } from 'lodash';\nimport {\n CodeSnippet,\n TableColumn,\n TableProps,\n WarningPanel,\n} from '@backstage/core-components';\nimport {\n useEntityListProvider,\n useStarredEntities,\n} from '@backstage/plugin-catalog-react';\nimport { DocsTable } from './DocsTable';\nimport * as actionFactories from './actions';\nimport * as columnFactories from './columns';\nimport { DocsTableRow } from './types';\n\nexport const EntityListDocsTable = ({\n columns,\n actions,\n}: {\n columns?: TableColumn<DocsTableRow>[];\n actions?: TableProps<DocsTableRow>['actions'];\n}) => {\n const { loading, error, entities, filters } = useEntityListProvider();\n const { isStarredEntity, toggleStarredEntity } = useStarredEntities();\n const [, copyToClipboard] = useCopyToClipboard();\n\n const title = capitalize(filters.user?.value ?? 'all');\n\n const defaultActions = [\n actionFactories.createCopyDocsUrlAction(copyToClipboard),\n actionFactories.createStarEntityAction(\n isStarredEntity,\n toggleStarredEntity,\n ),\n ];\n\n if (error) {\n return (\n <WarningPanel\n severity=\"error\"\n title=\"Could not load available documentation.\"\n >\n <CodeSnippet language=\"text\" text={error.toString()} />\n </WarningPanel>\n );\n }\n\n return (\n <DocsTable\n title={title}\n entities={entities}\n loading={loading}\n actions={actions || defaultActions}\n columns={columns}\n />\n );\n};\n\nEntityListDocsTable.columns = columnFactories;\nEntityListDocsTable.actions = actionFactories;\n","/*\n * Copyright 2021 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 from 'react';\n\nimport { PageWithHeader } from '@backstage/core-components';\nimport { useApi, configApiRef } from '@backstage/core-plugin-api';\n\ntype Props = {\n children?: React.ReactNode;\n};\n\nexport const TechDocsPageWrapper = ({ children }: Props) => {\n const configApi = useApi(configApiRef);\n const generatedSubtitle = `Documentation available in ${\n configApi.getOptionalString('organization.name') ?? 'Backstage'\n }`;\n\n return (\n <PageWithHeader\n title=\"Documentation\"\n subtitle={generatedSubtitle}\n themeId=\"documentation\"\n >\n {children}\n </PageWithHeader>\n );\n};\n","/*\n * Copyright 2021 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 { useEffect } from 'react';\nimport {\n CATALOG_FILTER_EXISTS,\n DefaultEntityFilters,\n EntityFilter,\n useEntityListProvider,\n} from '@backstage/plugin-catalog-react';\n\nclass TechDocsFilter implements EntityFilter {\n getCatalogFilters(): Record<string, string | symbol | (string | symbol)[]> {\n return {\n 'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS,\n };\n }\n}\n\ntype CustomFilters = DefaultEntityFilters & {\n techdocs?: TechDocsFilter;\n};\n\nexport const TechDocsPicker = () => {\n const { updateFilters } = useEntityListProvider<CustomFilters>();\n\n useEffect(() => {\n updateFilters({\n techdocs: new TechDocsFilter(),\n });\n }, [updateFilters]);\n\n return null;\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport {\n Content,\n ContentHeader,\n SupportButton,\n TableColumn,\n TableProps,\n} from '@backstage/core-components';\nimport {\n EntityListContainer,\n FilterContainer,\n FilteredEntityLayout,\n} from '@backstage/plugin-catalog';\nimport {\n EntityListProvider,\n EntityOwnerPicker,\n EntityTagPicker,\n UserListFilterKind,\n UserListPicker,\n} from '@backstage/plugin-catalog-react';\nimport { EntityListDocsTable } from './EntityListDocsTable';\nimport { TechDocsPageWrapper } from './TechDocsPageWrapper';\nimport { TechDocsPicker } from './TechDocsPicker';\nimport { DocsTableRow } from './types';\n\nexport const DefaultTechDocsHome = ({\n initialFilter = 'all',\n columns,\n actions,\n}: {\n initialFilter?: UserListFilterKind;\n columns?: TableColumn<DocsTableRow>[];\n actions?: TableProps<DocsTableRow>['actions'];\n}) => {\n return (\n <TechDocsPageWrapper>\n <Content>\n <ContentHeader title=\"\">\n <SupportButton>\n Discover documentation in your ecosystem.\n </SupportButton>\n </ContentHeader>\n <EntityListProvider>\n <FilteredEntityLayout>\n <FilterContainer>\n <TechDocsPicker />\n <UserListPicker initialFilter={initialFilter} />\n <EntityOwnerPicker />\n <EntityTagPicker />\n </FilterContainer>\n <EntityListContainer>\n <EntityListDocsTable actions={actions} columns={columns} />\n </EntityListContainer>\n </FilteredEntityLayout>\n </EntityListProvider>\n </Content>\n </TechDocsPageWrapper>\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\n\nimport { Entity } from '@backstage/catalog-model';\nimport { useApi, useRouteRef, configApiRef } from '@backstage/core-plugin-api';\nimport { Card, CardActions, CardContent, CardMedia } from '@material-ui/core';\nimport { rootDocsRouteRef } from '../../routes';\n\nimport {\n Button,\n ItemCardGrid,\n ItemCardHeader,\n} from '@backstage/core-components';\nimport { toLowerMaybe } from '../../helpers';\n\nexport const DocsCardGrid = ({\n entities,\n}: {\n entities: Entity[] | undefined;\n}) => {\n const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);\n const config = useApi(configApiRef);\n if (!entities) return null;\n return (\n <ItemCardGrid data-testid=\"docs-explore\">\n {!entities?.length\n ? null\n : entities.map((entity, index: number) => (\n <Card key={index}>\n <CardMedia>\n <ItemCardHeader\n title={entity.metadata.title ?? entity.metadata.name}\n />\n </CardMedia>\n <CardContent>{entity.metadata.description}</CardContent>\n <CardActions>\n <Button\n to={getRouteToReaderPageFor({\n namespace: toLowerMaybe(\n entity.metadata.namespace ?? 'default',\n config,\n ),\n kind: toLowerMaybe(entity.kind, config),\n name: toLowerMaybe(entity.metadata.name, config),\n })}\n color=\"primary\"\n data-testid=\"read_docs\"\n >\n Read Docs\n </Button>\n </CardActions>\n </Card>\n ))}\n </ItemCardGrid>\n );\n};\n","/*\n * Copyright 2021 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 {\n CodeSnippet,\n Progress,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useEntityListProvider } from '@backstage/plugin-catalog-react';\nimport React from 'react';\nimport { DocsCardGrid } from './DocsCardGrid';\n\nexport const EntityListDocsGrid = () => {\n const { loading, error, entities } = useEntityListProvider();\n\n if (error) {\n return (\n <WarningPanel\n severity=\"error\"\n title=\"Could not load available documentation.\"\n >\n <CodeSnippet language=\"text\" text={error.toString()} />\n </WarningPanel>\n );\n }\n\n if (loading || !entities) {\n return <Progress />;\n }\n\n entities.sort((a, b) =>\n (a.metadata.title ?? a.metadata.name).localeCompare(\n b.metadata.title ?? b.metadata.name,\n ),\n );\n\n return <DocsCardGrid entities={entities} />;\n};\n","/*\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 */\n\nimport { techdocsApiRef, techdocsStorageApiRef } from './api';\nimport { TechDocsClient, TechDocsStorageClient } from './client';\nimport {\n rootDocsRouteRef,\n rootRouteRef,\n rootCatalogDocsRouteRef,\n} from './routes';\nimport {\n configApiRef,\n createApiFactory,\n createComponentExtension,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nexport const techdocsPlugin = createPlugin({\n id: 'techdocs',\n apis: [\n createApiFactory({\n api: techdocsStorageApiRef,\n deps: {\n configApi: configApiRef,\n discoveryApi: discoveryApiRef,\n identityApi: identityApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, discoveryApi, identityApi, fetchApi }) =>\n new TechDocsStorageClient({\n configApi,\n discoveryApi,\n identityApi,\n fetchApi,\n }),\n }),\n createApiFactory({\n api: techdocsApiRef,\n deps: {\n configApi: configApiRef,\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, discoveryApi, fetchApi }) =>\n new TechDocsClient({\n configApi,\n discoveryApi,\n fetchApi,\n }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n docRoot: rootDocsRouteRef,\n entityContent: rootCatalogDocsRouteRef,\n },\n});\n\nexport const TechdocsPage = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechdocsPage',\n component: () => import('./Router').then(m => m.Router),\n mountPoint: rootRouteRef,\n }),\n);\n\nexport const EntityTechdocsContent = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'EntityTechdocsContent',\n component: () => import('./Router').then(m => m.EmbeddedDocsRouter),\n mountPoint: rootCatalogDocsRouteRef,\n }),\n);\n\n// takes a list of entities and renders documentation cards\nexport const DocsCardGrid = techdocsPlugin.provide(\n createComponentExtension({\n name: 'DocsCardGrid',\n component: {\n lazy: () =>\n import('./home/components/DocsCardGrid').then(m => m.DocsCardGrid),\n },\n }),\n);\n\n// takes a list of entities and renders table listing documentation\nexport const DocsTable = techdocsPlugin.provide(\n createComponentExtension({\n name: 'DocsTable',\n component: {\n lazy: () => import('./home/components/DocsTable').then(m => m.DocsTable),\n },\n }),\n);\n\n// takes a custom tabs config object and renders a documentation landing page\nexport const TechDocsCustomHome = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechDocsCustomHome',\n component: () =>\n import('./home/components/TechDocsCustomHome').then(\n m => m.TechDocsCustomHome,\n ),\n mountPoint: rootRouteRef,\n }),\n);\n\nexport const TechDocsIndexPage = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechDocsIndexPage',\n component: () =>\n import('./home/components/TechDocsIndexPage').then(\n m => m.TechDocsIndexPage,\n ),\n mountPoint: rootRouteRef,\n }),\n);\n\nexport const TechDocsReaderPage = techdocsPlugin.provide(\n createRoutableExtension({\n name: 'TechDocsReaderPage',\n component: () =>\n import('./reader/components/TechDocsPage').then(m => m.TechDocsPage),\n mountPoint: rootDocsRouteRef,\n }),\n);\n","/*\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 { EntityName } from '@backstage/catalog-model';\nimport { TechDocsStorageApi } from '../../api';\nimport type { Transformer } from './transformer';\n\ntype AddBaseUrlOptions = {\n techdocsStorageApi: TechDocsStorageApi;\n entityId: EntityName;\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' && attrVal.endsWith('.svg');\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(svgContent)}`,\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","/*\n * Copyright 2021 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 type { Transformer } from './index';\nimport {\n replaceGitHubUrlType,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport parseGitUrl from 'git-url-parse';\n\n// requires repo\nexport const addGitFeedbackLink = (\n scmIntegrationsApi: ScmIntegrationRegistry,\n): Transformer => {\n return dom => {\n // attempting to use selectors that are more likely to be static as MkDocs updates over time\n const sourceAnchor = dom.querySelector(\n '[title=\"Edit this page\"]',\n ) as HTMLAnchorElement;\n\n // don't show if edit link not available in raw page\n if (!sourceAnchor || !sourceAnchor.href) {\n return dom;\n }\n\n const sourceURL = new URL(sourceAnchor.href);\n const integration = scmIntegrationsApi.byUrl(sourceURL);\n\n // don't show if can't identify edit link hostname as a gitlab/github hosting\n if (integration?.type !== 'github' && integration?.type !== 'gitlab') {\n return dom;\n }\n\n // topmost h1 only contains title for whole page\n const title = (dom.querySelector('article>h1') as HTMLElement).childNodes[0]\n .textContent;\n const issueTitle = encodeURIComponent(`Documentation Feedback: ${title}`);\n const issueDesc = encodeURIComponent(\n `Page source:\\n${sourceAnchor.href}\\n\\nFeedback:`,\n );\n\n // Convert GitHub edit url to blob type so it can be parsed by git-url-parse correctly\n const gitUrl =\n integration?.type === 'github'\n ? replaceGitHubUrlType(sourceURL.href, 'blob')\n : sourceURL.href;\n const gitInfo = parseGitUrl(gitUrl);\n const repoPath = `/${gitInfo.organization}/${gitInfo.name}`;\n\n const feedbackLink = sourceAnchor.cloneNode() as HTMLAnchorElement;\n switch (integration?.type) {\n case 'gitlab':\n feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?issue[title]=${issueTitle}&issue[description]=${issueDesc}`;\n break;\n case 'github':\n feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?title=${issueTitle}&body=${issueDesc}`;\n break;\n default:\n return dom;\n }\n ReactDOM.render(React.createElement(FeedbackOutlinedIcon), feedbackLink);\n feedbackLink.style.paddingLeft = '5px';\n feedbackLink.title = 'Leave feedback for this page';\n feedbackLink.id = 'git-feedback-link';\n sourceAnchor?.insertAdjacentElement('beforebegin', feedbackLink);\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\nexport const rewriteDocLinks = (): Transformer => {\n return dom => {\n const updateDom = <T extends Element>(\n list: Array<T>,\n attributeName: string,\n ): void => {\n Array.from(list)\n .filter(elem => elem.hasAttribute(attributeName))\n .forEach((elem: T) => {\n const elemAttribute = elem.getAttribute(attributeName);\n if (elemAttribute) {\n // if link is external, add target to open in a new window or tab\n if (elemAttribute.match(/^https?:\\/\\//i)) {\n elem.setAttribute('target', '_blank');\n }\n\n try {\n const normalizedWindowLocation = normalizeUrl(\n window.location.href,\n );\n elem.setAttribute(\n attributeName,\n new URL(elemAttribute, normalizedWindowLocation).toString(),\n );\n } catch (_e) {\n // Non-parseable links should be re-written as plain text.\n elem.replaceWith(elem.textContent || elemAttribute);\n }\n }\n });\n };\n\n updateDom(Array.from(dom.getElementsByTagName('a')), 'href');\n\n return dom;\n };\n};\n\n/** Make sure that the input url always ends with a '/' */\nexport function normalizeUrl(input: string): string {\n const url = new URL(input);\n\n if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {\n url.pathname += '/';\n }\n\n return url.toString();\n}\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\ntype AddLinkClickListenerOptions = {\n baseUrl: string;\n onClick: (e: MouseEvent, newUrl: string) => void;\n};\n\nexport const addLinkClickListener = ({\n baseUrl,\n onClick,\n}: AddLinkClickListenerOptions): Transformer => {\n return dom => {\n Array.from(dom.getElementsByTagName('a')).forEach(elem => {\n elem.addEventListener('click', (e: MouseEvent) => {\n const target = elem as HTMLAnchorElement;\n const href = target.getAttribute('href');\n\n if (!href) return;\n if (href.startsWith(baseUrl) && !elem.hasAttribute('download')) {\n e.preventDefault();\n onClick(e, href);\n }\n });\n });\n\n return dom;\n };\n};\n","/*\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 type { Transformer } from './transformer';\n\n/**\n * Recreates copy-to-clipboard functionality attached to <code> snippets that\n * is native to mkdocs-material theme.\n */\nexport const copyToClipboard = (): Transformer => {\n return dom => {\n Array.from(dom.querySelectorAll('pre > code')).forEach(codeElem => {\n const button = document.createElement('button');\n const toBeCopied = codeElem.textContent || '';\n button.className = 'md-clipboard md-icon';\n button.title = 'Copy to clipboard';\n button.innerHTML =\n '<svg viewBox=\"0 0 24 24\"><path d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg>';\n button.addEventListener('click', () =>\n navigator.clipboard.writeText(toBeCopied),\n );\n codeElem?.parentElement?.prepend(button);\n });\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\nexport const removeMkdocsHeader = (): Transformer => {\n return dom => {\n // Remove the header\n dom.querySelector('.md-header')?.remove();\n\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\nexport const simplifyMkdocsFooter = (): Transformer => {\n return dom => {\n // Remove mkdocs copyright\n dom.querySelector('.md-footer-copyright')?.remove();\n\n return dom;\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\ntype OnCssReadyOptions = {\n docStorageUrl: string;\n onLoading: (dom: Element) => void;\n onLoaded: (dom: Element) => void;\n};\n\nexport const onCssReady = ({\n docStorageUrl,\n onLoading,\n onLoaded,\n}: OnCssReadyOptions): Transformer => {\n return dom => {\n const cssPages = Array.from(\n dom.querySelectorAll('head > link[rel=\"stylesheet\"]'),\n ).filter(elem => elem.getAttribute('href')?.startsWith(docStorageUrl));\n\n let count = cssPages.length;\n\n if (count > 0) {\n onLoading(dom);\n }\n\n cssPages.forEach(cssPage =>\n cssPage.addEventListener('load', () => {\n count -= 1;\n\n if (count === 0) {\n onLoaded(dom);\n }\n }),\n );\n\n return dom;\n };\n};\n","/*\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 */\n\nconst TECHDOCS_CSS = /main\\.[A-Fa-f0-9]{8}\\.min\\.css$/;\nconst GOOGLE_FONTS = /^https:\\/\\/fonts\\.googleapis\\.com/;\nconst GSTATIC_FONTS = /^https:\\/\\/fonts\\.gstatic\\.com/;\n\nexport const safeLinksHook = (node: Element) => {\n if (node.nodeName && node.nodeName === 'LINK') {\n const href = node.getAttribute('href') || '';\n if (href.match(TECHDOCS_CSS)) {\n node.setAttribute('rel', 'stylesheet');\n }\n if (href.match(GOOGLE_FONTS)) {\n node.setAttribute('rel', 'stylesheet');\n }\n if (href.match(GSTATIC_FONTS)) {\n node.setAttribute('rel', 'preconnect');\n }\n }\n return node;\n};\n\nconst filterIframeHook = (allowedIframeHosts: string[]) => (node: Element) => {\n if (node.nodeName === 'IFRAME') {\n const src = node.getAttribute('src');\n if (!src) {\n node.remove();\n return node;\n }\n\n try {\n const srcUrl = new URL(src);\n const isMatch = allowedIframeHosts.some(host => srcUrl.host === host);\n if (!isMatch) {\n node.remove();\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn(`Invalid iframe src, ${error}`);\n node.remove();\n }\n }\n return node;\n};\n\nimport { Config } from '@backstage/config';\nimport DOMPurify from 'dompurify';\nimport type { Transformer } from './transformer';\n\nexport const sanitizeDOM = (config?: Config): Transformer => {\n const allowedIframeHosts =\n config?.getOptionalStringArray('allowedIframeHosts') || [];\n\n return dom => {\n DOMPurify.addHook('afterSanitizeAttributes', safeLinksHook);\n const addTags = ['link'];\n\n if (allowedIframeHosts.length > 0) {\n DOMPurify.addHook(\n 'beforeSanitizeElements',\n filterIframeHook(allowedIframeHosts),\n );\n addTags.push('iframe');\n }\n\n return DOMPurify.sanitize(dom.innerHTML, {\n ADD_TAGS: addTags,\n FORBID_TAGS: ['style'],\n WHOLE_DOCUMENT: true,\n RETURN_DOM: true,\n });\n };\n};\n","/*\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 */\n\nimport type { Transformer } from './transformer';\n\ntype InjectCssOptions = {\n css: string;\n};\n\nexport const injectCss = ({ css }: InjectCssOptions): Transformer => {\n return dom => {\n dom\n .getElementsByTagName('head')[0]\n .insertAdjacentHTML('beforeend', `<style>${css}</style>`);\n\n return dom;\n };\n};\n","/*\n * Copyright 2021 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 type { Transformer } from './transformer';\n\nexport const scrollIntoAnchor = (): Transformer => {\n return dom => {\n setTimeout(() => {\n // Scroll to the desired anchor on initial navigation\n if (window.location.hash) {\n const hash = window.location.hash.slice(1);\n dom?.querySelector(`#${hash}`)?.scrollIntoView();\n }\n }, 200);\n return dom;\n };\n};\n","/*\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 */\n\nexport type Transformer = (dom: Element) => Element | Promise<Element>;\n\nexport const transform = async (\n html: string | Element,\n transformers: Transformer[],\n): Promise<Element> => {\n let dom: Element;\n\n if (typeof html === 'string') {\n dom = new DOMParser().parseFromString(html, 'text/html').documentElement;\n } else if (html instanceof Element) {\n dom = html;\n } else {\n throw new Error('dom is not a recognized type');\n }\n\n for (const transformer of transformers) {\n dom = await transformer(dom);\n }\n\n return dom;\n};\n","/*\n * Copyright 2021 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 { EntityName } from '@backstage/catalog-model';\nimport { SearchContextProvider, useSearch } from '@backstage/plugin-search';\nimport {\n CircularProgress,\n Grid,\n IconButton,\n InputAdornment,\n TextField,\n} from '@material-ui/core';\nimport SearchIcon from '@material-ui/icons/Search';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport React, { ChangeEvent, useEffect, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport useDebounce from 'react-use/lib/useDebounce';\nimport { DocsResultListItem } from '../../components/DocsResultListItem';\n\ntype TechDocsSearchProps = {\n entityId: EntityName;\n debounceTime?: number;\n};\n\ntype TechDocsDoc = {\n namespace: string;\n kind: string;\n name: string;\n path: string;\n location: string;\n title: string;\n};\n\ntype TechDocsSearchResult = {\n type: string;\n document: TechDocsDoc;\n};\n\nconst TechDocsSearchBar = ({\n entityId,\n debounceTime = 150,\n}: TechDocsSearchProps) => {\n const [open, setOpen] = useState(false);\n const navigate = useNavigate();\n const {\n term,\n setTerm,\n result: { loading, value: searchVal },\n } = useSearch();\n const [options, setOptions] = useState<any[]>([]);\n useEffect(() => {\n let mounted = true;\n\n if (mounted && searchVal) {\n // TODO: Change this into getting only subset of search results from the BE in the first place\n // once pagination is implemented for search engines\n // See: https://github.com/backstage/backstage/issues/6062\n const searchResults = searchVal.results.slice(0, 10);\n setOptions(searchResults);\n }\n return () => {\n mounted = false;\n };\n }, [loading, searchVal]);\n\n const [value, setValue] = useState<string>(term);\n\n useDebounce(() => setTerm(value), debounceTime, [value]);\n\n const handleQuery = (e: ChangeEvent<HTMLInputElement>) => {\n if (!open) {\n setOpen(true);\n }\n setValue(e.target.value);\n };\n\n const handleSelection = (_: any, selection: TechDocsSearchResult | null) => {\n if (selection?.document) {\n const { location } = selection.document;\n navigate(location);\n }\n };\n\n return (\n <Grid item xs={12}>\n <Autocomplete\n data-testid=\"techdocs-search-bar\"\n size=\"small\"\n open={open}\n getOptionLabel={() => ''}\n filterOptions={x => {\n return x; // This is needed to get renderOption to be called after options change. Bug in material-ui?\n }}\n onClose={() => {\n setOpen(false);\n }}\n onFocus={() => {\n setOpen(true);\n }}\n onChange={handleSelection}\n blurOnSelect\n noOptionsText=\"No results found\"\n value={null}\n options={options}\n renderOption={({ document }) => (\n <DocsResultListItem\n result={document}\n lineClamp={3}\n asListItem={false}\n asLink={false}\n title={document.title}\n />\n )}\n loading={loading}\n renderInput={params => (\n <TextField\n {...params}\n data-testid=\"techdocs-search-bar-input\"\n variant=\"outlined\"\n fullWidth\n placeholder={`Search ${entityId.name} docs`}\n value={value}\n onChange={handleQuery}\n InputProps={{\n ...params.InputProps,\n startAdornment: (\n <InputAdornment position=\"start\">\n <IconButton aria-label=\"Query\" disabled>\n <SearchIcon />\n </IconButton>\n </InputAdornment>\n ),\n endAdornment: (\n <React.Fragment>\n {loading ? (\n <CircularProgress color=\"inherit\" size={20} />\n ) : null}\n {params.InputProps.endAdornment}\n </React.Fragment>\n ),\n }}\n />\n )}\n />\n </Grid>\n );\n};\n\nexport const TechDocsSearch = (props: TechDocsSearchProps) => {\n const initialState = {\n term: '',\n types: ['techdocs'],\n pageCursor: '',\n filters: props.entityId,\n };\n return (\n <SearchContextProvider initialState={initialState}>\n <TechDocsSearchBar {...props} />\n </SearchContextProvider>\n );\n};\n","/*\n * Copyright 2021 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 { LogViewer } from '@backstage/core-components';\nimport {\n Button,\n createStyles,\n Drawer,\n Grid,\n IconButton,\n makeStyles,\n Theme,\n Typography,\n} from '@material-ui/core';\nimport Close from '@material-ui/icons/Close';\nimport React, { useState } from 'react';\n\nconst useDrawerStyles = makeStyles((theme: Theme) =>\n createStyles({\n paper: {\n width: '100%',\n [theme.breakpoints.up('sm')]: {\n width: '75%',\n },\n [theme.breakpoints.up('md')]: {\n width: '50%',\n },\n padding: theme.spacing(2.5),\n },\n root: {\n height: '100%',\n overflow: 'hidden',\n },\n logs: {\n background: theme.palette.background.default,\n },\n }),\n);\n\nexport const TechDocsBuildLogsDrawerContent = ({\n buildLog,\n onClose,\n}: {\n buildLog: string[];\n onClose: () => void;\n}) => {\n const classes = useDrawerStyles();\n const logText =\n buildLog.length === 0 ? 'Waiting for logs...' : buildLog.join('\\n');\n return (\n <Grid\n container\n direction=\"column\"\n className={classes.root}\n spacing={0}\n wrap=\"nowrap\"\n >\n <Grid\n item\n container\n justifyContent=\"space-between\"\n alignItems=\"center\"\n spacing={0}\n wrap=\"nowrap\"\n >\n <Typography variant=\"h5\">Build Details</Typography>\n <IconButton\n key=\"dismiss\"\n title=\"Close the drawer\"\n onClick={onClose}\n color=\"inherit\"\n >\n <Close />\n </IconButton>\n </Grid>\n <LogViewer text={logText} classes={{ root: classes.logs }} />\n </Grid>\n );\n};\n\nexport const TechDocsBuildLogs = ({ buildLog }: { buildLog: string[] }) => {\n const classes = useDrawerStyles();\n const [open, setOpen] = useState(false);\n\n return (\n <>\n <Button color=\"inherit\" onClick={() => setOpen(true)}>\n Show Build Logs\n </Button>\n <Drawer\n classes={{ paper: classes.paper }}\n anchor=\"right\"\n open={open}\n onClose={() => setOpen(false)}\n >\n <TechDocsBuildLogsDrawerContent\n buildLog={buildLog}\n onClose={() => setOpen(false)}\n />\n </Drawer>\n </>\n );\n};\n","/*\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 */\n\nimport React from 'react';\nimport { useApi, configApiRef } from '@backstage/core-plugin-api';\nimport { ErrorPage } from '@backstage/core-components';\n\ntype Props = {\n errorMessage?: string;\n};\n\nexport const TechDocsNotFound = ({ errorMessage }: Props) => {\n const techdocsBuilder =\n useApi(configApiRef).getOptionalString('techdocs.builder');\n\n let additionalInfo = '';\n if (techdocsBuilder !== 'local') {\n additionalInfo =\n \"Note that techdocs.builder is not set to 'local' in your config, which means this Backstage app will not \" +\n \"generate docs if they are not found. Make sure the project's docs are generated and published by some external \" +\n \"process (e.g. CI/CD pipeline). Or change techdocs.builder to 'local' to generate docs from this Backstage \" +\n 'instance.';\n }\n\n return (\n <ErrorPage\n status=\"404\"\n statusMessage={errorMessage || 'Documentation not found'}\n additionalInfo={additionalInfo}\n />\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport { Progress } from '@backstage/core-components';\nimport { CircularProgress, Button, makeStyles } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\n\nimport { TechDocsBuildLogs } from './TechDocsBuildLogs';\nimport { TechDocsNotFound } from './TechDocsNotFound';\nimport { useTechDocsReader } from './Reader';\n\nconst useStyles = makeStyles(() => ({\n message: {\n // `word-break: break-word` is deprecated, but gives legacy support to browsers not supporting `overflow-wrap` yet\n // https://developer.mozilla.org/en-US/docs/Web/CSS/word-break\n wordBreak: 'break-word',\n overflowWrap: 'anywhere',\n },\n}));\n\n/**\n * Note: this component is currently being exported so that we can rapidly\n * iterate on alternative <Reader /> implementations that extend core\n * functionality. There is no guarantee that this component will continue to be\n * exported by the package in the future!\n *\n * todo: Make public or stop exporting (ctrl+f \"altReaderExperiments\")\n * @internal\n */\nexport const TechDocsStateIndicator = () => {\n let StateAlert: JSX.Element | null = null;\n const classes = useStyles();\n\n const {\n state,\n contentReload,\n contentErrorMessage,\n syncErrorMessage,\n buildLog,\n } = useTechDocsReader();\n\n const ReaderProgress = state === 'CHECKING' ? <Progress /> : null;\n\n if (state === 'INITIAL_BUILD') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"info\"\n icon={<CircularProgress size=\"24px\" />}\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n >\n Documentation is accessed for the first time and is being prepared. The\n subsequent loads are much faster.\n </Alert>\n );\n }\n\n if (state === 'CONTENT_STALE_REFRESHING') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"info\"\n icon={<CircularProgress size=\"24px\" />}\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n >\n A newer version of this documentation is being prepared and will be\n available shortly.\n </Alert>\n );\n }\n\n if (state === 'CONTENT_STALE_READY') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"success\"\n action={\n <Button color=\"inherit\" onClick={() => contentReload()}>\n Refresh\n </Button>\n }\n >\n A newer version of this documentation is now available, please refresh\n to view.\n </Alert>\n );\n }\n\n if (state === 'CONTENT_STALE_ERROR') {\n StateAlert = (\n <Alert\n variant=\"outlined\"\n severity=\"error\"\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n classes={{ message: classes.message }}\n >\n Building a newer version of this documentation failed.{' '}\n {syncErrorMessage}\n </Alert>\n );\n }\n\n if (state === 'CONTENT_NOT_FOUND') {\n StateAlert = (\n <>\n {syncErrorMessage && (\n <Alert\n variant=\"outlined\"\n severity=\"error\"\n action={<TechDocsBuildLogs buildLog={buildLog} />}\n classes={{ message: classes.message }}\n >\n Building a newer version of this documentation failed.{' '}\n {syncErrorMessage}\n </Alert>\n )}\n <TechDocsNotFound errorMessage={contentErrorMessage} />\n </>\n );\n }\n\n return (\n <>\n {ReaderProgress}\n {StateAlert}\n </>\n );\n};\n","/*\n * Copyright 2021 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 { useApi } from '@backstage/core-plugin-api';\nimport { useMemo, useReducer, useRef } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport useAsyncRetry from 'react-use/lib/useAsyncRetry';\nimport { techdocsStorageApiRef } from '../../api';\n\n/**\n * A state representation that is used to configure the UI of <Reader />\n */\ntype ContentStateTypes =\n /** There is nothing to display but a loading indicator */\n | 'CHECKING'\n\n /** There is no content yet -> present a full screen loading page */\n | 'INITIAL_BUILD'\n\n /** There is content, but the backend is about to update it */\n | 'CONTENT_STALE_REFRESHING'\n\n /** There is content, but after a reload, the content will be different */\n | 'CONTENT_STALE_READY'\n\n /** There is content, the backend tried to update it, but failed */\n | 'CONTENT_STALE_ERROR'\n\n /** There is nothing to see but a \"not found\" page. Is also shown on page load errors */\n | 'CONTENT_NOT_FOUND'\n\n /** There is only the latest and greatest content */\n | 'CONTENT_FRESH';\n\n/**\n * Calculate the state that should be reported to the display component.\n */\nexport function calculateDisplayState({\n contentLoading,\n content,\n activeSyncState,\n}: Pick<\n ReducerState,\n 'contentLoading' | 'content' | 'activeSyncState'\n>): ContentStateTypes {\n // we have nothing to display yet\n if (contentLoading) {\n return 'CHECKING';\n }\n\n // the build is ready, but it triggered a content reload and the content variable is not trusted\n if (activeSyncState === 'BUILD_READY_RELOAD') {\n return 'CHECKING';\n }\n\n // there is no content, but the sync process is still evaluating\n if (!content && activeSyncState === 'CHECKING') {\n return 'CHECKING';\n }\n\n // there is no content yet so we assume that we are building it for the first time\n if (!content && activeSyncState === 'BUILDING') {\n return 'INITIAL_BUILD';\n }\n\n // if there is still no content after building, it might just not exist\n if (!content) {\n return 'CONTENT_NOT_FOUND';\n }\n\n // we are still building, but we already show stale content\n if (activeSyncState === 'BUILDING') {\n return 'CONTENT_STALE_REFRESHING';\n }\n\n // the build is ready, but the content is still stale\n if (activeSyncState === 'BUILD_READY') {\n return 'CONTENT_STALE_READY';\n }\n\n // the build failed, but the content is still stale\n if (activeSyncState === 'ERROR') {\n return 'CONTENT_STALE_ERROR';\n }\n\n // seems like the content is up-to-date (or we don't know yet and the sync process is still evaluating in the background)\n return 'CONTENT_FRESH';\n}\n\n/**\n * The state of the synchronization task. It checks whether the docs are\n * up-to-date. If they aren't, it triggers a build.\n */\ntype SyncStates =\n /** Checking if it should be synced */\n | 'CHECKING'\n\n /** Building the documentation */\n | 'BUILDING'\n\n /** Finished building the documentation */\n | 'BUILD_READY'\n\n /**\n * Finished building the documentation and triggered a content reload.\n * This state is left toward UP_TO_DATE when the content loading has finished.\n */\n | 'BUILD_READY_RELOAD'\n\n /** No need for a sync. The content was already up-to-date. */\n | 'UP_TO_DATE'\n\n /** An error occurred */\n | 'ERROR';\n\ntype ReducerActions =\n | {\n type: 'sync';\n state: SyncStates;\n syncError?: Error;\n }\n | { type: 'contentLoading' }\n | {\n type: 'content';\n path?: string;\n content?: string;\n contentError?: Error;\n }\n | { type: 'buildLog'; log: string };\n\ntype ReducerState = {\n /**\n * The path of the current page\n */\n path: string;\n\n /**\n * The current sync state\n */\n activeSyncState: SyncStates;\n\n /**\n * If true, the content is downloading from the storage.\n */\n contentLoading: boolean;\n /**\n * The content that has been downloaded and should be displayed.\n */\n content?: string;\n\n contentError?: Error;\n syncError?: Error;\n\n /**\n * A list of log messages that were emitted by the build process.\n */\n buildLog: string[];\n};\n\nexport function reducer(\n oldState: ReducerState,\n action: ReducerActions,\n): ReducerState {\n const newState = { ...oldState };\n\n switch (action.type) {\n case 'sync':\n // reset the build log when a new check starts\n if (action.state === 'CHECKING') {\n newState.buildLog = [];\n }\n\n newState.activeSyncState = action.state;\n newState.syncError = action.syncError;\n break;\n\n case 'contentLoading':\n newState.contentLoading = true;\n\n // only reset errors but keep the old content until it is replaced by the 'content' action\n newState.contentError = undefined;\n break;\n\n case 'content':\n // only override the path if it is part of the action\n if (typeof action.path === 'string') {\n newState.path = action.path;\n }\n\n newState.contentLoading = false;\n newState.content = action.content;\n newState.contentError = action.contentError;\n break;\n\n case 'buildLog':\n newState.buildLog = newState.buildLog.concat(action.log);\n break;\n\n default:\n throw new Error();\n }\n\n // a content update loads fresh content so the build is updated to being up-to-date\n if (\n ['BUILD_READY', 'BUILD_READY_RELOAD'].includes(newState.activeSyncState) &&\n ['contentLoading', 'content'].includes(action.type)\n ) {\n newState.activeSyncState = 'UP_TO_DATE';\n newState.buildLog = [];\n }\n\n return newState;\n}\n\nexport function useReaderState(\n kind: string,\n namespace: string,\n name: string,\n path: string,\n): {\n state: ContentStateTypes;\n path: string;\n contentReload: () => void;\n content?: string;\n contentErrorMessage?: string;\n syncErrorMessage?: string;\n buildLog: string[];\n} {\n const [state, dispatch] = useReducer(reducer, {\n activeSyncState: 'CHECKING',\n path,\n contentLoading: true,\n buildLog: [],\n });\n\n const techdocsStorageApi = useApi(techdocsStorageApiRef);\n\n // try to load the content. the function will fire events and we don't care for the return values\n const { retry: contentReload } = useAsyncRetry(async () => {\n dispatch({ type: 'contentLoading' });\n\n try {\n const entityDocs = await techdocsStorageApi.getEntityDocs(\n { kind, namespace, name },\n path,\n );\n\n // update content and path at the same time\n dispatch({ type: 'content', content: entityDocs, path });\n\n return entityDocs;\n } catch (e) {\n dispatch({ type: 'content', contentError: e, path });\n }\n\n return undefined;\n }, [techdocsStorageApi, kind, namespace, name, path]);\n\n // create a ref that holds the latest content. This provides a useAsync hook\n // with the latest content without restarting the useAsync hook.\n const contentRef = useRef<{ content?: string; reload: () => void }>({\n content: undefined,\n reload: () => {},\n });\n contentRef.current = { content: state.content, reload: contentReload };\n\n // try to derive the state. the function will fire events and we don't care for the return values\n useAsync(async () => {\n dispatch({ type: 'sync', state: 'CHECKING' });\n\n // should only switch to BUILDING if the request takes more than 1 seconds\n const buildingTimeout = setTimeout(() => {\n dispatch({ type: 'sync', state: 'BUILDING' });\n }, 1000);\n\n try {\n const result = await techdocsStorageApi.syncEntityDocs(\n {\n kind,\n namespace,\n name,\n },\n log => {\n dispatch({ type: 'buildLog', log });\n },\n );\n\n switch (result) {\n case 'updated':\n // if there was no content prior to building, retry the loading\n if (!contentRef.current.content) {\n contentRef.current.reload();\n dispatch({ type: 'sync', state: 'BUILD_READY_RELOAD' });\n } else {\n dispatch({ type: 'sync', state: 'BUILD_READY' });\n }\n break;\n case 'cached':\n dispatch({ type: 'sync', state: 'UP_TO_DATE' });\n break;\n\n default:\n dispatch({\n type: 'sync',\n state: 'ERROR',\n syncError: new Error('Unexpected return state'),\n });\n break;\n }\n } catch (e) {\n dispatch({ type: 'sync', state: 'ERROR', syncError: e });\n } finally {\n // Cancel the timer that sets the state \"BUILDING\"\n clearTimeout(buildingTimeout);\n }\n }, [kind, name, namespace, techdocsStorageApi, dispatch, contentRef]);\n\n const displayState = useMemo(\n () =>\n calculateDisplayState({\n activeSyncState: state.activeSyncState,\n contentLoading: state.contentLoading,\n content: state.content,\n }),\n [state.activeSyncState, state.content, state.contentLoading],\n );\n\n return {\n state: displayState,\n contentReload,\n path: state.path,\n content: state.content,\n contentErrorMessage: state.contentError?.toString(),\n syncErrorMessage: state.syncError?.toString(),\n buildLog: state.buildLog,\n };\n}\n","/*\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 */\n\nimport React, {\n PropsWithChildren,\n ComponentType,\n createContext,\n useContext,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { Grid, makeStyles, useTheme } from '@material-ui/core';\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { useApi, configApiRef } from '@backstage/core-plugin-api';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport { BackstageTheme } from '@backstage/theme';\nimport { SidebarPinStateContext } from '@backstage/core-components';\n\nimport { techdocsStorageApiRef } from '../../api';\n\nimport {\n addBaseUrl,\n addGitFeedbackLink,\n addLinkClickListener,\n injectCss,\n onCssReady,\n removeMkdocsHeader,\n rewriteDocLinks,\n sanitizeDOM,\n simplifyMkdocsFooter,\n scrollIntoAnchor,\n transform as transformer,\n copyToClipboard,\n} from '../transformers';\n\nimport { TechDocsSearch } from './TechDocsSearch';\nimport { TechDocsStateIndicator } from './TechDocsStateIndicator';\nimport { useReaderState } from './useReaderState';\n\ntype Props = {\n entityRef: EntityName;\n withSearch?: boolean;\n onReady?: () => void;\n};\n\nconst useStyles = makeStyles<BackstageTheme>(theme => ({\n searchBar: {\n marginLeft: '20rem',\n maxWidth: 'calc(100% - 20rem * 2 - 3rem)',\n marginTop: theme.spacing(1),\n '@media screen and (max-width: 76.1875em)': {\n marginLeft: '10rem',\n maxWidth: 'calc(100% - 10rem)',\n },\n },\n}));\n\ntype TechDocsReaderValue = ReturnType<typeof useReaderState>;\n\nconst TechDocsReaderContext = createContext<TechDocsReaderValue>(\n {} as TechDocsReaderValue,\n);\n\nconst TechDocsReaderProvider = ({\n children,\n entityRef,\n}: PropsWithChildren<{ entityRef: EntityName }>) => {\n const { '*': path } = useParams();\n const { kind, namespace, name } = entityRef;\n const value = useReaderState(kind, namespace, name, path);\n return (\n <TechDocsReaderContext.Provider value={value}>\n {children}\n </TechDocsReaderContext.Provider>\n );\n};\n\n/**\n * Note: this HOC is currently being exported so that we can rapidly\n * iterate on alternative <Reader /> implementations that extend core\n * functionality. There is no guarantee that this HOC will continue to be\n * exported by the package in the future!\n *\n * todo: Make public or stop exporting (ctrl+f \"altReaderExperiments\")\n * @internal\n */\nexport const withTechDocsReaderProvider =\n <T extends {}>(Component: ComponentType<T>, entityRef: EntityName) =>\n (props: T) =>\n (\n <TechDocsReaderProvider entityRef={entityRef}>\n <Component {...props} />\n </TechDocsReaderProvider>\n );\n\n/**\n * Note: this hook is currently being exported so that we can rapidly\n * iterate on alternative <Reader /> implementations that extend core\n * functionality. There is no guarantee that this hook will continue to be\n * exported by the package in the future!\n *\n * todo: Make public or stop exporting (ctrl+f \"altReaderExperiments\")\n * @internal\n */\nexport const useTechDocsReader = () => useContext(TechDocsReaderContext);\n\n/**\n * Hook that encapsulates the behavior of getting raw HTML and applying\n * transforms to it in order to make it function at a basic level in the\n * Backstage UI.\n *\n * Note: this hook is currently being exported so that we can rapidly iterate\n * on alternative <Reader /> implementations that extend core functionality.\n * There is no guarantee that this hook will continue to be exported by the\n * package in the future!\n *\n * todo: Make public or stop exporting (see others: \"altReaderExperiments\")\n * @internal\n */\nexport const useTechDocsReaderDom = (entityRef: EntityName): Element | null => {\n const navigate = useNavigate();\n const theme = useTheme<BackstageTheme>();\n const techdocsStorageApi = useApi(techdocsStorageApiRef);\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n const techdocsSanitizer = useApi(configApiRef);\n const { namespace = '', kind = '', name = '' } = entityRef;\n const { state, path, content: rawPage } = useTechDocsReader();\n const isDarkTheme = theme.palette.type === 'dark';\n\n const [sidebars, setSidebars] = useState<HTMLElement[]>();\n const [dom, setDom] = useState<HTMLElement | null>(null);\n\n // sidebar pinned status to be used in computing CSS style injections\n const { isPinned } = useContext(SidebarPinStateContext);\n\n const updateSidebarPosition = useCallback(() => {\n if (!dom || !sidebars) return;\n // set sidebar height so they don't initially render in wrong position\n const mdTabs = dom.querySelector('.md-container > .md-tabs');\n sidebars.forEach(sidebar => {\n const newTop = Math.max(dom.getBoundingClientRect().top, 0);\n sidebar.style.top = mdTabs\n ? `${newTop + mdTabs.getBoundingClientRect().height}px`\n : `${newTop}px`;\n });\n }, [dom, sidebars]);\n\n useEffect(() => {\n updateSidebarPosition();\n window.addEventListener('scroll', updateSidebarPosition, true);\n window.addEventListener('resize', updateSidebarPosition);\n return () => {\n window.removeEventListener('scroll', updateSidebarPosition, true);\n window.removeEventListener('resize', updateSidebarPosition);\n };\n // an update to \"state\" might lead to an updated UI so we include it as a trigger\n }, [updateSidebarPosition, state]);\n\n // dynamically set width of footer to accommodate for pinning of the sidebar\n const updateFooterWidth = useCallback(() => {\n if (!dom) return;\n const footer = dom.querySelector('.md-footer') as HTMLElement;\n if (footer) {\n footer.style.width = `${dom.getBoundingClientRect().width}px`;\n }\n }, [dom]);\n\n useEffect(() => {\n updateFooterWidth();\n window.addEventListener('resize', updateFooterWidth);\n return () => {\n window.removeEventListener('resize', updateFooterWidth);\n };\n });\n\n // a function that performs transformations that are executed prior to adding it to the DOM\n const preRender = useCallback(\n (rawContent: string, contentPath: string) =>\n transformer(rawContent, [\n sanitizeDOM(techdocsSanitizer.getOptionalConfig('techdocs.sanitizer')),\n addBaseUrl({\n techdocsStorageApi,\n entityId: {\n kind,\n name,\n namespace,\n },\n path: contentPath,\n }),\n rewriteDocLinks(),\n removeMkdocsHeader(),\n simplifyMkdocsFooter(),\n addGitFeedbackLink(scmIntegrationsApi),\n injectCss({\n css: `\n body {\n font-family: ${theme.typography.fontFamily};\n --md-text-color: ${theme.palette.text.primary};\n --md-text-link-color: ${theme.palette.primary.main};\n\n --md-code-fg-color: ${theme.palette.text.primary};\n --md-code-bg-color: ${theme.palette.background.paper};\n --md-accent-fg-color: ${theme.palette.primary.main};\n --md-default-fg-color--lightest: ${theme.palette.textVerySubtle};\n }\n .codehilite .gd, .highlight .gd { \n background-color: ${isDarkTheme ? 'rgba(248,81,73,0.65)' : '#fdd'};\n }\n .codehilite .gi, .highlight .gi {\n background-color: ${isDarkTheme ? 'rgba(46,160,67,0.65)' : '#dfd'};\n }\n .highlight .kd {\n color: ${isDarkTheme ? '#4aaaf7' : '#3b78e7'};\n }\n .highlight .k {\n color: ${isDarkTheme ? '#4aaaf7' : '#3b78e7'};\n }\n .highlight .nx {\n color: ${isDarkTheme ? '#ff53a3' : '#ec407a'};\n }\n .highlight .s1 {\n color: ${isDarkTheme ? '#1cad46' : 'rgb(13, 144, 79)'};\n }\n .highlight .kt {\n color: ${isDarkTheme ? '#4aaaf7' : '#3e61a2'};\n }\n .md-main__inner { margin-top: 0; }\n .md-sidebar { position: fixed; bottom: 100px; width: 20rem; }\n .md-sidebar--secondary { right: 2rem; }\n .md-content { margin-bottom: 50px }\n .md-footer { position: fixed; bottom: 0px; }\n .md-footer-nav__link { width: 20rem;}\n .md-content { margin-left: 20rem; max-width: calc(100% - 20rem * 2 - 3rem); }\n .md-typeset { font-size: 1rem; }\n .md-typeset h1, .md-typeset h2, .md-typeset h3 { font-weight: bold; }\n .md-nav { font-size: 1rem; }\n .md-grid { max-width: 90vw; margin: 0 }\n .md-typeset blockquote {\n color: ${theme.palette.textSubtle};\n border-left: 0.2rem solid ${theme.palette.textVerySubtle};\n }\n .md-typeset hr {\n border-bottom: 0.05rem dotted ${theme.palette.textVerySubtle};\n }\n .md-typeset table:not([class]) {\n font-size: 1rem;\n border: 1px solid ${theme.palette.text.primary};\n border-bottom: none;\n border-collapse: collapse;\n }\n .md-typeset table:not([class]) td, .md-typeset table:not([class]) th {\n border-bottom: 1px solid ${theme.palette.text.primary};\n }\n .md-typeset table:not([class]) th { font-weight: bold; }\n .md-typeset .admonition, .md-typeset details {\n font-size: inherit;\n }\n \n /* style the checkmarks of the task list */\n .md-typeset .task-list-control .task-list-indicator::before {\n background-color: ${theme.palette.action.disabledBackground};\n }\n .md-typeset .task-list-control [type=\"checkbox\"]:checked + .task-list-indicator:before {\n background-color: ${theme.palette.success.main};\n }\n /**/\n\n @media screen and (max-width: 76.1875em) {\n .md-nav {\n background-color: ${theme.palette.background.default};\n transition: none !important\n }\n .md-sidebar--secondary { display: none; }\n .md-sidebar--primary { left: ${\n isPinned ? '242px' : '72px'\n }; width: 10rem }\n .md-content { margin-left: 10rem; max-width: calc(100% - 10rem); }\n .md-content__inner { font-size: 0.9rem }\n .md-footer {\n position: static;\n padding-left: 10rem;\n }\n .md-footer-nav__link {\n /* footer links begin to overlap at small sizes without setting width */\n width: 50%;\n }\n .md-nav--primary .md-nav__title {\n white-space: normal;\n height: auto;\n line-height: 1rem;\n cursor: auto;\n }\n .md-nav--primary > .md-nav__title [for=\"none\"] {\n padding-top: 0;\n }\n }\n `,\n }),\n injectCss({\n // Disable CSS animations on link colors as they lead to issues in dark\n // mode. The dark mode color theme is applied later and theirfore there\n // is always an animation from light to dark mode when navigation\n // between pages.\n css: `\n .md-nav__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink {\n transition: none;\n }\n `,\n }),\n injectCss({\n // Properly style code blocks.\n css: `\n .md-typeset pre > code::-webkit-scrollbar-thumb {\n background-color: hsla(0, 0%, 0%, 0.32);\n }\n .md-typeset pre > code::-webkit-scrollbar-thumb:hover {\n background-color: hsla(0, 0%, 0%, 0.87);\n }\n `,\n }),\n injectCss({\n // Admonitions and others are using SVG masks to define icons. These\n // masks are defined as CSS variables.\n // As the MkDocs output is rendered in shadow DOM, the CSS variable\n // definitions on the root selector are not applied. Instead, the have\n // to be applied on :host.\n // As there is no way to transform the served main*.css yet (for\n // example in the backend), we have to copy from main*.css and modify\n // them.\n css: `\n :host {\n --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z\"/></svg>');\n --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M4 5h16v2H4V5m0 4h16v2H4V9m0 4h16v2H4v-2m0 4h10v2H4v-2z\"/></svg>');\n --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10A10 10 0 0012 2z\"/></svg>');\n --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M17.55 11.2c-.23-.3-.5-.56-.76-.82-.65-.6-1.4-1.03-2.03-1.66C13.3 7.26 13 4.85 13.91 3c-.91.23-1.75.75-2.45 1.32-2.54 2.08-3.54 5.75-2.34 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.22.1-.46.04-.64-.12a.83.83 0 01-.15-.17c-1.1-1.43-1.28-3.48-.53-5.12C5.89 10 5 12.3 5.14 14.47c.04.5.1 1 .27 1.5.14.6.4 1.2.72 1.73 1.04 1.73 2.87 2.97 4.84 3.22 2.1.27 4.35-.12 5.96-1.6 1.8-1.66 2.45-4.32 1.5-6.6l-.13-.26c-.2-.46-.47-.87-.8-1.25l.05-.01m-3.1 6.3c-.28.24-.73.5-1.08.6-1.1.4-2.2-.16-2.87-.82 1.19-.28 1.89-1.16 2.09-2.05.17-.8-.14-1.46-.27-2.23-.12-.74-.1-1.37.18-2.06.17.38.37.76.6 1.06.76 1 1.95 1.44 2.2 2.8.04.14.06.28.06.43.03.82-.32 1.72-.92 2.27h.01z\"/></svg>');\n --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>');\n --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M15.07 11.25l-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 00-2-2 2 2 0 00-2 2H8a4 4 0 014-4 4 4 0 014 4 3.2 3.2 0 01-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10c0-5.53-4.5-10-10-10z\"/></svg>');\n --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M13 14h-2v-4h2m0 8h-2v-2h2M1 21h22L12 2 1 21z\"/></svg>');\n --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 2c5.53 0 10 4.47 10 10s-4.47 10-10 10S2 17.53 2 12 6.47 2 12 2m3.59 5L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41 15.59 7z\"/></svg>');\n --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M11.5 20l4.86-9.73H13V4l-5 9.73h3.5V20M12 2c2.75 0 5.1 1 7.05 2.95C21 6.9 22 9.25 22 12s-1 5.1-2.95 7.05C17.1 21 14.75 22 12 22s-5.1-1-7.05-2.95C3 17.1 2 14.75 2 12s1-5.1 2.95-7.05C6.9 3 9.25 2 12 2z\"/></svg>');\n --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 00-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 00-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z\"/></svg>');\n --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 01.75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z\"/></svg>');\n --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z\"/></svg>');\n }\n :host {\n --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.42L5.83 13H21V7h-2z\"/></svg>');\n }\n :host {\n --md-details-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M8.59 16.58L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42z\"/></svg>');\n }\n :host {\n --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/></svg>');\n --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>');\n }\n `,\n }),\n ]),\n [\n techdocsSanitizer,\n techdocsStorageApi,\n kind,\n name,\n namespace,\n scmIntegrationsApi,\n theme.typography.fontFamily,\n theme.palette.text.primary,\n theme.palette.primary.main,\n theme.palette.background.paper,\n theme.palette.background.default,\n theme.palette.textVerySubtle,\n theme.palette.textSubtle,\n theme.palette.action.disabledBackground,\n theme.palette.success.main,\n isDarkTheme,\n isPinned,\n ],\n );\n\n // a function that performs transformations that are executed after adding it to the DOM\n const postRender = useCallback(\n async (transformedElement: Element) =>\n transformer(transformedElement, [\n scrollIntoAnchor(),\n copyToClipboard(),\n addLinkClickListener({\n baseUrl: window.location.origin,\n onClick: (event: MouseEvent, url: string) => {\n // detect if CTRL or META keys are pressed so that links can be opened in a new tab with `window.open`\n const modifierActive = event.ctrlKey || event.metaKey;\n const parsedUrl = new URL(url);\n\n // hash exists when anchor is clicked on secondary sidebar\n if (parsedUrl.hash) {\n if (modifierActive) {\n window.open(`${parsedUrl.pathname}${parsedUrl.hash}`, '_blank');\n } else {\n navigate(`${parsedUrl.pathname}${parsedUrl.hash}`);\n // Scroll to hash if it's on the current page\n transformedElement\n ?.querySelector(`#${parsedUrl.hash.slice(1)}`)\n ?.scrollIntoView();\n }\n } else {\n if (modifierActive) {\n window.open(parsedUrl.pathname, '_blank');\n } else {\n navigate(parsedUrl.pathname);\n // Scroll to top of reader if primary sidebar link is clicked\n transformedElement\n ?.querySelector('.md-content__inner')\n ?.scrollIntoView();\n }\n }\n },\n }),\n onCssReady({\n docStorageUrl: await techdocsStorageApi.getApiOrigin(),\n onLoading: (renderedElement: Element) => {\n (renderedElement as HTMLElement).style.setProperty('opacity', '0');\n },\n onLoaded: (renderedElement: Element) => {\n (renderedElement as HTMLElement).style.removeProperty('opacity');\n // disable MkDocs drawer toggling ('for' attribute => checkbox mechanism)\n renderedElement\n .querySelector('.md-nav__title')\n ?.removeAttribute('for');\n setSidebars(\n Array.from(renderedElement.querySelectorAll('.md-sidebar')),\n );\n },\n }),\n ]),\n [navigate, techdocsStorageApi],\n );\n\n useEffect(() => {\n if (!rawPage) return () => {};\n\n // if false, there is already a newer execution of this effect\n let shouldReplaceContent = true;\n\n // Pre-render\n preRender(rawPage, path).then(async preTransformedDomElement => {\n if (!preTransformedDomElement?.innerHTML) {\n return; // An unexpected error occurred\n }\n\n // don't manipulate the shadow dom if this isn't the latest effect execution\n if (!shouldReplaceContent) {\n return;\n }\n\n // Scroll to top after render\n window.scroll({ top: 0 });\n\n // Post-render\n const postTransformedDomElement = await postRender(\n preTransformedDomElement,\n );\n setDom(postTransformedDomElement as HTMLElement);\n });\n\n // cancel this execution\n return () => {\n shouldReplaceContent = false;\n };\n }, [rawPage, path, preRender, postRender]);\n\n return dom;\n};\n\nconst TheReader = ({\n entityRef,\n onReady = () => {},\n withSearch = true,\n}: Props) => {\n const classes = useStyles();\n const dom = useTechDocsReaderDom(entityRef);\n const shadowDomRef = useRef<HTMLDivElement>(null);\n\n const onReadyRef = useRef<() => void>(onReady);\n useEffect(() => {\n onReadyRef.current = onReady;\n }, [onReady]);\n\n useEffect(() => {\n if (!dom || !shadowDomRef.current) return;\n const shadowDiv = shadowDomRef.current;\n const shadowRoot =\n shadowDiv.shadowRoot || shadowDiv.attachShadow({ mode: 'open' });\n Array.from(shadowRoot.children).forEach(child =>\n shadowRoot.removeChild(child),\n );\n shadowRoot.appendChild(dom);\n onReadyRef.current();\n\n // this hook must ONLY be triggered by a changed dom\n }, [dom]);\n\n return (\n <>\n <TechDocsStateIndicator />\n {withSearch && shadowDomRef?.current?.shadowRoot?.innerHTML && (\n <Grid container className={classes.searchBar}>\n <TechDocsSearch entityId={entityRef} />\n </Grid>\n )}\n <div data-testid=\"techdocs-content-shadowroot\" ref={shadowDomRef} />\n </>\n );\n};\n\nexport const Reader = ({\n entityRef,\n onReady = () => {},\n withSearch = true,\n}: Props) => (\n <TechDocsReaderProvider entityRef={entityRef}>\n <TheReader\n entityRef={entityRef}\n onReady={onReady}\n withSearch={withSearch}\n />\n </TechDocsReaderProvider>\n);\n","/*\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 */\n\nimport { EntityName, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/core-plugin-api';\nimport {\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport CodeIcon from '@material-ui/icons/Code';\nimport React from 'react';\nimport { rootRouteRef } from '../../routes';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';\n\nexport type TechDocsPageHeaderProps = {\n entityRef: EntityName;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n};\n\nexport const TechDocsPageHeader = ({\n entityRef,\n entityMetadata,\n techDocsMetadata,\n}: TechDocsPageHeaderProps) => {\n const { name } = entityRef;\n\n const { site_name: siteName, site_description: siteDescription } =\n techDocsMetadata || {};\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=\"Component\"\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\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 ? <HeaderLabel label=\"Lifecycle\" value={lifecycle} /> : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <a\n href={locationMetadata.target}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <CodeIcon style={{ marginTop: '-25px', fill: '#fff' }} />\n </a>\n }\n />\n ) : null}\n </>\n );\n\n return (\n <Header\n title={siteName ? siteName : '.'}\n pageTitleOverride={siteName || name}\n subtitle={\n siteDescription && siteDescription !== 'None' ? siteDescription : ''\n }\n type=\"Docs\"\n typeLink={docsRootLink}\n >\n {labels}\n </Header>\n );\n};\n","/*\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 */\n\nimport React, { useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport useAsync from 'react-use/lib/useAsync';\nimport { techdocsApiRef } from '../../api';\nimport { TechDocsNotFound } from './TechDocsNotFound';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Page, Content } from '@backstage/core-components';\nimport { Reader } from './Reader';\nimport { TechDocsPageHeader } from './TechDocsPageHeader';\n\nexport const LegacyTechDocsPage = () => {\n const [documentReady, setDocumentReady] = useState<boolean>(false);\n const { namespace, kind, name } = useParams();\n\n const techdocsApi = useApi(techdocsApiRef);\n\n const { value: techdocsMetadataValue } = useAsync(() => {\n if (documentReady) {\n return techdocsApi.getTechDocsMetadata({ kind, namespace, name });\n }\n\n return Promise.resolve(undefined);\n }, [kind, namespace, name, techdocsApi, documentReady]);\n\n const { value: entityMetadataValue, error: entityMetadataError } =\n useAsync(() => {\n return techdocsApi.getEntityMetadata({ kind, namespace, name });\n }, [kind, namespace, name, techdocsApi]);\n\n const onReady = useCallback(() => {\n setDocumentReady(true);\n }, [setDocumentReady]);\n\n if (entityMetadataError) {\n return <TechDocsNotFound errorMessage={entityMetadataError.message} />;\n }\n\n return (\n <Page themeId=\"documentation\">\n <TechDocsPageHeader\n techDocsMetadata={techdocsMetadataValue}\n entityMetadata={entityMetadataValue}\n entityRef={{\n kind,\n namespace,\n name,\n }}\n />\n <Content data-testid=\"techdocs-content\">\n <Reader\n onReady={onReady}\n entityRef={{\n kind,\n namespace,\n name,\n }}\n />\n </Content>\n </Page>\n );\n};\n","/*\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 */\n\nimport React, { useCallback, useState } from 'react';\nimport { useOutlet } from 'react-router';\nimport { useParams } from 'react-router-dom';\nimport useAsync from 'react-use/lib/useAsync';\nimport { techdocsApiRef } from '../../api';\nimport { TechDocsNotFound } from './TechDocsNotFound';\nimport { LegacyTechDocsPage } from './LegacyTechDocsPage';\nimport { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';\nimport { EntityName } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Page } from '@backstage/core-components';\n\nexport type TechDocsPageRenderFunction = ({\n techdocsMetadataValue,\n entityMetadataValue,\n entityRef,\n}: {\n techdocsMetadataValue?: TechDocsMetadata | undefined;\n entityMetadataValue?: TechDocsEntityMetadata | undefined;\n entityRef: EntityName;\n onReady: () => void;\n}) => JSX.Element;\n\nexport type TechDocsPageProps = {\n children?: TechDocsPageRenderFunction | React.ReactNode;\n};\n\nexport const TechDocsPage = ({ children }: TechDocsPageProps) => {\n const outlet = useOutlet();\n\n const [documentReady, setDocumentReady] = useState<boolean>(false);\n const { namespace, kind, name } = useParams();\n\n const techdocsApi = useApi(techdocsApiRef);\n\n const { value: techdocsMetadataValue } = useAsync(() => {\n if (documentReady) {\n return techdocsApi.getTechDocsMetadata({ kind, namespace, name });\n }\n\n return Promise.resolve(undefined);\n }, [kind, namespace, name, techdocsApi, documentReady]);\n\n const { value: entityMetadataValue, error: entityMetadataError } =\n useAsync(() => {\n return techdocsApi.getEntityMetadata({ kind, namespace, name });\n }, [kind, namespace, name, techdocsApi]);\n\n const onReady = useCallback(() => {\n setDocumentReady(true);\n }, [setDocumentReady]);\n\n if (entityMetadataError) {\n return <TechDocsNotFound errorMessage={entityMetadataError.message} />;\n }\n\n if (!children) return outlet || <LegacyTechDocsPage />;\n\n return (\n <Page themeId=\"documentation\">\n {children instanceof Function\n ? children({\n techdocsMetadataValue,\n entityMetadataValue,\n entityRef: { kind, namespace, name },\n onReady,\n })\n : children}\n </Page>\n );\n};\n","/*\n * Copyright 2021 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, { useState } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport { makeStyles } from '@material-ui/core';\nimport { CSSProperties } from '@material-ui/styles';\nimport {\n CATALOG_FILTER_EXISTS,\n catalogApiRef,\n CatalogApi,\n isOwnerOf,\n useOwnUser,\n} from '@backstage/plugin-catalog-react';\nimport { Entity } from '@backstage/catalog-model';\nimport { DocsTable } from './DocsTable';\nimport { DocsCardGrid } from './DocsCardGrid';\nimport { TechDocsPageWrapper } from './TechDocsPageWrapper';\n\nimport {\n CodeSnippet,\n Content,\n HeaderTabs,\n Progress,\n WarningPanel,\n SupportButton,\n ContentHeader,\n} from '@backstage/core-components';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nconst panels = {\n DocsTable: DocsTable,\n DocsCardGrid: DocsCardGrid,\n};\n\nexport type PanelType = 'DocsCardGrid' | 'DocsTable';\n\nexport interface PanelConfig {\n title: string;\n description: string;\n panelType: PanelType;\n panelCSS?: CSSProperties;\n filterPredicate: ((entity: Entity) => boolean) | string;\n}\n\nexport interface TabConfig {\n label: string;\n panels: PanelConfig[];\n}\n\nexport type TabsConfig = TabConfig[];\n\nconst CustomPanel = ({\n config,\n entities,\n index,\n}: {\n config: PanelConfig;\n entities: Entity[];\n index: number;\n}) => {\n const useStyles = makeStyles({\n panelContainer: {\n marginBottom: '2rem',\n ...(config.panelCSS ? config.panelCSS : {}),\n },\n });\n const classes = useStyles();\n const { value: user } = useOwnUser();\n\n const Panel = panels[config.panelType];\n\n const shownEntities = entities.filter(entity => {\n if (config.filterPredicate === 'ownedByUser') {\n if (!user) {\n return false;\n }\n return isOwnerOf(user, entity);\n }\n\n return (\n typeof config.filterPredicate === 'function' &&\n config.filterPredicate(entity)\n );\n });\n\n return (\n <>\n <ContentHeader title={config.title} description={config.description}>\n {index === 0 ? (\n <SupportButton>\n Discover documentation in your ecosystem.\n </SupportButton>\n ) : null}\n </ContentHeader>\n <div className={classes.panelContainer}>\n <Panel data-testid=\"techdocs-custom-panel\" entities={shownEntities} />\n </div>\n </>\n );\n};\n\nexport const TechDocsCustomHome = ({\n tabsConfig,\n}: {\n tabsConfig: TabsConfig;\n}) => {\n const [selectedTab, setSelectedTab] = useState<number>(0);\n const catalogApi: CatalogApi = useApi(catalogApiRef);\n\n const {\n value: entities,\n loading,\n error,\n } = useAsync(async () => {\n const response = await catalogApi.getEntities({\n filter: {\n 'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'apiVersion',\n 'kind',\n 'metadata',\n 'relations',\n 'spec.owner',\n 'spec.type',\n ],\n });\n return response.items.filter((entity: Entity) => {\n return !!entity.metadata.annotations?.['backstage.io/techdocs-ref'];\n });\n });\n\n const currentTabConfig = tabsConfig[selectedTab];\n\n if (loading) {\n return (\n <TechDocsPageWrapper>\n <Content>\n <Progress />\n </Content>\n </TechDocsPageWrapper>\n );\n }\n\n if (error) {\n return (\n <TechDocsPageWrapper>\n <Content>\n <WarningPanel\n severity=\"error\"\n title=\"Could not load available documentation.\"\n >\n <CodeSnippet language=\"text\" text={error.toString()} />\n </WarningPanel>\n </Content>\n </TechDocsPageWrapper>\n );\n }\n\n return (\n <TechDocsPageWrapper>\n <HeaderTabs\n selectedIndex={selectedTab}\n onChange={index => setSelectedTab(index)}\n tabs={tabsConfig.map(({ label }, index) => ({\n id: index.toString(),\n label,\n }))}\n />\n <Content data-testid=\"techdocs-content\">\n {currentTabConfig.panels.map((config, index) => (\n <CustomPanel\n key={index}\n config={config}\n entities={!!entities ? entities : []}\n index={index}\n />\n ))}\n </Content>\n </TechDocsPageWrapper>\n );\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport { PanelType, TechDocsCustomHome } from './TechDocsCustomHome';\n\nexport const LegacyTechDocsHome = () => {\n const tabsConfig = [\n {\n label: 'Overview',\n panels: [\n {\n title: 'Overview',\n description:\n 'Explore your internal technical ecosystem through documentation.',\n panelType: 'DocsCardGrid' as PanelType,\n filterPredicate: () => true,\n },\n // uncomment this if you would like to have a secondary panel with owned documents\n // {\n // title: 'Owned',\n // description: 'Explore your owned internal documentation.',\n // panelType: 'DocsCardGrid' as PanelType,\n // filterPredicate: 'ownedByUser',\n // },\n ],\n },\n {\n label: 'Owned Documents',\n panels: [\n {\n title: 'Owned documents',\n description: 'Access your documentation.',\n panelType: 'DocsTable' as PanelType,\n // ownedByUser filters out entities owned by signed in user\n filterPredicate: 'ownedByUser',\n },\n ],\n },\n ];\n return <TechDocsCustomHome tabsConfig={tabsConfig} />;\n};\n","/*\n * Copyright 2021 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 from 'react';\nimport { useOutlet } from 'react-router';\nimport { LegacyTechDocsHome } from './LegacyTechDocsHome';\n\nexport const TechDocsIndexPage = () => {\n const outlet = useOutlet();\n\n return outlet || <LegacyTechDocsHome />;\n};\n","/*\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 */\n\nimport React from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport { Reader } from './reader';\nimport { toLowerMaybe } from './helpers';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nexport const EntityPageDocs = ({ entity }: { entity: Entity }) => {\n const config = useApi(configApiRef);\n return (\n <Reader\n withSearch={false}\n entityRef={{\n namespace: toLowerMaybe(entity.metadata.namespace ?? 'default', config),\n kind: toLowerMaybe(entity.kind, config),\n name: toLowerMaybe(entity.metadata.name, config),\n }}\n />\n );\n};\n","/*\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 */\n\nimport React from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { Route, Routes } from 'react-router-dom';\nimport { TechDocsIndexPage } from './home/components/TechDocsIndexPage';\nimport { TechDocsPage as TechDocsReaderPage } from './reader/components/TechDocsPage';\nimport { EntityPageDocs } from './EntityPageDocs';\nimport { MissingAnnotationEmptyState } from '@backstage/core-components';\n\nconst TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';\n\nexport const isTechDocsAvailable = (entity: Entity) =>\n Boolean(entity?.metadata?.annotations?.[TECHDOCS_ANNOTATION]);\n\nexport const Router = () => {\n return (\n <Routes>\n <Route path=\"/\" element={<TechDocsIndexPage />} />\n <Route\n path=\"/:namespace/:kind/:name/*\"\n element={<TechDocsReaderPage />}\n />\n </Routes>\n );\n};\n\ntype Props = {\n /** @deprecated The entity is now grabbed from context instead */\n entity?: Entity;\n};\n\nexport const EmbeddedDocsRouter = (_props: Props) => {\n const { entity } = useEntity();\n\n const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION];\n\n if (!projectId) {\n return <MissingAnnotationEmptyState annotation={TECHDOCS_ANNOTATION} />;\n }\n\n return (\n <Routes>\n <Route path=\"/*\" element={<EntityPageDocs entity={entity} />} />\n </Routes>\n );\n};\n"],"names":["useStyles","DocsTable","columnFactories.createNameColumn","columnFactories.createOwnerColumn","columnFactories.createTypeColumn","actionFactories.createCopyDocsUrlAction","actionFactories.createStarEntityAction","DocsCardGrid","TechDocsCustomHome","TechDocsIndexPage","Button","useNavigate","transformer","TechDocsReaderPage"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-techdocs",
|
|
3
3
|
"description": "The Backstage plugin that renders technical documentation for your components",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.4",
|
|
5
5
|
"main": "dist/index.esm.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"main": "dist/index.esm.js",
|
|
12
12
|
"types": "dist/index.d.ts"
|
|
13
13
|
},
|
|
14
|
+
"backstage": {
|
|
15
|
+
"role": "frontend-plugin"
|
|
16
|
+
},
|
|
14
17
|
"homepage": "https://backstage.io",
|
|
15
18
|
"repository": {
|
|
16
19
|
"type": "git",
|
|
@@ -22,27 +25,27 @@
|
|
|
22
25
|
"techdocs"
|
|
23
26
|
],
|
|
24
27
|
"scripts": {
|
|
25
|
-
"build": "backstage-cli
|
|
26
|
-
"start": "backstage-cli
|
|
27
|
-
"lint": "backstage-cli lint",
|
|
28
|
-
"test": "backstage-cli test",
|
|
28
|
+
"build": "backstage-cli package build",
|
|
29
|
+
"start": "backstage-cli package start",
|
|
30
|
+
"lint": "backstage-cli package lint",
|
|
31
|
+
"test": "backstage-cli package test",
|
|
29
32
|
"diff": "backstage-cli plugin:diff",
|
|
30
|
-
"prepack": "backstage-cli prepack",
|
|
31
|
-
"postpack": "backstage-cli postpack",
|
|
32
|
-
"clean": "backstage-cli clean"
|
|
33
|
+
"prepack": "backstage-cli package prepack",
|
|
34
|
+
"postpack": "backstage-cli package postpack",
|
|
35
|
+
"clean": "backstage-cli package clean"
|
|
33
36
|
},
|
|
34
37
|
"dependencies": {
|
|
35
|
-
"@backstage/catalog-model": "^0.
|
|
36
|
-
"@backstage/config": "^0.1.
|
|
37
|
-
"@backstage/core-components": "^0.8.
|
|
38
|
-
"@backstage/core-plugin-api": "^0.6.
|
|
39
|
-
"@backstage/errors": "^0.2.
|
|
40
|
-
"@backstage/integration": "^0.7.
|
|
41
|
-
"@backstage/integration-react": "^0.1.
|
|
42
|
-
"@backstage/plugin-catalog": "^0.
|
|
43
|
-
"@backstage/plugin-catalog-react": "^0.6.
|
|
44
|
-
"@backstage/plugin-search": "^0.
|
|
45
|
-
"@backstage/theme": "^0.2.
|
|
38
|
+
"@backstage/catalog-model": "^0.10.0",
|
|
39
|
+
"@backstage/config": "^0.1.14",
|
|
40
|
+
"@backstage/core-components": "^0.8.9",
|
|
41
|
+
"@backstage/core-plugin-api": "^0.6.1",
|
|
42
|
+
"@backstage/errors": "^0.2.1",
|
|
43
|
+
"@backstage/integration": "^0.7.3",
|
|
44
|
+
"@backstage/integration-react": "^0.1.22",
|
|
45
|
+
"@backstage/plugin-catalog": "^0.8.0",
|
|
46
|
+
"@backstage/plugin-catalog-react": "^0.6.15",
|
|
47
|
+
"@backstage/plugin-search": "^0.7.0",
|
|
48
|
+
"@backstage/theme": "^0.2.15",
|
|
46
49
|
"@material-ui/core": "^4.12.2",
|
|
47
50
|
"@material-ui/icons": "^4.9.1",
|
|
48
51
|
"@material-ui/lab": "4.0.0-alpha.57",
|
|
@@ -62,10 +65,10 @@
|
|
|
62
65
|
"react-dom": "^16.13.1 || ^17.0.0"
|
|
63
66
|
},
|
|
64
67
|
"devDependencies": {
|
|
65
|
-
"@backstage/cli": "^0.
|
|
66
|
-
"@backstage/core-app-api": "^0.5.
|
|
67
|
-
"@backstage/dev-utils": "^0.2.
|
|
68
|
-
"@backstage/test-utils": "^0.2.
|
|
68
|
+
"@backstage/cli": "^0.14.0",
|
|
69
|
+
"@backstage/core-app-api": "^0.5.3",
|
|
70
|
+
"@backstage/dev-utils": "^0.2.22",
|
|
71
|
+
"@backstage/test-utils": "^0.2.5",
|
|
69
72
|
"@testing-library/jest-dom": "^5.10.1",
|
|
70
73
|
"@testing-library/react": "^11.2.5",
|
|
71
74
|
"@testing-library/react-hooks": "^7.0.2",
|
|
@@ -74,7 +77,7 @@
|
|
|
74
77
|
"@types/jest": "^26.0.7",
|
|
75
78
|
"@types/node": "^14.14.32",
|
|
76
79
|
"canvas": "^2.6.1",
|
|
77
|
-
"cross-fetch": "^3.
|
|
80
|
+
"cross-fetch": "^3.1.5",
|
|
78
81
|
"msw": "^0.35.0"
|
|
79
82
|
},
|
|
80
83
|
"files": [
|
|
@@ -82,5 +85,5 @@
|
|
|
82
85
|
"config.d.ts"
|
|
83
86
|
],
|
|
84
87
|
"configSchema": "config.d.ts",
|
|
85
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "4805c3d13ce9bfc369e53c271b1b95e722b3b4dc"
|
|
86
89
|
}
|