@backstage/plugin-techdocs 1.12.3-next.3 → 1.12.4-next.0

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 CHANGED
@@ -1,5 +1,159 @@
1
1
  # @backstage/plugin-techdocs
2
2
 
3
+ ## 1.12.4-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 065e6b9: Excludes svg styling from sanitization
8
+ - b5a8208: Added `TechDocsAddonsBlueprint` extension to allow adding of techdocs addons.
9
+ - ed1cb3e: Adds the page name of techdoc to the document's title.
10
+ - fe4abb8: Updates logic to check for SVG sources when inlining svgs.
11
+ - Updated dependencies
12
+ - @backstage/plugin-search-react@1.8.7-next.0
13
+ - @backstage/plugin-catalog-react@1.16.0-next.0
14
+ - @backstage/plugin-techdocs-react@1.2.15-next.0
15
+ - @backstage/frontend-plugin-api@0.9.6-next.0
16
+ - @backstage/core-compat-api@0.3.7-next.0
17
+ - @backstage/integration-react@1.2.4
18
+
19
+ ## 1.12.3
20
+
21
+ ### Patch Changes
22
+
23
+ - eb3d91a: Use the custom error page if provided for displaying errors instead of the default error page
24
+ - 524f0af: Add missing route ref to the `/alpha` entity content extension.
25
+ - f4be934: Changed the base URL in addLinkClickListener from window.location.origin to app.baseUrl for improved path handling. This fixes an issue where Backstage, when running on a subpath, was unable to handle non-Backstage URLs of the same origin correctly.
26
+ - 1f40e6b: Add optional props to `TechDocCustomHome` to allow for more flexibility:
27
+
28
+ ```tsx
29
+ import { TechDocsCustomHome } from '@backstage/plugin-techdocs';
30
+ //...
31
+
32
+ const options = { emptyRowsWhenPaging: false };
33
+ const linkDestination = (entity: Entity): string | undefined => {
34
+ return entity.metadata.annotations?.['external-docs'];
35
+ };
36
+ const techDocsTabsConfig = [
37
+ {
38
+ label: 'Recommended Documentation',
39
+ panels: [
40
+ {
41
+ title: 'Golden Path',
42
+ description: 'Documentation about standards to follow',
43
+ panelType: 'DocsCardGrid',
44
+ panelProps: { CustomHeader: () => <ContentHeader title='Golden Path'/> },
45
+ filterPredicate: entity =>
46
+ entity?.metadata?.tags?.includes('golden-path') ?? false,
47
+ },
48
+ {
49
+ title: 'Recommended',
50
+ description: 'Useful documentation',
51
+ panelType: 'InfoCardGrid',
52
+ panelProps: {
53
+ CustomHeader: () => <ContentHeader title='Recommended' />
54
+ linkDestination: linkDestination,
55
+ },
56
+ filterPredicate: entity =>
57
+ entity?.metadata?.tags?.includes('recommended') ?? false,
58
+ },
59
+ ],
60
+ },
61
+ {
62
+ label: 'Browse All',
63
+ panels: [
64
+ {
65
+ description: 'Browse all docs',
66
+ filterPredicate: filterEntity,
67
+ panelType: 'TechDocsIndexPage',
68
+ title: 'All',
69
+ panelProps: { PageWrapper: React.Fragment, CustomHeader: React.Fragment, options: options },
70
+ },
71
+ ],
72
+ },
73
+ ];
74
+
75
+ const AppRoutes = () => {
76
+ <FlatRoutes>
77
+ <Route
78
+ path="/docs"
79
+ element={
80
+ <TechDocsCustomHome
81
+ tabsConfig={techDocsTabsConfig}
82
+ filter={{
83
+ kind: ['Location', 'Resource', 'Component'],
84
+ 'metadata.annotations.featured-docs': CATALOG_FILTER_EXISTS,
85
+ }}
86
+ CustomPageWrapper={({ children }: React.PropsWithChildren<{}>) => (<PageWithHeader title="Docs" themeId="documentation">{children}</PageWithHeader>)}
87
+ />
88
+ }
89
+ />
90
+ </FlatRoutes>;
91
+ };
92
+ ```
93
+
94
+ Add new Grid option called `InfoCardGrid` which is a more customizable card option for the Docs grid.
95
+
96
+ ```tsx
97
+ <InfoCardGrid
98
+ entities={entities}
99
+ linkContent="Learn more"
100
+ linkDestination={entity => entity.metadata['external-docs']}
101
+ />
102
+ ```
103
+
104
+ Expose existing `CustomDocsPanel` so that it can be used independently if desired.
105
+
106
+ ```tsx
107
+ const panels: PanelConfig[] = [
108
+ {
109
+ description: '',
110
+ filterPredicate: entity => {},
111
+ panelType: 'InfoCardGrid',
112
+ title: 'Standards',
113
+ panelProps: {
114
+ CustomHeader: () => <ContentHeader title='Recommended' />
115
+ linkDestination: linkDestination,
116
+ },
117
+ },
118
+ {
119
+ description: '',
120
+ filterPredicate: entity => {},
121
+ panelType: 'DocsCardGrid',
122
+ title: 'Contribute',
123
+ },
124
+ ];
125
+ {
126
+ panels.map((config, index) => (
127
+ <CustomDocsPanel
128
+ key={index}
129
+ config={config}
130
+ entities={!!entities ? entities : []}
131
+ index={index}
132
+ />
133
+ ));
134
+ }
135
+ ```
136
+
137
+ - 58ec9e7: Removed older versions of React packages as a preparatory step for upgrading to React 19. This commit does not introduce any functional changes, but removes dependencies on previous React versions, allowing for a cleaner upgrade path in subsequent commits.
138
+ - Updated dependencies
139
+ - @backstage/plugin-search-react@1.8.6
140
+ - @backstage/core-components@0.16.4
141
+ - @backstage/plugin-catalog-react@1.15.2
142
+ - @backstage/frontend-plugin-api@0.9.5
143
+ - @backstage/integration-react@1.2.4
144
+ - @backstage/core-compat-api@0.3.6
145
+ - @backstage/core-plugin-api@1.10.4
146
+ - @backstage/plugin-techdocs-react@1.2.14
147
+ - @backstage/plugin-auth-react@0.1.12
148
+ - @backstage/theme@0.6.4
149
+ - @backstage/catalog-client@1.9.1
150
+ - @backstage/catalog-model@1.7.3
151
+ - @backstage/config@1.3.2
152
+ - @backstage/errors@1.2.7
153
+ - @backstage/integration@1.16.1
154
+ - @backstage/plugin-search-common@1.2.17
155
+ - @backstage/plugin-techdocs-common@0.1.0
156
+
3
157
  ## 1.12.3-next.3
4
158
 
5
159
  ### Patch Changes
@@ -16,6 +16,22 @@ const Router = () => {
16
16
  }
17
17
  ));
18
18
  };
19
+ const TechDocsReaderRouter = (props) => {
20
+ const { children } = props;
21
+ const element = useRoutes([
22
+ {
23
+ path: "*",
24
+ element: /* @__PURE__ */ React.createElement(TechDocsReaderPage, null),
25
+ children: [
26
+ {
27
+ path: "*",
28
+ element: children
29
+ }
30
+ ]
31
+ }
32
+ ]);
33
+ return element;
34
+ };
19
35
  const EmbeddedDocsRouter = (props) => {
20
36
  const { children, emptyState, withSearch = true } = props;
21
37
  const { entity } = useEntity();
@@ -44,5 +60,5 @@ const LegacyEmbeddedDocsRouter = ({
44
60
  return /* @__PURE__ */ React.createElement(EmbeddedDocsRouter, { children, withSearch });
45
61
  };
46
62
 
47
- export { EmbeddedDocsRouter, LegacyEmbeddedDocsRouter, Router, isTechDocsAvailable };
63
+ export { EmbeddedDocsRouter, LegacyEmbeddedDocsRouter, Router, TechDocsReaderRouter, isTechDocsAvailable };
48
64
  //# sourceMappingURL=Router.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Router.esm.js","sources":["../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 React, { PropsWithChildren } from 'react';\nimport { Route, Routes, useRoutes } from 'react-router-dom';\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityPageDocs } from './EntityPageDocs';\nimport { TechDocsIndexPage } from './home/components/TechDocsIndexPage';\nimport { TechDocsReaderPage } from './reader/components/TechDocsReaderPage';\nimport {\n useEntity,\n MissingAnnotationEmptyState,\n} from '@backstage/plugin-catalog-react';\nimport {\n TECHDOCS_ANNOTATION,\n TECHDOCS_EXTERNAL_ANNOTATION,\n} from '@backstage/plugin-techdocs-common';\n\n/**\n * Helper that takes in entity and returns true/false if TechDocs is available for the entity\n *\n * @public\n */\nexport const isTechDocsAvailable = (entity: Entity) =>\n Boolean(entity?.metadata?.annotations?.[TECHDOCS_ANNOTATION]) ||\n Boolean(entity?.metadata?.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION]);\n\n/**\n * Responsible for registering routes for TechDocs, TechDocs Homepage and separate TechDocs page\n *\n * @public\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\nexport const EmbeddedDocsRouter = (\n props: PropsWithChildren<{\n emptyState?: React.ReactElement;\n withSearch?: boolean;\n }>,\n) => {\n const { children, emptyState, withSearch = true } = props;\n const { entity } = useEntity();\n\n // Using objects instead of <Route> elements, otherwise \"outlet\" will be null on sub-pages and add-ons won't render\n const element = useRoutes([\n {\n path: '/*',\n element: <EntityPageDocs entity={entity} withSearch={withSearch} />,\n children: [\n {\n path: '*',\n element: children,\n },\n ],\n },\n ]);\n\n const projectId =\n entity.metadata.annotations?.[TECHDOCS_ANNOTATION] ||\n entity.metadata.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION];\n\n if (!projectId) {\n return (\n emptyState ?? (\n <MissingAnnotationEmptyState annotation={[TECHDOCS_ANNOTATION]} />\n )\n );\n }\n\n return element;\n};\n\n/**\n * Responsible for registering route to view docs on Entity page\n *\n * @public\n */\nexport const LegacyEmbeddedDocsRouter = ({\n children,\n withSearch = true,\n}: PropsWithChildren<{ withSearch?: boolean }>) => {\n // Wrap the Router to avoid exposing the emptyState prop in the non-alpha\n // public API and make it easier for us to change later.\n return <EmbeddedDocsRouter children={children} withSearch={withSearch} />;\n};\n"],"names":[],"mappings":";;;;;;;;AAqCO,MAAM,mBAAsB,GAAA,CAAC,MAClC,KAAA,OAAA,CAAQ,QAAQ,QAAU,EAAA,WAAA,GAAc,mBAAmB,CAAC,KAC5D,OAAQ,CAAA,MAAA,EAAQ,QAAU,EAAA,WAAA,GAAc,4BAA4B,CAAC;AAOhE,MAAM,SAAS,MAAM;AAC1B,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,MACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,KAAM,EAAA,EAAA,IAAA,EAAK,KAAI,OAAS,kBAAA,KAAA,CAAA,aAAA,CAAC,iBAAkB,EAAA,IAAA,CAAA,EAAI,CAChD,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,2BAAA;AAAA,MACL,OAAA,sCAAU,kBAAmB,EAAA,IAAA;AAAA;AAAA,GAEjC,CAAA;AAEJ;AAEa,MAAA,kBAAA,GAAqB,CAChC,KAIG,KAAA;AACH,EAAA,MAAM,EAAE,QAAA,EAAU,UAAY,EAAA,UAAA,GAAa,MAAS,GAAA,KAAA;AACpD,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA;AAG7B,EAAA,MAAM,UAAU,SAAU,CAAA;AAAA,IACxB;AAAA,MACE,IAAM,EAAA,IAAA;AAAA,MACN,OAAS,kBAAA,KAAA,CAAA,aAAA,CAAC,cAAe,EAAA,EAAA,MAAA,EAAgB,UAAwB,EAAA,CAAA;AAAA,MACjE,QAAU,EAAA;AAAA,QACR;AAAA,UACE,IAAM,EAAA,GAAA;AAAA,UACN,OAAS,EAAA;AAAA;AACX;AACF;AACF,GACD,CAAA;AAED,EAAM,MAAA,SAAA,GACJ,OAAO,QAAS,CAAA,WAAA,GAAc,mBAAmB,CACjD,IAAA,MAAA,CAAO,QAAS,CAAA,WAAA,GAAc,4BAA4B,CAAA;AAE5D,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAA,OACE,8BACG,KAAA,CAAA,aAAA,CAAA,2BAAA,EAAA,EAA4B,UAAY,EAAA,CAAC,mBAAmB,CAAG,EAAA,CAAA;AAAA;AAKtE,EAAO,OAAA,OAAA;AACT;AAOO,MAAM,2BAA2B,CAAC;AAAA,EACvC,QAAA;AAAA,EACA,UAAa,GAAA;AACf,CAAmD,KAAA;AAGjD,EAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,kBAAmB,EAAA,EAAA,QAAA,EAAoB,UAAwB,EAAA,CAAA;AACzE;;;;"}
1
+ {"version":3,"file":"Router.esm.js","sources":["../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 React, { PropsWithChildren } from 'react';\nimport { Route, Routes, useRoutes } from 'react-router-dom';\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityPageDocs } from './EntityPageDocs';\nimport { TechDocsIndexPage } from './home/components/TechDocsIndexPage';\nimport { TechDocsReaderPage } from './reader/components/TechDocsReaderPage';\nimport {\n useEntity,\n MissingAnnotationEmptyState,\n} from '@backstage/plugin-catalog-react';\nimport {\n TECHDOCS_ANNOTATION,\n TECHDOCS_EXTERNAL_ANNOTATION,\n} from '@backstage/plugin-techdocs-common';\n\n/**\n * Helper that takes in entity and returns true/false if TechDocs is available for the entity\n *\n * @public\n */\nexport const isTechDocsAvailable = (entity: Entity) =>\n Boolean(entity?.metadata?.annotations?.[TECHDOCS_ANNOTATION]) ||\n Boolean(entity?.metadata?.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION]);\n\n/**\n * Responsible for registering routes for TechDocs, TechDocs Homepage and separate TechDocs page\n *\n * @public\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\nexport const TechDocsReaderRouter = (props: PropsWithChildren) => {\n const { children } = props;\n\n // Using objects instead of <Route> elements, otherwise \"outlet\" will be null on sub-pages and add-ons won't render\n const element = useRoutes([\n {\n path: '*',\n element: <TechDocsReaderPage />,\n children: [\n {\n path: '*',\n element: children,\n },\n ],\n },\n ]);\n\n return element;\n};\n\nexport const EmbeddedDocsRouter = (\n props: PropsWithChildren<{\n emptyState?: React.ReactElement;\n withSearch?: boolean;\n }>,\n) => {\n const { children, emptyState, withSearch = true } = props;\n const { entity } = useEntity();\n\n // Using objects instead of <Route> elements, otherwise \"outlet\" will be null on sub-pages and add-ons won't render\n const element = useRoutes([\n {\n path: '/*',\n element: <EntityPageDocs entity={entity} withSearch={withSearch} />,\n children: [\n {\n path: '*',\n element: children,\n },\n ],\n },\n ]);\n\n const projectId =\n entity.metadata.annotations?.[TECHDOCS_ANNOTATION] ||\n entity.metadata.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION];\n\n if (!projectId) {\n return (\n emptyState ?? (\n <MissingAnnotationEmptyState annotation={[TECHDOCS_ANNOTATION]} />\n )\n );\n }\n return element;\n};\n\n/**\n * Responsible for registering route to view docs on Entity page\n *\n * @public\n */\nexport const LegacyEmbeddedDocsRouter = ({\n children,\n withSearch = true,\n}: PropsWithChildren<{ withSearch?: boolean }>) => {\n // Wrap the Router to avoid exposing the emptyState prop in the non-alpha\n // public API and make it easier for us to change later.\n return <EmbeddedDocsRouter children={children} withSearch={withSearch} />;\n};\n"],"names":[],"mappings":";;;;;;;;AAqCO,MAAM,mBAAsB,GAAA,CAAC,MAClC,KAAA,OAAA,CAAQ,QAAQ,QAAU,EAAA,WAAA,GAAc,mBAAmB,CAAC,KAC5D,OAAQ,CAAA,MAAA,EAAQ,QAAU,EAAA,WAAA,GAAc,4BAA4B,CAAC;AAOhE,MAAM,SAAS,MAAM;AAC1B,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,MACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,KAAM,EAAA,EAAA,IAAA,EAAK,KAAI,OAAS,kBAAA,KAAA,CAAA,aAAA,CAAC,iBAAkB,EAAA,IAAA,CAAA,EAAI,CAChD,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,2BAAA;AAAA,MACL,OAAA,sCAAU,kBAAmB,EAAA,IAAA;AAAA;AAAA,GAEjC,CAAA;AAEJ;AAEa,MAAA,oBAAA,GAAuB,CAAC,KAA6B,KAAA;AAChE,EAAM,MAAA,EAAE,UAAa,GAAA,KAAA;AAGrB,EAAA,MAAM,UAAU,SAAU,CAAA;AAAA,IACxB;AAAA,MACE,IAAM,EAAA,GAAA;AAAA,MACN,OAAA,sCAAU,kBAAmB,EAAA,IAAA,CAAA;AAAA,MAC7B,QAAU,EAAA;AAAA,QACR;AAAA,UACE,IAAM,EAAA,GAAA;AAAA,UACN,OAAS,EAAA;AAAA;AACX;AACF;AACF,GACD,CAAA;AAED,EAAO,OAAA,OAAA;AACT;AAEa,MAAA,kBAAA,GAAqB,CAChC,KAIG,KAAA;AACH,EAAA,MAAM,EAAE,QAAA,EAAU,UAAY,EAAA,UAAA,GAAa,MAAS,GAAA,KAAA;AACpD,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA;AAG7B,EAAA,MAAM,UAAU,SAAU,CAAA;AAAA,IACxB;AAAA,MACE,IAAM,EAAA,IAAA;AAAA,MACN,OAAS,kBAAA,KAAA,CAAA,aAAA,CAAC,cAAe,EAAA,EAAA,MAAA,EAAgB,UAAwB,EAAA,CAAA;AAAA,MACjE,QAAU,EAAA;AAAA,QACR;AAAA,UACE,IAAM,EAAA,GAAA;AAAA,UACN,OAAS,EAAA;AAAA;AACX;AACF;AACF,GACD,CAAA;AAED,EAAM,MAAA,SAAA,GACJ,OAAO,QAAS,CAAA,WAAA,GAAc,mBAAmB,CACjD,IAAA,MAAA,CAAO,QAAS,CAAA,WAAA,GAAc,4BAA4B,CAAA;AAE5D,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAA,OACE,8BACG,KAAA,CAAA,aAAA,CAAA,2BAAA,EAAA,EAA4B,UAAY,EAAA,CAAC,mBAAmB,CAAG,EAAA,CAAA;AAAA;AAItE,EAAO,OAAA,OAAA;AACT;AAOO,MAAM,2BAA2B,CAAC;AAAA,EACvC,QAAA;AAAA,EACA,UAAa,GAAA;AACf,CAAmD,KAAA;AAGjD,EAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,kBAAmB,EAAA,EAAA,QAAA,EAAoB,UAAwB,EAAA,CAAA;AACzE;;;;"}
package/dist/alpha.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _backstage_catalog_model from '@backstage/catalog-model';
2
+ import * as _backstage_plugin_techdocs_react from '@backstage/plugin-techdocs-react';
2
3
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
3
4
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
4
5
  import * as _backstage_plugin_search_react_alpha from '@backstage/plugin-search-react/alpha';
@@ -46,25 +47,6 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
46
47
  }>;
47
48
  entityContent: _backstage_frontend_plugin_api.RouteRef<undefined>;
48
49
  }, {}, {
49
- "page:techdocs": _backstage_frontend_plugin_api.ExtensionDefinition<{
50
- kind: "page";
51
- name: undefined;
52
- config: {
53
- path: string | undefined;
54
- };
55
- configInput: {
56
- path?: string | undefined;
57
- };
58
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
59
- optional: true;
60
- }>;
61
- inputs: {};
62
- params: {
63
- defaultPath: string;
64
- loader: () => Promise<JSX.Element>;
65
- routeRef?: _backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams> | undefined;
66
- };
67
- }>;
68
50
  "nav-item:techdocs": _backstage_frontend_plugin_api.ExtensionDefinition<{
69
51
  kind: "nav-item";
70
52
  name: undefined;
@@ -104,6 +86,25 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
104
86
  factory: _backstage_frontend_plugin_api.AnyApiFactory;
105
87
  };
106
88
  }>;
89
+ "page:techdocs": _backstage_frontend_plugin_api.ExtensionDefinition<{
90
+ kind: "page";
91
+ name: undefined;
92
+ config: {
93
+ path: string | undefined;
94
+ };
95
+ configInput: {
96
+ path?: string | undefined;
97
+ };
98
+ output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
99
+ optional: true;
100
+ }>;
101
+ inputs: {};
102
+ params: {
103
+ defaultPath: string;
104
+ loader: () => Promise<JSX.Element>;
105
+ routeRef?: _backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams> | undefined;
106
+ };
107
+ }>;
107
108
  "search-result-list-item:techdocs": _backstage_frontend_plugin_api.ExtensionDefinition<{
108
109
  config: {
109
110
  title: string | undefined;
@@ -136,8 +137,6 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
136
137
  params: _backstage_plugin_search_react_alpha.SearchResultListItemBlueprintParams;
137
138
  }>;
138
139
  "page:techdocs/reader": _backstage_frontend_plugin_api.ExtensionDefinition<{
139
- kind: "page";
140
- name: "reader";
141
140
  config: {
142
141
  path: string | undefined;
143
142
  };
@@ -147,7 +146,14 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
147
146
  output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
148
147
  optional: true;
149
148
  }>;
150
- inputs: {};
149
+ inputs: {
150
+ addons: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_plugin_techdocs_react.TechDocsAddonOptions, "techdocs.addon", {}>, {
151
+ singleton: false;
152
+ optional: false;
153
+ }>;
154
+ };
155
+ kind: "page";
156
+ name: "reader";
151
157
  params: {
152
158
  defaultPath: string;
153
159
  loader: () => Promise<JSX.Element>;
@@ -159,11 +165,13 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
159
165
  path: string | undefined;
160
166
  title: string | undefined;
161
167
  filter: string | undefined;
168
+ group: string | false | undefined;
162
169
  };
163
170
  configInput: {
164
171
  filter?: string | undefined;
165
172
  title?: string | undefined;
166
173
  path?: string | undefined;
174
+ group?: string | false | undefined;
167
175
  };
168
176
  output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
169
177
  optional: true;
@@ -171,8 +179,14 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
171
179
  optional: true;
172
180
  }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
173
181
  optional: true;
182
+ }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-content-group", {
183
+ optional: true;
174
184
  }>;
175
185
  inputs: {
186
+ addons: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_plugin_techdocs_react.TechDocsAddonOptions, "techdocs.addon", {}>, {
187
+ singleton: false;
188
+ optional: false;
189
+ }>;
176
190
  emptyState: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React__default.JSX.Element, "core.reactElement", {
177
191
  optional: true;
178
192
  }>, {
@@ -186,6 +200,7 @@ declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{
186
200
  loader: () => Promise<JSX.Element>;
187
201
  defaultPath: string;
188
202
  defaultTitle: string;
203
+ defaultGroup?: (string & {}) | "documentation" | "development" | "deployment" | "observability" | undefined;
189
204
  routeRef?: _backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams> | undefined;
190
205
  filter?: string | ((entity: _backstage_catalog_model.Entity) => boolean) | undefined;
191
206
  };
package/dist/alpha.esm.js CHANGED
@@ -5,9 +5,23 @@ import { createApiFactory, configApiRef, discoveryApiRef, fetchApiRef } from '@b
5
5
  import { compatWrapper, convertLegacyRouteRef, convertLegacyRouteRefs } from '@backstage/core-compat-api';
6
6
  import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
7
7
  import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
8
- import { techdocsStorageApiRef, techdocsApiRef } from '@backstage/plugin-techdocs-react';
8
+ import { AddonBlueprint, attachTechDocsAddonComponentData } from '@backstage/plugin-techdocs-react/alpha';
9
9
  import { TechDocsStorageClient, TechDocsClient } from './client.esm.js';
10
10
  import { rootRouteRef, rootDocsRouteRef, rootCatalogDocsRouteRef } from './routes.esm.js';
11
+ import './reader/components/TechDocsReaderProvider.esm.js';
12
+ import { TechDocsReaderLayout } from './reader/components/TechDocsReaderPage/TechDocsReaderPage.esm.js';
13
+ import 'react-helmet';
14
+ import '@material-ui/core/Grid';
15
+ import '@material-ui/lab/Skeleton';
16
+ import '@material-ui/icons/Code';
17
+ import { techdocsStorageApiRef, techdocsApiRef, TechDocsAddons } from '@backstage/plugin-techdocs-react';
18
+ import '@backstage/plugin-catalog-react';
19
+ import '@backstage/catalog-model';
20
+ import '@backstage/core-components';
21
+ import 'lodash/capitalize';
22
+ import 'react-router-dom';
23
+ import './reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.esm.js';
24
+ import './reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.esm.js';
11
25
 
12
26
  const techDocsStorageApi = ApiBlueprint.make({
13
27
  name: "storage",
@@ -74,18 +88,32 @@ const techDocsPage = PageBlueprint.make({
74
88
  )
75
89
  }
76
90
  });
77
- const techDocsReaderPage = PageBlueprint.make({
91
+ const techDocsReaderPage = PageBlueprint.makeWithOverrides({
78
92
  name: "reader",
79
- params: {
80
- defaultPath: "/docs/:namespace/:kind/:name",
81
- routeRef: convertLegacyRouteRef(rootDocsRouteRef),
82
- loader: () => import('./reader/components/TechDocsReaderPage/index.esm.js').then(
83
- (m) => compatWrapper(/* @__PURE__ */ React.createElement(m.TechDocsReaderPage, null))
84
- )
93
+ inputs: {
94
+ addons: createExtensionInput([AddonBlueprint.dataRefs.addon])
95
+ },
96
+ factory(originalFactory, { inputs }) {
97
+ const addons = inputs.addons.map((output) => {
98
+ const options = output.get(AddonBlueprint.dataRefs.addon);
99
+ const Addon = options.component;
100
+ attachTechDocsAddonComponentData(Addon, options);
101
+ return /* @__PURE__ */ React.createElement(Addon, { key: options.name });
102
+ });
103
+ return originalFactory({
104
+ defaultPath: "/docs/:namespace/:kind/:name",
105
+ routeRef: convertLegacyRouteRef(rootDocsRouteRef),
106
+ loader: async () => await import('./Router.esm.js').then(({ TechDocsReaderRouter }) => {
107
+ return compatWrapper(
108
+ /* @__PURE__ */ React.createElement(TechDocsReaderRouter, null, /* @__PURE__ */ React.createElement(TechDocsReaderLayout, null), /* @__PURE__ */ React.createElement(TechDocsAddons, null, addons))
109
+ );
110
+ })
111
+ });
85
112
  }
86
113
  });
87
114
  const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
88
115
  inputs: {
116
+ addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),
89
117
  emptyState: createExtensionInput(
90
118
  [coreExtensionData.reactElement.optional()],
91
119
  {
@@ -100,18 +128,25 @@ const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
100
128
  defaultPath: "docs",
101
129
  defaultTitle: "TechDocs",
102
130
  routeRef: convertLegacyRouteRef(rootCatalogDocsRouteRef),
103
- loader: () => import('./Router.esm.js').then(
104
- ({ EmbeddedDocsRouter }) => compatWrapper(
131
+ loader: () => import('./Router.esm.js').then(({ EmbeddedDocsRouter }) => {
132
+ const addons = context.inputs.addons.map((output) => {
133
+ const options = output.get(AddonBlueprint.dataRefs.addon);
134
+ const Addon = options.component;
135
+ attachTechDocsAddonComponentData(Addon, options);
136
+ return /* @__PURE__ */ React.createElement(Addon, { key: options.name });
137
+ });
138
+ return compatWrapper(
105
139
  /* @__PURE__ */ React.createElement(
106
140
  EmbeddedDocsRouter,
107
141
  {
108
142
  emptyState: context.inputs.emptyState?.get(
109
143
  coreExtensionData.reactElement
110
144
  )
111
- }
145
+ },
146
+ /* @__PURE__ */ React.createElement(TechDocsAddons, null, addons)
112
147
  )
113
- )
114
- )
148
+ );
149
+ })
115
150
  },
116
151
  context
117
152
  );
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.esm.js","sources":["../src/alpha.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 LibraryBooks from '@material-ui/icons/LibraryBooks';\nimport {\n createFrontendPlugin,\n ApiBlueprint,\n PageBlueprint,\n NavItemBlueprint,\n createExtensionInput,\n coreExtensionData,\n createExtension,\n} from '@backstage/frontend-plugin-api';\nimport {\n configApiRef,\n createApiFactory,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\nimport {\n compatWrapper,\n convertLegacyRouteRef,\n convertLegacyRouteRefs,\n} from '@backstage/core-compat-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';\nimport {\n techdocsApiRef,\n techdocsStorageApiRef,\n} from '@backstage/plugin-techdocs-react';\nimport { TechDocsClient, TechDocsStorageClient } from './client';\nimport {\n rootCatalogDocsRouteRef,\n rootDocsRouteRef,\n rootRouteRef,\n} from './routes';\n\n/** @alpha */\nconst techDocsStorageApi = ApiBlueprint.make({\n name: 'storage',\n params: {\n factory: createApiFactory({\n api: techdocsStorageApiRef,\n deps: {\n configApi: configApiRef,\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, discoveryApi, fetchApi }) =>\n new TechDocsStorageClient({\n configApi,\n discoveryApi,\n fetchApi,\n }),\n }),\n },\n});\n\n/** @alpha */\nconst techDocsClientApi = ApiBlueprint.make({\n params: {\n factory: 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});\n\n/** @alpha */\nexport const techDocsSearchResultListItemExtension =\n SearchResultListItemBlueprint.makeWithOverrides({\n config: {\n schema: {\n title: z => z.string().optional(),\n lineClamp: z => z.number().default(5),\n asLink: z => z.boolean().default(true),\n asListItem: z => z.boolean().default(true),\n },\n },\n factory(originalFactory, { config }) {\n return originalFactory({\n predicate: result => result.type === 'techdocs',\n component: async () => {\n const { TechDocsSearchResultListItem } = await import(\n './search/components/TechDocsSearchResultListItem'\n );\n return props =>\n compatWrapper(\n <TechDocsSearchResultListItem {...props} {...config} />,\n );\n },\n });\n },\n });\n\n/**\n * Responsible for rendering the provided router element\n *\n * @alpha\n */\nconst techDocsPage = PageBlueprint.make({\n params: {\n defaultPath: '/docs',\n routeRef: convertLegacyRouteRef(rootRouteRef),\n loader: () =>\n import('./home/components/TechDocsIndexPage').then(m =>\n compatWrapper(<m.TechDocsIndexPage />),\n ),\n },\n});\n\n/**\n * Component responsible for composing a TechDocs reader page experience\n *\n * @alpha\n */\nconst techDocsReaderPage = PageBlueprint.make({\n name: 'reader',\n params: {\n defaultPath: '/docs/:namespace/:kind/:name',\n routeRef: convertLegacyRouteRef(rootDocsRouteRef),\n loader: () =>\n import('./reader/components/TechDocsReaderPage').then(m =>\n compatWrapper(<m.TechDocsReaderPage />),\n ),\n },\n});\n\n/**\n * Component responsible for rendering techdocs on entity pages\n *\n * @alpha\n */\nconst techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({\n inputs: {\n emptyState: createExtensionInput(\n [coreExtensionData.reactElement.optional()],\n {\n singleton: true,\n optional: true,\n },\n ),\n },\n factory(originalFactory, context) {\n return originalFactory(\n {\n defaultPath: 'docs',\n defaultTitle: 'TechDocs',\n routeRef: convertLegacyRouteRef(rootCatalogDocsRouteRef),\n loader: () =>\n import('./Router').then(({ EmbeddedDocsRouter }) =>\n compatWrapper(\n <EmbeddedDocsRouter\n emptyState={context.inputs.emptyState?.get(\n coreExtensionData.reactElement,\n )}\n />,\n ),\n ),\n },\n context,\n );\n },\n});\n\nconst techDocsEntityContentEmptyState = createExtension({\n kind: 'empty-state',\n name: 'entity-content',\n attachTo: { id: 'entity-content:techdocs', input: 'emptyState' },\n output: [coreExtensionData.reactElement.optional()],\n factory: () => [],\n});\n\n/** @alpha */\nconst techDocsNavItem = NavItemBlueprint.make({\n params: {\n icon: LibraryBooks,\n title: 'Docs',\n routeRef: convertLegacyRouteRef(rootRouteRef),\n },\n});\n\n/** @alpha */\nexport default createFrontendPlugin({\n id: 'techdocs',\n extensions: [\n techDocsClientApi,\n techDocsStorageApi,\n techDocsNavItem,\n techDocsPage,\n techDocsReaderPage,\n techDocsEntityContent,\n techDocsEntityContentEmptyState,\n techDocsSearchResultListItemExtension,\n ],\n routes: convertLegacyRouteRefs({\n root: rootRouteRef,\n docRoot: rootDocsRouteRef,\n entityContent: rootCatalogDocsRouteRef,\n }),\n});\n"],"names":[],"mappings":";;;;;;;;;;;AAoDA,MAAM,kBAAA,GAAqB,aAAa,IAAK,CAAA;AAAA,EAC3C,IAAM,EAAA,SAAA;AAAA,EACN,MAAQ,EAAA;AAAA,IACN,SAAS,gBAAiB,CAAA;AAAA,MACxB,GAAK,EAAA,qBAAA;AAAA,MACL,IAAM,EAAA;AAAA,QACJ,SAAW,EAAA,YAAA;AAAA,QACX,YAAc,EAAA,eAAA;AAAA,QACd,QAAU,EAAA;AAAA,OACZ;AAAA,MACA,OAAA,EAAS,CAAC,EAAE,SAAA,EAAW,cAAc,QAAS,EAAA,KAC5C,IAAI,qBAAsB,CAAA;AAAA,QACxB,SAAA;AAAA,QACA,YAAA;AAAA,QACA;AAAA,OACD;AAAA,KACJ;AAAA;AAEL,CAAC,CAAA;AAGD,MAAM,iBAAA,GAAoB,aAAa,IAAK,CAAA;AAAA,EAC1C,MAAQ,EAAA;AAAA,IACN,SAAS,gBAAiB,CAAA;AAAA,MACxB,GAAK,EAAA,cAAA;AAAA,MACL,IAAM,EAAA;AAAA,QACJ,SAAW,EAAA,YAAA;AAAA,QACX,YAAc,EAAA,eAAA;AAAA,QACd,QAAU,EAAA;AAAA,OACZ;AAAA,MACA,OAAA,EAAS,CAAC,EAAE,SAAA,EAAW,cAAc,QAAS,EAAA,KAC5C,IAAI,cAAe,CAAA;AAAA,QACjB,SAAA;AAAA,QACA,YAAA;AAAA,QACA;AAAA,OACD;AAAA,KACJ;AAAA;AAEL,CAAC,CAAA;AAGY,MAAA,qCAAA,GACX,8BAA8B,iBAAkB,CAAA;AAAA,EAC9C,MAAQ,EAAA;AAAA,IACN,MAAQ,EAAA;AAAA,MACN,KAAO,EAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAA,GAAS,QAAS,EAAA;AAAA,MAChC,WAAW,CAAK,CAAA,KAAA,CAAA,CAAE,MAAO,EAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,MACpC,QAAQ,CAAK,CAAA,KAAA,CAAA,CAAE,OAAQ,EAAA,CAAE,QAAQ,IAAI,CAAA;AAAA,MACrC,YAAY,CAAK,CAAA,KAAA,CAAA,CAAE,OAAQ,EAAA,CAAE,QAAQ,IAAI;AAAA;AAC3C,GACF;AAAA,EACA,OAAQ,CAAA,eAAA,EAAiB,EAAE,MAAA,EAAU,EAAA;AACnC,IAAA,OAAO,eAAgB,CAAA;AAAA,MACrB,SAAA,EAAW,CAAU,MAAA,KAAA,MAAA,CAAO,IAAS,KAAA,UAAA;AAAA,MACrC,WAAW,YAAY;AACrB,QAAA,MAAM,EAAE,4BAAA,EAAiC,GAAA,MAAM,OAC7C,yDACF,CAAA;AACA,QAAA,OAAO,CACL,KAAA,KAAA,aAAA;AAAA,0BACG,KAAA,CAAA,aAAA,CAAA,4BAAA,EAAA,EAA8B,GAAG,KAAA,EAAQ,GAAG,MAAQ,EAAA;AAAA,SACvD;AAAA;AACJ,KACD,CAAA;AAAA;AAEL,CAAC;AAOH,MAAM,YAAA,GAAe,cAAc,IAAK,CAAA;AAAA,EACtC,MAAQ,EAAA;AAAA,IACN,WAAa,EAAA,OAAA;AAAA,IACb,QAAA,EAAU,sBAAsB,YAAY,CAAA;AAAA,IAC5C,MAAQ,EAAA,MACN,OAAO,4CAAqC,CAAE,CAAA,IAAA;AAAA,MAAK,OACjD,aAAc,iBAAA,KAAA,CAAA,aAAA,CAAC,CAAE,CAAA,iBAAA,EAAF,IAAoB,CAAE;AAAA;AACvC;AAEN,CAAC,CAAA;AAOD,MAAM,kBAAA,GAAqB,cAAc,IAAK,CAAA;AAAA,EAC5C,IAAM,EAAA,QAAA;AAAA,EACN,MAAQ,EAAA;AAAA,IACN,WAAa,EAAA,8BAAA;AAAA,IACb,QAAA,EAAU,sBAAsB,gBAAgB,CAAA;AAAA,IAChD,MAAQ,EAAA,MACN,OAAO,qDAAwC,CAAE,CAAA,IAAA;AAAA,MAAK,OACpD,aAAc,iBAAA,KAAA,CAAA,aAAA,CAAC,CAAE,CAAA,kBAAA,EAAF,IAAqB,CAAE;AAAA;AACxC;AAEN,CAAC,CAAA;AAOD,MAAM,qBAAA,GAAwB,uBAAuB,iBAAkB,CAAA;AAAA,EACrE,MAAQ,EAAA;AAAA,IACN,UAAY,EAAA,oBAAA;AAAA,MACV,CAAC,iBAAA,CAAkB,YAAa,CAAA,QAAA,EAAU,CAAA;AAAA,MAC1C;AAAA,QACE,SAAW,EAAA,IAAA;AAAA,QACX,QAAU,EAAA;AAAA;AACZ;AACF,GACF;AAAA,EACA,OAAA,CAAQ,iBAAiB,OAAS,EAAA;AAChC,IAAO,OAAA,eAAA;AAAA,MACL;AAAA,QACE,WAAa,EAAA,MAAA;AAAA,QACb,YAAc,EAAA,UAAA;AAAA,QACd,QAAA,EAAU,sBAAsB,uBAAuB,CAAA;AAAA,QACvD,MAAQ,EAAA,MACN,OAAO,iBAAU,CAAE,CAAA,IAAA;AAAA,UAAK,CAAC,EAAE,kBAAA,EACzB,KAAA,aAAA;AAAA,4BACE,KAAA,CAAA,aAAA;AAAA,cAAC,kBAAA;AAAA,cAAA;AAAA,gBACC,UAAA,EAAY,OAAQ,CAAA,MAAA,CAAO,UAAY,EAAA,GAAA;AAAA,kBACrC,iBAAkB,CAAA;AAAA;AACpB;AAAA;AACF;AACF;AACF,OACJ;AAAA,MACA;AAAA,KACF;AAAA;AAEJ,CAAC,CAAA;AAED,MAAM,kCAAkC,eAAgB,CAAA;AAAA,EACtD,IAAM,EAAA,aAAA;AAAA,EACN,IAAM,EAAA,gBAAA;AAAA,EACN,QAAU,EAAA,EAAE,EAAI,EAAA,yBAAA,EAA2B,OAAO,YAAa,EAAA;AAAA,EAC/D,MAAQ,EAAA,CAAC,iBAAkB,CAAA,YAAA,CAAa,UAAU,CAAA;AAAA,EAClD,OAAA,EAAS,MAAM;AACjB,CAAC,CAAA;AAGD,MAAM,eAAA,GAAkB,iBAAiB,IAAK,CAAA;AAAA,EAC5C,MAAQ,EAAA;AAAA,IACN,IAAM,EAAA,YAAA;AAAA,IACN,KAAO,EAAA,MAAA;AAAA,IACP,QAAA,EAAU,sBAAsB,YAAY;AAAA;AAEhD,CAAC,CAAA;AAGD,YAAe,oBAAqB,CAAA;AAAA,EAClC,EAAI,EAAA,UAAA;AAAA,EACJ,UAAY,EAAA;AAAA,IACV,iBAAA;AAAA,IACA,kBAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,kBAAA;AAAA,IACA,qBAAA;AAAA,IACA,+BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,QAAQ,sBAAuB,CAAA;AAAA,IAC7B,IAAM,EAAA,YAAA;AAAA,IACN,OAAS,EAAA,gBAAA;AAAA,IACT,aAAe,EAAA;AAAA,GAChB;AACH,CAAC,CAAA;;;;"}
1
+ {"version":3,"file":"alpha.esm.js","sources":["../src/alpha.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 LibraryBooks from '@material-ui/icons/LibraryBooks';\nimport {\n createFrontendPlugin,\n ApiBlueprint,\n PageBlueprint,\n NavItemBlueprint,\n createExtensionInput,\n coreExtensionData,\n createExtension,\n} from '@backstage/frontend-plugin-api';\nimport {\n configApiRef,\n createApiFactory,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\nimport {\n compatWrapper,\n convertLegacyRouteRef,\n convertLegacyRouteRefs,\n} from '@backstage/core-compat-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';\nimport { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha';\nimport { TechDocsClient, TechDocsStorageClient } from './client';\nimport {\n rootCatalogDocsRouteRef,\n rootDocsRouteRef,\n rootRouteRef,\n} from './routes';\nimport { TechDocsReaderLayout } from './reader';\nimport { attachTechDocsAddonComponentData } from '@backstage/plugin-techdocs-react/alpha';\nimport {\n TechDocsAddons,\n techdocsApiRef,\n techdocsStorageApiRef,\n} from '@backstage/plugin-techdocs-react';\n\n/** @alpha */\nconst techDocsStorageApi = ApiBlueprint.make({\n name: 'storage',\n params: {\n factory: createApiFactory({\n api: techdocsStorageApiRef,\n deps: {\n configApi: configApiRef,\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, discoveryApi, fetchApi }) =>\n new TechDocsStorageClient({\n configApi,\n discoveryApi,\n fetchApi,\n }),\n }),\n },\n});\n\n/** @alpha */\nconst techDocsClientApi = ApiBlueprint.make({\n params: {\n factory: 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});\n\n/** @alpha */\nexport const techDocsSearchResultListItemExtension =\n SearchResultListItemBlueprint.makeWithOverrides({\n config: {\n schema: {\n title: z => z.string().optional(),\n lineClamp: z => z.number().default(5),\n asLink: z => z.boolean().default(true),\n asListItem: z => z.boolean().default(true),\n },\n },\n factory(originalFactory, { config }) {\n return originalFactory({\n predicate: result => result.type === 'techdocs',\n component: async () => {\n const { TechDocsSearchResultListItem } = await import(\n './search/components/TechDocsSearchResultListItem'\n );\n return props =>\n compatWrapper(\n <TechDocsSearchResultListItem {...props} {...config} />,\n );\n },\n });\n },\n });\n\n/**\n * Responsible for rendering the provided router element\n *\n * @alpha\n */\nconst techDocsPage = PageBlueprint.make({\n params: {\n defaultPath: '/docs',\n routeRef: convertLegacyRouteRef(rootRouteRef),\n loader: () =>\n import('./home/components/TechDocsIndexPage').then(m =>\n compatWrapper(<m.TechDocsIndexPage />),\n ),\n },\n});\n\n/**\n * Component responsible for composing a TechDocs reader page experience\n *\n * @alpha\n */\nconst techDocsReaderPage = PageBlueprint.makeWithOverrides({\n name: 'reader',\n inputs: {\n addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),\n },\n factory(originalFactory, { inputs }) {\n const addons = inputs.addons.map(output => {\n const options = output.get(AddonBlueprint.dataRefs.addon);\n const Addon = options.component;\n attachTechDocsAddonComponentData(Addon, options);\n return <Addon key={options.name} />;\n });\n\n return originalFactory({\n defaultPath: '/docs/:namespace/:kind/:name',\n routeRef: convertLegacyRouteRef(rootDocsRouteRef),\n loader: async () =>\n await import('./Router').then(({ TechDocsReaderRouter }) => {\n return compatWrapper(\n <TechDocsReaderRouter>\n <TechDocsReaderLayout />\n <TechDocsAddons>{addons}</TechDocsAddons>\n </TechDocsReaderRouter>,\n );\n }),\n });\n },\n});\n\n/**\n * Component responsible for rendering techdocs on entity pages\n *\n * @alpha\n */\nconst techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({\n inputs: {\n addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),\n emptyState: createExtensionInput(\n [coreExtensionData.reactElement.optional()],\n {\n singleton: true,\n optional: true,\n },\n ),\n },\n factory(originalFactory, context) {\n return originalFactory(\n {\n defaultPath: 'docs',\n defaultTitle: 'TechDocs',\n routeRef: convertLegacyRouteRef(rootCatalogDocsRouteRef),\n loader: () =>\n import('./Router').then(({ EmbeddedDocsRouter }) => {\n const addons = context.inputs.addons.map(output => {\n const options = output.get(AddonBlueprint.dataRefs.addon);\n const Addon = options.component;\n attachTechDocsAddonComponentData(Addon, options);\n return <Addon key={options.name} />;\n });\n return compatWrapper(\n <EmbeddedDocsRouter\n emptyState={context.inputs.emptyState?.get(\n coreExtensionData.reactElement,\n )}\n >\n <TechDocsAddons>{addons}</TechDocsAddons>\n </EmbeddedDocsRouter>,\n );\n }),\n },\n context,\n );\n },\n});\n\nconst techDocsEntityContentEmptyState = createExtension({\n kind: 'empty-state',\n name: 'entity-content',\n attachTo: { id: 'entity-content:techdocs', input: 'emptyState' },\n output: [coreExtensionData.reactElement.optional()],\n factory: () => [],\n});\n\n/** @alpha */\nconst techDocsNavItem = NavItemBlueprint.make({\n params: {\n icon: LibraryBooks,\n title: 'Docs',\n routeRef: convertLegacyRouteRef(rootRouteRef),\n },\n});\n\n/** @alpha */\nexport default createFrontendPlugin({\n id: 'techdocs',\n extensions: [\n techDocsClientApi,\n techDocsStorageApi,\n techDocsNavItem,\n techDocsPage,\n techDocsReaderPage,\n techDocsEntityContent,\n techDocsEntityContentEmptyState,\n techDocsSearchResultListItemExtension,\n ],\n routes: convertLegacyRouteRefs({\n root: rootRouteRef,\n docRoot: rootDocsRouteRef,\n entityContent: rootCatalogDocsRouteRef,\n }),\n});\n"],"names":[],"mappings":"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"}
@@ -145,6 +145,7 @@ class TechDocsStorageClient {
145
145
  onerror(err) {
146
146
  ctrl.abort();
147
147
  reject(err);
148
+ throw err;
148
149
  }
149
150
  });
150
151
  });
@@ -1 +1 @@
1
- {"version":3,"file":"client.esm.js","sources":["../src/client.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CompoundEntityRef } 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 {\n SyncResult,\n TechDocsApi,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n TechDocsStorageApi,\n} from '@backstage/plugin-techdocs-react';\nimport { fetchEventSource } from '@microsoft/fetch-event-source';\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 public async getCookie(): Promise<{ expiresAt: string }> {\n const apiOrigin = await this.getApiOrigin();\n const requestUrl = `${apiOrigin}/cookie`;\n const response = await this.fetchApi.fetch(`${requestUrl}`, {\n credentials: 'include',\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n return await response.json();\n }\n\n async getApiOrigin(): Promise<string> {\n return await this.discoveryApi.getBaseUrl('techdocs');\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(\n entityId: CompoundEntityRef,\n ): 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: CompoundEntityRef,\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 private fetchApi: FetchApi;\n\n constructor(options: {\n configApi: Config;\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n /** @deprecated identityApi is not needed any more */\n identityApi?: IdentityApi;\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 await this.discoveryApi.getBaseUrl('techdocs');\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.getOptionalString('techdocs.builder') || 'local';\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(\n entityId: CompoundEntityRef,\n path: string,\n ): 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: CompoundEntityRef,\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\n return new Promise((resolve, reject) => {\n const ctrl = new AbortController();\n fetchEventSource(url, {\n fetch: this.fetchApi.fetch,\n signal: ctrl.signal,\n onmessage(e: any) {\n if (e.event === 'log') {\n if (e.data) {\n logHandler(JSON.parse(e.data));\n }\n } else if (e.event === 'finish') {\n let updated: boolean = false;\n if (e.data) {\n ({ updated } = JSON.parse(e.data));\n }\n resolve(updated ? 'updated' : 'cached');\n } else if (e.event === 'error') {\n reject(new Error(e.data));\n }\n },\n onerror(err) {\n ctrl.abort();\n reject(err);\n },\n });\n });\n }\n\n async getBaseUrl(\n oldBaseUrl: string,\n entityId: CompoundEntityRef,\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.esm.js","sources":["../src/client.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CompoundEntityRef } 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 {\n SyncResult,\n TechDocsApi,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n TechDocsStorageApi,\n} from '@backstage/plugin-techdocs-react';\nimport { fetchEventSource } from '@microsoft/fetch-event-source';\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 public async getCookie(): Promise<{ expiresAt: string }> {\n const apiOrigin = await this.getApiOrigin();\n const requestUrl = `${apiOrigin}/cookie`;\n const response = await this.fetchApi.fetch(`${requestUrl}`, {\n credentials: 'include',\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n return await response.json();\n }\n\n async getApiOrigin(): Promise<string> {\n return await this.discoveryApi.getBaseUrl('techdocs');\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(\n entityId: CompoundEntityRef,\n ): 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: CompoundEntityRef,\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 private fetchApi: FetchApi;\n\n constructor(options: {\n configApi: Config;\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n /** @deprecated identityApi is not needed any more */\n identityApi?: IdentityApi;\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 await this.discoveryApi.getBaseUrl('techdocs');\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.getOptionalString('techdocs.builder') || 'local';\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(\n entityId: CompoundEntityRef,\n path: string,\n ): 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: CompoundEntityRef,\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\n return new Promise((resolve, reject) => {\n const ctrl = new AbortController();\n fetchEventSource(url, {\n fetch: this.fetchApi.fetch,\n signal: ctrl.signal,\n onmessage(e: any) {\n if (e.event === 'log') {\n if (e.data) {\n logHandler(JSON.parse(e.data));\n }\n } else if (e.event === 'finish') {\n let updated: boolean = false;\n if (e.data) {\n ({ updated } = JSON.parse(e.data));\n }\n resolve(updated ? 'updated' : 'cached');\n } else if (e.event === 'error') {\n reject(new Error(e.data));\n }\n },\n onerror(err) {\n ctrl.abort();\n reject(err);\n throw err; // rethrow to stop the operation\n },\n });\n });\n }\n\n async getBaseUrl(\n oldBaseUrl: string,\n entityId: CompoundEntityRef,\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"],"names":[],"mappings":"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"}
@@ -4,18 +4,21 @@ import Grid from '@material-ui/core/Grid';
4
4
  import Skeleton from '@material-ui/lab/Skeleton';
5
5
  import CodeIcon from '@material-ui/icons/Code';
6
6
  import { useTechDocsAddons, useTechDocsReaderPage, TechDocsAddonLocations } from '@backstage/plugin-techdocs-react';
7
- import { getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
8
- import { RELATION_OWNED_BY } from '@backstage/catalog-model';
7
+ import { entityPresentationApiRef, getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
8
+ import { RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
9
9
  import { HeaderLabel, Header } from '@backstage/core-components';
10
10
  import { useApi, configApiRef, useRouteRef } from '@backstage/core-plugin-api';
11
- import { capitalize } from 'lodash';
11
+ import capitalize from 'lodash/capitalize';
12
12
  import { rootRouteRef } from '../../../routes.esm.js';
13
+ import { useParams } from 'react-router-dom';
13
14
 
14
15
  const skeleton = /* @__PURE__ */ React.createElement(Skeleton, { animation: "wave", variant: "text", height: 40 });
15
16
  const TechDocsReaderPageHeader = (props) => {
16
17
  const { children } = props;
17
18
  const addons = useTechDocsAddons();
18
19
  const configApi = useApi(configApiRef);
20
+ const entityPresentationApi = useApi(entityPresentationApiRef);
21
+ const { "*": path = "" } = useParams();
19
22
  const {
20
23
  title,
21
24
  setTitle,
@@ -37,7 +40,6 @@ const TechDocsReaderPageHeader = (props) => {
37
40
  });
38
41
  }, [metadata, setTitle, setSubtitle]);
39
42
  const appTitle = configApi.getOptional("app.title") || "Backstage";
40
- const tabTitle = [title, subtitle, appTitle].filter(Boolean).join(" | ");
41
43
  const { locationMetadata, spec } = entityMetadata || {};
42
44
  const lifecycle = spec?.lifecycle;
43
45
  const ownedByRelations = entityMetadata ? getEntityRelations(entityMetadata, RELATION_OWNED_BY) : [];
@@ -80,6 +82,15 @@ const TechDocsReaderPageHeader = (props) => {
80
82
  const noEntMetadata = !entityMetadataLoading && entityMetadata === void 0;
81
83
  const noTdMetadata = !metadataLoading && metadata === void 0;
82
84
  if (noEntMetadata || noTdMetadata) return null;
85
+ const stringEntityRef = stringifyEntityRef(entityRef);
86
+ const entityDisplayName = entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;
87
+ const removeTrailingSlash = (str) => str.replace(/\/$/, "");
88
+ const normalizeAndSpace = (str) => str.replace(/-/g, " ").split(" ").map(capitalize).join(" ");
89
+ let techdocsTabTitleItems = [];
90
+ if (path !== "")
91
+ techdocsTabTitleItems = removeTrailingSlash(path).split("/").slice(0, 3).map(normalizeAndSpace);
92
+ const tabTitleItems = [appTitle, entityDisplayName, ...techdocsTabTitleItems];
93
+ const tabTitle = tabTitleItems.join(" | ");
83
94
  return /* @__PURE__ */ React.createElement(
84
95
  Header,
85
96
  {
@@ -1 +1 @@
1
- {"version":3,"file":"TechDocsReaderPageHeader.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useEffect } from 'react';\nimport Helmet from 'react-helmet';\n\nimport Grid from '@material-ui/core/Grid';\nimport Skeleton from '@material-ui/lab/Skeleton';\nimport CodeIcon from '@material-ui/icons/Code';\n\nimport {\n TechDocsAddonLocations as locations,\n useTechDocsAddons,\n useTechDocsReaderPage,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n} from '@backstage/plugin-techdocs-react';\nimport {\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { RELATION_OWNED_BY, CompoundEntityRef } from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { capitalize } from 'lodash';\n\nimport { rootRouteRef } from '../../../routes';\n\nconst skeleton = <Skeleton animation=\"wave\" variant=\"text\" height={40} />;\n\n/**\n * Props for {@link TechDocsReaderPageHeader}\n *\n * @public\n * @deprecated No need to pass down properties anymore. The component consumes data from `TechDocsReaderPageContext` instead. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom header.\n */\nexport type TechDocsReaderPageHeaderProps = PropsWithChildren<{\n entityRef?: CompoundEntityRef;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n}>;\n\n/**\n * Renders the reader page header.\n * This component does not accept props, please use\n * the Tech Docs add-ons to customize it\n * @public\n */\nexport const TechDocsReaderPageHeader = (\n props: TechDocsReaderPageHeaderProps,\n) => {\n const { children } = props;\n const addons = useTechDocsAddons();\n const configApi = useApi(configApiRef);\n\n const {\n title,\n setTitle,\n subtitle,\n setSubtitle,\n entityRef,\n metadata: { value: metadata, loading: metadataLoading },\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n } = useTechDocsReaderPage();\n\n useEffect(() => {\n if (!metadata) return;\n setTitle(metadata.site_name);\n setSubtitle(() => {\n let { site_description } = metadata;\n if (!site_description || site_description === 'None') {\n site_description = '';\n }\n return site_description;\n });\n }, [metadata, setTitle, setSubtitle]);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n const tabTitle = [title, subtitle, appTitle].filter(Boolean).join(' | ');\n\n const { locationMetadata, spec } = entityMetadata || {};\n const lifecycle = spec?.lifecycle;\n\n const ownedByRelations = entityMetadata\n ? getEntityRelations(entityMetadata, RELATION_OWNED_BY)\n : [];\n\n const docsRootLink = useRouteRef(rootRouteRef)();\n\n const labels = (\n <>\n <HeaderLabel\n label={capitalize(entityMetadata?.kind || 'entity')}\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\n title={entityMetadata?.metadata.title}\n defaultKind=\"Component\"\n />\n }\n />\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label=\"Owner\"\n value={\n <EntityRefLinks\n color=\"inherit\"\n entityRefs={ownedByRelations}\n defaultKind=\"group\"\n />\n }\n />\n )}\n {lifecycle ? (\n <HeaderLabel label=\"Lifecycle\" value={String(lifecycle)} />\n ) : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <Grid container direction=\"column\" alignItems=\"center\">\n <Grid style={{ padding: 0 }} item>\n <CodeIcon style={{ marginTop: '-25px' }} />\n </Grid>\n <Grid style={{ padding: 0 }} item>\n Source\n </Grid>\n </Grid>\n }\n url={locationMetadata.target}\n />\n ) : null}\n </>\n );\n\n // If there is no entity or techdocs metadata, there's no reason to show the\n // header (hides the header on 404 error pages).\n const noEntMetadata = !entityMetadataLoading && entityMetadata === undefined;\n const noTdMetadata = !metadataLoading && metadata === undefined;\n if (noEntMetadata || noTdMetadata) return null;\n\n return (\n <Header\n type=\"Documentation\"\n typeLink={docsRootLink}\n title={title || skeleton}\n subtitle={subtitle === '' ? undefined : subtitle || skeleton}\n >\n <Helmet titleTemplate=\"%s\">\n <title>{tabTitle}</title>\n </Helmet>\n {labels}\n {children}\n {addons.renderComponentsByLocation(locations.Header)}\n </Header>\n );\n};\n"],"names":["locations"],"mappings":"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"}
1
+ {"version":3,"file":"TechDocsReaderPageHeader.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useEffect } from 'react';\nimport Helmet from 'react-helmet';\n\nimport Grid from '@material-ui/core/Grid';\nimport Skeleton from '@material-ui/lab/Skeleton';\nimport CodeIcon from '@material-ui/icons/Code';\n\nimport {\n TechDocsAddonLocations as locations,\n useTechDocsAddons,\n useTechDocsReaderPage,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n} from '@backstage/plugin-techdocs-react';\nimport {\n entityPresentationApiRef,\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport {\n RELATION_OWNED_BY,\n CompoundEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport capitalize from 'lodash/capitalize';\n\nimport { rootRouteRef } from '../../../routes';\nimport { useParams } from 'react-router-dom';\n\nconst skeleton = <Skeleton animation=\"wave\" variant=\"text\" height={40} />;\n\n/**\n * Props for {@link TechDocsReaderPageHeader}\n *\n * @public\n * @deprecated No need to pass down properties anymore. The component consumes data from `TechDocsReaderPageContext` instead. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom header.\n */\nexport type TechDocsReaderPageHeaderProps = PropsWithChildren<{\n entityRef?: CompoundEntityRef;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n}>;\n\n/**\n * Renders the reader page header.\n * This component does not accept props, please use\n * the Tech Docs add-ons to customize it\n * @public\n */\nexport const TechDocsReaderPageHeader = (\n props: TechDocsReaderPageHeaderProps,\n) => {\n const { children } = props;\n const addons = useTechDocsAddons();\n const configApi = useApi(configApiRef);\n\n const entityPresentationApi = useApi(entityPresentationApiRef);\n const { '*': path = '' } = useParams();\n\n const {\n title,\n setTitle,\n subtitle,\n setSubtitle,\n entityRef,\n metadata: { value: metadata, loading: metadataLoading },\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n } = useTechDocsReaderPage();\n\n useEffect(() => {\n if (!metadata) return;\n setTitle(metadata.site_name);\n setSubtitle(() => {\n let { site_description } = metadata;\n if (!site_description || site_description === 'None') {\n site_description = '';\n }\n return site_description;\n });\n }, [metadata, setTitle, setSubtitle]);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n const { locationMetadata, spec } = entityMetadata || {};\n const lifecycle = spec?.lifecycle;\n\n const ownedByRelations = entityMetadata\n ? getEntityRelations(entityMetadata, RELATION_OWNED_BY)\n : [];\n\n const docsRootLink = useRouteRef(rootRouteRef)();\n\n const labels = (\n <>\n <HeaderLabel\n label={capitalize(entityMetadata?.kind || 'entity')}\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\n title={entityMetadata?.metadata.title}\n defaultKind=\"Component\"\n />\n }\n />\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label=\"Owner\"\n value={\n <EntityRefLinks\n color=\"inherit\"\n entityRefs={ownedByRelations}\n defaultKind=\"group\"\n />\n }\n />\n )}\n {lifecycle ? (\n <HeaderLabel label=\"Lifecycle\" value={String(lifecycle)} />\n ) : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <Grid container direction=\"column\" alignItems=\"center\">\n <Grid style={{ padding: 0 }} item>\n <CodeIcon style={{ marginTop: '-25px' }} />\n </Grid>\n <Grid style={{ padding: 0 }} item>\n Source\n </Grid>\n </Grid>\n }\n url={locationMetadata.target}\n />\n ) : null}\n </>\n );\n\n // If there is no entity or techdocs metadata, there's no reason to show the\n // header (hides the header on 404 error pages).\n const noEntMetadata = !entityMetadataLoading && entityMetadata === undefined;\n const noTdMetadata = !metadataLoading && metadata === undefined;\n if (noEntMetadata || noTdMetadata) return null;\n\n const stringEntityRef = stringifyEntityRef(entityRef);\n\n const entityDisplayName =\n entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;\n\n const removeTrailingSlash = (str: string) => str.replace(/\\/$/, '');\n const normalizeAndSpace = (str: string) =>\n str.replace(/-/g, ' ').split(' ').map(capitalize).join(' ');\n\n let techdocsTabTitleItems: string[] = [];\n\n if (path !== '')\n techdocsTabTitleItems = removeTrailingSlash(path)\n .split('/')\n .slice(0, 3)\n .map(normalizeAndSpace);\n\n const tabTitleItems = [appTitle, entityDisplayName, ...techdocsTabTitleItems];\n const tabTitle = tabTitleItems.join(' | ');\n\n return (\n <Header\n type=\"Documentation\"\n typeLink={docsRootLink}\n title={title || skeleton}\n subtitle={subtitle === '' ? undefined : subtitle || skeleton}\n >\n <Helmet titleTemplate=\"%s\">\n <title>{tabTitle}</title>\n </Helmet>\n {labels}\n {children}\n {addons.renderComponentsByLocation(locations.Header)}\n </Header>\n );\n};\n"],"names":["locations"],"mappings":";;;;;;;;;;;;;;AAiDA,MAAM,QAAA,uCAAY,QAAS,EAAA,EAAA,SAAA,EAAU,QAAO,OAAQ,EAAA,MAAA,EAAO,QAAQ,EAAI,EAAA,CAAA;AAoB1D,MAAA,wBAAA,GAA2B,CACtC,KACG,KAAA;AACH,EAAM,MAAA,EAAE,UAAa,GAAA,KAAA;AACrB,EAAA,MAAM,SAAS,iBAAkB,EAAA;AACjC,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAM,MAAA,qBAAA,GAAwB,OAAO,wBAAwB,CAAA;AAC7D,EAAA,MAAM,EAAE,GAAA,EAAK,IAAO,GAAA,EAAA,KAAO,SAAU,EAAA;AAErC,EAAM,MAAA;AAAA,IACJ,KAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAU,EAAA,EAAE,KAAO,EAAA,QAAA,EAAU,SAAS,eAAgB,EAAA;AAAA,IACtD,cAAgB,EAAA,EAAE,KAAO,EAAA,cAAA,EAAgB,SAAS,qBAAsB;AAAA,MACtE,qBAAsB,EAAA;AAE1B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAU,EAAA;AACf,IAAA,QAAA,CAAS,SAAS,SAAS,CAAA;AAC3B,IAAA,WAAA,CAAY,MAAM;AAChB,MAAI,IAAA,EAAE,kBAAqB,GAAA,QAAA;AAC3B,MAAI,IAAA,CAAC,gBAAoB,IAAA,gBAAA,KAAqB,MAAQ,EAAA;AACpD,QAAmB,gBAAA,GAAA,EAAA;AAAA;AAErB,MAAO,OAAA,gBAAA;AAAA,KACR,CAAA;AAAA,GACA,EAAA,CAAC,QAAU,EAAA,QAAA,EAAU,WAAW,CAAC,CAAA;AAEpC,EAAA,MAAM,QAAW,GAAA,SAAA,CAAU,WAAY,CAAA,WAAW,CAAK,IAAA,WAAA;AAEvD,EAAA,MAAM,EAAE,gBAAA,EAAkB,IAAK,EAAA,GAAI,kBAAkB,EAAC;AACtD,EAAA,MAAM,YAAY,IAAM,EAAA,SAAA;AAExB,EAAA,MAAM,mBAAmB,cACrB,GAAA,kBAAA,CAAmB,cAAgB,EAAA,iBAAiB,IACpD,EAAC;AAEL,EAAM,MAAA,YAAA,GAAe,WAAY,CAAA,YAAY,CAAE,EAAA;AAE/C,EAAA,MAAM,yBAEF,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAO,EAAA,UAAA,CAAW,cAAgB,EAAA,IAAA,IAAQ,QAAQ,CAAA;AAAA,MAClD,KACE,kBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,aAAA;AAAA,QAAA;AAAA,UACC,KAAM,EAAA,SAAA;AAAA,UACN,SAAA;AAAA,UACA,KAAA,EAAO,gBAAgB,QAAS,CAAA,KAAA;AAAA,UAChC,WAAY,EAAA;AAAA;AAAA;AACd;AAAA,GAEJ,EACC,gBAAiB,CAAA,MAAA,GAAS,CACzB,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAM,EAAA,OAAA;AAAA,MACN,KACE,kBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,KAAM,EAAA,SAAA;AAAA,UACN,UAAY,EAAA,gBAAA;AAAA,UACZ,WAAY,EAAA;AAAA;AAAA;AACd;AAAA,KAIL,SACC,mBAAA,KAAA,CAAA,aAAA,CAAC,eAAY,KAAM,EAAA,WAAA,EAAY,OAAO,MAAO,CAAA,SAAS,CAAG,EAAA,CAAA,GACvD,MACH,gBACD,IAAA,gBAAA,CAAiB,SAAS,KAC1B,IAAA,gBAAA,CAAiB,SAAS,MACxB,mBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAM,EAAA,EAAA;AAAA,MACN,uBACG,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,SAAS,EAAA,IAAA,EAAC,WAAU,QAAS,EAAA,UAAA,EAAW,QAC5C,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,KAAO,EAAA,EAAE,OAAS,EAAA,CAAA,IAAK,IAAI,EAAA,IAAA,EAAA,kBAC9B,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA,EAAS,OAAO,EAAE,SAAA,EAAW,OAAQ,EAAA,EAAG,CAC3C,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,EAAE,OAAS,EAAA,CAAA,IAAK,IAAI,EAAA,IAAA,EAAA,EAAC,QAElC,CACF,CAAA;AAAA,MAEF,KAAK,gBAAiB,CAAA;AAAA;AAAA,MAEtB,IACN,CAAA;AAKF,EAAM,MAAA,aAAA,GAAgB,CAAC,qBAAA,IAAyB,cAAmB,KAAA,KAAA,CAAA;AACnE,EAAM,MAAA,YAAA,GAAe,CAAC,eAAA,IAAmB,QAAa,KAAA,KAAA,CAAA;AACtD,EAAI,IAAA,aAAA,IAAiB,cAAqB,OAAA,IAAA;AAE1C,EAAM,MAAA,eAAA,GAAkB,mBAAmB,SAAS,CAAA;AAEpD,EAAA,MAAM,iBACJ,GAAA,qBAAA,CAAsB,SAAU,CAAA,eAAe,EAAE,QAAS,CAAA,YAAA;AAE5D,EAAA,MAAM,sBAAsB,CAAC,GAAA,KAAgB,GAAI,CAAA,OAAA,CAAQ,OAAO,EAAE,CAAA;AAClE,EAAA,MAAM,iBAAoB,GAAA,CAAC,GACzB,KAAA,GAAA,CAAI,QAAQ,IAAM,EAAA,GAAG,CAAE,CAAA,KAAA,CAAM,GAAG,CAAE,CAAA,GAAA,CAAI,UAAU,CAAA,CAAE,KAAK,GAAG,CAAA;AAE5D,EAAA,IAAI,wBAAkC,EAAC;AAEvC,EAAA,IAAI,IAAS,KAAA,EAAA;AACX,IAAwB,qBAAA,GAAA,mBAAA,CAAoB,IAAI,CAAA,CAC7C,KAAM,CAAA,GAAG,CACT,CAAA,KAAA,CAAM,CAAG,EAAA,CAAC,CACV,CAAA,GAAA,CAAI,iBAAiB,CAAA;AAE1B,EAAA,MAAM,aAAgB,GAAA,CAAC,QAAU,EAAA,iBAAA,EAAmB,GAAG,qBAAqB,CAAA;AAC5E,EAAM,MAAA,QAAA,GAAW,aAAc,CAAA,IAAA,CAAK,KAAK,CAAA;AAEzC,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,eAAA;AAAA,MACL,QAAU,EAAA,YAAA;AAAA,MACV,OAAO,KAAS,IAAA,QAAA;AAAA,MAChB,QAAU,EAAA,QAAA,KAAa,EAAK,GAAA,KAAA,CAAA,GAAY,QAAY,IAAA;AAAA,KAAA;AAAA,wCAEnD,MAAO,EAAA,EAAA,aAAA,EAAc,wBACnB,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,IAAA,EAAO,QAAS,CACnB,CAAA;AAAA,IACC,MAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA,CAAO,0BAA2B,CAAAA,sBAAA,CAAU,MAAM;AAAA,GACrD;AAEJ;;;;"}
@@ -1,5 +1,6 @@
1
1
  const isSvgNeedingInlining = (attrName, attrVal, apiOrigin) => {
2
- const isSrcToSvg = attrName === "src" && attrVal.endsWith(".svg");
2
+ const pathname = new URL(attrVal, "https://ignored.com").pathname;
3
+ const isSrcToSvg = attrName === "src" && pathname.endsWith(".svg");
3
4
  const isRelativeUrl = !attrVal.match(/^([a-z]*:)?\/\//i);
4
5
  const pointsToOurBackend = attrVal.startsWith(apiOrigin);
5
6
  return isSrcToSvg && (isRelativeUrl || pointsToOurBackend);
@@ -1 +1 @@
1
- {"version":3,"file":"addBaseUrl.esm.js","sources":["../../../src/reader/transformers/addBaseUrl.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { TechDocsStorageApi } from '../../api';\nimport type { Transformer } from './transformer';\n\ntype AddBaseUrlOptions = {\n techdocsStorageApi: TechDocsStorageApi;\n entityId: CompoundEntityRef;\n path: string;\n};\n\n/**\n * TechDocs backend serves SVGs with text/plain content-type for security. This\n * helper determines if an SVG is being loaded from the backend, and thus needs\n * inlining to be displayed properly.\n */\nconst isSvgNeedingInlining = (\n attrName: string,\n attrVal: string,\n apiOrigin: string,\n) => {\n const isSrcToSvg = attrName === 'src' && 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(\n unescape(encodeURIComponent(svgContent)),\n )}`,\n );\n } catch (e) {\n elem.setAttribute('alt', `Error: ${elemAttribute}`);\n }\n } else {\n elem.setAttribute(attributeName, newValue);\n }\n }\n }\n };\n\n await Promise.all([\n updateDom<HTMLImageElement>(dom.querySelectorAll('img'), 'src'),\n updateDom<HTMLScriptElement>(dom.querySelectorAll('script'), 'src'),\n updateDom<HTMLSourceElement>(dom.querySelectorAll('source'), 'src'),\n updateDom<HTMLLinkElement>(dom.querySelectorAll('link'), 'href'),\n updateDom<HTMLAnchorElement>(dom.querySelectorAll('a[download]'), 'href'),\n ]);\n\n return dom;\n };\n};\n"],"names":[],"mappings":"AA8BA,MAAM,oBAAuB,GAAA,CAC3B,QACA,EAAA,OAAA,EACA,SACG,KAAA;AACH,EAAA,MAAM,UAAa,GAAA,QAAA,KAAa,KAAS,IAAA,OAAA,CAAQ,SAAS,MAAM,CAAA;AAChE,EAAA,MAAM,aAAgB,GAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,kBAAkB,CAAA;AACvD,EAAM,MAAA,kBAAA,GAAqB,OAAQ,CAAA,UAAA,CAAW,SAAS,CAAA;AACvD,EAAA,OAAO,eAAe,aAAiB,IAAA,kBAAA,CAAA;AACzC,CAAA;AAEO,MAAM,aAAa,CAAC;AAAA,EACzB,kBAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAsC,KAAA;AACpC,EAAA,OAAO,OAAM,GAAO,KAAA;AAClB,IAAM,MAAA,SAAA,GAAY,MAAM,kBAAA,CAAmB,YAAa,EAAA;AAExD,IAAM,MAAA,SAAA,GAAY,OAChB,IAAA,EACA,aACG,KAAA;AACH,MAAA,KAAA,MAAW,QAAQ,IAAM,EAAA;AACvB,QAAI,IAAA,IAAA,CAAK,YAAa,CAAA,aAAa,CAAG,EAAA;AACpC,UAAM,MAAA,aAAA,GAAgB,IAAK,CAAA,YAAA,CAAa,aAAa,CAAA;AACrD,UAAA,IAAI,CAAC,aAAe,EAAA;AAGpB,UAAM,MAAA,QAAA,GAAW,MAAM,kBAAmB,CAAA,UAAA;AAAA,YACxC,aAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACF;AAEA,UAAA,IAAI,oBAAqB,CAAA,aAAA,EAAe,aAAe,EAAA,SAAS,CAAG,EAAA;AACjE,YAAI,IAAA;AACF,cAAA,MAAM,MAAM,MAAM,KAAA,CAAM,UAAU,EAAE,WAAA,EAAa,WAAW,CAAA;AAC5D,cAAM,MAAA,UAAA,GAAa,MAAM,GAAA,CAAI,IAAK,EAAA;AAClC,cAAK,IAAA,CAAA,YAAA;AAAA,gBACH,aAAA;AAAA,gBACA,CAA6B,0BAAA,EAAA,IAAA;AAAA,kBAC3B,QAAA,CAAS,kBAAmB,CAAA,UAAU,CAAC;AAAA,iBACxC,CAAA;AAAA,eACH;AAAA,qBACO,CAAG,EAAA;AACV,cAAA,IAAA,CAAK,YAAa,CAAA,KAAA,EAAO,CAAU,OAAA,EAAA,aAAa,CAAE,CAAA,CAAA;AAAA;AACpD,WACK,MAAA;AACL,YAAK,IAAA,CAAA,YAAA,CAAa,eAAe,QAAQ,CAAA;AAAA;AAC3C;AACF;AACF,KACF;AAEA,IAAA,MAAM,QAAQ,GAAI,CAAA;AAAA,MAChB,SAA4B,CAAA,GAAA,CAAI,gBAAiB,CAAA,KAAK,GAAG,KAAK,CAAA;AAAA,MAC9D,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,QAAQ,GAAG,KAAK,CAAA;AAAA,MAClE,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,QAAQ,GAAG,KAAK,CAAA;AAAA,MAClE,SAA2B,CAAA,GAAA,CAAI,gBAAiB,CAAA,MAAM,GAAG,MAAM,CAAA;AAAA,MAC/D,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,aAAa,GAAG,MAAM;AAAA,KACzE,CAAA;AAED,IAAO,OAAA,GAAA;AAAA,GACT;AACF;;;;"}
1
+ {"version":3,"file":"addBaseUrl.esm.js","sources":["../../../src/reader/transformers/addBaseUrl.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { TechDocsStorageApi } from '../../api';\nimport type { Transformer } from './transformer';\n\ntype AddBaseUrlOptions = {\n techdocsStorageApi: TechDocsStorageApi;\n entityId: CompoundEntityRef;\n path: string;\n};\n\n/**\n * TechDocs backend serves SVGs with text/plain content-type for security. This\n * helper determines if an SVG is being loaded from the backend, and thus needs\n * inlining to be displayed properly.\n */\nconst isSvgNeedingInlining = (\n attrName: string,\n attrVal: string,\n apiOrigin: string,\n) => {\n // Let's let the URL object do automatic parsing of the pathname for us\n const pathname = new URL(attrVal, 'https://ignored.com').pathname;\n const isSrcToSvg = attrName === 'src' && pathname.endsWith('.svg');\n\n const isRelativeUrl = !attrVal.match(/^([a-z]*:)?\\/\\//i);\n const pointsToOurBackend = attrVal.startsWith(apiOrigin);\n return isSrcToSvg && (isRelativeUrl || pointsToOurBackend);\n};\n\nexport const addBaseUrl = ({\n techdocsStorageApi,\n entityId,\n path,\n}: AddBaseUrlOptions): Transformer => {\n return async dom => {\n const apiOrigin = await techdocsStorageApi.getApiOrigin();\n\n const updateDom = async <T extends Element>(\n list: HTMLCollectionOf<T> | NodeListOf<T>,\n attributeName: string,\n ) => {\n for (const elem of list) {\n if (elem.hasAttribute(attributeName)) {\n const elemAttribute = elem.getAttribute(attributeName);\n if (!elemAttribute) return;\n\n // Special handling for SVG images.\n const newValue = await techdocsStorageApi.getBaseUrl(\n elemAttribute,\n entityId,\n path,\n );\n\n if (isSvgNeedingInlining(attributeName, elemAttribute, apiOrigin)) {\n try {\n const svg = await fetch(newValue, { credentials: 'include' });\n const svgContent = await svg.text();\n elem.setAttribute(\n attributeName,\n `data:image/svg+xml;base64,${btoa(\n unescape(encodeURIComponent(svgContent)),\n )}`,\n );\n } catch (e) {\n elem.setAttribute('alt', `Error: ${elemAttribute}`);\n }\n } else {\n elem.setAttribute(attributeName, newValue);\n }\n }\n }\n };\n\n await Promise.all([\n updateDom<HTMLImageElement>(dom.querySelectorAll('img'), 'src'),\n updateDom<HTMLScriptElement>(dom.querySelectorAll('script'), 'src'),\n updateDom<HTMLSourceElement>(dom.querySelectorAll('source'), 'src'),\n updateDom<HTMLLinkElement>(dom.querySelectorAll('link'), 'href'),\n updateDom<HTMLAnchorElement>(dom.querySelectorAll('a[download]'), 'href'),\n ]);\n\n return dom;\n };\n};\n"],"names":[],"mappings":"AA8BA,MAAM,oBAAuB,GAAA,CAC3B,QACA,EAAA,OAAA,EACA,SACG,KAAA;AAEH,EAAA,MAAM,QAAW,GAAA,IAAI,GAAI,CAAA,OAAA,EAAS,qBAAqB,CAAE,CAAA,QAAA;AACzD,EAAA,MAAM,UAAa,GAAA,QAAA,KAAa,KAAS,IAAA,QAAA,CAAS,SAAS,MAAM,CAAA;AAEjE,EAAA,MAAM,aAAgB,GAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,kBAAkB,CAAA;AACvD,EAAM,MAAA,kBAAA,GAAqB,OAAQ,CAAA,UAAA,CAAW,SAAS,CAAA;AACvD,EAAA,OAAO,eAAe,aAAiB,IAAA,kBAAA,CAAA;AACzC,CAAA;AAEO,MAAM,aAAa,CAAC;AAAA,EACzB,kBAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAsC,KAAA;AACpC,EAAA,OAAO,OAAM,GAAO,KAAA;AAClB,IAAM,MAAA,SAAA,GAAY,MAAM,kBAAA,CAAmB,YAAa,EAAA;AAExD,IAAM,MAAA,SAAA,GAAY,OAChB,IAAA,EACA,aACG,KAAA;AACH,MAAA,KAAA,MAAW,QAAQ,IAAM,EAAA;AACvB,QAAI,IAAA,IAAA,CAAK,YAAa,CAAA,aAAa,CAAG,EAAA;AACpC,UAAM,MAAA,aAAA,GAAgB,IAAK,CAAA,YAAA,CAAa,aAAa,CAAA;AACrD,UAAA,IAAI,CAAC,aAAe,EAAA;AAGpB,UAAM,MAAA,QAAA,GAAW,MAAM,kBAAmB,CAAA,UAAA;AAAA,YACxC,aAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACF;AAEA,UAAA,IAAI,oBAAqB,CAAA,aAAA,EAAe,aAAe,EAAA,SAAS,CAAG,EAAA;AACjE,YAAI,IAAA;AACF,cAAA,MAAM,MAAM,MAAM,KAAA,CAAM,UAAU,EAAE,WAAA,EAAa,WAAW,CAAA;AAC5D,cAAM,MAAA,UAAA,GAAa,MAAM,GAAA,CAAI,IAAK,EAAA;AAClC,cAAK,IAAA,CAAA,YAAA;AAAA,gBACH,aAAA;AAAA,gBACA,CAA6B,0BAAA,EAAA,IAAA;AAAA,kBAC3B,QAAA,CAAS,kBAAmB,CAAA,UAAU,CAAC;AAAA,iBACxC,CAAA;AAAA,eACH;AAAA,qBACO,CAAG,EAAA;AACV,cAAA,IAAA,CAAK,YAAa,CAAA,KAAA,EAAO,CAAU,OAAA,EAAA,aAAa,CAAE,CAAA,CAAA;AAAA;AACpD,WACK,MAAA;AACL,YAAK,IAAA,CAAA,YAAA,CAAa,eAAe,QAAQ,CAAA;AAAA;AAC3C;AACF;AACF,KACF;AAEA,IAAA,MAAM,QAAQ,GAAI,CAAA;AAAA,MAChB,SAA4B,CAAA,GAAA,CAAI,gBAAiB,CAAA,KAAK,GAAG,KAAK,CAAA;AAAA,MAC9D,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,QAAQ,GAAG,KAAK,CAAA;AAAA,MAClE,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,QAAQ,GAAG,KAAK,CAAA;AAAA,MAClE,SAA2B,CAAA,GAAA,CAAI,gBAAiB,CAAA,MAAM,GAAG,MAAM,CAAA;AAAA,MAC/D,SAA6B,CAAA,GAAA,CAAI,gBAAiB,CAAA,aAAa,GAAG,MAAM;AAAA,KACzE,CAAA;AAED,IAAO,OAAA,GAAA;AAAA,GACT;AACF;;;;"}
@@ -45,7 +45,7 @@ const useSanitizerTransformer = () => {
45
45
  return DOMPurify.sanitize(dom.outerHTML, {
46
46
  ADD_TAGS: tags,
47
47
  FORBID_TAGS: ["style"],
48
- ADD_ATTR: ["http-equiv", "content"],
48
+ ADD_ATTR: ["http-equiv", "content", "dominant-baseline"],
49
49
  WHOLE_DOCUMENT: true,
50
50
  RETURN_DOM: true,
51
51
  CUSTOM_ELEMENT_HANDLING: {
@@ -1 +1 @@
1
- {"version":3,"file":"transformer.esm.js","sources":["../../../../src/reader/transformers/html/transformer.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport DOMPurify from 'dompurify';\nimport { useCallback, useMemo } from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { Transformer } from '../transformer';\nimport { removeUnsafeIframes, removeUnsafeLinks } from './hooks';\n\n/**\n * Returns html sanitizer configuration\n */\nconst useSanitizerConfig = () => {\n const configApi = useApi(configApiRef);\n\n return useMemo(() => {\n return configApi.getOptionalConfig('techdocs.sanitizer');\n }, [configApi]);\n};\n\n/**\n * Returns a transformer that sanitizes the dom\n */\nexport const useSanitizerTransformer = (): Transformer => {\n const config = useSanitizerConfig();\n\n return useCallback(\n async (dom: Element) => {\n const hosts = config?.getOptionalStringArray('allowedIframeHosts');\n\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeLinks);\n const tags = ['link', 'meta'];\n\n if (hosts) {\n tags.push('iframe');\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts));\n }\n\n // Only allow meta tags if they are used for refreshing the page. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeElement', (currNode, data) => {\n if (data.tagName === 'meta') {\n const isMetaRefreshTag =\n currNode.getAttribute('http-equiv') === 'refresh' &&\n currNode.getAttribute('content')?.includes('url=');\n if (!isMetaRefreshTag) {\n currNode.parentNode?.removeChild(currNode);\n }\n }\n });\n\n // Only allow http-equiv and content attributes on meta tags. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeAttribute', (currNode, data) => {\n if (currNode.tagName !== 'meta') {\n if (data.attrName === 'http-equiv' || data.attrName === 'content') {\n currNode.removeAttribute(data.attrName);\n }\n }\n });\n\n const tagNameCheck = config?.getOptionalString(\n 'allowedCustomElementTagNameRegExp',\n );\n const attributeNameCheck = config?.getOptionalString(\n 'allowedCustomElementAttributeNameRegExp',\n );\n\n // using outerHTML as we want to preserve the html tag attributes (lang)\n return DOMPurify.sanitize(dom.outerHTML, {\n ADD_TAGS: tags,\n FORBID_TAGS: ['style'],\n ADD_ATTR: ['http-equiv', 'content'],\n WHOLE_DOCUMENT: true,\n RETURN_DOM: true,\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: tagNameCheck ? new RegExp(tagNameCheck) : undefined,\n attributeNameCheck: attributeNameCheck\n ? new RegExp(attributeNameCheck)\n : undefined,\n },\n });\n },\n [config],\n );\n};\n"],"names":[],"mappings":";;;;;;AA2BA,MAAM,qBAAqB,MAAM;AAC/B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAO,OAAA,SAAA,CAAU,kBAAkB,oBAAoB,CAAA;AAAA,GACzD,EAAG,CAAC,SAAS,CAAC,CAAA;AAChB,CAAA;AAKO,MAAM,0BAA0B,MAAmB;AACxD,EAAA,MAAM,SAAS,kBAAmB,EAAA;AAElC,EAAO,OAAA,WAAA;AAAA,IACL,OAAO,GAAiB,KAAA;AACtB,MAAM,MAAA,KAAA,GAAQ,MAAQ,EAAA,sBAAA,CAAuB,oBAAoB,CAAA;AAEjE,MAAU,SAAA,CAAA,OAAA,CAAQ,0BAA0B,iBAAiB,CAAA;AAC7D,MAAM,MAAA,IAAA,GAAO,CAAC,MAAA,EAAQ,MAAM,CAAA;AAE5B,MAAA,IAAI,KAAO,EAAA;AACT,QAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,QAAA,SAAA,CAAU,OAAQ,CAAA,wBAAA,EAA0B,mBAAoB,CAAA,KAAK,CAAC,CAAA;AAAA;AAIxE,MAAA,SAAA,CAAU,OAAQ,CAAA,qBAAA,EAAuB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC3D,QAAI,IAAA,IAAA,CAAK,YAAY,MAAQ,EAAA;AAC3B,UAAM,MAAA,gBAAA,GACJ,QAAS,CAAA,YAAA,CAAa,YAAY,CAAA,KAAM,SACxC,IAAA,QAAA,CAAS,YAAa,CAAA,SAAS,CAAG,EAAA,QAAA,CAAS,MAAM,CAAA;AACnD,UAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,YAAS,QAAA,CAAA,UAAA,EAAY,YAAY,QAAQ,CAAA;AAAA;AAC3C;AACF,OACD,CAAA;AAGD,MAAA,SAAA,CAAU,OAAQ,CAAA,uBAAA,EAAyB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC7D,QAAI,IAAA,QAAA,CAAS,YAAY,MAAQ,EAAA;AAC/B,UAAA,IAAI,IAAK,CAAA,QAAA,KAAa,YAAgB,IAAA,IAAA,CAAK,aAAa,SAAW,EAAA;AACjE,YAAS,QAAA,CAAA,eAAA,CAAgB,KAAK,QAAQ,CAAA;AAAA;AACxC;AACF,OACD,CAAA;AAED,MAAA,MAAM,eAAe,MAAQ,EAAA,iBAAA;AAAA,QAC3B;AAAA,OACF;AACA,MAAA,MAAM,qBAAqB,MAAQ,EAAA,iBAAA;AAAA,QACjC;AAAA,OACF;AAGA,MAAO,OAAA,SAAA,CAAU,QAAS,CAAA,GAAA,CAAI,SAAW,EAAA;AAAA,QACvC,QAAU,EAAA,IAAA;AAAA,QACV,WAAA,EAAa,CAAC,OAAO,CAAA;AAAA,QACrB,QAAA,EAAU,CAAC,YAAA,EAAc,SAAS,CAAA;AAAA,QAClC,cAAgB,EAAA,IAAA;AAAA,QAChB,UAAY,EAAA,IAAA;AAAA,QACZ,uBAAyB,EAAA;AAAA,UACvB,YAAc,EAAA,YAAA,GAAe,IAAI,MAAA,CAAO,YAAY,CAAI,GAAA,KAAA,CAAA;AAAA,UACxD,kBAAoB,EAAA,kBAAA,GAChB,IAAI,MAAA,CAAO,kBAAkB,CAC7B,GAAA,KAAA;AAAA;AACN,OACD,CAAA;AAAA,KACH;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AACF;;;;"}
1
+ {"version":3,"file":"transformer.esm.js","sources":["../../../../src/reader/transformers/html/transformer.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport DOMPurify from 'dompurify';\nimport { useCallback, useMemo } from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { Transformer } from '../transformer';\nimport { removeUnsafeIframes, removeUnsafeLinks } from './hooks';\n\n/**\n * Returns html sanitizer configuration\n */\nconst useSanitizerConfig = () => {\n const configApi = useApi(configApiRef);\n\n return useMemo(() => {\n return configApi.getOptionalConfig('techdocs.sanitizer');\n }, [configApi]);\n};\n\n/**\n * Returns a transformer that sanitizes the dom\n */\nexport const useSanitizerTransformer = (): Transformer => {\n const config = useSanitizerConfig();\n\n return useCallback(\n async (dom: Element) => {\n const hosts = config?.getOptionalStringArray('allowedIframeHosts');\n\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeLinks);\n const tags = ['link', 'meta'];\n\n if (hosts) {\n tags.push('iframe');\n DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts));\n }\n\n // Only allow meta tags if they are used for refreshing the page. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeElement', (currNode, data) => {\n if (data.tagName === 'meta') {\n const isMetaRefreshTag =\n currNode.getAttribute('http-equiv') === 'refresh' &&\n currNode.getAttribute('content')?.includes('url=');\n if (!isMetaRefreshTag) {\n currNode.parentNode?.removeChild(currNode);\n }\n }\n });\n\n // Only allow http-equiv and content attributes on meta tags. They are required for the redirect feature.\n DOMPurify.addHook('uponSanitizeAttribute', (currNode, data) => {\n if (currNode.tagName !== 'meta') {\n if (data.attrName === 'http-equiv' || data.attrName === 'content') {\n currNode.removeAttribute(data.attrName);\n }\n }\n });\n\n const tagNameCheck = config?.getOptionalString(\n 'allowedCustomElementTagNameRegExp',\n );\n const attributeNameCheck = config?.getOptionalString(\n 'allowedCustomElementAttributeNameRegExp',\n );\n\n // using outerHTML as we want to preserve the html tag attributes (lang)\n return DOMPurify.sanitize(dom.outerHTML, {\n ADD_TAGS: tags,\n FORBID_TAGS: ['style'],\n ADD_ATTR: ['http-equiv', 'content', 'dominant-baseline'],\n WHOLE_DOCUMENT: true,\n RETURN_DOM: true,\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: tagNameCheck ? new RegExp(tagNameCheck) : undefined,\n attributeNameCheck: attributeNameCheck\n ? new RegExp(attributeNameCheck)\n : undefined,\n },\n });\n },\n [config],\n );\n};\n"],"names":[],"mappings":";;;;;;AA2BA,MAAM,qBAAqB,MAAM;AAC/B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAO,OAAA,SAAA,CAAU,kBAAkB,oBAAoB,CAAA;AAAA,GACzD,EAAG,CAAC,SAAS,CAAC,CAAA;AAChB,CAAA;AAKO,MAAM,0BAA0B,MAAmB;AACxD,EAAA,MAAM,SAAS,kBAAmB,EAAA;AAElC,EAAO,OAAA,WAAA;AAAA,IACL,OAAO,GAAiB,KAAA;AACtB,MAAM,MAAA,KAAA,GAAQ,MAAQ,EAAA,sBAAA,CAAuB,oBAAoB,CAAA;AAEjE,MAAU,SAAA,CAAA,OAAA,CAAQ,0BAA0B,iBAAiB,CAAA;AAC7D,MAAM,MAAA,IAAA,GAAO,CAAC,MAAA,EAAQ,MAAM,CAAA;AAE5B,MAAA,IAAI,KAAO,EAAA;AACT,QAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,QAAA,SAAA,CAAU,OAAQ,CAAA,wBAAA,EAA0B,mBAAoB,CAAA,KAAK,CAAC,CAAA;AAAA;AAIxE,MAAA,SAAA,CAAU,OAAQ,CAAA,qBAAA,EAAuB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC3D,QAAI,IAAA,IAAA,CAAK,YAAY,MAAQ,EAAA;AAC3B,UAAM,MAAA,gBAAA,GACJ,QAAS,CAAA,YAAA,CAAa,YAAY,CAAA,KAAM,SACxC,IAAA,QAAA,CAAS,YAAa,CAAA,SAAS,CAAG,EAAA,QAAA,CAAS,MAAM,CAAA;AACnD,UAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,YAAS,QAAA,CAAA,UAAA,EAAY,YAAY,QAAQ,CAAA;AAAA;AAC3C;AACF,OACD,CAAA;AAGD,MAAA,SAAA,CAAU,OAAQ,CAAA,uBAAA,EAAyB,CAAC,QAAA,EAAU,IAAS,KAAA;AAC7D,QAAI,IAAA,QAAA,CAAS,YAAY,MAAQ,EAAA;AAC/B,UAAA,IAAI,IAAK,CAAA,QAAA,KAAa,YAAgB,IAAA,IAAA,CAAK,aAAa,SAAW,EAAA;AACjE,YAAS,QAAA,CAAA,eAAA,CAAgB,KAAK,QAAQ,CAAA;AAAA;AACxC;AACF,OACD,CAAA;AAED,MAAA,MAAM,eAAe,MAAQ,EAAA,iBAAA;AAAA,QAC3B;AAAA,OACF;AACA,MAAA,MAAM,qBAAqB,MAAQ,EAAA,iBAAA;AAAA,QACjC;AAAA,OACF;AAGA,MAAO,OAAA,SAAA,CAAU,QAAS,CAAA,GAAA,CAAI,SAAW,EAAA;AAAA,QACvC,QAAU,EAAA,IAAA;AAAA,QACV,WAAA,EAAa,CAAC,OAAO,CAAA;AAAA,QACrB,QAAU,EAAA,CAAC,YAAc,EAAA,SAAA,EAAW,mBAAmB,CAAA;AAAA,QACvD,cAAgB,EAAA,IAAA;AAAA,QAChB,UAAY,EAAA,IAAA;AAAA,QACZ,uBAAyB,EAAA;AAAA,UACvB,YAAc,EAAA,YAAA,GAAe,IAAI,MAAA,CAAO,YAAY,CAAI,GAAA,KAAA,CAAA;AAAA,UACxD,kBAAoB,EAAA,kBAAA,GAChB,IAAI,MAAA,CAAO,kBAAkB,CAC7B,GAAA,KAAA;AAAA;AACN,OACD,CAAA;AAAA,KACH;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AACF;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-techdocs",
3
- "version": "1.12.3-next.3",
3
+ "version": "1.12.4-next.0",
4
4
  "description": "The Backstage plugin that renders technical documentation for your components",
5
5
  "backstage": {
6
6
  "role": "frontend-plugin",
@@ -74,20 +74,20 @@
74
74
  "@backstage/catalog-client": "1.9.1",
75
75
  "@backstage/catalog-model": "1.7.3",
76
76
  "@backstage/config": "1.3.2",
77
- "@backstage/core-compat-api": "0.3.6-next.3",
78
- "@backstage/core-components": "0.16.4-next.1",
79
- "@backstage/core-plugin-api": "1.10.4-next.0",
77
+ "@backstage/core-compat-api": "0.3.7-next.0",
78
+ "@backstage/core-components": "0.16.4",
79
+ "@backstage/core-plugin-api": "1.10.4",
80
80
  "@backstage/errors": "1.2.7",
81
- "@backstage/frontend-plugin-api": "0.9.5-next.3",
81
+ "@backstage/frontend-plugin-api": "0.9.6-next.0",
82
82
  "@backstage/integration": "1.16.1",
83
- "@backstage/integration-react": "1.2.4-next.0",
84
- "@backstage/plugin-auth-react": "0.1.12-next.1",
85
- "@backstage/plugin-catalog-react": "1.15.2-next.3",
83
+ "@backstage/integration-react": "1.2.4",
84
+ "@backstage/plugin-auth-react": "0.1.12",
85
+ "@backstage/plugin-catalog-react": "1.16.0-next.0",
86
86
  "@backstage/plugin-search-common": "1.2.17",
87
- "@backstage/plugin-search-react": "1.8.6-next.3",
87
+ "@backstage/plugin-search-react": "1.8.7-next.0",
88
88
  "@backstage/plugin-techdocs-common": "0.1.0",
89
- "@backstage/plugin-techdocs-react": "1.2.14-next.1",
90
- "@backstage/theme": "0.6.4-next.0",
89
+ "@backstage/plugin-techdocs-react": "1.2.15-next.0",
90
+ "@backstage/theme": "0.6.4",
91
91
  "@material-ui/core": "^4.12.2",
92
92
  "@material-ui/icons": "^4.9.1",
93
93
  "@material-ui/lab": "4.0.0-alpha.61",
@@ -101,12 +101,12 @@
101
101
  "react-use": "^17.2.4"
102
102
  },
103
103
  "devDependencies": {
104
- "@backstage/cli": "0.30.0-next.3",
105
- "@backstage/core-app-api": "1.15.5-next.0",
106
- "@backstage/dev-utils": "1.1.7-next.3",
107
- "@backstage/plugin-catalog": "1.27.0-next.3",
108
- "@backstage/plugin-techdocs-module-addons-contrib": "1.1.21-next.1",
109
- "@backstage/test-utils": "1.7.5-next.0",
104
+ "@backstage/cli": "0.30.0",
105
+ "@backstage/core-app-api": "1.15.5",
106
+ "@backstage/dev-utils": "1.1.8-next.0",
107
+ "@backstage/plugin-catalog": "1.28.0-next.0",
108
+ "@backstage/plugin-techdocs-module-addons-contrib": "1.1.22-next.0",
109
+ "@backstage/test-utils": "1.7.5",
110
110
  "@testing-library/dom": "^10.0.0",
111
111
  "@testing-library/jest-dom": "^6.0.0",
112
112
  "@testing-library/react": "^16.0.0",