@rwdocs/backstage-plugin-rw 0.1.0 → 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 +1 -0
- package/dist/api/RwClient.esm.js +4 -0
- package/dist/api/RwClient.esm.js.map +1 -1
- package/dist/components/RwDocsViewer.esm.js +41 -23
- package/dist/components/RwDocsViewer.esm.js.map +1 -1
- package/dist/components/RwEntityDocsViewer.esm.js +54 -0
- package/dist/components/RwEntityDocsViewer.esm.js.map +1 -0
- package/dist/components/constants.esm.js +5 -0
- package/dist/components/constants.esm.js.map +1 -0
- package/dist/components/entityPath.esm.js +10 -0
- package/dist/components/entityPath.esm.js.map +1 -0
- package/dist/components/parseAnnotation.esm.js +35 -0
- package/dist/components/parseAnnotation.esm.js.map +1 -0
- package/dist/components/useSectionRefResolver.esm.js +56 -0
- package/dist/components/useSectionRefResolver.esm.js.map +1 -0
- package/dist/hooks/useRwDocsIconLinkProps.esm.js +22 -0
- package/dist/hooks/useRwDocsIconLinkProps.esm.js.map +1 -0
- package/dist/index.d.ts +17 -33
- package/dist/plugin.esm.js +15 -14
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +16 -6
package/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export interface Config {}
|
package/dist/api/RwClient.esm.js
CHANGED
|
@@ -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":";;
|
|
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 '@
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
53
|
+
apiBaseUrl,
|
|
35
54
|
initialPath,
|
|
36
|
-
|
|
55
|
+
sectionRef,
|
|
37
56
|
fetchFn: rwApi.getFetch(),
|
|
38
|
-
colorScheme: theme.palette.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (window.location.pathname !==
|
|
57
|
+
colorScheme: theme.palette.type,
|
|
58
|
+
resolveSectionRefs,
|
|
59
|
+
onNavigate: (href) => {
|
|
60
|
+
if (window.location.pathname !== href) {
|
|
42
61
|
rwNavigatingRef.current = true;
|
|
43
|
-
navigateRef.current(
|
|
62
|
+
navigateRef.current(href, { replace: false });
|
|
44
63
|
}
|
|
45
64
|
}
|
|
46
65
|
});
|
|
47
|
-
}
|
|
48
|
-
|
|
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
|
-
}, [
|
|
73
|
+
}, [apiBaseUrl, sectionRef]);
|
|
56
74
|
useEffect(() => {
|
|
57
|
-
instanceRef.current?.setColorScheme(theme.palette.
|
|
58
|
-
}, [theme.palette.
|
|
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"
|
|
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 \"@
|
|
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 @@
|
|
|
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
|
-
"
|
|
62
|
-
kind: "
|
|
63
|
-
name:
|
|
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
|
-
|
|
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<
|
|
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<
|
|
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
|
-
|
|
94
|
-
|
|
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
|
|
package/dist/plugin.esm.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
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: "
|
|
21
|
+
title: "Docs",
|
|
28
22
|
group: "documentation",
|
|
29
|
-
|
|
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,
|
|
35
|
-
routes: { root: rootRouteRef }
|
|
36
|
+
extensions: [rwApi, rwEntityContent, rwEntityIconLink]
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
export { rwPlugin };
|
package/dist/plugin.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import {
|
|
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,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rwdocs/backstage-plugin-rw",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "MIT OR Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/rwdocs/backstage-plugins.git",
|
|
8
|
+
"directory": "plugins/rw"
|
|
9
|
+
},
|
|
5
10
|
"main": "dist/index.cjs.js",
|
|
6
11
|
"types": "dist/index.d.ts",
|
|
7
12
|
"exports": {
|
|
@@ -30,7 +35,9 @@
|
|
|
30
35
|
".": "@backstage/FrontendPlugin"
|
|
31
36
|
}
|
|
32
37
|
},
|
|
38
|
+
"configSchema": "config.d.ts",
|
|
33
39
|
"files": [
|
|
40
|
+
"config.d.ts",
|
|
34
41
|
"dist",
|
|
35
42
|
"LICENSE-MIT",
|
|
36
43
|
"LICENSE-APACHE"
|
|
@@ -47,14 +54,16 @@
|
|
|
47
54
|
"postpack": "backstage-cli package postpack"
|
|
48
55
|
},
|
|
49
56
|
"dependencies": {
|
|
50
|
-
"@
|
|
57
|
+
"@backstage/catalog-model": "^1.7.6",
|
|
58
|
+
"@material-ui/icons": "^4.11.3",
|
|
59
|
+
"@rwdocs/viewer": "^0.1.18"
|
|
51
60
|
},
|
|
52
61
|
"peerDependencies": {
|
|
53
62
|
"@backstage/core-components": "^0.18.0",
|
|
54
63
|
"@backstage/core-plugin-api": "^1.0.0",
|
|
55
|
-
"@backstage/frontend-plugin-api": "^0.14.0",
|
|
64
|
+
"@backstage/frontend-plugin-api": "^0.14.0 || ^0.15.0",
|
|
56
65
|
"@backstage/plugin-catalog-react": "^2.0.0",
|
|
57
|
-
"@
|
|
66
|
+
"@material-ui/core": "^4.12.2",
|
|
58
67
|
"react": "^18.0.0",
|
|
59
68
|
"react-dom": "^18.0.0",
|
|
60
69
|
"react-router-dom": "^6.0.0"
|
|
@@ -65,10 +74,11 @@
|
|
|
65
74
|
}
|
|
66
75
|
},
|
|
67
76
|
"devDependencies": {
|
|
68
|
-
"@backstage/cli": "^0.
|
|
77
|
+
"@backstage/cli": "^0.36.0",
|
|
78
|
+
"@backstage/config": "^1.3.6",
|
|
69
79
|
"@backstage/core-components": "^0.18.0",
|
|
70
80
|
"@backstage/core-plugin-api": "^1.0.0",
|
|
71
|
-
"@backstage/frontend-plugin-api": "^0.
|
|
81
|
+
"@backstage/frontend-plugin-api": "^0.15.0",
|
|
72
82
|
"@backstage/plugin-catalog-react": "^2.0.0",
|
|
73
83
|
"@backstage/test-utils": "^1.7.0",
|
|
74
84
|
"@testing-library/dom": "^10.0.0",
|