@rwdocs/backstage-plugin-rw 0.1.1 → 0.1.2

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/config.d.ts ADDED
@@ -0,0 +1 @@
1
+ export interface Config {}
@@ -11,6 +11,10 @@ class RwClient {
11
11
  async getBaseUrl() {
12
12
  return this.discoveryApi.getBaseUrl("rw");
13
13
  }
14
+ async getSiteBaseUrl(entityRef) {
15
+ const base = await this.discoveryApi.getBaseUrl("rw");
16
+ return `${base}/site/${entityRef}`;
17
+ }
14
18
  getFetch() {
15
19
  return this.fetchApi.fetch;
16
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RwClient.esm.js","sources":["../../src/api/RwClient.ts"],"sourcesContent":["import { createApiRef } from \"@backstage/core-plugin-api\";\nimport type { DiscoveryApi, FetchApi } from \"@backstage/core-plugin-api\";\n\nexport interface RwApi {\n getBaseUrl(): Promise<string>;\n getFetch(): typeof fetch;\n}\n\nexport const rwApiRef = createApiRef<RwApi>({ id: \"plugin.rw.api\" });\n\nexport class RwClient implements RwApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl(): Promise<string> {\n return this.discoveryApi.getBaseUrl(\"rw\");\n }\n\n getFetch(): typeof fetch {\n return this.fetchApi.fetch;\n }\n}\n"],"names":[],"mappings":";;AAQO,MAAM,QAAA,GAAW,YAAA,CAAoB,EAAE,EAAA,EAAI,iBAAiB;AAE5D,MAAM,QAAA,CAA0B;AAAA,EACpB,YAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAA,EAA6D;AACvE,IAAA,IAAA,CAAK,eAAe,OAAA,CAAQ,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAA,GAA8B;AAClC,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,UAAA,CAAW,IAAI,CAAA;AAAA,EAC1C;AAAA,EAEA,QAAA,GAAyB;AACvB,IAAA,OAAO,KAAK,QAAA,CAAS,KAAA;AAAA,EACvB;AACF;;;;"}
1
+ {"version":3,"file":"RwClient.esm.js","sources":["../../src/api/RwClient.ts"],"sourcesContent":["import { createApiRef } from \"@backstage/core-plugin-api\";\nimport type { DiscoveryApi, FetchApi } from \"@backstage/core-plugin-api\";\n\nexport interface RwApi {\n getBaseUrl(): Promise<string>;\n getSiteBaseUrl(entityRef: string): Promise<string>;\n getFetch(): typeof fetch;\n}\n\nexport const rwApiRef = createApiRef<RwApi>({ id: \"plugin.rw.api\" });\n\nexport class RwClient implements RwApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl(): Promise<string> {\n return this.discoveryApi.getBaseUrl(\"rw\");\n }\n\n async getSiteBaseUrl(entityRef: string): Promise<string> {\n const base = await this.discoveryApi.getBaseUrl(\"rw\");\n return `${base}/site/${entityRef}`;\n }\n\n getFetch(): typeof fetch {\n return this.fetchApi.fetch;\n }\n}\n"],"names":[],"mappings":";;AASO,MAAM,QAAA,GAAW,YAAA,CAAoB,EAAE,EAAA,EAAI,iBAAiB;AAE5D,MAAM,QAAA,CAA0B;AAAA,EACpB,YAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAA,EAA6D;AACvE,IAAA,IAAA,CAAK,eAAe,OAAA,CAAQ,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAA,GAA8B;AAClC,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,UAAA,CAAW,IAAI,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAM,eAAe,SAAA,EAAoC;AACvD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,YAAA,CAAa,WAAW,IAAI,CAAA;AACpD,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,MAAA,EAAS,SAAS,CAAA,CAAA;AAAA,EAClC;AAAA,EAEA,QAAA,GAAyB;AACvB,IAAA,OAAO,KAAK,QAAA,CAAS,KAAA;AAAA,EACvB;AACF;;;;"}
@@ -1,18 +1,20 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { useRef, useState, useEffect } from 'react';
2
+ import { useRef, useState, useCallback, useEffect } from 'react';
3
3
  import { useApi } from '@backstage/core-plugin-api';
4
4
  import { ErrorPanel } from '@backstage/core-components';
5
- import { useTheme } from '@mui/material/styles';
5
+ import { useTheme } from '@material-ui/core/styles';
6
6
  import { useLocation, useNavigate, useParams } from 'react-router-dom';
7
7
  import { rwApiRef } from '../api/RwClient.esm.js';
8
+ import { useSectionRefResolver } from './useSectionRefResolver.esm.js';
8
9
  import { mountRw } from '@rwdocs/viewer';
9
10
  import '@rwdocs/viewer/embed.css';
10
11
 
11
- function RwDocsViewer() {
12
+ function RwDocsViewer({ apiBaseUrl, sectionRef, sourceEntityRef }) {
12
13
  const ref = useRef(null);
13
14
  const rwApi = useApi(rwApiRef);
14
15
  const theme = useTheme();
15
16
  const [error, setError] = useState(null);
17
+ const catalogResolver = useSectionRefResolver(sourceEntityRef);
16
18
  const location = useLocation();
17
19
  const navigate = useNavigate();
18
20
  const navigateRef = useRef(navigate);
@@ -21,41 +23,57 @@ function RwDocsViewer() {
21
23
  const basePath = subPath ? location.pathname.slice(0, -(subPath.length + 1)) : location.pathname;
22
24
  const basePathRef = useRef(basePath);
23
25
  basePathRef.current = basePath;
26
+ const resolveSectionRefs = useCallback(
27
+ async (refs) => {
28
+ const otherRefs = refs.filter((r) => r !== sectionRef);
29
+ const result = otherRefs.length > 0 ? await catalogResolver(otherRefs) : {};
30
+ if (refs.includes(sectionRef)) {
31
+ result[sectionRef] = basePathRef.current;
32
+ }
33
+ return result;
34
+ },
35
+ [catalogResolver, sectionRef]
36
+ );
24
37
  const instanceRef = useRef(null);
25
38
  const prevSubPathRef = useRef(subPath);
26
39
  const rwNavigatingRef = useRef(false);
27
40
  useEffect(() => {
28
- let cancelled = false;
29
- rwApi.getBaseUrl().then((baseUrl) => {
30
- if (cancelled || !ref.current) return;
31
- const base = basePathRef.current;
32
- const initialPath = subPath ? `/${subPath}` : "/";
41
+ if (!ref.current) {
42
+ return void 0;
43
+ }
44
+ try {
45
+ let initialPath = "/";
46
+ if (subPath) {
47
+ initialPath = `/${subPath}`;
48
+ }
49
+ if (location.hash) {
50
+ initialPath += location.hash;
51
+ }
33
52
  instanceRef.current = mountRw(ref.current, {
34
- apiBaseUrl: baseUrl,
53
+ apiBaseUrl,
35
54
  initialPath,
36
- basePath: base,
55
+ sectionRef,
37
56
  fetchFn: rwApi.getFetch(),
38
- colorScheme: theme.palette.mode,
39
- onNavigate: (rwPath) => {
40
- const browserPath = rwPath === "/" ? base : `${base}${rwPath}`;
41
- if (window.location.pathname !== browserPath) {
57
+ colorScheme: theme.palette.type,
58
+ resolveSectionRefs,
59
+ onNavigate: (href) => {
60
+ if (window.location.pathname !== href) {
42
61
  rwNavigatingRef.current = true;
43
- navigateRef.current(browserPath, { replace: false });
62
+ navigateRef.current(href, { replace: false });
44
63
  }
45
64
  }
46
65
  });
47
- }).catch((err) => {
48
- if (!cancelled) setError(err);
49
- });
66
+ } catch (err) {
67
+ setError(err instanceof Error ? err : new Error(String(err)));
68
+ }
50
69
  return () => {
51
- cancelled = true;
52
70
  instanceRef.current?.destroy();
53
71
  instanceRef.current = null;
54
72
  };
55
- }, [rwApi]);
73
+ }, [apiBaseUrl, sectionRef]);
56
74
  useEffect(() => {
57
- instanceRef.current?.setColorScheme(theme.palette.mode);
58
- }, [theme.palette.mode]);
75
+ instanceRef.current?.setColorScheme(theme.palette.type);
76
+ }, [theme.palette.type]);
59
77
  useEffect(() => {
60
78
  if (subPath === prevSubPathRef.current) return;
61
79
  prevSubPathRef.current = subPath;
@@ -69,7 +87,7 @@ function RwDocsViewer() {
69
87
  if (error) {
70
88
  return /* @__PURE__ */ jsx(ErrorPanel, { error });
71
89
  }
72
- return /* @__PURE__ */ jsx("div", { ref, className: "rw-root", style: { height: "100vh" } });
90
+ return /* @__PURE__ */ jsx("div", { ref, className: "rw-root" });
73
91
  }
74
92
 
75
93
  export { RwDocsViewer };
@@ -1 +1 @@
1
- {"version":3,"file":"RwDocsViewer.esm.js","sources":["../../src/components/RwDocsViewer.tsx"],"sourcesContent":["import { useRef, useEffect, useState } from \"react\";\nimport { useApi } from \"@backstage/core-plugin-api\";\nimport { ErrorPanel } from \"@backstage/core-components\";\nimport { useTheme } from \"@mui/material/styles\";\nimport { useLocation, useNavigate, useParams } from \"react-router-dom\";\nimport { rwApiRef } from \"../api/RwClient\";\nimport { mountRw } from \"@rwdocs/viewer\";\nimport type { RwInstance } from \"@rwdocs/viewer\";\nimport \"@rwdocs/viewer/embed.css\";\n\nexport function RwDocsViewer() {\n const ref = useRef<HTMLDivElement>(null);\n const rwApi = useApi(rwApiRef);\n const theme = useTheme();\n const [error, setError] = useState<Error | null>(null);\n\n const location = useLocation();\n const navigate = useNavigate();\n const navigateRef = useRef(navigate);\n navigateRef.current = navigate;\n const { \"*\": subPath = \"\" } = useParams();\n\n // Derive the plugin's base path by stripping the sub-path (and its leading slash) from the URL.\n // e.g. URL=\"/rw-docs/getting-started\", subPath=\"getting-started\" → base=\"/rw-docs\"\n const basePath = subPath ? location.pathname.slice(0, -(subPath.length + 1)) : location.pathname;\n const basePathRef = useRef(basePath);\n basePathRef.current = basePath;\n\n const instanceRef = useRef<RwInstance | null>(null);\n const prevSubPathRef = useRef(subPath);\n const rwNavigatingRef = useRef(false);\n\n useEffect(() => {\n let cancelled = false;\n\n rwApi\n .getBaseUrl()\n .then((baseUrl) => {\n if (cancelled || !ref.current) return;\n\n const base = basePathRef.current;\n const initialPath = subPath ? `/${subPath}` : \"/\";\n\n instanceRef.current = mountRw(ref.current, {\n apiBaseUrl: baseUrl,\n initialPath,\n basePath: base,\n fetchFn: rwApi.getFetch(),\n colorScheme: theme.palette.mode,\n onNavigate: (rwPath: string) => {\n const browserPath = rwPath === \"/\" ? base : `${base}${rwPath}`;\n if (window.location.pathname !== browserPath) {\n rwNavigatingRef.current = true;\n navigateRef.current(browserPath, { replace: false });\n }\n },\n });\n })\n .catch((err) => {\n if (!cancelled) setError(err);\n });\n\n return () => {\n cancelled = true;\n instanceRef.current?.destroy();\n instanceRef.current = null;\n };\n // Re-mount when API changes — theme and navigation sync are handled by effects below\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [rwApi]);\n\n // Sync Backstage theme to the RW viewer\n useEffect(() => {\n instanceRef.current?.setColorScheme(theme.palette.mode);\n }, [theme.palette.mode]);\n\n // Sync external navigation (browser back/forward) to the RW app\n useEffect(() => {\n if (subPath === prevSubPathRef.current) return;\n prevSubPathRef.current = subPath;\n\n if (rwNavigatingRef.current) {\n rwNavigatingRef.current = false;\n return;\n }\n\n // External navigation — tell RW to navigate\n const rwPath = subPath ? `/${subPath}` : \"/\";\n instanceRef.current?.navigateTo(rwPath);\n }, [subPath]);\n\n if (error) {\n return <ErrorPanel error={error} />;\n }\n\n return <div ref={ref} className=\"rw-root\" style={{ height: \"100vh\" }} />;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RwDocsViewer.esm.js","sources":["../../src/components/RwDocsViewer.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback } from \"react\";\nimport { useApi } from \"@backstage/core-plugin-api\";\nimport { ErrorPanel } from \"@backstage/core-components\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport { useLocation, useNavigate, useParams } from \"react-router-dom\";\nimport { rwApiRef } from \"../api/RwClient\";\nimport { useSectionRefResolver } from \"./useSectionRefResolver\";\nimport { mountRw } from \"@rwdocs/viewer\";\nimport type { RwInstance } from \"@rwdocs/viewer\";\nimport \"@rwdocs/viewer/embed.css\";\n\ninterface RwDocsViewerProps {\n apiBaseUrl: string;\n sectionRef: string;\n sourceEntityRef: string;\n}\n\nexport function RwDocsViewer({ apiBaseUrl, sectionRef, sourceEntityRef }: RwDocsViewerProps) {\n const ref = useRef<HTMLDivElement>(null);\n const rwApi = useApi(rwApiRef);\n const theme = useTheme();\n const [error, setError] = useState<Error | null>(null);\n const catalogResolver = useSectionRefResolver(sourceEntityRef);\n\n const location = useLocation();\n const navigate = useNavigate();\n const navigateRef = useRef(navigate);\n navigateRef.current = navigate;\n const { \"*\": subPath = \"\" } = useParams();\n\n const basePath = subPath ? location.pathname.slice(0, -(subPath.length + 1)) : location.pathname;\n const basePathRef = useRef(basePath);\n basePathRef.current = basePath;\n\n const resolveSectionRefs = useCallback(\n async (refs: string[]): Promise<Record<string, string>> => {\n const otherRefs = refs.filter((r) => r !== sectionRef);\n const result = otherRefs.length > 0 ? await catalogResolver(otherRefs) : {};\n if (refs.includes(sectionRef)) {\n result[sectionRef] = basePathRef.current;\n }\n return result;\n },\n [catalogResolver, sectionRef],\n );\n\n const instanceRef = useRef<RwInstance | null>(null);\n const prevSubPathRef = useRef(subPath);\n const rwNavigatingRef = useRef(false);\n\n useEffect(() => {\n if (!ref.current) {\n return undefined;\n }\n\n try {\n let initialPath = \"/\";\n if (subPath) {\n initialPath = `/${subPath}`;\n }\n if (location.hash) {\n initialPath += location.hash;\n }\n\n instanceRef.current = mountRw(ref.current, {\n apiBaseUrl,\n initialPath,\n sectionRef,\n fetchFn: rwApi.getFetch(),\n colorScheme: theme.palette.type,\n resolveSectionRefs,\n onNavigate: (href: string) => {\n if (window.location.pathname !== href) {\n rwNavigatingRef.current = true;\n navigateRef.current(href, { replace: false });\n }\n },\n });\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n\n return () => {\n instanceRef.current?.destroy();\n instanceRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [apiBaseUrl, sectionRef]);\n\n useEffect(() => {\n instanceRef.current?.setColorScheme(theme.palette.type);\n }, [theme.palette.type]);\n\n useEffect(() => {\n if (subPath === prevSubPathRef.current) return;\n prevSubPathRef.current = subPath;\n\n if (rwNavigatingRef.current) {\n rwNavigatingRef.current = false;\n return;\n }\n\n const rwPath = subPath ? `/${subPath}` : \"/\";\n instanceRef.current?.navigateTo(rwPath);\n }, [subPath]);\n\n if (error) {\n return <ErrorPanel error={error} />;\n }\n\n return <div ref={ref} className=\"rw-root\" />;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useState, useMemo, useEffect } from 'react';
3
+ import { useApi } from '@backstage/core-plugin-api';
4
+ import { useEntity } from '@backstage/plugin-catalog-react';
5
+ import { getCompoundEntityRef } from '@backstage/catalog-model';
6
+ import { ErrorPanel, Progress } from '@backstage/core-components';
7
+ import { rwApiRef } from '../api/RwClient.esm.js';
8
+ import { toEntityPath } from './entityPath.esm.js';
9
+ import { ANNOTATION_KEY } from './constants.esm.js';
10
+ import { parseAnnotation } from './parseAnnotation.esm.js';
11
+ import { RwDocsViewer } from './RwDocsViewer.esm.js';
12
+
13
+ function RwEntityDocsViewer() {
14
+ const { entity } = useEntity();
15
+ const rwApi = useApi(rwApiRef);
16
+ const [apiBaseUrl, setApiBaseUrl] = useState(null);
17
+ const [fetchError, setFetchError] = useState(null);
18
+ const annotationValue = entity.metadata.annotations?.[ANNOTATION_KEY];
19
+ const selfEntityRef = useMemo(() => toEntityPath(getCompoundEntityRef(entity)), [entity]);
20
+ const parsed = parseAnnotation(annotationValue, selfEntityRef);
21
+ useEffect(() => {
22
+ if (!parsed) return void 0;
23
+ let cancelled = false;
24
+ rwApi.getSiteBaseUrl(parsed.entityPath).then((url) => {
25
+ if (!cancelled) setApiBaseUrl(url);
26
+ }).catch((err) => {
27
+ if (!cancelled) setFetchError(err);
28
+ });
29
+ return () => {
30
+ cancelled = true;
31
+ };
32
+ }, [rwApi, parsed?.entityPath]);
33
+ if (!parsed) {
34
+ return /* @__PURE__ */ jsx(ErrorPanel, { error: new Error(`Entity is missing the "${ANNOTATION_KEY}" annotation`) });
35
+ }
36
+ if (fetchError) {
37
+ return /* @__PURE__ */ jsx(ErrorPanel, { error: fetchError });
38
+ }
39
+ if (!apiBaseUrl) {
40
+ return /* @__PURE__ */ jsx(Progress, {});
41
+ }
42
+ const sectionRef = parsed.sectionRef ?? selfEntityRef;
43
+ return /* @__PURE__ */ jsx(
44
+ RwDocsViewer,
45
+ {
46
+ apiBaseUrl,
47
+ sectionRef,
48
+ sourceEntityRef: parsed.entityRef
49
+ }
50
+ );
51
+ }
52
+
53
+ export { RwEntityDocsViewer };
54
+ //# sourceMappingURL=RwEntityDocsViewer.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RwEntityDocsViewer.esm.js","sources":["../../src/components/RwEntityDocsViewer.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport { useApi } from \"@backstage/core-plugin-api\";\nimport { useEntity } from \"@backstage/plugin-catalog-react\";\nimport { getCompoundEntityRef } from \"@backstage/catalog-model\";\nimport { ErrorPanel, Progress } from \"@backstage/core-components\";\nimport { rwApiRef } from \"../api/RwClient\";\nimport { toEntityPath } from \"./entityPath\";\nimport { ANNOTATION_KEY } from \"./constants\";\nimport { parseAnnotation } from \"./parseAnnotation\";\nimport { RwDocsViewer } from \"./RwDocsViewer\";\n\nexport function RwEntityDocsViewer() {\n const { entity } = useEntity();\n const rwApi = useApi(rwApiRef);\n const [apiBaseUrl, setApiBaseUrl] = useState<string | null>(null);\n const [fetchError, setFetchError] = useState<Error | null>(null);\n\n const annotationValue = entity.metadata.annotations?.[ANNOTATION_KEY];\n const selfEntityRef = useMemo(() => toEntityPath(getCompoundEntityRef(entity)), [entity]);\n const parsed = parseAnnotation(annotationValue, selfEntityRef);\n\n useEffect(() => {\n if (!parsed) return undefined;\n\n let cancelled = false;\n rwApi\n .getSiteBaseUrl(parsed.entityPath)\n .then((url) => {\n if (!cancelled) setApiBaseUrl(url);\n })\n .catch((err) => {\n if (!cancelled) setFetchError(err);\n });\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- parsed is derived from annotationValue+selfEntityRef; using entityPath avoids object-identity churn\n }, [rwApi, parsed?.entityPath]);\n\n if (!parsed) {\n return <ErrorPanel error={new Error(`Entity is missing the \"${ANNOTATION_KEY}\" annotation`)} />;\n }\n\n if (fetchError) {\n return <ErrorPanel error={fetchError} />;\n }\n\n if (!apiBaseUrl) {\n return <Progress />;\n }\n\n const sectionRef = parsed.sectionRef ?? selfEntityRef;\n return (\n <RwDocsViewer\n apiBaseUrl={apiBaseUrl}\n sectionRef={sectionRef}\n sourceEntityRef={parsed.entityRef}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAWO,SAAS,kBAAA,GAAqB;AACnC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,KAAA,GAAQ,OAAO,QAAQ,CAAA;AAC7B,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAwB,IAAI,CAAA;AAChE,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAuB,IAAI,CAAA;AAE/D,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,cAAc,CAAA;AACpE,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,MAAM,YAAA,CAAa,oBAAA,CAAqB,MAAM,CAAC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AACxF,EAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,eAAA,EAAiB,aAAa,CAAA;AAE7D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAQ,OAAO,MAAA;AAEpB,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,KAAA,CACG,eAAe,MAAA,CAAO,UAAU,CAAA,CAChC,IAAA,CAAK,CAAC,GAAA,KAAQ;AACb,MAAA,IAAI,CAAC,SAAA,EAAW,aAAA,CAAc,GAAG,CAAA;AAAA,IACnC,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,IAAI,CAAC,SAAA,EAAW,aAAA,CAAc,GAAG,CAAA;AAAA,IACnC,CAAC,CAAA;AACH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EAEF,CAAA,EAAG,CAAC,KAAA,EAAO,MAAA,EAAQ,UAAU,CAAC,CAAA;AAE9B,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,uBAAO,GAAA,CAAC,cAAW,KAAA,EAAO,IAAI,MAAM,CAAA,uBAAA,EAA0B,cAAc,cAAc,CAAA,EAAG,CAAA;AAAA,EAC/F;AAEA,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,uBAAO,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAO,UAAA,EAAY,CAAA;AAAA,EACxC;AAEA,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,2BAAQ,QAAA,EAAA,EAAS,CAAA;AAAA,EACnB;AAEA,EAAA,MAAM,UAAA,GAAa,OAAO,UAAA,IAAc,aAAA;AACxC,EAAA,uBACE,GAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,UAAA;AAAA,MACA,UAAA;AAAA,MACA,iBAAiB,MAAA,CAAO;AAAA;AAAA,GAC1B;AAEJ;;;;"}
@@ -0,0 +1,5 @@
1
+ const ANNOTATION_KEY = "rwdocs.org/ref";
2
+ const ROOT_SECTION_REF = "section:default/root";
3
+
4
+ export { ANNOTATION_KEY, ROOT_SECTION_REF };
5
+ //# sourceMappingURL=constants.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.esm.js","sources":["../../src/components/constants.ts"],"sourcesContent":["export const ANNOTATION_KEY = \"rwdocs.org/ref\";\nexport const ROOT_SECTION_REF = \"section:default/root\";\n"],"names":[],"mappings":"AAAO,MAAM,cAAA,GAAiB;AACvB,MAAM,gBAAA,GAAmB;;;;"}
@@ -0,0 +1,10 @@
1
+ import { parseEntityRef } from '@backstage/catalog-model';
2
+
3
+ function toEntityPath(ref) {
4
+ const parsed = typeof ref === "string" ? parseEntityRef(ref) : ref;
5
+ const ns = parsed.namespace ?? "default";
6
+ return `${ns}/${parsed.kind}/${parsed.name}`.toLocaleLowerCase("en-US");
7
+ }
8
+
9
+ export { toEntityPath };
10
+ //# sourceMappingURL=entityPath.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityPath.esm.js","sources":["../../src/components/entityPath.ts"],"sourcesContent":["import { parseEntityRef } from \"@backstage/catalog-model\";\n\n/**\n * Converts an entity ref (e.g. \"component:default/arch\") or a compound ref\n * object to the slash-delimited, lowercased path used in API URLs\n * (e.g. \"default/component/arch\").\n *\n * Uses namespace/kind/name ordering to match Backstage catalog URL convention.\n *\n * NOTE: The backend plugin has a similar utility at\n * plugins/rw-backend/src/entityPath.ts — keep in sync if changing logic.\n */\nexport function toEntityPath(\n ref: string | { kind: string; namespace?: string; name: string },\n): string {\n const parsed = typeof ref === \"string\" ? parseEntityRef(ref) : ref;\n const ns = parsed.namespace ?? \"default\";\n return `${ns}/${parsed.kind}/${parsed.name}`.toLocaleLowerCase(\"en-US\");\n}\n"],"names":[],"mappings":";;AAYO,SAAS,aACd,GAAA,EACQ;AACR,EAAA,MAAM,SAAS,OAAO,GAAA,KAAQ,QAAA,GAAW,cAAA,CAAe,GAAG,CAAA,GAAI,GAAA;AAC/D,EAAA,MAAM,EAAA,GAAK,OAAO,SAAA,IAAa,SAAA;AAC/B,EAAA,OAAO,CAAA,EAAG,EAAE,CAAA,CAAA,EAAI,MAAA,CAAO,IAAI,IAAI,MAAA,CAAO,IAAI,CAAA,CAAA,CAAG,iBAAA,CAAkB,OAAO,CAAA;AACxE;;;;"}
@@ -0,0 +1,35 @@
1
+ import { stringifyEntityRef, parseEntityRef } from '@backstage/catalog-model';
2
+ import { toEntityPath } from './entityPath.esm.js';
3
+
4
+ function parseAnnotation(value, selfEntityRef) {
5
+ if (!value) return void 0;
6
+ const hashIndex = value.indexOf("#");
7
+ let entity;
8
+ let sectionRef;
9
+ if (hashIndex === -1) {
10
+ entity = value;
11
+ sectionRef = void 0;
12
+ } else {
13
+ entity = value.slice(0, hashIndex);
14
+ sectionRef = value.slice(hashIndex + 1) || void 0;
15
+ }
16
+ if (entity === ".") {
17
+ return { entityPath: selfEntityRef, entityRef: fromEntityPath(selfEntityRef), sectionRef };
18
+ }
19
+ try {
20
+ return {
21
+ entityPath: toEntityPath(entity),
22
+ entityRef: stringifyEntityRef(parseEntityRef(entity)),
23
+ sectionRef
24
+ };
25
+ } catch {
26
+ return void 0;
27
+ }
28
+ }
29
+ function fromEntityPath(path) {
30
+ const [namespace, kind, name] = path.split("/");
31
+ return stringifyEntityRef({ kind, namespace, name });
32
+ }
33
+
34
+ export { parseAnnotation };
35
+ //# sourceMappingURL=parseAnnotation.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseAnnotation.esm.js","sources":["../../src/components/parseAnnotation.ts"],"sourcesContent":["import { parseEntityRef, stringifyEntityRef } from \"@backstage/catalog-model\";\nimport { toEntityPath } from \"./entityPath\";\n\nexport interface ParsedAnnotation {\n /** Slash-delimited path for API URLs (e.g. \"default/component/arch\"). */\n entityPath: string;\n /** Standard Backstage entity ref (e.g. \"component:default/arch\"). */\n entityRef: string;\n sectionRef: string | undefined;\n}\n\nexport function parseAnnotation(\n value: string | undefined,\n selfEntityRef: string,\n): ParsedAnnotation | undefined {\n if (!value) return undefined;\n\n const hashIndex = value.indexOf(\"#\");\n let entity: string;\n let sectionRef: string | undefined;\n\n if (hashIndex === -1) {\n entity = value;\n sectionRef = undefined;\n } else {\n entity = value.slice(0, hashIndex);\n sectionRef = value.slice(hashIndex + 1) || undefined;\n }\n\n if (entity === \".\") {\n return { entityPath: selfEntityRef, entityRef: fromEntityPath(selfEntityRef), sectionRef };\n }\n\n try {\n return {\n entityPath: toEntityPath(entity),\n entityRef: stringifyEntityRef(parseEntityRef(entity)),\n sectionRef,\n };\n } catch {\n return undefined;\n }\n}\n\n/** Convert slash-delimited path (namespace/kind/name) back to colon-format entity ref. */\nfunction fromEntityPath(path: string): string {\n const [namespace, kind, name] = path.split(\"/\");\n return stringifyEntityRef({ kind, namespace, name });\n}\n"],"names":[],"mappings":";;;AAWO,SAAS,eAAA,CACd,OACA,aAAA,EAC8B;AAC9B,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AAEnB,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AACnC,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,UAAA;AAEJ,EAAA,IAAI,cAAc,EAAA,EAAI;AACpB,IAAA,MAAA,GAAS,KAAA;AACT,IAAA,UAAA,GAAa,MAAA;AAAA,EACf,CAAA,MAAO;AACL,IAAA,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA;AACjC,IAAA,UAAA,GAAa,KAAA,CAAM,KAAA,CAAM,SAAA,GAAY,CAAC,CAAA,IAAK,MAAA;AAAA,EAC7C;AAEA,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,OAAO,EAAE,UAAA,EAAY,aAAA,EAAe,WAAW,cAAA,CAAe,aAAa,GAAG,UAAA,EAAW;AAAA,EAC3F;AAEA,EAAA,IAAI;AACF,IAAA,OAAO;AAAA,MACL,UAAA,EAAY,aAAa,MAAM,CAAA;AAAA,MAC/B,SAAA,EAAW,kBAAA,CAAmB,cAAA,CAAe,MAAM,CAAC,CAAA;AAAA,MACpD;AAAA,KACF;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAGA,SAAS,eAAe,IAAA,EAAsB;AAC5C,EAAA,MAAM,CAAC,SAAA,EAAW,IAAA,EAAM,IAAI,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AAC9C,EAAA,OAAO,kBAAA,CAAmB,EAAE,IAAA,EAAM,SAAA,EAAW,MAAM,CAAA;AACrD;;;;"}
@@ -0,0 +1,56 @@
1
+ import { useRef, useCallback } from 'react';
2
+ import { useApi, useRouteRef } from '@backstage/core-plugin-api';
3
+ import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
4
+ import { parseEntityRef } from '@backstage/catalog-model';
5
+ import { ROOT_SECTION_REF, ANNOTATION_KEY } from './constants.esm.js';
6
+
7
+ const DOCS_PATH_SUFFIX = "/docs";
8
+ function useSectionRefResolver(sourceEntityRef) {
9
+ const catalogApi = useApi(catalogApiRef);
10
+ const entityRoute = useRouteRef(entityRouteRef);
11
+ const cache = useRef(/* @__PURE__ */ new Map());
12
+ return useCallback(
13
+ async (refs) => {
14
+ const unknown = refs.filter((r) => !cache.current.has(r));
15
+ const catalogRefs = [];
16
+ for (const ref of unknown) {
17
+ if (ref === ROOT_SECTION_REF) {
18
+ const { kind, namespace, name } = parseEntityRef(sourceEntityRef);
19
+ const routeUrl = entityRoute({ kind, namespace, name }) + DOCS_PATH_SUFFIX;
20
+ cache.current.set(ref, routeUrl);
21
+ } else {
22
+ catalogRefs.push(ref);
23
+ }
24
+ }
25
+ if (catalogRefs.length > 0) {
26
+ try {
27
+ const { items } = await catalogApi.getEntitiesByRefs({ entityRefs: catalogRefs });
28
+ for (let i = 0; i < catalogRefs.length; i++) {
29
+ const ref = catalogRefs[i];
30
+ const entity = items[i];
31
+ if (entity?.metadata.annotations?.[ANNOTATION_KEY]) {
32
+ const { kind, namespace, name } = parseEntityRef(ref);
33
+ const routeUrl = entityRoute({ kind, namespace, name }) + DOCS_PATH_SUFFIX;
34
+ cache.current.set(ref, routeUrl);
35
+ } else {
36
+ cache.current.set(ref, null);
37
+ }
38
+ }
39
+ } catch {
40
+ }
41
+ }
42
+ const result = {};
43
+ for (const ref of refs) {
44
+ const url = cache.current.get(ref);
45
+ if (url !== null && url !== void 0) {
46
+ result[ref] = url;
47
+ }
48
+ }
49
+ return result;
50
+ },
51
+ [catalogApi, entityRoute, sourceEntityRef]
52
+ );
53
+ }
54
+
55
+ export { useSectionRefResolver };
56
+ //# sourceMappingURL=useSectionRefResolver.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSectionRefResolver.esm.js","sources":["../../src/components/useSectionRefResolver.ts"],"sourcesContent":["import { useCallback, useRef } from \"react\";\nimport { useApi, useRouteRef } from \"@backstage/core-plugin-api\";\nimport { catalogApiRef, entityRouteRef } from \"@backstage/plugin-catalog-react\";\nimport { parseEntityRef } from \"@backstage/catalog-model\";\nimport { ANNOTATION_KEY, ROOT_SECTION_REF } from \"./constants\";\n\nconst DOCS_PATH_SUFFIX = \"/docs\";\n\nexport function useSectionRefResolver(\n sourceEntityRef: string,\n): (refs: string[]) => Promise<Record<string, string>> {\n const catalogApi = useApi(catalogApiRef);\n const entityRoute = useRouteRef(entityRouteRef);\n const cache = useRef(new Map<string, string | null>());\n\n return useCallback(\n async (refs: string[]): Promise<Record<string, string>> => {\n const unknown = refs.filter((r) => !cache.current.has(r));\n\n const catalogRefs: string[] = [];\n for (const ref of unknown) {\n if (ref === ROOT_SECTION_REF) {\n const { kind, namespace, name } = parseEntityRef(sourceEntityRef);\n const routeUrl = entityRoute({ kind, namespace, name }) + DOCS_PATH_SUFFIX;\n cache.current.set(ref, routeUrl);\n } else {\n catalogRefs.push(ref);\n }\n }\n\n if (catalogRefs.length > 0) {\n try {\n const { items } = await catalogApi.getEntitiesByRefs({ entityRefs: catalogRefs });\n for (let i = 0; i < catalogRefs.length; i++) {\n const ref = catalogRefs[i];\n const entity = items[i];\n if (entity?.metadata.annotations?.[ANNOTATION_KEY]) {\n const { kind, namespace, name } = parseEntityRef(ref);\n const routeUrl = entityRoute({ kind, namespace, name }) + DOCS_PATH_SUFFIX;\n cache.current.set(ref, routeUrl);\n } else {\n cache.current.set(ref, null);\n }\n }\n } catch {\n // On failure, leave uncached so they can be retried\n }\n }\n\n const result: Record<string, string> = {};\n for (const ref of refs) {\n const url = cache.current.get(ref);\n if (url !== null && url !== undefined) {\n result[ref] = url;\n }\n }\n return result;\n },\n [catalogApi, entityRoute, sourceEntityRef],\n );\n}\n"],"names":[],"mappings":";;;;;;AAMA,MAAM,gBAAA,GAAmB,OAAA;AAElB,SAAS,sBACd,eAAA,EACqD;AACrD,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,WAAA,GAAc,YAAY,cAAc,CAAA;AAC9C,EAAA,MAAM,KAAA,GAAQ,MAAA,iBAAO,IAAI,GAAA,EAA4B,CAAA;AAErD,EAAA,OAAO,WAAA;AAAA,IACL,OAAO,IAAA,KAAoD;AACzD,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAC,CAAA;AAExD,MAAA,MAAM,cAAwB,EAAC;AAC/B,MAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,QAAA,IAAI,QAAQ,gBAAA,EAAkB;AAC5B,UAAA,MAAM,EAAE,IAAA,EAAM,SAAA,EAAW,IAAA,EAAK,GAAI,eAAe,eAAe,CAAA;AAChE,UAAA,MAAM,WAAW,WAAA,CAAY,EAAE,MAAM,SAAA,EAAW,IAAA,EAAM,CAAA,GAAI,gBAAA;AAC1D,UAAA,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,QAAQ,CAAA;AAAA,QACjC,CAAA,MAAO;AACL,UAAA,WAAA,CAAY,KAAK,GAAG,CAAA;AAAA,QACtB;AAAA,MACF;AAEA,MAAA,IAAI,WAAA,CAAY,SAAS,CAAA,EAAG;AAC1B,QAAA,IAAI;AACF,UAAA,MAAM,EAAE,OAAM,GAAI,MAAM,WAAW,iBAAA,CAAkB,EAAE,UAAA,EAAY,WAAA,EAAa,CAAA;AAChF,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,WAAA,CAAY,QAAQ,CAAA,EAAA,EAAK;AAC3C,YAAA,MAAM,GAAA,GAAM,YAAY,CAAC,CAAA;AACzB,YAAA,MAAM,MAAA,GAAS,MAAM,CAAC,CAAA;AACtB,YAAA,IAAI,MAAA,EAAQ,QAAA,CAAS,WAAA,GAAc,cAAc,CAAA,EAAG;AAClD,cAAA,MAAM,EAAE,IAAA,EAAM,SAAA,EAAW,IAAA,EAAK,GAAI,eAAe,GAAG,CAAA;AACpD,cAAA,MAAM,WAAW,WAAA,CAAY,EAAE,MAAM,SAAA,EAAW,IAAA,EAAM,CAAA,GAAI,gBAAA;AAC1D,cAAA,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,QAAQ,CAAA;AAAA,YACjC,CAAA,MAAO;AACL,cAAA,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA;AAAA,YAC7B;AAAA,UACF;AAAA,QACF,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAEA,MAAA,MAAM,SAAiC,EAAC;AACxC,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AACjC,QAAA,IAAI,GAAA,KAAQ,IAAA,IAAQ,GAAA,KAAQ,MAAA,EAAW;AACrC,UAAA,MAAA,CAAO,GAAG,CAAA,GAAI,GAAA;AAAA,QAChB;AAAA,MACF;AACA,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IACA,CAAC,UAAA,EAAY,WAAA,EAAa,eAAe;AAAA,GAC3C;AACF;;;;"}
@@ -0,0 +1,22 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import DocsIcon from '@material-ui/icons/Description';
3
+ import { useRouteRef } from '@backstage/core-plugin-api';
4
+ import { useEntity, entityRouteRef } from '@backstage/plugin-catalog-react';
5
+
6
+ const docsIcon = /* @__PURE__ */ jsx(DocsIcon, {});
7
+ function useRwDocsIconLinkProps() {
8
+ const { entity } = useEntity();
9
+ const entityRoute = useRouteRef(entityRouteRef);
10
+ const kind = entity.kind.toLocaleLowerCase("en-US");
11
+ const namespace = entity.metadata.namespace?.toLocaleLowerCase("en-US") ?? "default";
12
+ const name = entity.metadata.name;
13
+ return {
14
+ label: "View Docs",
15
+ disabled: !entityRoute,
16
+ icon: docsIcon,
17
+ href: entityRoute ? `${entityRoute({ kind, namespace, name })}/docs` : ""
18
+ };
19
+ }
20
+
21
+ export { useRwDocsIconLinkProps };
22
+ //# sourceMappingURL=useRwDocsIconLinkProps.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRwDocsIconLinkProps.esm.js","sources":["../../src/hooks/useRwDocsIconLinkProps.tsx"],"sourcesContent":["import DocsIcon from \"@material-ui/icons/Description\";\nimport { useRouteRef } from \"@backstage/core-plugin-api\";\nimport { entityRouteRef, useEntity } from \"@backstage/plugin-catalog-react\";\n\nconst docsIcon = <DocsIcon />;\n\nexport function useRwDocsIconLinkProps() {\n const { entity } = useEntity();\n const entityRoute = useRouteRef(entityRouteRef);\n\n const kind = entity.kind.toLocaleLowerCase(\"en-US\");\n const namespace = entity.metadata.namespace?.toLocaleLowerCase(\"en-US\") ?? \"default\";\n const name = entity.metadata.name;\n\n return {\n label: \"View Docs\",\n disabled: !entityRoute,\n icon: docsIcon,\n href: entityRoute ? `${entityRoute({ kind, namespace, name })}/docs` : \"\",\n };\n}\n"],"names":[],"mappings":";;;;;AAIA,MAAM,QAAA,uBAAY,QAAA,EAAA,EAAS,CAAA;AAEpB,SAAS,sBAAA,GAAyB;AACvC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,WAAA,GAAc,YAAY,cAAc,CAAA;AAE9C,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,iBAAA,CAAkB,OAAO,CAAA;AAClD,EAAA,MAAM,YAAY,MAAA,CAAO,QAAA,CAAS,SAAA,EAAW,iBAAA,CAAkB,OAAO,CAAA,IAAK,SAAA;AAC3E,EAAA,MAAM,IAAA,GAAO,OAAO,QAAA,CAAS,IAAA;AAE7B,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,WAAA;AAAA,IACP,UAAU,CAAC,WAAA;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,IAAA,EAAM,WAAA,GAAc,CAAA,EAAG,WAAA,CAAY,EAAE,MAAM,SAAA,EAAW,IAAA,EAAM,CAAC,CAAA,KAAA,CAAA,GAAU;AAAA,GACzE;AACF;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,11 @@
1
+ import * as _backstage_core_components from '@backstage/core-components';
1
2
  import * as _backstage_catalog_model from '@backstage/catalog-model';
2
3
  import * as react from 'react';
3
4
  import * as _backstage_filter_predicates from '@backstage/filter-predicates';
4
5
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
5
6
  import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
6
7
 
7
- declare const rwPlugin: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
8
- root: _backstage_frontend_plugin_api.RouteRef<undefined>;
9
- }, {}, {
8
+ declare const rwPlugin: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{}, {}, {
10
9
  "api:rw": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
11
10
  kind: "api";
12
11
  name: undefined;
@@ -58,51 +57,35 @@ declare const rwPlugin: _backstage_frontend_plugin_api.OverridableFrontendPlugin
58
57
  filter?: string | _backstage_filter_predicates.FilterPredicate | ((entity: _backstage_catalog_model.Entity) => boolean);
59
58
  };
60
59
  }>;
61
- "page:rw": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
62
- kind: "page";
63
- name: undefined;
60
+ "entity-icon-link:rw/view-docs": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
61
+ kind: "entity-icon-link";
62
+ name: "view-docs";
64
63
  config: {
65
- path: string | undefined;
64
+ label: string | undefined;
66
65
  title: string | undefined;
66
+ filter: _backstage_filter_predicates.FilterPredicate | undefined;
67
67
  };
68
68
  configInput: {
69
+ filter?: _backstage_filter_predicates.FilterPredicate | undefined;
70
+ label?: string | undefined;
69
71
  title?: string | undefined;
70
- path?: string | undefined;
71
72
  };
72
- output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
73
- optional: true;
74
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
73
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<(entity: _backstage_catalog_model.Entity) => boolean, "catalog.entity-filter-function", {
75
74
  optional: true;
76
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
75
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
77
76
  optional: true;
78
- }>;
79
- inputs: {
80
- pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.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", {
81
- optional: true;
82
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
83
- optional: true;
84
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
85
- optional: true;
86
- }>, {
87
- singleton: false;
88
- optional: false;
89
- internal: false;
90
- }>;
91
- };
77
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<() => _backstage_core_components.IconLinkVerticalProps, "entity-icon-link-props", {}>;
78
+ inputs: {};
92
79
  params: {
93
- defaultPath?: [Error: `Use the 'path' param instead`];
94
- path: string;
95
- title?: string;
96
- icon?: _backstage_frontend_plugin_api.IconElement;
97
- loader?: () => Promise<react.JSX.Element>;
98
- routeRef?: _backstage_frontend_plugin_api.RouteRef;
99
- noHeader?: boolean;
80
+ useProps: () => Omit<_backstage_core_components.IconLinkVerticalProps, "color">;
81
+ filter?: _backstage_filter_predicates.FilterPredicate | ((entity: _backstage_catalog_model.Entity) => boolean);
100
82
  };
101
83
  }>;
102
84
  }>;
103
85
 
104
86
  interface RwApi {
105
87
  getBaseUrl(): Promise<string>;
88
+ getSiteBaseUrl(entityRef: string): Promise<string>;
106
89
  getFetch(): typeof fetch;
107
90
  }
108
91
  declare const rwApiRef: _backstage_frontend_plugin_api.ApiRef<RwApi>;
@@ -114,6 +97,7 @@ declare class RwClient implements RwApi {
114
97
  fetchApi: FetchApi;
115
98
  });
116
99
  getBaseUrl(): Promise<string>;
100
+ getSiteBaseUrl(entityRef: string): Promise<string>;
117
101
  getFetch(): typeof fetch;
118
102
  }
119
103
 
@@ -1,10 +1,11 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { createRouteRef, ApiBlueprint, PageBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
2
+ import { ApiBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
3
3
  import { createApiFactory, fetchApiRef, discoveryApiRef } from '@backstage/core-plugin-api';
4
- import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
4
+ import { EntityContentBlueprint, EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha';
5
5
  import { RwClient, rwApiRef } from './api/RwClient.esm.js';
6
+ import { ANNOTATION_KEY } from './components/constants.esm.js';
7
+ import { useRwDocsIconLinkProps } from './hooks/useRwDocsIconLinkProps.esm.js';
6
8
 
7
- const rootRouteRef = createRouteRef();
8
9
  const rwApi = ApiBlueprint.make({
9
10
  params: (defineParams) => defineParams(
10
11
  createApiFactory({
@@ -14,25 +15,25 @@ const rwApi = ApiBlueprint.make({
14
15
  })
15
16
  )
16
17
  });
17
- const rwPage = PageBlueprint.make({
18
- params: {
19
- path: "/docs",
20
- routeRef: rootRouteRef,
21
- loader: () => import('./components/RwDocsViewer.esm.js').then((m) => /* @__PURE__ */ jsx(m.RwDocsViewer, {}))
22
- }
23
- });
24
18
  const rwEntityContent = EntityContentBlueprint.make({
25
19
  params: {
26
20
  path: "docs",
27
- title: "Documentation",
21
+ title: "Docs",
28
22
  group: "documentation",
29
- loader: () => import('./components/RwDocsViewer.esm.js').then((m) => /* @__PURE__ */ jsx(m.RwDocsViewer, {}))
23
+ filter: (entity) => Boolean(entity.metadata.annotations?.[ANNOTATION_KEY]),
24
+ loader: () => import('./components/RwEntityDocsViewer.esm.js').then((m) => /* @__PURE__ */ jsx(m.RwEntityDocsViewer, {}))
25
+ }
26
+ });
27
+ const rwEntityIconLink = EntityIconLinkBlueprint.make({
28
+ name: "view-docs",
29
+ params: {
30
+ filter: (entity) => Boolean(entity.metadata.annotations?.[ANNOTATION_KEY]),
31
+ useProps: useRwDocsIconLinkProps
30
32
  }
31
33
  });
32
34
  const rwPlugin = createFrontendPlugin({
33
35
  pluginId: "rw",
34
- extensions: [rwApi, rwPage, rwEntityContent],
35
- routes: { root: rootRouteRef }
36
+ extensions: [rwApi, rwEntityContent, rwEntityIconLink]
36
37
  });
37
38
 
38
39
  export { rwPlugin };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import {\n createFrontendPlugin,\n PageBlueprint,\n createRouteRef,\n ApiBlueprint,\n} from \"@backstage/frontend-plugin-api\";\nimport { createApiFactory, discoveryApiRef, fetchApiRef } from \"@backstage/core-plugin-api\";\nimport { EntityContentBlueprint } from \"@backstage/plugin-catalog-react/alpha\";\nimport { rwApiRef, RwClient } from \"./api/RwClient\";\n\nconst rootRouteRef = createRouteRef();\n\nconst rwApi = ApiBlueprint.make({\n params: (defineParams) =>\n defineParams(\n createApiFactory({\n api: rwApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) => new RwClient({ discoveryApi, fetchApi }),\n }),\n ),\n});\n\nconst rwPage = PageBlueprint.make({\n params: {\n path: \"/docs\",\n routeRef: rootRouteRef,\n loader: () => import(\"./components/RwDocsViewer\").then((m) => <m.RwDocsViewer />),\n },\n});\n\nconst rwEntityContent = EntityContentBlueprint.make({\n params: {\n path: \"docs\",\n title: \"Documentation\",\n group: \"documentation\",\n loader: () => import(\"./components/RwDocsViewer\").then((m) => <m.RwDocsViewer />),\n },\n});\n\nexport const rwPlugin = createFrontendPlugin({\n pluginId: \"rw\",\n extensions: [rwApi, rwPage, rwEntityContent],\n routes: { root: rootRouteRef },\n});\n\nexport default rwPlugin;\n"],"names":[],"mappings":";;;;;;AAUA,MAAM,eAAe,cAAA,EAAe;AAEpC,MAAM,KAAA,GAAQ,aAAa,IAAA,CAAK;AAAA,EAC9B,MAAA,EAAQ,CAAC,YAAA,KACP,YAAA;AAAA,IACE,gBAAA,CAAiB;AAAA,MACf,GAAA,EAAK,QAAA;AAAA,MACL,IAAA,EAAM,EAAE,YAAA,EAAc,eAAA,EAAiB,UAAU,WAAA,EAAY;AAAA,MAC7D,OAAA,EAAS,CAAC,EAAE,YAAA,EAAc,QAAA,EAAS,KAAM,IAAI,QAAA,CAAS,EAAE,YAAA,EAAc,QAAA,EAAU;AAAA,KACjF;AAAA;AAEP,CAAC,CAAA;AAED,MAAM,MAAA,GAAS,cAAc,IAAA,CAAK;AAAA,EAChC,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,OAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,MAAA,EAAQ,MAAM,OAAO,kCAA2B,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,qBAAM,GAAA,CAAC,CAAA,CAAE,YAAA,EAAF,EAAe,CAAE;AAAA;AAEpF,CAAC,CAAA;AAED,MAAM,eAAA,GAAkB,uBAAuB,IAAA,CAAK;AAAA,EAClD,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,eAAA;AAAA,IACP,KAAA,EAAO,eAAA;AAAA,IACP,MAAA,EAAQ,MAAM,OAAO,kCAA2B,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,qBAAM,GAAA,CAAC,CAAA,CAAE,YAAA,EAAF,EAAe,CAAE;AAAA;AAEpF,CAAC,CAAA;AAEM,MAAM,WAAW,oBAAA,CAAqB;AAAA,EAC3C,QAAA,EAAU,IAAA;AAAA,EACV,UAAA,EAAY,CAAC,KAAA,EAAO,MAAA,EAAQ,eAAe,CAAA;AAAA,EAC3C,MAAA,EAAQ,EAAE,IAAA,EAAM,YAAA;AAClB,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import { createFrontendPlugin, ApiBlueprint } from \"@backstage/frontend-plugin-api\";\nimport { createApiFactory, discoveryApiRef, fetchApiRef } from \"@backstage/core-plugin-api\";\nimport {\n EntityContentBlueprint,\n EntityIconLinkBlueprint,\n} from \"@backstage/plugin-catalog-react/alpha\";\nimport { rwApiRef, RwClient } from \"./api/RwClient\";\nimport { ANNOTATION_KEY } from \"./components/constants\";\nimport { useRwDocsIconLinkProps } from \"./hooks/useRwDocsIconLinkProps\";\n\nconst rwApi = ApiBlueprint.make({\n params: (defineParams) =>\n defineParams(\n createApiFactory({\n api: rwApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) => new RwClient({ discoveryApi, fetchApi }),\n }),\n ),\n});\n\nconst rwEntityContent = EntityContentBlueprint.make({\n params: {\n path: \"docs\",\n title: \"Docs\",\n group: \"documentation\",\n filter: (entity) => Boolean(entity.metadata.annotations?.[ANNOTATION_KEY]),\n loader: () => import(\"./components/RwEntityDocsViewer\").then((m) => <m.RwEntityDocsViewer />),\n },\n});\n\nconst rwEntityIconLink = EntityIconLinkBlueprint.make({\n name: \"view-docs\",\n params: {\n filter: (entity) => Boolean(entity.metadata.annotations?.[ANNOTATION_KEY]),\n useProps: useRwDocsIconLinkProps,\n },\n});\n\nexport const rwPlugin = createFrontendPlugin({\n pluginId: \"rw\",\n extensions: [rwApi, rwEntityContent, rwEntityIconLink],\n});\n\nexport default rwPlugin;\n"],"names":[],"mappings":";;;;;;;;AAUA,MAAM,KAAA,GAAQ,aAAa,IAAA,CAAK;AAAA,EAC9B,MAAA,EAAQ,CAAC,YAAA,KACP,YAAA;AAAA,IACE,gBAAA,CAAiB;AAAA,MACf,GAAA,EAAK,QAAA;AAAA,MACL,IAAA,EAAM,EAAE,YAAA,EAAc,eAAA,EAAiB,UAAU,WAAA,EAAY;AAAA,MAC7D,OAAA,EAAS,CAAC,EAAE,YAAA,EAAc,QAAA,EAAS,KAAM,IAAI,QAAA,CAAS,EAAE,YAAA,EAAc,QAAA,EAAU;AAAA,KACjF;AAAA;AAEP,CAAC,CAAA;AAED,MAAM,eAAA,GAAkB,uBAAuB,IAAA,CAAK;AAAA,EAClD,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,MAAA;AAAA,IACP,KAAA,EAAO,eAAA;AAAA,IACP,MAAA,EAAQ,CAAC,MAAA,KAAW,OAAA,CAAQ,OAAO,QAAA,CAAS,WAAA,GAAc,cAAc,CAAC,CAAA;AAAA,IACzE,MAAA,EAAQ,MAAM,OAAO,wCAAiC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,qBAAM,GAAA,CAAC,CAAA,CAAE,kBAAA,EAAF,EAAqB,CAAE;AAAA;AAEhG,CAAC,CAAA;AAED,MAAM,gBAAA,GAAmB,wBAAwB,IAAA,CAAK;AAAA,EACpD,IAAA,EAAM,WAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ,CAAC,MAAA,KAAW,OAAA,CAAQ,OAAO,QAAA,CAAS,WAAA,GAAc,cAAc,CAAC,CAAA;AAAA,IACzE,QAAA,EAAU;AAAA;AAEd,CAAC,CAAA;AAEM,MAAM,WAAW,oBAAA,CAAqB;AAAA,EAC3C,QAAA,EAAU,IAAA;AAAA,EACV,UAAA,EAAY,CAAC,KAAA,EAAO,eAAA,EAAiB,gBAAgB;AACvD,CAAC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rwdocs/backstage-plugin-rw",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "license": "MIT OR Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,9 @@
35
35
  ".": "@backstage/FrontendPlugin"
36
36
  }
37
37
  },
38
+ "configSchema": "config.d.ts",
38
39
  "files": [
40
+ "config.d.ts",
39
41
  "dist",
40
42
  "LICENSE-MIT",
41
43
  "LICENSE-APACHE"
@@ -52,14 +54,16 @@
52
54
  "postpack": "backstage-cli package postpack"
53
55
  },
54
56
  "dependencies": {
55
- "@rwdocs/viewer": "^0.1.15"
57
+ "@backstage/catalog-model": "^1.7.6",
58
+ "@material-ui/icons": "^4.11.3",
59
+ "@rwdocs/viewer": "^0.1.18"
56
60
  },
57
61
  "peerDependencies": {
58
62
  "@backstage/core-components": "^0.18.0",
59
63
  "@backstage/core-plugin-api": "^1.0.0",
60
- "@backstage/frontend-plugin-api": "^0.14.0",
64
+ "@backstage/frontend-plugin-api": "^0.14.0 || ^0.15.0",
61
65
  "@backstage/plugin-catalog-react": "^2.0.0",
62
- "@mui/material": "^5.0.0",
66
+ "@material-ui/core": "^4.12.2",
63
67
  "react": "^18.0.0",
64
68
  "react-dom": "^18.0.0",
65
69
  "react-router-dom": "^6.0.0"
@@ -70,10 +74,11 @@
70
74
  }
71
75
  },
72
76
  "devDependencies": {
73
- "@backstage/cli": "^0.35.0",
77
+ "@backstage/cli": "^0.36.0",
78
+ "@backstage/config": "^1.3.6",
74
79
  "@backstage/core-components": "^0.18.0",
75
80
  "@backstage/core-plugin-api": "^1.0.0",
76
- "@backstage/frontend-plugin-api": "^0.14.0",
81
+ "@backstage/frontend-plugin-api": "^0.15.0",
77
82
  "@backstage/plugin-catalog-react": "^2.0.0",
78
83
  "@backstage/test-utils": "^1.7.0",
79
84
  "@testing-library/dom": "^10.0.0",